feedback2: merge WF — 钩索师 / 推击手 may stand on a 高台 (branch trait 可以放置于远程位; GitHub #32 item 4)

This commit is contained in:
sganggs committed 2026-10-03 23:39:47 +08:00
commit 2f5cc6edbc
13 files changed
+286 -21

No files matched your search

+11 -4
View File
@@ -131,7 +131,7 @@ Ownership rule for parallel agents: **only edit files assigned to you**; if you
- **Partner display** cols 11–18 of rows 9–12 (the unite field uses them).
- **Boss field**: rows 1–5; left half cols 2–10, right half cols 10–18.
- Enemy preview pen rows 14–18 × cols 7–13 (row 16 empty): during prep the next round's enemies idle there (upper-gate enemies rows 17–18, lower-gate rows 14–15, ≤ 50 models, spawn-time order) — research 08 §4 / 09 §2.
- Tile semantics come from `data/stages.json` legend: height (LOW/HIGH), buildable (ALL/MELEE/RANGED/NONE — the **effective** deploy type: the 深水区 `tile_deepsea` refuses deployment, NONE with the level's `buildableType` ALL kept, PRTS 深水区 地形信息 "拒绝部署(待补充)"; an active 特制水上平台 makes its tile deployable in the prep deploy map only, §21.3), ground-passable, fly-only, special terrain (mire, smog, deepsea, infection). The fenced tiles (围墙 `tile_fence_bound` / 围栏 `tile_fence`, legend `b`: LOW, buildable ALL, passable to flyers only) take units, which attack normally from there but block no ground enemy (PRTS 地形机制, §21.23). Melee chess may stand only on LOW tiles with buildable ALL/MELEE; ranged chess on ALL/RANGED **and** on LOW ALL/MELEE tiles ("所有行动内远程干员可部署在近战位"). Tokens follow their own `position`; a summon marked `ownerRange` (狼群, 流形: "只能部署在召唤者攻击范围内") goes only on a tile of its owner's attack range — the loadout grid rotated by the owner's facing around its tile (`PlayerState.summonRange`, §21.3).
- Tile semantics come from `data/stages.json` legend: height (LOW/HIGH), buildable (ALL/MELEE/RANGED/NONE — the **effective** deploy type: the 深水区 `tile_deepsea` refuses deployment, NONE with the level's `buildableType` ALL kept, PRTS 深水区 地形信息 "拒绝部署(待补充)"; an active 特制水上平台 makes its tile deployable in the prep deploy map only, §21.3), ground-passable, fly-only, special terrain (mire, smog, deepsea, infection). The fenced tiles (围墙 `tile_fence_bound` / 围栏 `tile_fence`, legend `b`: LOW, buildable ALL, passable to flyers only) take units, which attack normally from there but block no ground enemy (PRTS 地形机制, §21.23). Melee chess may stand only on LOW tiles with buildable ALL/MELEE — except a branch whose own trait reads "可以放置于远程位" (钩索师 歌蕾蒂娅 / 崖心, 推击手 见行者: chess.json `placement` 'all'), which may use the ranged tiles too (the remake's reading, §22.6); ranged chess on ALL/RANGED **and** on LOW ALL/MELEE tiles ("所有行动内远程干员可部署在近战位"). Tokens follow their own `position`; a summon marked `ownerRange` (狼群, 流形: "只能部署在召唤者攻击范围内") goes only on a tile of its owner's attack range — the loadout grid rotated by the owner's facing around its tile (`PlayerState.summonRange`, §21.3).
- **Facing (corrected, see research 09 §1.2):** like standard Arknights, every board piece has a direction `dir ∈ UP|RIGHT|DOWN|LEFT` chosen with the 4-direction deploy wheel after dropping it on a tile (also re-orientable in place); default `RIGHT`; persists across rounds. Range grid entries are `[dRow, dCol]` relative to facing RIGHT and are rotated: RIGHT `(dr,dc)`, UP `(dc,−dr)`, LEFT `(−dr,−dc)`, DOWN `(−dc,dr)` (row 0 = bottom); `rangeExtend` applies along +dCol before rotating — to a running skill's range too, unless that skill ignores 攻击距离 (`targeting.noRangeExtend`, 信仰搅拌机 S3, §21.16). The right-side Final Assault player is mirrored col `c→20−c` with RIGHT↔LEFT (UP/DOWN unchanged).
- **Range test:** an enemy is inside a grid range if the tile containing it (`round(y), round(x)`) is one of the range tiles; a huge enemy (data `hitArea`: 巨型单位, a 4.95 × 2.95 rectangle moved 1.0 up) if any tile its hit rectangle overlaps is one (PRTS 作战机制 "巨型BOSS单位的每一个占据的格子都可以让其本身通过格子判定"; `sim/body.js`, §19.4). Radius ranges (enemies, auras, AoE) use Euclidean distance in tiles — to a huge enemy's rectangle [ASSUMED] — except splash around a struck target, a 中点判定 on positions (PRTS 作战机制 "中点判定…案例:阻挡,酒神1天赋的1.3溅射半径"). A ranged enemy's normal attack takes an ally whose collider (radius 0.25, PRTS 作战机制 §碰撞体积 "我方干员碰撞体积基本均为以0.25格为半径的圆形") touches its range circle: centre distance ≤ `rangeRadius` + 0.25 (`constants.js ALLY_COLLIDER_RADIUS`, `ai.js enemyAttack`; §20.4).
- **Fields at runtime:** a `Battle` receives a **field rect** (rows/cols subset) and the full stage grid. Normal battles use rows 9–12 × cols 0–10; unite uses rows 9–12 × cols 0–20; boss uses rows 0–5 × cols 0–20 (solo boss uses the left half only). Pathfinding never leaves the rect.
@@ -384,7 +384,7 @@ Per match: pick stage (weighted among the mode's `config.modes[m].stages`, weigh
### 6.6 AI players (bot.js)
Deterministic-ish heuristic player used for (a) AI teammates, (b) auto-play for disconnected humans (only auto-ready + auto-picks, not shopping, unless "AI 托管" is toggled), (c) tests. Strategy (§21.6): pick a band (weighted by starting LP; never one built around a bond the mode switches off — `gd.bandBondIds`, §21.26); each prep: take offers, sell dead bench weight, fill the board, complete affordable merges, level on the curve, then buy only merge progress / lineup upgrades worth more than the refreshes their price would buy (shop odds of completing held pairs), freeze for an unaffordable third copy (坎诺特 keeps 5 funds back); build around a committed focus core bond (a teammate's main bond on its strip counts against it; bonds the mode switches off never) + a second bond; value dealers by DPS vs the round's DEF / RES (an attack on every enemy in range counts double — 阵法术师 / 轰击术师), with the range the unit is deployed with; place by the exposure model + rehearsal on the server's deploy map (no 深水区; a tactician's 援军 within `PlayerState.summonRange`; `g.move` judges legality); items by effect (META §1.5: 突变细胞 on the least valuable normal single below 6阶, a cell left in temp gets a hand slot made for it, the operator its transformation gains is deployed by the next prep's lineup step like any owned unit — AI 托管 too; the buy loop's bench shed never sells a piece gained since the bot's last prep, §21.1); 机变 bounties by expected payout − expected LP loss over the offered cards; resolve temp — on a human seat under AI 托管 never by destroying an item while a bench operator can be sold for the slot —; ready. A bot's whole prep — shop decisions and layout planning (`botPrepBeginSteps`, 25–70 ms late in a 4-bot match), the layout rehearsal and `botPrepEndSteps` — runs in wall-clock slices of `botSliceMs` (8 ms) per scheduler callback on a real host (step generators that yield between whole actions, never with a transient board; the same actions in the same order, so decisions are unchanged; at once in virtual time).
Deterministic-ish heuristic player used for (a) AI teammates, (b) auto-play for disconnected humans (only auto-ready + auto-picks, not shopping, unless "AI 托管" is toggled), (c) tests. Strategy (§21.6): pick a band (weighted by starting LP; never one built around a bond the mode switches off — `gd.bandBondIds`, §21.26); each prep: take offers, sell dead bench weight, fill the board, complete affordable merges, level on the curve, then buy only merge progress / lineup upgrades worth more than the refreshes their price would buy (shop odds of completing held pairs), freeze for an unaffordable third copy (坎诺特 keeps 5 funds back); build around a committed focus core bond (a teammate's main bond on its strip counts against it; bonds the mode switches off never) + a second bond; value dealers by DPS vs the round's DEF / RES (an attack on every enemy in range counts double — 阵法术师 / 轰击术师), with the range the unit is deployed with; place by the exposure model + rehearsal on the server's deploy map (no 深水区; a tactician's 援军 within `PlayerState.summonRange`; a 钩索师 / 推击手 on the ground tiles, as a blocker — §22.6; `g.move` judges legality); items by effect (META §1.5: 突变细胞 on the least valuable normal single below 6阶, a cell left in temp gets a hand slot made for it, the operator its transformation gains is deployed by the next prep's lineup step like any owned unit — AI 托管 too; the buy loop's bench shed never sells a piece gained since the bot's last prep, §21.1); 机变 bounties by expected payout − expected LP loss over the offered cards; resolve temp — on a human seat under AI 托管 never by destroying an item while a bench operator can be sold for the slot —; ready. A bot's whole prep — shop decisions and layout planning (`botPrepBeginSteps`, 25–70 ms late in a 4-bot match), the layout rehearsal and `botPrepEndSteps` — runs in wall-clock slices of `botSliceMs` (8 ms) per scheduler callback on a real host (step generators that yield between whole actions, never with a transient board; the same actions in the same order, so decisions are unchanged; at once in virtual time).
---
@@ -1498,6 +1498,13 @@ TBD-22.4
TBD-22.5
### 22.6 钩索师 and 推击手 on 高台 (#32 item 4)
### 22.6 钩索师 and 推击手 on 高台 (#32 item 4) — `tools/build-data.mjs PLACE_ON_RANGED_RE / branchTraitText / validateAll`, chess.json `placement`, `match/board.js positionClass / basePositionClass`, `ui/gameLogic.js piecePosition`, `match/bot.js isBlocker / planLayoutSteps`, `tools/balance.mjs`
TBD-22.6
- **Seen**: public issue #32 item 4 — "哥蕾蒂娅无法部署到高台". 0.1.1 refused every 高台 tile for her (server `BAD_TILE`; the client lit no 高台 and said 近战单位只能部署在地面), and the same for 崖心 (钩索师) and 见行者 (推击手), normal and elite.
- **Official**: the branch trait of both 钩索师 chess (歌蕾蒂娅 `chess_char_4_12`, 崖心 `chess_char_2_03`) reads 「技能可以使敌人产生位移\n可以放置于远程位」, the 推击手 见行者's (`chess_char_3_07`) 「同时攻击阻挡的所有敌人\n可以放置于远程位」 — character_table `description` (their `trait` is null: the branch's own text), position MELEE; act2autochess `charChessDataDict` sets only status, bonds and garrisons for them. PRTS 新人入门 (branch table): 钩索师 and 推击手 "可部署在高台和地面". PRTS 卫戍协议/帮助 §战斗部署: "所有行动内远程干员可部署在近战位", and 部署效果 do not apply in this placement — "携带Y模组的教官仍无法部署至高台位". That 教官 ability is a module talent ("可以额外部署在远程位", battle_equip_table uniequip_003_pallas / uniequip_002_doberm / uniequip_002_rdoc, blackboard `buildable_type` 2), a deploy effect; the 钩索师 / 推击手 line is the branch trait itself — no talent, no blackboard, and every module of the three keeps it (HOK-X, HOK-Y, PUS-X). No Y-module 教官 is in the pool (诗怀雅 has INS-X). No source states the 钩索师 case for 卫戍协议 in so many words: **the remake reads the branch trait as still applying** [ASSUMED]; the old "melee never on ranged-only tiles" was itself an assumption (research 03 C2).
- **Cause**: build-data copied position MELEE and nothing read 可以放置于远程位; `board.js positionClass` mapped MELEE to 'melee' (melee tiles only), so `PlayerState._legal` refused the 高台 (`g.move` → `BAD_TILE`), and the client mirror (`gameLogic.js piecePosition` / `tileAllows`) agreed.
- **Now**: build-data sets `placement: 'all'` on a chess whose branch trait without module (`branchTraitText`: the trait candidate's text, else the description) contains 可以放置于远程位 — exactly 崖心, 见行者 and 歌蕾蒂娅, normal and elite (6 records, the only data change; `validateAll` allows it on a MELEE record only; module-only text never counts). `positionClass` returns 'all' for them, so the server takes them on every deployable tile — `g.move`, swaps, a merge's elite (`mergeTile`), the terrain re-check (`_evictIllegal`), invariants and audit —, and the client's `piecePosition` 'ALL' lights the 高台 while dragging; every other melee unit keeps the refusal and its text. The battle keeps position MELEE: on a ground tile they block as before; on a 高台 (`unit.ground` false) they block nothing and attack from there — the sim needed no change (act2 m01, one 萨卡兹枯朽前锋 on the lower lane, 40 s: 歌蕾蒂娅 on (10,4) facing it 2 hits, no block, on (9,5) 12 hits, blocking; 崖心 and 见行者 alike; no errors).
- **Bots** read the record's own position (`basePositionClass`): they still count these units as blockers (`isBlocker`; with the widened class they would not, and on act2 m01 the planner put 歌蕾蒂娅 and 崖心 on the 高台 as non-blockers, leaving 见行者 the only blocker on the road) and plan them on the ground tiles, as in 0.1.1 [ASSUMED]: offered the 高台, the planner still chose road tiles in the cases measured, but the lineup's blocker count assumes they block and a greedy 高台 pick could take a ranged dealer's tile — not benchmarked. `tools/balance.mjs` uses the same blocker test. Tokens are unchanged; the sim's automatic placements (the 突袭 landing tile, summon / device tiles) still treat them as melee, ground tiles only [ASSUMED].
- **Players notice**: 歌蕾蒂娅, 崖心 and 见行者 can be dragged onto a 高台 (PLAYING §4); there they attack but block nothing.
- Normative lines rewritten with it: §3 (placement), §6.6 (the bot); research 03 C2; DATA §2 (`placement`).
- Tests: `test/match/feedback1-placement.test.js` (#32-4: the data rule, cross-checked against character_table when the cache exists; `g.move` onto (10,4) / (11,4) / (12,4) of 战场#01(下半) for the six records; a 重装 refused with its text; ranged unchanged; client highlights = the server's tiles; swaps; the merge's elite on the 高台; the battle input and the sim on / off the 高台; bots), `test/match/board.test.js` (`positionClass` / `basePositionClass`), `test/ui/gameLogic.test.js` (the client mirror).