feedback7 dober: 教鞭 is the official personal choice of three 战术特训 cards again, not a random one (PR #353 by @Sukvii, the owner's decision of 2026-10-08); golden: coop2-NORMAL-8-serverrun and coop2-ABYSS-10-boosted move
Seen: the Art 教鞭 (杜宾's strategy) drew three 战术特训 cards and added a random one — a documented simplification (docs/META.md §2.5: a personal choice would need its own phase, protocol message, timer and AI takeover). Official: act2autochess choice_event hunter_band_1 is a PERSONAL_CHOOSE; PRTS 下半 记录 §法术 教鞭 "使用后销毁,于3个战术特训的悬赏任务中选择一项". The owner decided on 2026-10-08 to follow it. Taken from PR #353 (code and tests by @Sukvii), ported to the split 0.2.2 modules. Now: ctx.offerBountyChoice -> Match.offerBountyChoice keeps up to three different mode-eligible cards (15 of 20 in 标准 / 险境, 20 in 绝境 / 终极; one meta-rng shuffle) in PlayerState.personalChoice, m.private only (same cards after a reconnect); g.choice { idx, choiceId } confirms it (Match.pickPersonalChoice: owner, id, round, index; a request without a choiceId is still the public draft, a stale id never falls through to it). No new phase or timer: Ready is refused while it is open, the prep's deadline picks at random before the temp pieces and Ready, a bot or a 托管 seat picks with the bounty scorer (botPickCard, the bots' seeded rng; at the prep's start, after the cast, before Ready; the scheduler's fallback picks at random if a step fails). The client reuses ChoiceOverlay (personal: no order, no turns, the prep's countdown) over a locked prep, the Ready reasons, the mock harness; six UI strings in en / ja / ko / zh-TW. An eliminated player's offer goes with it; invariants.js flags an offer outside its owner's own open prep. Not taken from the PR: its §25.24 in the released docs/history/0.2.0.md (the 0.2.2 section is the integrator's), its edits of the Ready toggle beyond the personal-choice case, its tail placement of the i18n keys. Golden: coop2-NORMAL-8-serverrun (ai_0 杜宾 casts 教鞭 from R2 on and now scores the three cards: teamLp 39 -> 34, virtualMs 121400 -> 118400, rng shop 511 -> 483 / bots 18063 -> 18070 / meta 112 -> 103) and coop2-ABYSS-10-boosted (ai_1 杜宾: virtualMs 121650 -> 124650, shop 590 -> 589 / bots 22075 -> 22096 / meta 141 -> 154); both first differ in round 2, every other scenario and family is unchanged; node tools/golden.mjs --twice is identical. Tests: items.test.js (three distinct cards, the tapped one applied once, nothing before the confirm, a refused cast keeps the Art / count / rng / uid, an eliminated player's offer), economy.test.js (Ready refused, the deadline picks before endPrep, solo prep untimed), connection.test.js (two players' offers, reconnect, foreign / duplicate / old ids, an offline player's pick at the deadline), bot.test.js (托管 takeover incl. the failing-step fallback), bot-smarter.test.js (the scorer's pick, deterministic per seed, no rng draw when nothing is pending), followups.test.js (R14 / R15), draft.test.js, unitStats.test.js (protocol), hardening.test.js (invariant), gameLogic / playtest3 / playtest4 (view model, Ready reason, ChoiceView, actions), mock.e2e.test.js and playtest2.real.e2e.test.js (browser, SP_E2E=1).
This commit is contained in:
1 parent
05eb2b807f
commit
56a7b918a9
48 files changed
+901
-159
No files matched your search
@@ -13,17 +13,19 @@
|
||||
// the target's items skipped the item after a merge)
|
||||
// 紧急调度券 a shop operator leaves its slot only when it was actually granted (built-in cleared the slot first)
|
||||
// 寻呼模块 the special refresh shows `refresh_cnt` DIFFERENT operators (fewer when the pool has no more)
|
||||
// 教鞭 trap_create_self_choice {choice_event: hunter_band_1}: the bounty is a 战术特训 card (choices.json
|
||||
// cards.bounty payout `perfect`, e.g. 战术特训·飞行I "若各自行动阶段就达成完美作战,获得1资金") — PRTS
|
||||
// 卫戍协议:盟约 下半/PRTS盟约记录 §法术 教鞭 "于3个战术特训的悬赏任务中选择一项", §机变阶段 "※以下悬赏任务仅由
|
||||
// 法术教鞭生成", and 杜宾 加练! "<教鞭>:使用后为下场战斗添加额外敌人,若自身战斗完美作战可获得资金" (user
|
||||
// playtest #6 item 4 review; the built-in drew any tier ≤ II kill bounty, and research 04 §7 [ASSUMED]
|
||||
// the band family `enemyeffect_b_*`).
|
||||
// “神秘顾客” the same trap_create_self_choice ("选择一项特殊悬赏任务"; no band grants it in act2, research 04): a band
|
||||
// bounty `enemyeffect_b_*` (research 04 §7 [ASSUMED]: "adds 1 enemy to your next battle, killer gets `coin`
|
||||
// funds").
|
||||
// Both: [ASSUMED simplification, engine: no PERSONAL_CHOOSE overlay] 3 cards whose enemy can appear in the
|
||||
// mode are drawn and one of them is taken at random; the built-in runs when the family is empty.
|
||||
// 教鞭 trap_create_self_choice {choice_event: hunter_band_1, choiceType PERSONAL_CHOOSE}: a personal choice of
|
||||
// one of THREE 战术特训 cards (choices.json cards.bounty payout `perfect`, e.g. 战术特训·飞行I "若各自行动阶段就
|
||||
// 达成完美作战,获得1资金") whose enemy the mode can field — PRTS 卫戍协议:盟约 下半/PRTS盟约记录 §法术 教鞭
|
||||
// "使用后销毁,于3个战术特训的悬赏任务中选择一项", §机变阶段 "※以下悬赏任务仅由法术教鞭生成", and 杜宾 加练!
|
||||
// "<教鞭>:使用后为下场战斗添加额外敌人,若自身战斗完美作战可获得资金". onArt → ctx.offerBountyChoice
|
||||
// (Match.offerBountyChoice): the three cards go to the owner alone, who confirms one inside the PREP
|
||||
// (g.choice with the offer's id); the prep's deadline or an AI seat resolves it (docs/META.md §2.5). Until
|
||||
// 0.2.2 the engine took one of the three at random (user playtest #6 item 4 review; the built-in drew any
|
||||
// tier ≤ II kill bounty, and research 04 §7 [ASSUMED] the band family `enemyeffect_b_*`).
|
||||
// “神秘顾客” the same trap_create_self_choice ("选择一项特殊悬赏任务"; no band grants it in act2, research 04, and PRTS
|
||||
// lists no pool): still [ASSUMED simplification] 3 band bounties `enemyeffect_b_*` (research 04 §7 [ASSUMED]:
|
||||
// "adds 1 enemy to your next battle, killer gets `coin` funds") whose enemy can appear in the mode, one of them
|
||||
// taken at random; the built-in runs when the family is empty.
|
||||
// “神秘顾客” trap_disney_special: when actively destroyed, +count funds and the Art passes to the next alive player
|
||||
// (seat order, cyclic)
|
||||
// 天师古鼎 equip_with_another_gain_coin_when_gain_char: a 【炎】 carrier also holding 炎国短刀 (either quality)
|
||||
@@ -48,7 +50,7 @@ function pieceIsMember(ctx, piece, bondId) {
|
||||
return bonds.includes('maniShip') && isCoreBond(bondId) && ctx.bondActive('maniShip') && ctx.bondActive(bondId);
|
||||
}
|
||||
|
||||
/** Offer size of the personal bounty choice (PRTS 法术 教鞭 "于3个战术特训的悬赏任务中选择一项"). */
|
||||
/** Cards drawn before “神秘顾客” takes one at random. */
|
||||
const OFFER_SIZE = 3;
|
||||
/** cards.bounty entries matching `test` whose enemy can appear in this mode. */
|
||||
function bountyCards(ctx, test) {
|
||||
@@ -103,18 +105,24 @@ export function registerMeta(registry) {
|
||||
},
|
||||
}));
|
||||
|
||||
// 教鞭 / “神秘顾客” — trap_create_self_choice {choice_event}: 教鞭 a 战术特训 card, “神秘顾客” a band bounty (enemyeffect_b_*)
|
||||
for (const [key, family] of [['chess_item_6_03_m', isTraining], ['chess_item_6_01_m', isBandBounty]]) {
|
||||
wrap(registry, key, (base) => ({
|
||||
onArt(ctx, ev) {
|
||||
const cards = bountyCards(ctx, family);
|
||||
if (!cards.length) { if (typeof base.onArt === 'function') base.onArt.call(base, ctx, ev); return; }
|
||||
const offer = ctx.rng.shuffle(cards.slice()).slice(0, OFFER_SIZE);
|
||||
const card = ctx.rng.pick(offer);
|
||||
if (!card || !ctx.addBounty(card)) { ev.error = 'BAD_TARGET'; ev.detail = 'no bounty available'; }
|
||||
},
|
||||
}));
|
||||
}
|
||||
// 教鞭 — trap_create_self_choice {choice_event}: the server keeps the offered 战术特训 cards until confirmed.
|
||||
wrap(registry, 'chess_item_6_03_m', () => ({
|
||||
onArt(ctx, ev) {
|
||||
const result = ctx.offerBountyChoice(bountyCards(ctx, isTraining), ev.item.id);
|
||||
if (!result.ok) { ev.error = result.error; ev.detail = result.detail; }
|
||||
},
|
||||
}));
|
||||
|
||||
// “神秘顾客” — a random band bounty (enemyeffect_b_*).
|
||||
wrap(registry, 'chess_item_6_01_m', (base) => ({
|
||||
onArt(ctx, ev) {
|
||||
const cards = bountyCards(ctx, isBandBounty);
|
||||
if (!cards.length) { if (typeof base.onArt === 'function') base.onArt.call(base, ctx, ev); return; }
|
||||
const offer = ctx.rng.shuffle(cards.slice()).slice(0, OFFER_SIZE);
|
||||
const card = ctx.rng.pick(offer);
|
||||
if (!card || !ctx.addBounty(card)) { ev.error = 'BAD_TARGET'; ev.detail = 'no bounty available'; }
|
||||
},
|
||||
}));
|
||||
|
||||
// 画卷 — trap_copy_front_char: copy the operator in range (elite status included) with its equipment. A copied
|
||||
// normal item that completes a pair with an owned one merges at once and the golden stays in the hand (research 04
|
||||
|
||||
Reference in new issue
Block a user