feedback5 WY: a newly merged elite 拉普兰德 is a new 拉普兰德 — its first manual refresh this round adds +8 even when its copies already fired (GitHub #169; the owner's decision of 2026-10-06)
garrison_123 「<刷新时>若为本回合首次主动刷新…此干员在整备区时也有效」 counts per piece (§21.1). No source says whether a merge resets the count; since 0.1.1 _mergeChess carried the copies' highest per-piece round count onto the elite ([ASSUMED], conservative), so two white copies that had fired this round made an elite that did not fire again. The owner decided on 2026-10-06 that a newly merged elite counts as a new 拉普兰德. server/match/player/acquire.js _mergeChess no longer carries the per-piece round counters (拉普兰德's refresh count, garrison:SERVER_GAIN_BOND_LAYER_BY_REFRESH_CNT:refreshes, is the only one): the elite starts at 0 like any new piece; an in-place promotion keeps its count. test/match/feedback1-meta.test.js: the test that locked the old inherit is updated on purpose (the elite adds +8 on its next refresh, once), and the case "two white copies refreshed this round, merge, refresh → +8" is new; both fail without the change. Docs: META (counter API, edge cases), PLAYING, the comments (PlayerState, pieces, effectsMeta, garrisons/meta), DESIGN §25.13.3 and the §25.10 table. Golden unchanged (no corpus match merges 拉普兰德 after a refresh in the same round).
This commit is contained in:
1 parent
269d7b0bcf
commit
661f1c074e
9 files changed
+79
-36
No files matched your search
+7
-7
@@ -398,8 +398,8 @@ board() hand() temp() piece(uid) pieceAt(row, col) pieceBonds(uid) garrisonsOf(u
|
||||
shopSlots() effect(id)` (`piece(uid)` adds `area`, `holderUid`, `idx`; "身前一格" of (r, c) is (r, c + 1)),
|
||||
counters `counter(k) setCounter(k, v) incCounter(k, n)` (player scope, persistent; prefix keys with your module) and
|
||||
`pieceCounter(uid, k) incPieceCounter(uid, k, n)` (per piece, current round only: 0 in a new round and for a new piece —
|
||||
bought, granted, transformed —; a move keeps it; an elite merged this round keeps the highest of its copies'
|
||||
[ASSUMED]; `PlayerState.pieceRoundCount`; prefix keys with your module too) — 拉普兰德's "本回合首次主动刷新" is the
|
||||
bought, granted, transformed, or an elite merged this round (GitHub #169, the owner's decision of 2026-10-06) —; a move
|
||||
keeps it; `PlayerState.pieceRoundCount`; prefix keys with your module too) — 拉普兰德's "本回合首次主动刷新" is the
|
||||
first manual refresh that copy witnesses (player feedback after 0.1.0: "获得该干员后该回合的首次刷新" also stacks; a copy
|
||||
bought after selling one this round is a new copy and fires on its own first refresh [ASSUMED]).
|
||||
|
||||
@@ -825,11 +825,11 @@ round was over. The official 1 s `broadcastBeginDelay` is not modelled.
|
||||
* Promotions by effects (升华, 博士投影) keep the equipment; merges return it; 突变细胞's transformation returns it (the
|
||||
cell included) before its new operator is gained into the 整备区 — the carrier's tile is left empty (official footage,
|
||||
DESIGN §21.1).
|
||||
* An elite merged in a round keeps the highest per-piece round counter of its copies (`pieceRoundCount`): an elite made
|
||||
from 拉普兰德 copies that already fired this round does not fire again before the next round (conservative; the
|
||||
official server's instance handling is not observable). A 拉普兰德 bought after selling one in the same round is a new
|
||||
copy and fires on its own first refresh ("获得该干员后"; each such +4 costs 3 + 1 refresh − 1 refund and needs her in
|
||||
the shop).
|
||||
* An elite merged in a round is a new piece: its per-piece round counters start at 0 (`pieceRoundCount`), so an elite
|
||||
拉普兰德 fires +8 on its own first manual refresh of the round even when its copies already fired (GitHub #169; the
|
||||
owner's decision of 2026-10-06 — a newly merged elite counts as a new 拉普兰德; until 0.2.0 it kept the highest count of
|
||||
its copies). A 拉普兰德 bought after selling one in the same round is a new copy and fires on its own first refresh
|
||||
("获得该干员后"; each such +4 costs 3 + 1 refresh − 1 refund and needs her in the shop).
|
||||
* Chess granted by effects need a free pool copy unless `requirePool: false` (then they hold 0 copies).
|
||||
* Boss-round `local` pack spawns (boss parts) all spawn; content scripts (bosses.js) decide their behaviour.
|
||||
* The Final Assault ends as a defeat when every field finished with the boss pool above 0 (boss escaped).
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@
|
||||
- **干员持有(补位)**:大厅、房间和「确认本局信息」里的「干员调配」按钮打开的界面有两个标签页:「干员调配」选择每名干员携带的技能和精锐的模组;「**干员持有**」可以把你没有的干员**下掉**(点一下卡片切换「持有 / 未持有」)。和官方一样只有 55 张「普通」棋子能下掉(界面按阶级列出本赛季商店里的 53 名,每张写着由谁替补、用几技能);74 张「预设」棋子(多是 1–2 阶的常见干员)无论是否持有都由本人上场,不在其中。默认全部持有。下掉的干员和官方一样**显示为替补干员**:商店和晋升奖励的卡片、整备区和临时整备区里的单位、作战区、结算界面的阵容,都是替补干员的头像、名字和模型(例如银灰显示为 Sharp),带一个小小的「替补」标记;详情卡显示替补干员的立绘、名字、技能、天赋和模组,名字下面写着「银灰的替补」。**盟约、特质、阶级、价格、合成仍按原干员**(卡片和详情卡上的盟约、特质就是原干员的),作战时的属性、技能、天赋、模组、攻击范围、近战位 / 远程位都是替补干员的(例如银灰 → Sharp 的 2 技能「亮剑」)。同一名替补干员可能替补好几名干员(Sharp 替补忍冬、银灰、隐德来希等),它们按各自的原干员分别合成:看阶级和盟约区分,点开详情卡能看到替补的是谁。同盟模拟中队友查看你的整备区和作战区、你的盟约成员时,看到的也是替补干员。替补的技能和模组由棋子固定,「干员调配」对它不生效;替补干员没有召唤物(下掉缪尔赛思就没有流形)。这是**局外设置**:下一局开始时生效,对局中修改只保存到下一局;同盟模拟中只影响你自己的棋子,队友的棋子按队友的设置。没有官方的好友借用:想用本人上场,改回「持有」即可。「全部持有」一键恢复,「导出 / 导入」可以把设置带到别的浏览器;大厅的「干员调配」按钮上会显示「替补 N」提醒你有几名干员被下掉。
|
||||
- **自选编队**:同一个界面的第三个标签页「**自选编队**」有 4 个名额:5 阶两个、6 阶两个。点名额打开选择列表,每个名额可以放一名**你持有的 6★ 干员**(棋子池里没有的那些,标「已持有」:3 个技能任选其一,精锐的模组任选其一或不装备;只在集成战略或生息演算里生效的模组不能选:ISW-A、SO-A / SO-B、RA-A)或一名**原型干员**(标「原型」:Sharp、Mechanist 等 9 名 6★,5 阶另有 6 名 4★ 预备干员;技能和模组与补位时相同,不能更改)。同一名原型干员可以同时放进一个 5 阶和一个 6 阶名额;你持有的干员只能放一个名额;同一阶的两个名额不能放同一人。列表只列出已经做好战斗逻辑的干员(原型干员全部可选,持有的 6★ 随战斗逻辑完成陆续加入)。对局中,自选干员**只出现在你自己的商店里**:调度中心升到 5 级后才会刷出 5 阶名额的干员,6 级后才会刷出 6 阶的;刷新概率和同阶干员一样,每个名额有自己的库存(5 阶 8 张、6 阶 5 张,不与队友共用),价格与同阶干员相同,晋升奖励里也可能出现;效果、奖励或机变卡「获得一名随机干员」时,也可能抽到你自己的自选干员(按那个效果自己的阶级规则,不看调度中心等级)。三张合成精锐,精锐时所选模组生效(5 阶 1 级、6 阶 3 级)。自选干员**没有特质**,盟约按干员的实际阵营分配(推进之王是维多利亚,煌同时是炎和维多利亚,没有对应阵营的是协防干员),和普通棋子一样计入盟约人数;本局禁用了它全部的盟约时,它不会出现在商店里(协防不会被禁用),开局的「确认本局信息」、选策略时的「本局信息」和对局中的「本局信息」都会在「本局禁用干员」下写明是哪一名。商店卡片、整备区、作战区和详情卡上都标着「自选」,详情卡显示所选的技能和模组。这同样是**局外设置**:下一局开始时生效,对局中修改只保存到下一局;AI 队友不使用自选;你开启 AI 托管时,AI 会优先买你的自选干员,并在快能刷出自选名额时提早一回合升级调度中心;信标不会把自选干员送给队友。「导出 / 导入」「全部清空」可用,大厅的「干员调配」按钮上会显示「自选 N」。
|
||||
- 每回合商店会自动刷新未冻结的格子。
|
||||
- **「本回合首次主动刷新」按每名干员各自计算**(拉普兰德):她在本回合遇到的第一次主动刷新生效,在整备区里也算——先刷新、再买下拉普兰德,下一次刷新她仍会叠层;同一回合内用已经生效过的拉普兰德合成的精锐,本回合不再生效。
|
||||
- **「本回合首次主动刷新」按每名干员各自计算**(拉普兰德):她在本回合遇到的第一次主动刷新生效,在整备区里也算——先刷新、再买下拉普兰德,下一次刷新她仍会叠层;本回合新合成的精锐算作一名新的拉普兰德——即使合成用的几名本回合已经生效,精锐在本回合的下一次主动刷新仍会 +8。
|
||||
|
||||
## 4. 摆阵与朝向
|
||||
|
||||
|
||||
+21
-3
@@ -22,8 +22,8 @@ rules (GitHub #181, #187 / #170, follow-ups) → §25.7; WE2, data / match / UI
|
||||
what stays [ASSUMED] and the questions left to the owner → §25.10. The closing additions: the owner's decisions of
|
||||
2026-10-06 (望's stock, skill triggers through summon areas, the 突袭 landing, GitHub #148) and the items the sources
|
||||
settled (泥岩's 闭锁; 忍冬, 深靛, 阿) → §25.11; release packaging and the asset manifest (the 自选 voices, the summons
|
||||
with no Spine model) → §25.12; round 18 — 拟态物质 at the copy cap (GitHub #207), 华法琳's 7 / 14 (GitHub #175, PR #192)
|
||||
→ §25.13.
|
||||
with no Spine model) → §25.12; round 18 — 拟态物质 at the copy cap (GitHub #207), 华法琳's 7 / 14 (GitHub #175, PR #192),
|
||||
a merged elite 拉普兰德 as a new one (GitHub #169) → §25.13.
|
||||
|
||||
Decisions of the owner: 2026-10-04 — the server stays on plain `node:http` + `ws`, no web framework (§25.1);
|
||||
2026-10-05 — the 补位 plan (§25.3); the 自选编队 rules, the collab operators out of the pool, ISW-A refused (§25.4);
|
||||
@@ -33,7 +33,7 @@ enemy in range, 纯烬艾雅法拉 S1 at full SP, 伺夜 S1 only with his pack o
|
||||
does not own shows as its stand-in everywhere (the owner's recall of the official mode, §25.3); the enemy damage frame
|
||||
kept (§25.7.2); 望's placed 棋子 count in her stock, skills that act through summons cast on an enemy in a summon's area,
|
||||
a melee 突袭 member lands where it can block (§25.11); 华法琳's granted trait at the data's 7 / 14, replacing the 12 / 24
|
||||
lock (§25.13.2).
|
||||
lock (§25.13.2); a newly merged elite 拉普兰德 counts as a new 拉普兰德 (§25.13.3).
|
||||
|
||||
### 25.1 Refactor and tooling: the big files split, golden results, lint, types, import boundaries (refactor plan; owner's decision of 2026-10-04: no web framework) — `server/http/`, `server/match/match/`, `server/match/player/`, `server/sim/battle/`, `content/enemies/`, `kits/ops/`, `public/js/ui/gameLogic/`, `render/fx/`, `render/app/`, `screens/game/`, `tools/golden.mjs`, `tools/check-imports.mjs`, `eslint.config.js`, `jsconfig.json`
|
||||
|
||||
@@ -662,6 +662,7 @@ its tile, §18.1; §25.9.2). Where a 0.1.4 change met 0.2.0 work:
|
||||
| Skills that act through summons cast on an enemy in a summon's area: 麦哲伦 S1, 令 S3, 电弧 S2 / S3 (§25.11.2) | the owner, 2026-10-06 (the larger-range rule) |
|
||||
| A melee 突袭 member lands on a tile where it can block before one where it cannot (GitHub #148, §25.11.3) | the owner, 2026-10-06 |
|
||||
| 华法琳's granted trait stops at the data's 7 / 14 (PRTS 3/27: lowered from 12 / 24; GitHub #175, PR #192, §25.13.2) | the owner, 2026-10-06 (replaces the 12 / 24 lock) |
|
||||
| A newly merged elite 拉普兰德 is a new 拉普兰德: +8 on its first manual refresh of the round (GitHub #169, §25.13.3) | the owner, 2026-10-06 (was [ASSUMED]: the copies' count carried) |
|
||||
|
||||
Settled by the sources in W5V, no longer questions: 泥岩 S3's dormancy is 闭锁 — she lets go and blocks nobody (PRTS,
|
||||
§25.11.4); 阿 S2 / S3 keep the data's DEFAULT, 忍冬 S2's and 深靛 S1's notes are what the kits do (§25.11.5).
|
||||
@@ -876,3 +877,20 @@ ported PR credits its author there and here.
|
||||
`test/content/garrisons_battle.test.js` (updated on purpose: the cap is the data's 7 / 14 — the 7th cast reaches it,
|
||||
the 8th adds nothing), `test/match/feedback1-gaps.test.js` (the client-result bound with 华法琳 + 古米: 60 + 4·3 +
|
||||
2 × cap passes, one more is refused), `test/ui/playtest3.test.js` (both cards show 至多7层 / 至多14层).
|
||||
|
||||
#### 25.13.3 A newly merged elite 拉普兰德 is a new 拉普兰德: its first manual refresh this round adds +8 (GitHub #169; the owner's decision of 2026-10-06) — `server/match/player/acquire.js _mergeChess`, `sim/content/garrisons/meta.js SERVER_GAIN_BOND_LAYER_BY_REFRESH_CNT`
|
||||
|
||||
- **Seen** (GitHub #169, new comments): two white 拉普兰德 that had already fired this round merged into the elite, and
|
||||
the elite's next refresh added nothing. The triage confirmed the rest of the report is what the code does: each white
|
||||
copy fires +4 on the first manual refresh it witnesses, also in the 整备区 (§21.1).
|
||||
- **Official**: garrison_123 「<刷新时>若为本回合首次主动刷新,使已激活的【叙拉古】层数+4,此干员在整备区时也有效」 (the elite +8;
|
||||
bb `refresh_cnt` 1); PRTS 下半 3月27日更新#2 changed only the amounts (+3 / +6 → +4 / +8). No source says whether a merge
|
||||
resets the count; the code carried the copies' highest count onto the elite ([ASSUMED], conservative, since 0.1.1).
|
||||
- **Decision**: the owner's decision of 2026-10-06 — a newly merged elite counts as a new 拉普兰德.
|
||||
- **Now**: `_mergeChess` carries no per-piece round counter over (拉普兰德's refresh count is the only one): the elite starts
|
||||
at 0 like any new piece, so its next manual refresh this round adds +8 and a later one nothing; an in-place promotion
|
||||
(升华, 博士投影) keeps the piece and its count. A copy re-bought after a sale is still a new copy ([ASSUMED], unchanged).
|
||||
- Golden: unchanged (no corpus match merges 拉普兰德 after a refresh in the same round). Tests:
|
||||
`test/match/feedback1-meta.test.js` (updated on purpose: the elite made from copies that already fired adds +8 on its
|
||||
next refresh, once; new: two white copies refreshed this round, merge, refresh → +8). Docs: docs/META.md (the counter
|
||||
API, the edge cases), docs/PLAYING.md, the code comments.
|
||||
@@ -35,7 +35,7 @@
|
||||
// outside PREP (a SETTLE merge's elite waits in temp for the next prep).
|
||||
// * Per-piece round counters (pieceRoundCount, piece.meta.round): an operator's own counts of the current round
|
||||
// (拉普兰德: the manual refreshes she witnessed — player feedback after 0.1.0); a new piece starts at 0, an elite
|
||||
// merged this round keeps the highest of its copies' [ASSUMED].
|
||||
// merged this round too (a new 拉普兰德 — GitHub #169, the owner's decision of 2026-10-06).
|
||||
// * Transformations (transformChess, 突变细胞 — PRTS 备注 "生效时,原干员销毁,获得一名高一阶的随机初始干员"): a destroy
|
||||
// followed by a gain. The carrier leaves wherever it stands (a board tile is freed, the deploy count drops), its
|
||||
// equipment — the cell included — returns to the hand first (overflow temp), then the new chess is gained like any
|
||||
|
||||
@@ -493,8 +493,8 @@ export function makeCtx(m, ps, source, hook, ev = null) {
|
||||
counter: (k) => (Number.isFinite(ps.counters[k]) ? ps.counters[k] : 0),
|
||||
setCounter: (k, v) => { if (typeof k === 'string' && Number.isFinite(v)) ps.counters[k] = v; return ps.counters[k] ?? 0; },
|
||||
incCounter: (k, n = 1) => { if (typeof k !== 'string' || !Number.isFinite(n)) return 0; ps.counters[k] = (Number.isFinite(ps.counters[k]) ? ps.counters[k] : 0) + n; return ps.counters[k]; },
|
||||
/** Per-piece counter of the current round (0 in a new round / for a new piece; an elite merged this round keeps the
|
||||
* highest of its copies' — PlayerState.pieceRoundCount). */
|
||||
/** Per-piece counter of the current round (0 in a new round / for a new piece, an elite merged this round included —
|
||||
* PlayerState.pieceRoundCount). */
|
||||
pieceCounter: (uid, k) => { const l = ps.find(uid); return l ? ps.pieceRoundCount(l.piece, k) : 0; },
|
||||
incPieceCounter: (uid, k, n = 1) => { const l = ps.find(uid); return l ? ps.bumpPieceRoundCount(l.piece, k, n) : 0; },
|
||||
|
||||
|
||||
@@ -98,13 +98,10 @@ export class PlayerAcquire {
|
||||
for (const it of l.piece.items || []) items.push(it);
|
||||
l.piece.items = [];
|
||||
}
|
||||
// a new piece: this round's per-piece counters start at 0, none is carried over from the copies — a newly merged
|
||||
// elite 拉普兰德 is a new 拉普兰德 and fires +8 on its own first manual refresh this round (GitHub #169; the owner's
|
||||
// decision of 2026-10-06; pieceRoundCount)
|
||||
const elite = this.newPiece('chess', goldenId, { poolCopies: copies });
|
||||
// this round's per-piece counters: the highest of the copies' (an elite made from 拉普兰德 that already saw their
|
||||
// first refresh this round does not fire again this round — [ASSUMED] conservative, pieceRoundCount)
|
||||
for (const l of consumed) {
|
||||
const rc = l.piece.meta && l.piece.meta.round;
|
||||
if (rc && rc.r === this.m.round) for (const [k, v] of Object.entries(rc.n)) this.bumpPieceRoundCount(elite, k, Math.max(0, v - this.pieceRoundCount(elite, k)));
|
||||
}
|
||||
const deployed = consumed.filter((l) => l.key && !this.board.has(l.key)).map((l) => ({ key: l.key, dir: pieceDir(l.piece) }));
|
||||
const toTile = (t) => { elite.dir = parseDir(t.dir) || 'RIGHT'; this.board.set(t.key, elite); return 'board'; };
|
||||
const tile = mergeTile(deployed, (r, c) => this._legal(elite, r, c));
|
||||
|
||||
@@ -114,9 +114,10 @@ export class PlayerPieces {
|
||||
|
||||
/**
|
||||
* Per-piece counter of the current round (`piece.meta.round` = { r, n: { key: count } }): 0 for a key not counted yet
|
||||
* this round. The counters belong to the operator: a move keeps them, a new piece (bought, granted, transformed)
|
||||
* starts at 0, and an elite merged this round keeps the highest count of its copies (_mergeChess) — 拉普兰德's
|
||||
* "本回合首次主动刷新" is the first manual refresh she witnesses (player feedback after 0.1.0, garrisons/meta.js).
|
||||
* this round. The counters belong to the operator: a move keeps them, a new piece (bought, granted, transformed, or
|
||||
* an elite merged this round — _mergeChess carries none over: GitHub #169, the owner's decision of 2026-10-06)
|
||||
* starts at 0 — 拉普兰德's "本回合首次主动刷新" is the first manual refresh she witnesses (player feedback after 0.1.0,
|
||||
* garrisons/meta.js).
|
||||
*/
|
||||
pieceRoundCount(piece, key) {
|
||||
const rc = piece && piece.meta && piece.meta.round;
|
||||
|
||||
@@ -22,8 +22,9 @@
|
||||
// of any tier (the text gives no tier cap); a bond without an available chess falls through to the next tied one.
|
||||
// * [ASSUMED] 松果: the "免费特殊招募" is a free pick-one offer of `rewardOffer.count` (3) chess of the pool's tier.
|
||||
// * 拉普兰德 SERVER_GAIN_BOND_LAYER_BY_REFRESH_CNT "若为本回合首次主动刷新": per copy — the first manual refresh this
|
||||
// operator witnesses in the round (players' report after 0.1.0); [ASSUMED] an elite merged this round keeps its
|
||||
// copies' count, and a copy bought after selling one this round is a new copy (fires on its own first refresh).
|
||||
// operator witnesses in the round (players' report after 0.1.0); an elite merged this round is a new 拉普兰德 and
|
||||
// fires +8 on its own first one (GitHub #169, the owner's decision of 2026-10-06); [ASSUMED] a copy bought after
|
||||
// selling one this round is a new copy (fires on its own first refresh).
|
||||
// "本回合每刷新过1次" (SERVER_ADD_REFRESH_CNT_MULTIPLIER_BOND_LAYER, 阿罗玛 / 安洁莉娜 / 售出时)
|
||||
// fires on another event and reads the player's refreshes of the round (roundStats), like 本回合每获得过 / 每花费.
|
||||
|
||||
@@ -198,11 +199,12 @@ H.SERVER_ADD_REFRESH_CNT_MULTIPLIER_BOND_LAYER = {
|
||||
// refreshes this copy witnessed this round (board or hand), so a 拉普兰德 bought after the round's first refresh still
|
||||
// fires on the next one (players' report after 0.1.0: "获得该干员后该回合的首次刷新" also stacks — the official behaviour;
|
||||
// read as each trait instance counting its own SERVER_REFRESH_SHOP triggers against bb.refresh_cnt). A re-triggered trait
|
||||
// (ev.trigger) is no manual refresh: it neither fires nor counts. A new copy (bought, granted) starts at 0. [ASSUMED]:
|
||||
// the copies of an elite merged this round pass on their highest count (PlayerState.pieceRoundCount — no second trigger
|
||||
// that round, conservative); a copy bought after selling one this round is a new copy — "获得该干员后" — and fires on its
|
||||
// own first refresh (the server cannot tell it from any other copy; each such +4 costs her price + a refresh − the
|
||||
// 1-fund refund, and needs her in the shop again).
|
||||
// (ev.trigger) is no manual refresh: it neither fires nor counts. A new copy (bought, granted) starts at 0, and so does
|
||||
// an elite merged this round: a new 拉普兰德, its own first manual refresh this round adds +8 even when its copies
|
||||
// already fired (GitHub #169; the owner's decision of 2026-10-06 — PlayerState._mergeChess carries no counter over).
|
||||
// [ASSUMED]: a copy bought after selling one this round is a new copy — "获得该干员后" — and fires on its own first
|
||||
// refresh (the server cannot tell it from any other copy; each such +4 costs her price + a refresh − the 1-fund refund,
|
||||
// and needs her in the shop again).
|
||||
const REFRESH_CNT_KEY = 'garrison:SERVER_GAIN_BOND_LAYER_BY_REFRESH_CNT:refreshes'; // per-piece counter (module-prefixed)
|
||||
H.SERVER_GAIN_BOND_LAYER_BY_REFRESH_CNT = {
|
||||
onRefresh(ctx, ev) {
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// g.move / g.equip, the round loop's SETTLE):
|
||||
// #1 拉普兰德 garrison_123 "<刷新时>若为本回合首次主动刷新,使已激活的【叙拉古】层数+4(+8),此干员在整备区时也有效": the
|
||||
// players' official behaviour — "获得该干员后该回合的首次刷新" also stacks. The refresh count is the 拉普兰德's own
|
||||
// (each copy counts the manual refreshes it witnessed this round), not the player's.
|
||||
// (each copy counts the manual refreshes it witnessed this round), not the player's; an elite merged this round is a
|
||||
// new 拉普兰德 and starts at 0 (GitHub #169, the owner's decision of 2026-10-06).
|
||||
// #4 昆图斯 突变细胞 "战斗结束后,装备者替换为高一阶的随机干员": the cell is not consumed — the original operator is
|
||||
// destroyed (PRTS 备注 "生效时,原干员销毁,获得一名高一阶的随机初始干员(最高六阶)") and its equipment, the cell
|
||||
// included, returns to the hand, to be equipped again ("之后就是一直打针,扎到核心卡…就换人扎"); then the new operator
|
||||
@@ -94,7 +95,7 @@ test('#1 players\' scenario: 普罗旺斯 + 德克萨斯 deployed, refresh, buy
|
||||
m.dispose();
|
||||
});
|
||||
|
||||
test('#1 each copy counts its own refreshes: a bench copy (整备区时也有效) bought later fires on its own first refresh; an elite keeps "already fired"', () => {
|
||||
test('#1 each copy counts its own refreshes: a bench copy (整备区时也有效) bought later fires on its own first refresh; an elite merged this round is a new 拉普兰德 (GitHub #169)', () => {
|
||||
const s = setup();
|
||||
const { m, ps } = s;
|
||||
s.activate();
|
||||
@@ -107,18 +108,20 @@ test('#1 each copy counts its own refreshes: a bench copy (整备区时也有效
|
||||
assert.equal(s.L(), 8, 'copy B: its first refresh (A already fired this round)');
|
||||
s.refresh();
|
||||
assert.equal(s.L(), 8, 'nothing more this round');
|
||||
// the third copy completes the elite: A and B already fired this round, so the elite does not fire again this round
|
||||
// [ASSUMED: conservative — the elite keeps the highest refresh count of its copies]
|
||||
// the third copy completes the elite: a new 拉普兰德 (the owner's decision of 2026-10-06; until 0.2.0 it kept its
|
||||
// copies' count and did not fire again this round) — its own first refresh of the round adds +8
|
||||
const elite = s.buy(LAP);
|
||||
assert.equal(elite.id, LAP_B, 'merged into the elite');
|
||||
assert.ok(!ps.find(a.uid) && !ps.find(b.uid), 'copies consumed');
|
||||
s.refresh();
|
||||
assert.equal(s.L(), 8, 'the elite made this round from copies that already fired: no second trigger');
|
||||
assert.equal(s.L(), 8 + 8, 'the elite made this round from copies that already fired: its own first refresh +8');
|
||||
s.refresh();
|
||||
assert.equal(s.L(), 16, 'once');
|
||||
s.h.toPrep(2);
|
||||
ps.funds = 50;
|
||||
s.activate();
|
||||
s.refresh();
|
||||
assert.equal(s.L(), 8 + 8, 'R2: the elite fires +8 on the round\'s first refresh');
|
||||
assert.equal(s.L(), 16 + 8, 'R2: the elite fires +8 on the round\'s first refresh');
|
||||
checkInvariants(m);
|
||||
m.dispose();
|
||||
});
|
||||
@@ -146,6 +149,28 @@ test('#1 an elite merged from copies that had not fired yet this round fires on
|
||||
m.dispose();
|
||||
});
|
||||
|
||||
test('#1 two white copies refreshed this round, merge, refresh → +8: the newly merged elite is a new 拉普兰德 (GitHub #169; the owner\'s decision of 2026-10-06)', () => {
|
||||
const s = setup();
|
||||
const { m, ps } = s;
|
||||
s.activate();
|
||||
const a = give(m, ps, LAP, 'hand');
|
||||
const b = give(m, ps, LAP, 'hand');
|
||||
s.refresh();
|
||||
assert.equal(s.L(), 8, 'both white copies fire on the refresh they witness: 4 + 4');
|
||||
s.refresh();
|
||||
assert.equal(s.L(), 8, 'each only once');
|
||||
const elite = s.buy(LAP);
|
||||
assert.equal(elite.id, LAP_B, 'the third copy merges');
|
||||
assert.ok(!ps.find(a.uid) && !ps.find(b.uid), 'copies consumed');
|
||||
assert.equal(ps.pieceRoundCount(ps.find(elite.uid).piece, `${KEY}:refreshes`), 0, 'the elite carries no refresh count');
|
||||
s.refresh();
|
||||
assert.equal(s.L(), 16, 'its first manual refresh this round adds +8');
|
||||
s.refresh();
|
||||
assert.equal(s.L(), 16, 'nothing more this round');
|
||||
checkInvariants(m);
|
||||
m.dispose();
|
||||
});
|
||||
|
||||
test('#1 only manual refreshes count: a re-triggered "刷新时" trait (ctx.triggerGarrisons) neither fires nor uses up her first refresh', () => {
|
||||
const s = setup();
|
||||
const { m, ps } = s;
|
||||
|
||||
Reference in new issue
Block a user