48 KiB
DESIGN §24 — 0.1.4 — community reports after 0.1.3
Part of DESIGN.md (the index; section numbers are global).
24. Community reports after 0.1.3
Reports after the 0.1.3 release. Each was checked against the official data and PRTS; anything with no source is marked [ASSUMED]. Where each is handled: a skill clip with no Begin and no own Idle plays once (德克萨斯 S2 剑雨) → §24.1; 高台 for every melee chess whose trait reads 「可以放置于远程位」 (GitHub #153, PR #69) → §24.2; the 5-阿戈尔 revives go to the first 3 members knocked out (GitHub #105, #140) → §24.3; 联防 阿戈尔 devours the operator in front whoever owns it (GitHub #140) → §24.4; a special terrain tile explains itself on a tap (GitHub #184, PR #185) → §24.5; 活性源石 was two different materials on the two boards (GitHub #184, PR #185) → §24.6; 阿戈尔's devoured base ATK is a 最终加算 and a fallen marker's marks still resolve (GitHub #165, PR #176) → §24.7; 缇缇 S2's sleep pulses count for her trait (GitHub #162) → §24.8; a sleeping enemy cannot be blocked and takes no block slot (GitHub #140) → §24.9. Merged pull requests keep their own notes where they changed the rules: deploy-timed skills as duration skills (PR #109, §5 skill contract), the teammate's hand / temp / equipment in prep scouting (PR #129), 灵巧's knocked-out aura and the hidden-layer bonds (PR #66, research 02).
24.1 A skill clip with no Begin and no own Idle plays once — 德克萨斯 S2 剑雨 (player report) — render/spine.js SpineActor.setSkill / update (SKILL_CLIP_MIN, skillClipOnce, skillEndPending)
- Seen (player report after 0.1.3): 德克萨斯's S2 剑雨 casts with no skill animation — she stays in her idle through the whole cast.
- Official [DATA]: her Front model has a single skill clip,
Skill(2.167 s) — noSkill_Begin, noSkill_Idle, noSkill_End, so the manifest roles areanims.skill {begin: null, loop: 'Skill', end: null}andanims.skills["0"] / ["1"]are the same clip. Both her skills are instant (durationType NONE, duration 0; S1 冲锋号令·γ型, S2 剑雨). The official plays thatSkillclip on the cast. - Cause:
setSkill(true)played a clip only when the skill had a Begin clip; withbegin === nullit fell through toelse if (this.mode === 'base') this._play(this._baseName())— the idle. The loop clip was reachable only through an attack (_attackClip), and an instant skill makes no attack inside its activation window: simskills.jsfires['skill', id, 1]and['skill', id, 0]in the same tick (activate→end('instant')) and only holds the anim codeSKILLfor 0.5 s (snapshot.js animOf), so the actor was back on its idle before a frame passed. - Now: a skill clip with neither a Begin nor its own Idle (its loop is not its idle, and it is not borrowed from the attack clip:
via !== 'attack') plays that clip once for its own length —max(SKILL_CLIP_MIN 0.2 s, dur(loop))— as the skill's own window; asetSkill(false)inside that window only marks the transition, so when the window ends the actor plays the skill's End clip (if it has one) or the idle. An instant skill therefore shows its animation instead of a one-frame flash, and a longer skill of the same shape still rests in its idle between attacks while its attacks use the loop, exactly as before. Untouched: a Begin clip with its queued idle / loop, a skill with an idle clip of its own (§23.13),via: 'attack'clips,loop === idlestances and enemies. - Scale: 90 such clips on 61 Front models (96 clips have no Begin at all — the 6 with an own Idle keep the §23.13 path); 57 of the 90 belong to instant skills.
- A PASSIVE skill reaches none of this through
activate():Skill.activate()returns early forkind === 'passive'(sim/skills.js); a passive goes through_startPassive()(active + mods +onStart). Three of the 57 instant clips are passives in the data — 琳琅诗怀雅 S1 仗义疏财 / S2 “见面礼” and 凯瑟琳 S1; 凯瑟琳 S1 turned out to bekind: instantin the sim (so it already casts), and the two 琳琅诗怀雅 ones now fire the same window from_startPassive— on at the deployment, off afterSKILL_ANIM_WINDOW(0.5 s) while the passive itself stays active. So all 57 are live. - Deploy-time passives with a duration were left alone here (since PR #109, merged in the same release, they are duration skills —
activateOnDeploy, §5 — with their own start / end events) (缄默德克萨斯 S1–S3 8–12 s, 野鬃 S1, 伊内丝 S3, 耀骑士临光 S2): the sim keeps a passive active until death, so how long its stance should show is a separate question. A clip that IS the normal attack clip (anims.skill.loop === anims.attack.loop: 缄默德克萨斯 S1, 砾, 普罗旺斯, 斯卡蒂 S2) is never played as a skill animation — the new branch requires a distinct clip, so a deploy-time passive on those shows no attack twitch. - Tests:
test/render/feedback4-texas-skill-clip.test.js(德克萨斯 S2: the clip plays, holds through the same-tick off, then the idle, with no base flash in between; two more real instant entries —char_140_whitewS1 日晷,char_206_gnosisS2 零度爆发; 银灰 S3 真银斩 — the same shape but long — rests without freezing on the clip's last frame and still attacks with the loop; the deploy-time passives 琳琅诗怀雅 S1/S2 hold their 1 s clip through the sim's 0.5 s window and then idle; a clip that is the attack clip falls through to the base clip; unchanged: 折桠 S2 begin → idle, 耀骑士临光 S3 own idle, 宴 begin → loop).test/sim/skills.test.jscovers the sim side (the passive window opens at the deployment and closes 0.5 s later, the passive staying active; a timed passive fires nothing).test/render/feedback3-skill-idle.test.js,feedback3-move-deploy.test.js,loadout-skill.test.jsand the full suite stay green.
24.2 高台 for every melee chess whose trait reads 「可以放置于远程位」 (GitHub #153, #148; PR #69; the owner's decision of 2026-10-05) — shared/highGround.js, match/board.js positionClass / placeClass, PlayerState._placementOf, ui/gameLogic.js piecePosition, match/bot.js planLayoutSteps
- Seen: GitHub #153 「歌蕾蒂娅无法放置在高台位置」: 0.1.3 (§23.35) let only elite 歌蕾蒂娅 carrying HOK-Y 淡金坠饰
stand on a 高台; a normal 歌蕾蒂娅, the elite with HOK-X (her default module) or with no module was refused
(
BAD_TILE, client 「近战单位只能部署在地面」). PR #69 (@sunstricken) raised the PRTS source for the trait: the line is the 钩索师 base branch trait, with no elite or module condition. (GitHub #148, a 突袭 melee operator shown on a 高台, is not this rule — see the note below.) - Official: character_table writes 「可以放置于远程位」 in the branch trait of the 钩索师 (歌蕾蒂娅
char_474_glady, 崖心char_173_slchan) and the 推击手 (见行者char_4036_forcer); PRTS 歌蕾蒂娅 · 特性 lists it under the branch trait (「技能可以使敌人产生位移 / 可以放置于远程位」), PRTS 新人入门 says both branches "可部署在高台和地面". Every module of the three keeps the line in its trait override (HOK-X, HOK-Y, PUS-X). PRTS 卫戍协议/帮助 §战斗部署 switches off 部署效果 ("携带Y模组的教官仍无法部署至高台位") — that is a module talent (教官 Y "可以额外部署在远程位"), not a trait, and no 教官 Y is in the pool. - Decision: the owner's decision of 2026-10-05 reverses the one of 2026-10-04 (§23.35) and follows PRTS: every MELEE chess whose trait reads 「可以放置于远程位」 may stand on a 高台 — normal or elite, with any module or none. Raised with the PRTS source in PR #69 by @sunstricken (its implementation covered 歌蕾蒂娅 only; its test page was not taken).
- Cause:
meleeOnHighGround(rec, moduleId)requiredisGolden,charId === char_474_gladyand the moduleuniequip_003_glady. - Now:
meleeOnHighGround(rec)is true for a record with position MELEE whose trait without a module (traitBase.desc, which an elite with modules carries, elsetrait.desc) contains 「可以放置于远程位」 — exactly six records: 歌蕾蒂娅, 崖心, 见行者, normal and elite. The loadout is no longer read:positionClass(rec)is'all'for them, andplaceClass(ps, rec)(the server's entry point:g.move, swaps, the merge tile,_evictIllegal, the audit, the invariants, the bot) returnspositionClass(rec). The client'spiecePositionlights the same tiles. data/ is unchanged (noplacementfield;build-data --offlinegives identical files). - Bots:
planLayoutStepsalready preferred a 高台 for a widened melee chess when one of its 高台 options covers an enemy road tile (owner 2026-10-04); that now covers all six records, any module. They still count as blockers in the lineup (basePositionClass), and with every ground tile taken a plain melee unit gets no plan while they still get a 高台. - Battle (unchanged): position stays MELEE — on a 高台 they attack and block nothing (
unit.groundfalse), on the ground they block. 地面干员 effects keep reading the melee position (§23.28). 崖心 and 见行者 are hidden chess this season (never in the pool, and a player cannot set their loadout), so 歌蕾蒂娅 is the one players meet; 见行者's elite PUS-X refund on a ranged tile (kits/tier3.js!unit.ground) stays moot. - [ASSUMED] (kept from §23.35): the sim's automatic tile searches (突袭 landing, summon / device tiles,
grid.canStandwithoutranged) still treat every MELEE unit as ground-only; a prep placement on a 高台 is the tile the battle deploys and redeploys on. - Note on GitHub #148 (「突袭近战干员部署在高台」, a battle screenshot on the 深水区 map): not changed here (0.2.0: a melee 突袭 member prefers a landing tile where it can block — the owner's decision of 2026-10-06, §25.11.3). The rule above admits no 突袭 operator on a 高台, and the 突袭 landing search is meant to be ground-only (the [ASSUMED] above); a melee 突袭 member standing on a 高台 after its jump needs its own reproduction.
- Integrator: §23.35 (title and Now) describes the 2026-10-04 rule; it needs a pointer here ("withdrawn in 0.1.4"), as §22.6 got in 0.1.3. §22.6's "Withdrawn in 0.1.3 (§23.35)" line can say the trait reading is back.
- Docs: DESIGN §3 (placement) and §6.6 (the bot), DATA (
placement), PLAYING §4, META (board.js), research 03 C2, code comments (board.js, gameLogic.js, bot.js, PlayerState.js, build-data.mjs). - Golden: 16 scenarios moved, all of them the ones with 歌蕾蒂娅 laid out by
tools/golden.mjs layout(it places bypositionClass, so a widened chess goes beside the enemy path — possibly on a 高台 — instead of on the busiest melee tile, and the others shift): roster-010, -012, -029, -040, -041; bond-deputShip-high, bond-indomShip-low, bond-preciShip-low, bond-preciShip-high, bond-suntShip-low, bond-swiftShip-high; boss-boss_2-pair, boss-boss_4-pair, boss-boss_6-pair, hidden-boss_8-pair, hidden-boss_10-pair. roster-031 (elite 歌蕾蒂娅 with HOK-Y, already widened) and the 16 matches did not move. - Tests:
test/match/board.test.js(the class table: the trait line, another wording, a ranged record, the six records on (10,4) and the ground, 角峰 refused);test/match/feedback1-placement.test.js(the six records are exactly the trait holders and every module keeps the line;g.moveonto the three 高台 of 战场#01(下半) for each record with its default / no module / every module (崖心 and 见行者: default only — hidden, no loadout); a 重装 refused; ranged unchanged; client highlights = the server under four loadouts; the swap; the battle input and the sim on / off the 高台; the bots plan each record on a 高台 that covers the road, a 重装 on the road, no 高台 fallback for it);test/ui/gameLogic.test.js(the drag mirror under four loadouts).
24.3 阿戈尔 5: the 3 revives go to the first 3 members knocked out (GitHub #105, #140; the owner's decision of 2026-10-05) — content/bonds/core.js installEgir
- Seen: GitHub #105 (comment 2) and #140 (comment 3), and a report the owner quoted: 「没被吃的阿戈尔干员也会占用复活名额」 — with 5 阿戈尔, "whatever the food chain eats, only 3 阿戈尔 stand at the end". #140 item 3 (screenshot): the chain ends on 斯卡蒂 (elite, her module saves her) and 幽灵鲨 falls and stays down. 0.1.3 (§23.20) fixed the 3 holders at battle start as the first 3 members by position, so uneaten front members held slots they never used while the eaten food further right stayed down.
- Official: PRTS 卫戍协议:盟约 下半/PRTS盟约记录, 阿戈尔: "<在场5名不同【阿戈尔】干员>前3名【阿戈尔】干员首次被击倒时立刻复活"
(bonds.json
max_free_respawn_cnt3); "※标记按付与顺序触发【吞噬】效果,目标首次被击倒后解除自身被付与但还未触发的【吞噬】 效果"; "※“复活”的实现方式为:受益者因移动之外的原因退场时下次部署的再部署时间和费用归零". PRTS names no holder order; §23.20 read "前3名" as the deployment order from the devour note and players' videos. - Decision: the owner's decision of 2026-10-05, following the players' reports: the 3 slots go to the FIRST 3 阿戈尔 knocked out, in knock-out order, the battle-start devour (5000 物理流失) included; a member never knocked out takes none (survivors = uneaten + 3); a member uses at most one slot, on its first knock-out, however many 阿戈尔 eat it; an operator's own revive must not use a slot.
- Cause:
installEgirfixedholdersatbattleStartfromegirOrder(position) and revived only them. - Now: the
deathhook (priority 11) takes a member operator's first'killed'of the battle; while fewer than 3 revives are spent it redeploys it at once (free) where it lies and counts one. A member's later knock-outs never take one, and a first knock-out after the 3 are gone stays down (left to 不屈 at priority 10 and the rest). The devour is unchanged: it resolves the marks in marking order and cancels a target's pending marks once it is knocked out (a new deployment or off the field), so a member marked by several 阿戈尔 spends one slot at most. Thefatalsavers all run before anydeathhook — kits' own savers (斯卡蒂's DRE-Y −50, others 10 … −60), 坚固维式重锤PRIO_REVIVE−100, M3茧甲PRIO_RESPAWN−101, 埃芒加德 −110 — so a save is no knock-out and leaves the slot for the unit's first real knock-out. The 0.1.3 rule that an in-place save spends the holder's revive is gone. A 联防 member entering down (FORCED_EXIT) is not knocked out (no slot) until it stands up and falls again; a 调和 member counts; each battle (normal, 联防, boss) counts its own; a mirrored boss player counts its own knock-outs. - Verified (the report "each devour of the same operator wastes a revive count"): not reproduced on 0.1.3 — a
member marked three times (A → B → X with C facing X) is knocked out by the first mark and the other two are cancelled;
usedlet each holder revive once. What players saw was the fixed holders: in that layout 0.1.3 revived B and left X down while uneaten A and C held the other two slots. Now B and X take two slots and one is left for the fight. - Measured (the §22.3 chain 浊心斯卡蒂 → 乌尔比安 → 幽灵鲨 → 海霓 → 深巡 → 隐现, normal and 联防): 乌尔比安 survives his mark (7046 HP, DEF 0), 幽灵鲨 / 海霓 / 深巡 are knocked out 1st–3rd and stand again at t = 0; all 5 members stand.
- [ASSUMED]: (1) 埃芒加德 (a band, not the operator's own kit or item) is treated like M3茧甲, the item revive the owner named: its in-place save is no knock-out and takes no slot — so with 埃芒加德 and 5 阿戈尔 the battle's first 3 knock-downs are its, and the first 3 real 阿戈尔 knock-outs after that are the bond's. Officially both are the same "复活" (0-time redeploy), and PRTS's 不屈 note says a held 复活 is consumed even when another effect redeploys the unit; if the owner reads 埃芒加德 as consuming the slot too, it is one condition in the hook. (2) A slot is counted only when the redeploy happens (a member that cannot be redeployed — removed, its tile taken — does not spend one).
- Not changed: 埃芒加德's own rule (the battle's first 3 operator knock-downs,
bands/battle.js, a separatefatalhook that shares no code with this bond) and its test; the devour; 不屈's priority. - Integrator: §23.20 (title, Now, [ASSUMED] (1)–(2)) describes the position rule and needs a pointer here ("withdrawn in 0.1.4"); §23.2's "a member it knocks out spends a 5-tier revive like any first knock-out" stays true.
- Docs: research 02 §3.6 and its JSON spec (data/bonds.json
egirShip.specregenerated withbuild-data --offline; a JSON compare shows onlyspec.tiers[2].effectchanged), the core.js comments. - Golden: no scenario moved — no corpus battle has a 5-阿戈尔 knock-out whose slot differs (bond-egirShip-high runs at 999 layers: nobody falls to the devour).
- Tests:
test/content/bonds_core.test.js— rewritten '阿戈尔 5: the 3 slots go to the first 3 members knocked out, wherever they stand …' (the last two by position revive, the first by position stays down, second knock-out final, the relocation case, 3 members: none), '阿戈尔 5 + the devour chain (GitHub #33, #105, #140) …' (6 members: the 3 eaten first revive, the 4th eaten stays down, the 2 uneaten take none — survivors = 2 + 3, normal and 联防), new '… a member devoured by several 阿戈尔 spends one slot at most …' (three marks, one knock-out, one slot; a tougher target falls to the third mark: still one), new '… an operator's own save uses no slot …' (斯卡蒂 elite at half HP in 联防: DRE-Y saves her from 乌尔比安's mark, all 3 slots left, her first real knock-out takes one; M3茧甲; 埃芒加德 spends its 3, the bond's 3 are untouched), rewritten '… in 联防 and on the boss field …' (a member entering down, 调和, the mirrored player). All five fail on 0.1.3's code.feedback3-egir-down,feedback2-doll,feedback1f-hammer,feedback3-ulpia-move,bands,itemspass unchanged.
24.4 联防: 阿戈尔 devours the operator in front whoever owns it — a teammate's standing or down operator too (GitHub #140 comment 4, #105; the owner's decision of 2026-10-05) — content/bonds/core.js devour
- Seen: GitHub #140 comment 4 (two screenshots of a remake 联防): 「阿格尔在联防阶段无法吃再部署状态的队友,导致无法获得 阿格尔加成,官方卫戍协议则可以正常获得加成」. #105 (on 0.1.2): 「联防阶段……攻击加成吃不到,作战的时候深巡3000攻,联防的时候就 500了」 — players report the 联防 block / ATK gains differ from the own combat.
- Official: PRTS 卫戍协议:盟约 下半/PRTS盟约记录, 阿戈尔: "战斗开始时【阿戈尔】干员依次吞噬身前一格干员(以及被自身吞噬的 【阿戈尔】干员身前一格干员)造成5000点物理流失并获得其基础攻击力和阻挡数"; the 备注's marking rule reads "身前一格干员" and "有自身标记的【阿戈尔】干员的身前一格干员" — no own-side limit. PRTS 卫戍协议/帮助 §联防阶段: "该阶段不能叠加层数", "上一阶段为 退场状态的干员强制退场".
- Decision: the owner's decision of 2026-10-05: in 联防 an 阿戈尔 devours whoever is on her front tile — a teammate's living operator, or a teammate's operator that entered 联防 knocked out (carry.down body) — and gains its base ATK and block count exactly as in a solo battle. 联防 keeps its ban on in-battle layer gains.
- Cause:
devour'sopAttook only the devouring player's operators (S.allyAt(…, pid),d.ownerId === pid), and the chain went on only through the player's own members. - Now:
opAttakes the operator on the front tile whoever owns it: a living one (S.allyAtwithout an owner), else one lying there since the 联防 start (egirDownAtStart: forced out, carry.down). The chain goes on through a marked 阿戈尔 byS.isMember— for the player's own operators exactly its members, as before; a teammate's by its own bonds. Everything else is the solo devour: the marker gains the target's base ATK (atkFlat when written; a 最终加算,atkFinal, since §24.7) and block count; a mark on a standing teammate's operator resolves (5000 物理流失 less its DEF, the kill credited to the marker), a mark on a down one resolves nothing (it stays forced out — §19.3's "still counts as standing for its marking"); the knocked-out teammate's operator belongs to its owner (their 5-tier slot, 不屈 …). Layers: none in 联防 (layerGainsEnabledfalse, unchanged). The rule is the same on a boss field (two players, no own-side limit). - Measured / verified: the 0.1.3 own-board case holds through unite.js's real carry: a 30-s own combat in which the
devour knocks out 幽灵鲨 → 歌蕾蒂娅 → fodder (3 阿戈尔, no revive) hands them to 联防 with
carryState.down, and 乌尔比安 gets exactly the own combat's atkFlat / blockCnt, with no layers. A teammate's fodder in front gives the same atkFlat / blockCnt / 物理流失 as an own one and as a solo battle. (§24.7 moved the ATK gain toatkFinal; the tests compare that key now, with the same outcome.) - Geometry (for the owner): the remake's two 联防 helpers stand on board cols 3–9 (left) and 11–17 (right, colOffset 8) with the road column 10 between them, and the boss halves are cols 3–9 / 11–17 too, so on the current maps no teammate's operator is ever on an 阿戈尔's front tile at the battle start. The tests place one on col 10 to exercise the rule. If the report meant the player's own operators waiting to redeploy, that has worked since 0.1.3 (§23.2; the reporter's screenshots are of a remake 联防).
- [ASSUMED]: (1) the chain continues through a teammate's 阿戈尔 (PRTS "【阿戈尔】干员" names the bond's operators, not an owner); (2) each player's devour is its own pass, in the players' order (p1's resolves before p2's starts), so a unit p1's marks knock out is no longer "in front" for p2's members.
- Docs: research 02 §3.6 (the [ASSUMED] own-operators line replaced) and its JSON spec (data/bonds.json
egirShip.spec.assumed[1], regenerated withbuild-data --offline; only that string changed), the core.js comments. - Golden: no scenario moved (no corpus field has a teammate's operator in front of an 阿戈尔).
- Tests:
test/content/feedback4-egir-unite.test.js(new): a teammate's living fodder on (10,10) in front of 乌尔比安 — the solo gains, the same 物理流失, knocked out and credited to him, no layer gain; a teammate's down operator — the standing one's gains, no 物理流失, no knock-out, still forced out (and the same for an own down one); the chain through a teammate's 幽灵鲨 equals the own chain, and her knock-out takes her owner's 5-tier slot; the own-board case throughuniteBattleOpts(carry.down from a real own combat, the own combat's gains). The first three fail on the previous code.feedback3-egir-down,bonds_core,unitedown,feedback3-unitecarrypass unchanged.
24.5 A special terrain tile explains itself on a tap (GitHub #184, player request 「建议加入对于特殊地形的单击信息提示」) — render/app.js (tileClick, emitTileClick), ui/fallbackField.js (the board tile), ui/screens/game.js (tileClick → terrainInfo, live.terrainStage / terrainTile), ui/gameLogic.js (terrainInfo, TERRAIN_TIPS), ui/detailPanel.js (TerrainDetail, resolveDetail)
- Asked (GitHub #184, after 0.1.3): a tap on a special terrain tile should say what it is. The mode draws 活性源石, 沼泽, 排气格栅, 深水区, the gates and the teleports, but nothing in the client names them — the legend lived in
docs/DATA.mdand the data only. - Why the ground was free: a press on a tile that had nothing on it only closed what was open (a piece / unit card, the selection).
render/app.jsnow emitstileClick { row, col }when neither a battle unit, a prep piece (the drag controller), a pen enemy nor the boss leader took the press, andui/fallbackField.jsemits the same from its own tile divs, so both field implementations behave alike. - What the card says:
gameLogic.terrainInfo(stage, row, col)reads the glyph fromstage.rows, its legend entry fromstage.tiles(tileKey / height / buildable / passability) and that stage's own terrain parameters fromstage.special[<terrain>]— the valuessim/content/devices.jsruns on: 活性源石damage 70 / atk 0.2 / attack_speed 20 / duration 300(PRTS 特殊地形 "部署于其上的我军和经过的敌军在{duration}s内每秒受到{damage}真实伤害…"), 沼泽's stack rule (intervalSec1,aspdPerStack−0.05,moveMulPerStack−0.05,maxStacks10,heavyWeight3, cleared on leaving — PRTS 沼泽控制), 深水区's drowning (damage 40, aspd −0.6, move ×0.6) plus 拒绝部署 (PRTS 深水区 地形信息;grid.js DEPLOY_REFUSED_TILES), 排气格栅's "operators on it cannot be targeted by enemy ranged attacks" — completed in review with the two halves that matter in play: the operator it blocks still gets attacked (the tile's buff is a 隐匿-likestealthflag,devices.js enterTerrain), and 深水区's drowning damage is sourceless damage that no 干员's 增伤 / 穿透 / 装备 applies to and is not 环境伤害 (tickDeepsea's tags aredot/periodic/deepsea, deliberately notterrain). The gates and teleports carry nospecial, so they key ontileKey— 红门 (enemy gate) / 蓝门 (protection objective) / 传送入口 / 传送出口, in the wordsdocs/PLAYING.mdalready uses. The card is a normalResultDialog-lessDetailPanelcard: read-only, no protocol frame, closed by the next field press (closesOnFieldPressnow liststerrain) or its own ✕. - An ordinary tile still says nothing (road / floor / wall / fence / bench → null): the press keeps its other meanings there — deselecting a piece, closing a card — and a drag still starts only on a piece.
- Coordinate spaces (corrected in review): a tap reports BOARD coordinates —
emitTileClickpicks withpickBoardTile, so the boss-prep transform (prepXf.toBoard) is applied once, in the renderer. (The first version reported the DRAWN tile fromgroundTileand the screen mapped it again, so on a Final Assault / Hidden Core prep the tap was converted twice and explained another tile.) Everywhere but that prep board the two spaces are the same: a 最终攻势 / 隐秘核心 battle renders the stage's own rows (GEO.BOSS_RECT), the normal and 联防 rects are stage rows. A boss PREP draws the player's half — stage rows 2–5 — as board rows 9–12, and the screen turns the board tile into the stage tile withgameLogic.fieldTilebefore the lookup (and reads the stage the board on screen is built from: the own one with the player's 机变 tile overrides,effectiveStage, or the plain stage while watching a teammate). - [ASSUMED]: that a player wants the official's 地形信息 here (the official shows terrain descriptions in its own level / terrain UI, not in-match — research 09 records none) and the wording of the mechanism lines: the numbers are the data's, the prose is ours (marked as ours in
TERRAIN_TIPS). A 机变 card that switches a terrain off (模拟战场演变 · 沙尘暴停止) is not reflected — the tip explains the TILE, not the round's modifier. - Tests:
test/ui/gameLogic.test.js(every terrain of the real stages with its own numbers: 活性源石 in 战场#04, 沼泽 in 战场#06, 排气格栅 in 战场#07, 深水区 in 战场#05; the four gate / teleport tiles; an ordinary glyph → null; out-of-range, fractional and legend-less input; the fact lines from the legend entry; a stage missing its parameters still explains the mechanism),test/ui/terrain-detail.test.js(the tap's target resolves into the card; a tip-less or malformed one does not),test/ui/terrain-tip.e2e.test.js(headless Chrome on the mock harness: a tap on 蓝门 / 红门 opens the card with its own line on BOTH field implementations — the engine canvas at the tile's own screen position and the DOM fallback's tile — while a tap on a floor tile opens nothing; and on a Final Assault PREP the tapped tile is the one the board tile maps to on the boss field, the regression the review asked for).
24.6 活性源石 is ONE material on both boards (GitHub #184, same report's second half) — render/style.js (ORIGINIUM, linearToSrgb255 / linearToHex), render/textures.js (originiumCanvas / originiumOverlay, MAT_DRAW.infection / infection2, PROC_LAYERS.originium / originium2, DEFAULT_ART_LAYERS.infection / infection2), render/tiles.js (variantMat, the pulse tint), render/board3d/materials.js (infectionMaterial)
- Reported (GitHub #184): 「感觉源石地板的图片有点奇怪(有点简陋,另外两个源石地板的贴图不一致)」 — two originium floors that do not match. They were two different materials: with the board art installed (or
?board=2d) the tile came from the 2D atlas cell — a beveled brick with seven randomly placed orange crystal clusters — while the 3D board drew a world-space crust with pulsing veins over the official concrete. And that 3D crust was multiplied by a per-tile fade (edge = smoothstep(0, 0.1, min(vUv, 1 − vUv))), so every tile of a 活性源石 area faded out at its own border and the area read as separate patches rather than one floor. - One palette:
style.js ORIGINIUMholds the crust's dark and lit sides, the vein and the crystal grains. The values are the 3D shader's own working colours (its fragment ends in#include <colorspace_fragment>, so they are linear); the canvas converts them withlinearToSrgb255— without that the 2D tile came out darker than the 3D one, which is what the first version of this change did. - One recipe: two octaves of noise make the crust mask (
smoothstep(0.38, 0.62, n·0.7 + n2·0.45)), the ridge of the fine octave its veins (smoothstep(0.86, 0.98, 1 − |2·n2 − 1|), brightened where the crust is), the rare top of a third octave the grains — the shader runs it over world-space noise,textures.js originiumCanvasover a toroidal field (the noise wraps, so a cell that is repeated over the board lines up with itself: the veins run through the tile's edge instead of stopping at it). - Both reach the tile's edges: the 3D material no longer has the per-tile fade, and the 2D cell has no frame any more (the old
bevelTileis gone). The mire / water / smog materials keep their own fades — this section is about 活性源石, the tile that was reported. - With the board art installed the 2D tile is composed exactly like the 3D one:
DEFAULT_ART_LAYERS.infectiondraws the official concrete slab (board3d/atlas.js concrete, the same atlas rect) and then the crust as a procedural layer; without the art the cell falls back toMAT_DRAW.infection(the procedural ground + the same crust). A second variant (infection2, the same art mirrored;PROC_LAYERS.originium2for the art path) is picked by tile (tiles.js variantMat), so a field of 活性源石 does not repeat one pattern. - Not [ASSUMED] — this is our own art: the palette and the recipe are ours, the tile it dresses and its numbers are the data's (a 机变 card can switch the terrain off, §24.5).
- Tests:
test/render/originium-art.test.js(the couplings: both renderers take the palette fromORIGINIUM, the canvas converts it, the recipe's thresholds agree, the 2D base slab is the same atlas rect the 3D board lays its crust on, the infection shader has no per-tile fade while the mire keeps its own, two variants are registered and picked, the pulse tint is the shared one),test/render/originium-cell.e2e.test.js(headless Chrome: the cell's veins hit the palette's displayed colour (linearToHex(ORIGINIUM.vein)), veins reach the border like the middle (no frame), the veins at opposite edges are alike (the cell joins itself), and the second variant is a different pattern).
24.7 阿戈尔: the devoured base ATK is a 最终加算, and a fallen marker's marks still resolve (GitHub #165 points 2–3, PR #176 by @SpiritedAwayCN; the owner's decision of 2026-10-06) — content/bonds/core.js devour, units.js _recalc (atkFinal), buffs.js ADD_KEYS
- Seen: GitHub #165 point 2 (PR #176 by @SpiritedAwayCN): PRTS makes the devoured base ATK a 最终加算, but the remake added it before the percentages, so the marker's ATK +% scaled it — 1000 base ATK, a +100 % skill and 2000 devoured gave 6000 instead of 4000. Point 3: the marks of a marker knocked out earlier in the pass were dropped, while PRTS cancels only the knocked-out target's pending marks — in A → B → C with B devoured first, C never took B's 5000.
- Official: PRTS 卫戍协议:盟约 下半/PRTS盟约记录, 阿戈尔 ※: 「该付与来源获得所有标记单位的基础攻击力(最终加算)和阻挡数」;
PRTS 游戏数据基础 属性基本公式
A_f = F_t[(A + D_p)(1 + D_t) + F_p]— the 最终加算 F_p comes after the 直接乘算 D_t and inside the 最终乘算 F_t. The marks: 「所有【阿戈尔】干员完成标记后,每个标记令目标受到一次5000点物理流失的【吞噬】效果」, 「标记按付与顺序触发【吞噬】效果,目标首次被击倒后解除自身被付与但还未触发的【吞噬】效果」 — only the knocked-out TARGET's pending marks are cancelled —, and 「【吞噬】的物理流失来源为被付与目标自身;单位被【吞噬】击杀时,击杀来源始终为对应标记的付与来源」. - Decision: the owner's decision of 2026-10-06: both as PRTS says (points 2 and 3 of #165, as PR #176 proposed).
- Cause:
devourput the gained ATK inatkFlat, inside(base + atkFlat) × (1 + atkPct) × atkMul, so every ATK +% scaled it; the resolve loop skipped a mark whose marker was neither alive nor down since its own combat (!(m.alive || downAtStart(m)), the rule since 0.1.0 — §22.3's "a marker off the field gives no further mark"). - Now: a new additive mod key
atkFinal, the 最终加算 (no existing channel had that place: 鼓舞 emulates one with a compensatedatkFlatrefreshed by its aura):ATK = ((base + ΣatkFlat) × (1 + ΣatkPct) + ΣatkFinal) × ΠatkMul. The devour buff is{ atkFinal, blockCnt }: 1000 base, +100 %, +2000 devoured → 4000 (0.1.3: 6000); a 最终乘算 (atkMul, e.g. 虚弱) still scales the whole, the devoured part included, as F_t. Every other key keeps its meaning. Each mark resolves unless its target was knocked out during the pass (or lies down since the 联防 start), whatever became of its marker: B's mark lands on C after B fell to A's, and a knock-out by it is credited to B (source: m). Unchanged (§24.3 / §24.4): the order of the marks and of their resolution, a target's pending marks cancelled once it is knocked out (revived or not), the 3 revive slots in knock-out order, the 联防 front-tile devour of any owner's operator, the down-at-start rules (it marks and resolves its marks, nothing resolves on it), the layers once per devoured unit. - Open (point 5): PRTS makes the 流失's source the target itself and only the kill source the marker.
Battle.loseHphas one source for the hooks and the credit (onlysourcelesssplits them), so thedamaged/fatalhooks still see the marker; a split needs a newloseHpoption and changes what content and every devour test read from the hooks — not the small, contained change the owner allowed, so it waits. - [ASSUMED]: a fallen marker's credited kill fires its kill effects like any credited kill in the engine (海霓's first kill, 斯卡蒂's count): PRTS names the kill source, not whether a knocked-out operator's traits still answer.
- Docs: DESIGN §5.2 / §5.3 and SIM.md §3 (the formula, the mod key), research 02 §3.6 and its JSON spec (data/bonds.json
egirShip.specregenerated withbuild-data --offline; onlyspec.algorithm[2]/[3]changed), the core.js comments; §24.4'satkFlatlines are marked. §22.3's marker line above is withdrawn by this section. - Golden:
bond-egirShip-high— the three devourers (elite 深巡 / 幽灵鲨 / 斯卡蒂 on row 9) no longer scale the devoured base ATK by their ATK +% (2867 / 2593 / 2024 ATK at 1 s instead of 3552 / 3288 / 2274), so its 19 enemies fall later (74.03 → 89.03 s);coop2-ABYSS-10-boosted— from round 8 on ai_0 has 3 more 阿戈尔 and 3 more 奥术 layers (海霓's first-kill trait, the one effect that adds both, now fires in that battle) and both bots' boss damage moves. The fallen-marker rule moves no scenario. - Tests:
test/content/bonds_core.test.js— new '阿戈尔 devour ATK is a 最终加算 …' (1000 / +100 % / +2000 → 4000, a 直接加算 still scaled, ×0.5 scales the whole), rewritten '… a fallen marker's marks still resolve …' (g2 falls to g1's mark, its own on the fodder lands: 2 × (5000 − 500)), new '… A → B → C with B devoured first …' (C falls to B's mark, the kill is B's); all three fail on 0.1.3's code.feedback3-egir-down,feedback4-egir-uniteandfeedback3-ulpia-movereadatkFinalinstead ofatkFlat;test/match/bosshp.test.jsrecomputes ATK with the 最终加算.facing,feedback2-doll,sim/corepass unchanged.
24.8 缇缇 S2 封护: every sleep pulse of the ward is a new 沉睡 entry for her trait (GitHub #162; the owner's decision of 2026-10-06) — kits/tier5.js (缇缇 skchr_titi_2 onTick), Battle.applyStatus (reenter)
- Seen: GitHub #162: the reporter remembers first-hand that in the official mode 缇缇's trait (garrison_125: 萨尔贡 / 精准 +1 per entry, elite +2, at most 24 / 48 per battle) climbs fast while S2 runs (「攻击范围内有陷入沉睡就开始迅速增加直至 上限」); in the remake an enemy held asleep by S2 counted once.
- Official: the trait 「<战斗中>每当范围内有敌人或干员进入沉睡或晕眩时,使已激活的【萨尔贡】【精准】层数+1(每场战斗至多24层)」 (elite +2, 48); S2 「…技能开启时使自身与攻击范围内生命比例最低的干员陷入沉睡直到技能结束,期间持续使自身与该目标周围的敌人陷入 沉睡…」 (12 s / elite 15 s). Neither the data nor PRTS gives the pulse's interval or sleep length.
- Decision: the owner's decision of 2026-10-06, following the report: each pulse counts as an entry.
- Cause: the pulse re-applies a 0.5 s sleep every 0.25 s (
AURA_DUR/AURA_IV), so an enemy beside a ward never leaves 沉睡 andstatusApplied.entered(the target did not carry the status) is true at the first pulse only; the garrison event counts new entries only. - Now: the pulse calls
applyStatus(enemy, 'sleep', { duration: 0.5, source, reenter }), a new opt-in that reportsenteredanyway (the sleep buff refreshes as before): once per enemy and pulse — an enemy beside both wards re-enters once — and not while a longer sleep from elsewhere holds it (more than 0.5 s left). The garrison rule is untouched: a refresh elsewhere is still no entry (the 125 test's stun refresh), and the sleeper must be in 缇缇's attack range (S.inRange) — an enemy at her back is slept by every pulse and adds nothing. One enemy in range: one entry per pulse (the 0.25 s timer runs in 1/30 s ticks: every 8 ticks, 0.27 s), the cap (24 / 48, with the ward's own two operators) 5.63 s into S2. No enemy wakes between pulses (the 0.5 s outlasts the pulse) and no status event is added, so the fight changes only through the earlier layers.enteredis read by the two garrison events only (冻结 for 初雪 / 银灰; 沉睡 / 晕眩 by garrison_125_a / _b alone). - [ASSUMED]: the pulse timing (0.25 s / 0.5 s, unchanged) and that each pulse is an entry (the reporter's memory; no source gives the official interval); a longer sleep from another source is no re-entry.
- Golden:
roster-042(缇缇 S2) — her trait's 24 layers now come within her first S2 (26.07–37.03 s) instead of over four casts up to 176.73 s; the totals are the same (capped), and the earlier 萨尔贡 / 精准 layers end the fight at 166.97 s instead of 182.03 s with 5 knock-outs instead of 7. - Tests:
test/content/garrisons_battle.test.js— new '缇缇 125 + S2 封护 (GitHub #162) …' (the real 缇缇, normal and elite, S2 cast: an enemy at her back sleeps through 8 s of pulses and adds nothing beyond the ward's two operators; an enemy in range beside both wards — one entry per pulse — takes the trait to 24 / 48 within 6.5 s; fails on 0.1.3's code). The 125 test (a stun refresh is not an entry) andkits_alt_t5's S2 test pass unchanged.
24.9 A sleeping enemy cannot be blocked: 沉睡 frees the block slot (GitHub #140; the owner's decision of 2026-10-06) — sim/battle/blocking.js _checkBlock, sim/battle/status.js applyStatus, sim/ai.js updateEnemy, sim/buffs.js (sleep), kits/ops/chess_char_3_12-blemsh.js (瑕光 S2) (the 0.2.0 files; 0.1.4 had them in sim/Battle.js and kits/tier3.js; the port: §25.9.1)
- Seen: GitHub #140 — a player tested 瑕光's S2 in the official mode (the 爱国者 stage): she put 4 enemies to sleep, the hounds that came next were still blocked, and after the skill no more were — 「被沉睡的敌人是不占用阻挡数的」. In the remake an enemy that fell asleep while blocked stayed blocked and kept its blocker's slot, so 瑕光 S2's sleepers filled her 3 block for the whole skill and the next enemies walked past her (the owner's first reply on #140 had kept that reading).
- Official: PRTS 异常效果 — the 异常组合 SLEEPING 沉睡 = 「无法行动+无敌+不可阻挡」, and the 异常效果 BLOCK_FREE 不可阻挡 = 「无法阻挡/被阻挡,自动解除阻挡」. 小睡 (DOZE) and 闭锁 (SHELTERING, 泥岩 S3) hold 不可阻挡 too — on operators, outside this change.
- Decision: the owner's decision of 2026-10-06, as PRTS and the report say: a sleeping enemy cannot be blocked and takes no block slot.
- Cause: 沉睡 was in neither block check.
Battle._checkBlockrefusedunblockable/levitate/fearonly, the release inBattle.applyStatusfired for those three flags, andai.js updateEnemyreturns early for an enemy that cannot act (a sleeper isstuntoo:units.js) before its own release check. The sleep template'snoBlockonly stops a sleeping operator from blocking. - Now: 沉睡 is one of the 不可阻挡 states.
applyStatus(enemy, 'sleep')releases the enemy at once (_unblock: the blocker's slot frees for the next enemy; a 隐匿 enemy's block-end window starts, §22.8);_checkBlocknever blocks a sleeper; a sleep added as a plain buff (flags.sleep) is let go on the enemy's next update (the cannot-act branch ofupdateEnemy). The sleeper stays where it fell asleep — overlapping its former blocker and whatever that blocker takes next — and when it wakes it is checked like any unblocked enemy: blocked again where it stands by a blocker in contact with a free slot (usually the same one), else it walks on through the full blocker. 瑕光 S2 (the ground enemies on her tile and the enemies she blocks): the sleepers free her 3 slots, she blocks the next 3 during the skill, and when S2 ends the sleepers wake beside her and, her slots full, walk on — the report. A stun, a freeze or a refused sleep (23 enemies are 沉睡免疫) keeps the block as before; sleeping operators are unchanged (they already released what they block). - Other readers of the block (checked):
Battle.blockedTargets— blocked-first targeting, the DEFAULT trigger, the 强攻手 / 重剑手 / 推击手 attack on every blocked enemy — no longer lists a sleeper. OnlyhitSleepattackers could target one, and of those only 瑕光 blocks: her 仁慈 (sleepers in her range first, ×atk_scale) reads her range, so a sleeper she let go in front of her is still her first target and one behind her is out of reach. A 要塞 that blocked nothing but a sleeper stalled (its melee branch found no target); it now fires its ranged splash. The "while blocking" effects (unit.blocking) do not count a sleeper; “庞贝”'s self-blast clock restarts when a sleep ends its block (as a 浮空 already did); the snapshot's blocked bit (UF.BLOCKED, the client's block marker) follows. 荒芜拉普兰德 S1's 「非移动敌人」 reads the sleep itself. - [ASSUMED]: a woken sleeper is re-blocked in the enemy update order (an enemy that reaches the blocker earlier in the same tick takes the slot first; PRTS gives no order) and where it stands, never pulled back to the blocker's front; “庞贝”'s clock restarts although its talent lists 沉睡 among the pauses — the 不可阻挡 ends the block, and 「解除阻挡时重置计时」.
- Golden: every moved scenario fields one of the three kits that sleep enemies (小满 S2, 瑕光 S2, 缇缇) and sleeps enemies
that were blocked (counted with a probe):
roster-003(小满 S2, 6 such sleeps: 82 kills instead of 83, 3 leaks instead of 2 at the 110 s timeout);roster-005(elite 小满 S2, 3: cleared at 136.73 s with 83 kills and 1 leak instead of the 140 s timeout with 82 and 2);roster-013(缇缇 S3, 13: the same fight — only theblockedcount, 23 → 34 with the woken sleepers held again, and the snapshot hashes, the blocked bit, move);roster-032(缇缇 S1, 4: 7 knock-outs instead of 5, 缇缇 among them, and 24 萨尔贡 layers instead of 34; same kills and leaks);roster-038(elite 缇缇 S2, 2: ends at 197.43 s instead of 197.27 s, 4 knock-outs instead of 3);roster-039(瑕光 S2, normal and elite, 32 + 22: the enemies they hold and put to sleep walk on when they wake with both blocks full — 41 of 52 killed instead of 47, 11 leaks instead of 5 at the 170 s timeout);roster-042(缇缇 S2, 4: ends at 161.17 s instead of §24.8's 166.97 s);bond-maniShip-high(小满 S2 among the members, 1: only theblockedcount, 29 → 30, and the snapshot hashes);coop4-FUNNY-6(ai_0's 缇缇: from round 9 her trait's 萨尔贡 / 精准 layers and ai_0 / ai_2's boss damage move);coop4-ABYSS-7(ai_2's 小满: rounds 11–12, one more kill and one fewer leak in round 11, four more kills in round 12);coop2-NORMAL-8-serverrun(ai_1's 缇缇: from round 10 its battles move — one more leak in round 10 — and the team is eliminated in round 13 instead of winning after round 14).fieldsdid not move. - Tests:
test/sim/feedback4-sleep-unblock.test.js(new; every case fails without this change) — a block-1 operator holding A: A falls asleep → released at once, the slot frees and B is blocked, A stays put; A wakes while B holds the slot → not blocked, it walks on; B knocked out while A sleeps → A stays unblocked beside the free operator and is blocked again the moment it wakes, never having moved; a plain-buff sleep is let go on the next update, a stun or a refused sleep keeps the block;blockedTargetsdrops the sleeper and takes B; the real 瑕光 S2 — her 3 blocked enemies sleep and are let go, she blocks the next 3 during S2, and when it ends the sleepers wake with no room and walk on.playtest5_blocking(its 瑕光 S2 test included),combat,engine-requests,kits_t1t2/kits_alt_t2(小满),kits_t3/kits_alt_t3(瑕光),kits_t5/kits_alt_t5/garrisons_battle(缇缇) and the other sleep and block suites pass unchanged.