feedback7: merge fb7-owner-sim (48a2d37e) — #316: 突袭 members jump to 隐匿 ground enemies too and one in range counts as 范围内有敌人 (the owner's decision of 2026-10-08)
- Golden union: coop2-ABYSS-10-boosted and coop2-NORMAL-14-standins differ from HEAD though the commit alone left them — HEAD's side had moved both, and on the new paths 隐匿 enemies meet active 突袭 members (14-standins: raidShip on; round 10 kills 26 → 27, leaks 3 → 2, team LP 39 → 40; ABYSS-10: a few 阿戈尔 layers, team LP 793 → 795); no outcome change. - Broad node --test 5575 (5574 pass, 0 fail, 1 skipped); lint 0 errors.
This commit is contained in:
commit
3969e4b012
4 files changed
+141
-29
No files matched your search
@@ -922,7 +922,7 @@ Members (5 chess, 4 in current shop pool; by tier in shop: {'2': 1, '3': 1, '5':
|
||||
|
||||
**Implementable spec**
|
||||
|
||||
- **[2 distinct]** If a 突袭 member has not attacked for 10 s, or its skill is ready, and no enemy is in its range: it is immediately redeployed (keeping SP) onto a tile next to a ground enemy; while in that redeployed state its ATK and maxHP x(1 + 0.25 + 0.01*L). A passive skill that is on counts as ready (GitHub #49: the reporter's footage shows 缄默德克萨斯 jumping within her passive's 10 s; the engine keeps a passive on for the whole deployment [ASSUMED]), and so does a deploy-timed skill while its window runs (#109 made 宴 S2, 斯卡蒂 S2, 伊内丝 S3, 缄默德克萨斯 S1–S3 and 耀骑士临光 S2 duration skills that start at each deployment; once the window has ended only the idle trigger is left); the landing tile must have the target in range (GitHub #51 [ASSUMED]).
|
||||
- **[2 distinct]** If a 突袭 member has not attacked for 10 s, or its skill is ready, and no enemy is in its range: it is immediately redeployed (keeping SP) onto a tile next to a ground enemy; while in that redeployed state its ATK and maxHP x(1 + 0.25 + 0.01*L). A passive skill that is on counts as ready (GitHub #49: the reporter's footage shows 缄默德克萨斯 jumping within her passive's 10 s; the engine keeps a passive on for the whole deployment [ASSUMED]), and so does a deploy-timed skill while its window runs (#109 made 宴 S2, 斯卡蒂 S2, 伊内丝 S3, 缄默德克萨斯 S1–S3 and 耀骑士临光 S2 duration skills that start at each deployment; once the window has ended only the idle trigger is left); the landing tile must have the target in range (GitHub #51 [ASSUMED]). A 隐匿 (unrevealed) ground enemy is a target too, and one inside the member's range counts as an enemy in range (no jump) — the owner's decision of 2026-10-08 from the official game (GitHub #316); asleep, untargetable and flying enemies are not.
|
||||
- **[L >= 50]** ALL operators ASPD +50.
|
||||
- Formulas: `atkHpMultiplier = 1.25 + 0.01*L`; `idleSec = 10`; `aspdAll(L>=50) = 50`
|
||||
- How layers are gained: 史尔特尔 部署时 +8 (<=50/battle); 休谟斯 each 2 kills +1; 瑕光 +4/deploy; 斯卡蒂; 伊内丝 +5; 机变 "斯卡蒂的盟誓" +8, "德克萨斯的盟誓" +10.
|
||||
|
||||
@@ -33,7 +33,9 @@
|
||||
// 助力 deputShip all operators DEF +(base + per·L), redeploy time ×(1 + respawn_time)
|
||||
// 突袭 raidShip member idle ≥ no_attack_duration s (or skill ready — a passive skill that is on counts, GitHub
|
||||
// #49, and so does a deploy-timed skill while it runs, #109) with no enemy in range → "保留技力立即再部署"
|
||||
// next to the most advanced ground enemy it can reach: on a free tile of a board of its field
|
||||
// next to the most advanced ground enemy it can reach — a 隐匿 (unrevealed) ground enemy too, and one
|
||||
// in its range counts for 「范围内没有敌人」 (the owner's decision of 2026-10-08 from the official game,
|
||||
// GitHub #316; asleep, untargetable and flying ones stay out: raidGroundEnemy): on a free tile of a board of its field
|
||||
// (Battle.onFieldBoard: its own or a teammate's on the same field — the two-helper 联防 field and a
|
||||
// pair field open both halves, the owner's decision of 2026-10-07; never a boss field's hand /
|
||||
// 临时整备区 rows, never the empty other half of a lone 联防 helper or a solo boss field — community
|
||||
@@ -62,9 +64,9 @@
|
||||
// Hooks registered only when a bond needs them. Priorities: `hit` −20 (坚守 redirect, after other modifiers had their
|
||||
// say), `death` 10 (不屈, the bond slot of the revive/redeploy convention); everything else 0.
|
||||
|
||||
import { canTargetEnemy, extendedGrid } from '../../../targeting.js';
|
||||
import { canTargetEnemy, enemyStealthed, extendedGrid } from '../../../targeting.js';
|
||||
import { normDir, rotateOffset, localOrder, localBefore } from '../../../dir.js';
|
||||
import { bodyKeys } from '../../../body.js';
|
||||
import { bodyKeys, bodyInKeys } from '../../../body.js';
|
||||
import { isHpLoss } from '../../../damage.js';
|
||||
import {
|
||||
num, bondRecord, buffParams, bondTier, bondLayers, isMember, isElite, isGroundOp, onField, playerOps, passiveBuff,
|
||||
@@ -315,15 +317,39 @@ function raidTile(battle, u, e, reach) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Jump candidates of player `pid`, in priority order: the ground enemies an operator may target (not flying,
|
||||
* canTargetEnemy) — those of the player's own field (`ownerId`), the others only when it has none —, the most advanced
|
||||
* first (least remaining path distance, then the earliest spawned) [ASSUMED, research 02 §3.18]. The same list for
|
||||
* every member of the player (canTargetEnemy reads the enemy, not the attacker).
|
||||
* A ground enemy 突袭 jumps toward, and that counts for 「范围内没有敌人」 while it stands in a member's range: one an
|
||||
* operator may target (canTargetEnemy, ground), or a 隐匿 one that only its 隐匿 keeps out — the owner's decision of
|
||||
* 2026-10-08 from the official game (GitHub #316 "Raid operator doesn't deploy to invisible enemy units, it does in
|
||||
* official"; the bond text says 敌人 / 地面敌人). Asleep, untargetable and flying enemies stay out, as before.
|
||||
*/
|
||||
function raidGroundEnemy(u, e) {
|
||||
if (!e || e.isFlying) return false;
|
||||
if (canTargetEnemy(u, e, { canHitFly: false })) return true;
|
||||
if (!e.alive || e.hidden || !e.deployed || !enemyStealthed(e)) return false;
|
||||
const f = e.s.flags;
|
||||
if (f.untargetable || f.sleep) return false;
|
||||
const skip = u.profile && u.profile.skipEnemy;
|
||||
return !(typeof skip === 'function' && skip(e));
|
||||
}
|
||||
|
||||
/** A 隐匿 ground enemy (raidGroundEnemy) on `u`'s range: enemiesInKeys leaves 隐匿 out, so the poll checks this too. */
|
||||
function raidStealthInRange(battle, u) {
|
||||
const keys = u.rangeKeySet || u.rangeKeys;
|
||||
if (!keys) return false;
|
||||
for (const e of battle.enemies) if (e.alive && enemyStealthed(e) && raidGroundEnemy(u, e) && bodyInKeys(e, keys)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Jump candidates of player `pid`, in priority order: the ground enemies of raidGroundEnemy (visible, or 隐匿; not
|
||||
* flying, asleep or untargetable) — those of the player's own field (`ownerId`), the others only when it has none —,
|
||||
* the most advanced first (least remaining path distance, then the earliest spawned) [ASSUMED, research 02 §3.18].
|
||||
* The same list for every member of the player (the checks read the enemy, not the attacker).
|
||||
*/
|
||||
function raidTargets(battle, u, pid) {
|
||||
const own = [], other = [];
|
||||
for (const e of battle.enemies) {
|
||||
if (e.isFlying || !canTargetEnemy(u, e, { canHitFly: false })) continue;
|
||||
if (!raidGroundEnemy(u, e)) continue;
|
||||
(e.ownerId === pid ? own : other).push(e);
|
||||
}
|
||||
const list = own.length ? own : other;
|
||||
@@ -352,7 +378,8 @@ function raidPoll(battle, st) {
|
||||
const ready = !!(sk && !sk.noSkill && ((sk.ready && !(sk.active && sk.isTimed)) || deploySkillOn));
|
||||
const idleOk = battle.time - since >= idle - 1e-9;
|
||||
if (!(ready || idleOk)) continue;
|
||||
if (battle.enemiesInKeys(u.rangeKeys || [], u, u.profile).length) continue;
|
||||
// 「若范围内没有敌人」: a targetable enemy, or a 隐匿 ground one (the owner's decision of 2026-10-08, raidGroundEnemy)
|
||||
if (battle.enemiesInKeys(u.rangeKeys || [], u, u.profile).length || raidStealthInRange(battle, u)) continue;
|
||||
const list = (targets ??= raidTargets(battle, u, st.pid));
|
||||
if (!list.length) continue;
|
||||
// either trigger: raidTile only offers tiles with the target in range (without that a ready skill that finds no
|
||||
|
||||
+19
-19
@@ -1398,7 +1398,7 @@
|
||||
"combat": "client",
|
||||
"boost": {"lp":400,"layers":300},
|
||||
"setup": {"stageId":"act2autochess_m04","bossId":"boss_5","hiddenBossId":"boss_9","factions":["FLY","ELEMENT","INVISIBLE"]},
|
||||
"end": {"victory":true,"roundsPassed":15,"hiddenReached":true,"hiddenCleared":true,"reason":"victory","teamLp":793,"bossPoolLeft":0,"virtualMs":124650},
|
||||
"end": {"victory":true,"roundsPassed":15,"hiddenReached":true,"hiddenCleared":true,"reason":"victory","teamLp":795,"bossPoolLeft":0,"virtualMs":124650},
|
||||
"rng": {"setup":25,"shop":750,"waves":0,"draft":61,"bots":22165,"meta":198},
|
||||
"errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0},
|
||||
"rounds": [
|
||||
@@ -1444,27 +1444,27 @@
|
||||
},
|
||||
{
|
||||
"round": 9,
|
||||
"ai_0": [1,400,12,0,0,4,8,"cdec5dfa",2214,"arcaneShip 2/318, deputShip 0/304, egirShip 1/322, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/302, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/310, raidShip 1/330, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/304, steadShip 1/330, suntShip 1/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",24,24,0,77],
|
||||
"ai_0": [1,400,12,0,0,4,8,"cdec5dfa",2231,"arcaneShip 2/318, deputShip 0/304, egirShip 1/319, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/302, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/310, raidShip 1/353, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/304, steadShip 1/327, suntShip 1/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",24,24,0,77],
|
||||
"ai_1": [1,400,12,0,3,6,8,"36a202d9",2210,"arcaneShip 0/302, deputShip 1/326, egirShip 0/300, emptyShip 0/308, indomShip 0/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 1/306, preciShip 0/309, raidShip 0/300, sargonShip 0/300, siracusaShip 1/342, skillfulShip 1/302, soloShip 1/316, steadShip 0/312, suntShip 1/302, swiftShip 1/316, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",29,25,0,114]
|
||||
},
|
||||
{
|
||||
"round": 10,
|
||||
"ai_0": [1,400,12,0,0,5,8,"582856d0",2234,"arcaneShip 2/318, deputShip 0/304, egirShip 1/330, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/304, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/310, raidShip 1/338, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/304, steadShip 1/334, suntShip 1/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",12,12,0,89],
|
||||
"ai_0": [1,400,12,0,0,5,8,"582856d0",2251,"arcaneShip 2/318, deputShip 0/304, egirShip 1/327, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/304, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/310, raidShip 1/363, sargonShip 0/300, siracusaShip 0/300, skillfulShip 0/300, soloShip 1/304, steadShip 1/329, suntShip 1/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",12,12,0,89],
|
||||
"ai_1": [1,400,15,0,2,6,8,"3a43d0ba",2260,"arcaneShip 0/302, deputShip 1/328, egirShip 0/300, emptyShip 0/308, indomShip 0/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 1/314, preciShip 0/309, raidShip 0/300, sargonShip 0/300, siracusaShip 1/370, skillfulShip 1/306, soloShip 1/318, steadShip 0/312, suntShip 1/304, swiftShip 1/320, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",13,13,0,130]
|
||||
},
|
||||
{
|
||||
"round": 11,
|
||||
"ai_0": [1,400,12,0,1,5,8,"9ac6e043",2267,"arcaneShip 2/332, deputShip 0/304, egirShip 1/343, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/308, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/315, raidShip 1/365, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/308, soloShip 1/304, steadShip 0/338, suntShip 1/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",38,34,0,102],
|
||||
"ai_0": [1,400,12,0,1,5,8,"9ac6e043",2281,"arcaneShip 2/332, deputShip 0/304, egirShip 1/340, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/308, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/315, raidShip 1/382, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/308, soloShip 1/304, steadShip 0/333, suntShip 1/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",38,34,0,102],
|
||||
"ai_1": [1,400,14,0,1,6,8,"120b46d6",2628,"arcaneShip 1/318, deputShip 1/330, egirShip 0/300, emptyShip 0/308, indomShip 0/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 1/322, preciShip 0/311, raidShip 0/300, sargonShip 0/300, siracusaShip 1/390, skillfulShip 1/318, soloShip 1/320, steadShip 0/312, suntShip 1/306, swiftShip 1/324, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",34,34,0,144]
|
||||
},
|
||||
{
|
||||
"round": 12,
|
||||
"ai_0": [1,400,13,0,0,6,8,"afebef50",2311,"arcaneShip 2/340, deputShip 0/304, egirShip 1/348, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/308, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/320, raidShip 1/383, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/316, soloShip 1/304, steadShip 0/338, suntShip 1/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",6,14,8,115],
|
||||
"ai_0": [1,400,13,0,0,6,8,"afebef50",2325,"arcaneShip 2/340, deputShip 0/304, egirShip 1/345, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/308, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/320, raidShip 1/400, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/316, soloShip 1/304, steadShip 0/333, suntShip 1/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",6,14,8,115],
|
||||
"ai_1": [1,400,13,0,5,6,8,"9e462d87",2688,"arcaneShip 1/333, deputShip 1/332, egirShip 0/300, emptyShip 0/308, indomShip 0/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 1/328, preciShip 0/311, raidShip 0/300, sargonShip 0/300, siracusaShip 1/411, skillfulShip 1/326, soloShip 1/322, steadShip 0/312, suntShip 1/308, swiftShip 1/328, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",15,15,0,152]
|
||||
},
|
||||
{
|
||||
"round": 13,
|
||||
"ai_0": [1,400,12,0,2,6,8,"97689296",2364,"arcaneShip 2/349, deputShip 0/304, egirShip 1/361, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/308, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/325, raidShip 1/403, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/322, soloShip 1/304, steadShip 0/338, suntShip 1/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",41,41,0,126],
|
||||
"ai_0": [1,400,12,0,2,6,8,"97689296",2380,"arcaneShip 2/349, deputShip 0/304, egirShip 1/359, emptyShip 0/300, indomShip 0/300, investShip 0/300, kazimierzShip 0/302, kjeragShip 0/308, lateranoShip 0/300, maniShip 0/300, miraShip 0/300, preciShip 1/325, raidShip 1/421, sargonShip 0/300, siracusaShip 0/300, skillfulShip 1/322, soloShip 1/304, steadShip 0/333, suntShip 1/300, swiftShip 0/302, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",41,41,0,126],
|
||||
"ai_1": [1,400,17,0,0,6,8,"36a5d772",2717,"arcaneShip 1/345, deputShip 0/332, egirShip 0/300, emptyShip 0/308, indomShip 0/300, investShip 0/300, kazimierzShip 0/300, kjeragShip 0/300, lateranoShip 0/300, maniShip 0/300, miraShip 1/334, preciShip 1/311, raidShip 0/300, sargonShip 0/300, siracusaShip 1/429, skillfulShip 1/338, soloShip 1/322, steadShip 0/312, suntShip 1/308, swiftShip 1/330, victoriaShip 0/300, visiShip 0/300, yanShip 0/300",41,41,0,168]
|
||||
}
|
||||
],
|
||||
@@ -1474,26 +1474,26 @@
|
||||
"victory": true,
|
||||
"roundsPassed": 15,
|
||||
"eliminatedRound": null,
|
||||
"lp": 397,
|
||||
"lp": 398,
|
||||
"bandId": "band_sarkazb",
|
||||
"title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"},
|
||||
"trophies": 8,
|
||||
"reward": 425,
|
||||
"stats": {"activatedLayers":3346,"bossDamage":7712110,"dmgDealt":20535636,"gold":154,"itemsEquipped":21,"kills":287,"leaks":8,"lpLost":0,"merges":7,"perfectRounds":12,"refreshes":21},
|
||||
"stats": {"activatedLayers":3362,"bossDamage":7735924,"dmgDealt":20519044,"gold":154,"itemsEquipped":21,"kills":287,"leaks":8,"lpLost":0,"merges":7,"perfectRounds":12,"refreshes":21},
|
||||
"lineup": ["4_09_b@11,5[3_11_e_a+3_11_e_b]","6_11_a@11,8","3_13_b@10,3","3_05_b@10,8[1_01_e_a+6_09_e_a]","6_16_a@9,6","6_05_a@9,7[3_02_e_a]","4_14_b@9,8[2_01_e_a+1_02_e_b]","5_07_a@9,9[4_10_e_b]"],
|
||||
"bonds": ["arcaneShip 1/365","deputShip 0/304","egirShip 1/364","emptyShip 0/300","indomShip 0/300","investShip 0/300","kazimierzShip 0/302","kjeragShip 0/308","lateranoShip 0/300","maniShip 1/300","miraShip 0/300","preciShip 1/345","raidShip 1/405","sargonShip 1/300","siracusaShip 0/300","skillfulShip 1/338","soloShip 1/307","steadShip 0/338","suntShip 1/300","swiftShip 1/322","victoriaShip 0/300","visiShip 0/300","yanShip 0/300"]
|
||||
"bonds": ["arcaneShip 1/365","deputShip 0/304","egirShip 1/362","emptyShip 0/300","indomShip 0/300","investShip 0/300","kazimierzShip 0/302","kjeragShip 0/308","lateranoShip 0/300","maniShip 1/300","miraShip 0/300","preciShip 1/345","raidShip 1/423","sargonShip 1/300","siracusaShip 0/300","skillfulShip 1/338","soloShip 1/307","steadShip 0/333","suntShip 1/300","swiftShip 1/322","victoriaShip 0/300","visiShip 0/300","yanShip 0/300"]
|
||||
},
|
||||
"ai_1": {
|
||||
"alive": true,
|
||||
"victory": true,
|
||||
"roundsPassed": 15,
|
||||
"eliminatedRound": null,
|
||||
"lp": 396,
|
||||
"lp": 397,
|
||||
"bandId": "band_doberm",
|
||||
"title": {"id":"comment_3","name":"坚若磐石","picId":"comment_icon_3","text":"目标生命值损失最少"},
|
||||
"trophies": 8,
|
||||
"reward": 425,
|
||||
"stats": {"activatedLayers":2801,"bossDamage":6180513,"dmgDealt":24059056,"gold":191,"itemsEquipped":32,"kills":287,"leaks":1,"lpLost":0,"merges":5,"perfectRounds":12,"refreshes":55},
|
||||
"stats": {"activatedLayers":2801,"bossDamage":6156699,"dmgDealt":23996575,"gold":191,"itemsEquipped":32,"kills":287,"leaks":1,"lpLost":0,"merges":5,"perfectRounds":12,"refreshes":55},
|
||||
"lineup": ["4_10_a@11,5[3_05_e_a+4_03_e_a]","6_05_a@11,8[4_08_e_a+6_09_e_a]","6_16_a@10,3[3_11_e_a+1_05_e_b]","2_13_a@10,7[4_04_e_a+4_05_e_a]","1_08_b@10,8[4_07_e_b+5_07_e_a]","6_08_a@9,6[2_01_e_b+3_01_e_b]","5_20_a@9,7[1_05_e_a+3_04_e_a]","6_08_a@9,8[3_03_e_a+1_01_e_a]","3_19_b@9,9[2_04_e_a+3_07_e_b]"],
|
||||
"bonds": ["arcaneShip 1/371","deputShip 0/332","egirShip 0/300","emptyShip 0/308","indomShip 0/300","investShip 0/300","kazimierzShip 0/300","kjeragShip 0/300","lateranoShip 0/300","maniShip 0/300","miraShip 1/334","preciShip 1/311","raidShip 0/300","sargonShip 0/300","siracusaShip 1/465","skillfulShip 1/356","soloShip 1/322","steadShip 0/312","suntShip 1/308","swiftShip 1/334","victoriaShip 0/300","visiShip 0/300","yanShip 0/300"]
|
||||
}
|
||||
@@ -1509,7 +1509,7 @@
|
||||
"combat": "client",
|
||||
"boost": null,
|
||||
"setup": {"stageId":"act1autochess_m01","bossId":"boss_5","hiddenBossId":"boss_10","factions":["DOT","INVISIBLE","REFLECTION"]},
|
||||
"end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":39,"bossPoolLeft":0,"virtualMs":121400},
|
||||
"end": {"victory":true,"roundsPassed":14,"hiddenReached":false,"hiddenCleared":false,"reason":"victory","teamLp":40,"bossPoolLeft":0,"virtualMs":121400},
|
||||
"rng": {"setup":25,"shop":489,"waves":0,"draft":34,"bots":18039,"meta":11},
|
||||
"errors": {"engine":0,"logged":0,"sim":0,"dispatcher":0},
|
||||
"rounds": [
|
||||
@@ -1565,17 +1565,17 @@
|
||||
},
|
||||
{
|
||||
"round": 11,
|
||||
"h_0": [1,26,12,0,0,5,8,"7c35f14c",146,"arcaneShip 1/11, kazimierzShip 1/40, miraShip 1/4, preciShip 0/8, raidShip 1/76, skillfulShip 0/3, soloShip 1/0, suntShip 1/0, swiftShip 1/15, yanShip 0/1",22,25,3,100],
|
||||
"h_0": [1,27,12,0,0,5,8,"7c35f14c",146,"arcaneShip 1/11, kazimierzShip 1/40, miraShip 1/4, preciShip 0/8, raidShip 1/76, skillfulShip 0/3, soloShip 1/0, suntShip 1/0, swiftShip 1/15, yanShip 0/1",23,25,2,100],
|
||||
"ai_0": [1,23,12,0,0,5,8,"0e29ce35",193,"arcaneShip 0/2, deputShip 0/16, kazimierzShip 0/8, lateranoShip 1/41, preciShip 2/40, sargonShip 0/2, skillfulShip 1/19, soloShip 1/5, suntShip 1/14, swiftShip 1/74",25,25,0,101]
|
||||
},
|
||||
{
|
||||
"round": 12,
|
||||
"h_0": [1,26,12,0,0,6,8,"c8e0476d",181,"arcaneShip 1/14, kazimierzShip 1/52, miraShip 1/8, preciShip 0/8, raidShip 1/88, skillfulShip 0/3, soloShip 1/0, suntShip 1/0, swiftShip 1/19, yanShip 0/1",27,31,4,112],
|
||||
"h_0": [1,27,12,0,0,6,8,"c8e0476d",181,"arcaneShip 1/14, kazimierzShip 1/52, miraShip 1/8, preciShip 0/8, raidShip 1/88, skillfulShip 0/3, soloShip 1/0, suntShip 1/0, swiftShip 1/19, yanShip 0/1",27,31,4,112],
|
||||
"ai_0": [1,23,12,0,0,6,8,"55f8656e",229,"arcaneShip 0/2, deputShip 0/16, kazimierzShip 0/8, lateranoShip 1/47, preciShip 2/53, sargonShip 0/2, skillfulShip 1/24, soloShip 1/5, suntShip 1/14, swiftShip 1/86",31,31,0,112]
|
||||
},
|
||||
{
|
||||
"round": 13,
|
||||
"h_0": [1,16,12,0,0,6,8,"64fc1bc4",199,"arcaneShip 0/14, kazimierzShip 1/64, miraShip 1/12, preciShip 0/8, raidShip 1/100, skillfulShip 0/3, soloShip 1/0, suntShip 1/0, swiftShip 1/23, yanShip 0/1",28,57,29,124],
|
||||
"h_0": [1,17,12,0,0,6,8,"b18b37b2",191,"arcaneShip 0/14, kazimierzShip 1/60, miraShip 1/12, preciShip 0/8, raidShip 1/96, skillfulShip 0/3, soloShip 1/0, suntShip 1/0, swiftShip 1/23, yanShip 0/1",17,57,40,124],
|
||||
"ai_0": [1,23,12,0,0,6,8,"dc15adde",265,"arcaneShip 0/2, deputShip 0/16, kazimierzShip 0/8, lateranoShip 1/53, preciShip 2/63, sargonShip 0/2, skillfulShip 1/29, soloShip 1/5, suntShip 1/14, swiftShip 1/101, visiShip 1/0",57,57,0,126]
|
||||
}
|
||||
],
|
||||
@@ -1585,14 +1585,14 @@
|
||||
"victory": true,
|
||||
"roundsPassed": 14,
|
||||
"eliminatedRound": null,
|
||||
"lp": 16,
|
||||
"lp": 17,
|
||||
"bandId": "band_amedic",
|
||||
"title": {"id":"comment_4","name":"精英云集","picId":"comment_icon_4","text":"晋升精锐次数最多"},
|
||||
"trophies": 4,
|
||||
"reward": 400,
|
||||
"stats": {"activatedLayers":206,"bossDamage":301759,"dmgDealt":2977771,"gold":136,"itemsEquipped":21,"kills":274,"leaks":37,"lpLost":12,"merges":3,"perfectRounds":9,"refreshes":21},
|
||||
"stats": {"activatedLayers":198,"bossDamage":380567,"dmgDealt":2997433,"gold":136,"itemsEquipped":21,"kills":268,"leaks":47,"lpLost":11,"merges":3,"perfectRounds":9,"refreshes":21},
|
||||
"lineup": ["6_18_a@12,3","4_10_a@12,4[2_01_e_b+2_04_e_a]","1_17_b@11,3[4_03_e_a+1_01_e_a]","5_20_a@11,8[6_05_e_a+3_06_e_a]","3_17_b@10,3","3_12_a@10,4[1_02_e_a+3_11_e_a]","5_07_a@10,7","1_19_b@10,8[2_01_e_b+4_04_e_b]"],
|
||||
"bonds": ["arcaneShip 2/18","kazimierzShip 1/64","miraShip 1/12","preciShip 0/8","raidShip 1/100","siracusaShip 1/12","skillfulShip 0/3","soloShip 1/0","suntShip 1/0","swiftShip 0/23","yanShip 0/1"]
|
||||
"bonds": ["arcaneShip 2/18","kazimierzShip 1/60","miraShip 1/12","preciShip 0/8","raidShip 1/96","siracusaShip 1/12","skillfulShip 0/3","soloShip 1/0","suntShip 1/0","swiftShip 0/23","yanShip 0/1"]
|
||||
},
|
||||
"ai_0": {
|
||||
"alive": true,
|
||||
@@ -1604,7 +1604,7 @@
|
||||
"title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"},
|
||||
"trophies": 4,
|
||||
"reward": 400,
|
||||
"stats": {"activatedLayers":296,"bossDamage":478241,"dmgDealt":3742103,"gold":140,"itemsEquipped":17,"kills":315,"leaks":0,"lpLost":0,"merges":3,"perfectRounds":13,"refreshes":22},
|
||||
"stats": {"activatedLayers":296,"bossDamage":399433,"dmgDealt":3904432,"gold":140,"itemsEquipped":17,"kills":327,"leaks":0,"lpLost":0,"merges":3,"perfectRounds":13,"refreshes":22},
|
||||
"lineup": ["3_21_a@12,3[4_08_e_a+6_02_e_a]","4_25_a@12,4","2_01_a@12,5[3_01_e_a]","6_12_a@11,3[1_05_e_a+3_11_e_a]","4_14_b@11,4[1_05_e_b+4_07_e_a]","1_01_b@10,3[3_08_e_a]","5_01_a@10,4[4_05_e_a]","2_13_a@9,4[4_02_e_b]"],
|
||||
"bonds": ["arcaneShip 0/2","deputShip 0/16","emptyShip 1/0","kazimierzShip 0/8","lateranoShip 1/56","preciShip 2/76","sargonShip 0/2","skillfulShip 1/34","soloShip 0/5","suntShip 1/14","swiftShip 1/113","visiShip 1/3"]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// test/sim/feedback7-raid-stealth.test.js — 突袭 and 隐匿 ground enemies: the owner's decision of 2026-10-08 (GitHub #316
|
||||
// "Raid operator doesn't deploy to invisible enemy units, it does in official", confirmed by the owner's own play): a 突袭
|
||||
// member jumps next to a 隐匿 (unrevealed) ground enemy, and one already in its range counts for 「若范围内没有敌人」 (no
|
||||
// hop). Asleep, untargetable and flying enemies stay out, as before (bond text: 「再部署至一名地面敌人周围」).
|
||||
// Run: node --test test/sim/feedback7-raid-stealth.test.js
|
||||
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { makeBattle, chessRec, enemyRec, checkInvariants } from '../helpers/battleHarness.js';
|
||||
import { enemyStealthed } from '../../server/sim/targeting.js';
|
||||
|
||||
const HOME = [12, 3];
|
||||
const BONDS = { raidShip: { count: 2, active: true, tier: 1, layers: 0 } };
|
||||
/**
|
||||
* A field where (10,7) is the only tile a melee member may land on beside the enemy at (10,8): the rest of rows 9–12 is
|
||||
* undeployable floor ('f'), its home (12,3) road.
|
||||
*/
|
||||
const ONE_SPOT = { rows: {
|
||||
9: '##Efffffff' + 'S' + 'fffffffS##',
|
||||
10: '##hffffrff' + 'f' + 'ffffffff##',
|
||||
11: '##hfffffff' + 'f' + 'ffffffff##',
|
||||
12: '##hrffffff' + 'S' + 'fffffffS##',
|
||||
} };
|
||||
|
||||
/** `foe` at `pos` (with the buff flags `flags`), and optionally a visible `vis` at `visible`. */
|
||||
function field({ pos = [10, 8], flags = null, fly = false, visible = null, range = [[0, 0], [0, 1]] } = {}) {
|
||||
const h = makeBattle({
|
||||
flat: ONE_SPOT, bonds: BONDS, autoFinish: false, timeLimit: 60, hooks: ['deploy'],
|
||||
defs: {
|
||||
chess: { raider: chessRec({ id: 'raider', profession: 'WARRIOR', position: 'MELEE', bonds: ['raidShip'], rangeGrid: range, skill: { spCost: 10, initSp: 10 } }) },
|
||||
enemies: {
|
||||
foe: enemyRec({ key: 'foe', hp: 1e7, speed: 0, atk: 0, motion: fly ? 'FLY' : 'WALK' }),
|
||||
vis: enemyRec({ key: 'vis', hp: 1e7, speed: 0, atk: 0 }),
|
||||
},
|
||||
},
|
||||
units: [{ chessId: 'raider', row: HOME[0], col: HOME[1], dir: 'RIGHT' }],
|
||||
enemies: [{ key: 'foe', pos }, ...(visible ? [{ key: 'vis', pos: visible }] : [])],
|
||||
setup(b) {
|
||||
if (flags) b.on('enemySpawn', (c) => { if (/foe$/.test(c.enemy.defId)) b.addBuff(c.enemy, { key: 'test:mark', persist: true, flags }); });
|
||||
},
|
||||
});
|
||||
return h;
|
||||
}
|
||||
const jumps = (h) => h.hooksOf('deploy').filter((c) => c.unit === h.unit('raider') && !c.initial).length;
|
||||
const at = (h) => [h.unit('raider').tileR, h.unit('raider').tileC];
|
||||
const foe = (h) => h.b.enemies.find((e) => e.alive && /foe$/.test(e.defId));
|
||||
|
||||
test('a ready 突袭 member jumps to the only landing spot beside a 隐匿 ground enemy, and does not hop again', () => {
|
||||
const h = field({ flags: { stealth: true } });
|
||||
h.run(1);
|
||||
assert.equal(enemyStealthed(foe(h)), true, 'still 隐匿 (not blocked, not revealed)');
|
||||
assert.deepEqual(at(h), [10, 7], 'landed on the one tile beside it');
|
||||
assert.equal(jumps(h), 1);
|
||||
h.run(3);
|
||||
assert.equal(jumps(h), 1, 'the 隐匿 enemy in its range is an enemy in range: no hop');
|
||||
assert.deepEqual(at(h), [10, 7]);
|
||||
checkInvariants(h.b);
|
||||
});
|
||||
|
||||
test('a visible ground enemy: the same landing (control)', () => {
|
||||
const h = field();
|
||||
h.run(1);
|
||||
assert.deepEqual(at(h), [10, 7]);
|
||||
assert.equal(jumps(h), 1);
|
||||
checkInvariants(h.b);
|
||||
});
|
||||
|
||||
test('a 隐匿 ground enemy already in its range: 「范围内没有敌人」 is false — it stays, though a visible one waits elsewhere', () => {
|
||||
const h = field({ pos: [12, 4], flags: { stealth: true }, visible: [10, 8] });
|
||||
h.run(3);
|
||||
assert.equal(enemyStealthed(foe(h)), true);
|
||||
assert.deepEqual(at(h), HOME);
|
||||
assert.equal(jumps(h), 0);
|
||||
checkInvariants(h.b);
|
||||
});
|
||||
|
||||
test('asleep or flying 隐匿 enemies are still no destination', () => {
|
||||
for (const opts of [{ flags: { stealth: true, sleep: true } }, { flags: { stealth: true }, fly: true }]) {
|
||||
const h = field(opts);
|
||||
h.run(3);
|
||||
assert.deepEqual(at(h), HOME, JSON.stringify(opts));
|
||||
assert.equal(jumps(h), 0);
|
||||
checkInvariants(h.b);
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user