feedback5 WE: the 整备区 rules of PRTS 卫戍协议/帮助 §手牌区 (GitHub #82, second part) — a full regular hand refuses every purchase and reward pick, also one whose copy would complete a merge at once ("当常规手牌区全满无空位时,玩家将无法执行使手牌溢出的操作" — "例如招募/购入等通常情况下会增加手牌的操作"; BWIKI 盟约 says the same with no exception; the reporter checked it in the official game), and a free regular slot pulls the 临时整备区 in at once ("常规手牌区出现空位时自动移入"): PlayerState._fillHandFromTemp runs in every recompute, the temp row right to left (the order it fills, [ASSUMED]) into the hand's free slots right to left — after a sale, a deployment from the hand, a merge that consumed hand copies, an item equipped / destroyed / used, a summon stack removed with its owner. invariants.js: temp holds pieces only while the hand is full. The client's refusal path follows (gameLogic.shopBlockReason: 整备区已满 on every card — a one-line change in a file the R5 split moves). Bots: a full hand never tries a purchase, an affordable third copy of a held pair sheds the weakest hand single first (never one of this prep), resolveTemp decides temp right to left. Tests: test/match/feedback5-hand.test.js; merge / hardening / playtest6-elite-to-board / economy / prep-bench / uiFixes updated to the rule. Docs: META §1 Hand, DESIGN §6, PLAYING §3, research 00-INDEX / 01 (the old "a merge-completing purchase is allowed" corrected). Golden: 3 match scenarios moved (coop2-NORMAL-3, solo-HARD-9-boosted, coop2-ABYSS-10-boosted — bot seats that reached a full hand: the merge-completing purchase refused or a single shed first, temp pieces moving in by themselves); every battle digest unchanged
This commit is contained in:
1 parent
b1ec04b58c
commit
db1b44bb71
22 files changed
+356
-101
No files matched your search
@@ -97,6 +97,12 @@ export class PlayerEconomy {
|
||||
this.dirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* g.buy {slot}. A full regular hand refuses every purchase, also one whose copy would complete a merge at once (PRTS
|
||||
* 卫戍协议/帮助 §手牌区 "当常规手牌区全满无空位时,玩家将无法执行使手牌溢出的操作" — "例如招募/购入等通常情况下会增加手牌的
|
||||
* 操作"; BWIKI 盟约 "当整备区达到上限后将无法进行涉及到整备区的操作(购买、撤回等)"; GitHub #82: the reporter's first-hand
|
||||
* check in the official game, "哪怕是已经有了两个相同干员情况"). Until 0.1.3 such a purchase was let through.
|
||||
*/
|
||||
buy(slotIdx) {
|
||||
const g = this._gate(); if (g) return g;
|
||||
if (!Number.isInteger(slotIdx) || slotIdx < 0 || slotIdx >= this.shop.slots.length) return fail(ERR.BAD_TARGET);
|
||||
@@ -113,13 +119,13 @@ export class PlayerEconomy {
|
||||
const base = this.gd.baseIdOf(slot.id);
|
||||
const need = rec.isGolden ? this.gd.goldenCopies : 1;
|
||||
if (this.m.pool.has(base) && this.m.pool.left(base) < need) return fail(ERR.SOLD_OUT);
|
||||
if (handFull && !this.completesChessMerge(slot.id)) return fail(ERR.HAND_FULL);
|
||||
if (handFull) return fail(ERR.HAND_FULL);
|
||||
this.spend(price);
|
||||
slot.sold = true;
|
||||
piece = this.acquireChess(slot.id, { source: 'buy' });
|
||||
} else {
|
||||
if (!this.gd.item(slot.id)) return fail(ERR.BAD_TARGET);
|
||||
if (handFull && !this.completesItemMerge(slot.id)) return fail(ERR.HAND_FULL);
|
||||
if (handFull) return fail(ERR.HAND_FULL);
|
||||
this.spend(price);
|
||||
slot.sold = true;
|
||||
piece = this.acquireItem(slot.id, { source: 'buy' });
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// server/match/player/pieces.js — PlayerState methods: piece bookkeeping — new pieces, lookup by uid (board / hand /
|
||||
// temp / equipped), the chess locations, detach and stow (hand first, overflow temp), the per-piece round counters
|
||||
// (拉普兰德), pool copies back to the shared pool, and summon stacks (removal, lifting, counts, the stack granted for an
|
||||
// owner).
|
||||
// temp / equipped), the chess locations, detach and stow (hand first, overflow temp; a free hand slot pulls a temp piece
|
||||
// in, _fillHandFromTemp), the per-piece round counters (拉普兰德), pool copies back to the shared pool, and summon stacks
|
||||
// (removal, lifting, counts, the stack granted for an owner).
|
||||
// Installed on PlayerState.prototype by server/match/PlayerState.js (a method container: never instantiated; `this` is
|
||||
// the player state).
|
||||
|
||||
@@ -68,7 +68,8 @@ export class PlayerPieces {
|
||||
|
||||
/**
|
||||
* Put a piece into the hand (right→left) or, when `allowTemp`, the temp slots (due at the deadline of the first prep
|
||||
* in which the player can act on it, _tempDueNow). Returns 'hand' | 'temp' | null.
|
||||
* in which the player can act on it, _tempDueNow). Returns 'hand' | 'temp' | null. A piece put into temp while a hand
|
||||
* slot is free (`toTemp`) moves into the hand at the next recompute (_fillHandFromTemp).
|
||||
*/
|
||||
stow(piece, { allowTemp = true, toTemp = false, preferIdx = null } = {}) {
|
||||
if (!toTemp) {
|
||||
@@ -85,6 +86,31 @@ export class PlayerPieces {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* PRTS 卫戍协议/帮助 §手牌区 "溢出单位会自动进入临时手牌区,常规手牌区出现空位时自动移入" (GitHub #82; until 0.1.3 the
|
||||
* player had to drag them back): every free regular hand slot takes a temp piece at once. Order [ASSUMED] (PRTS names
|
||||
* none): the temp row empties in the order it fills — right→left, the way stow fills it (freeSlot), so the piece that
|
||||
* overflowed first moves first — and each piece takes the hand's next free slot right→left ("被发送至手牌区的物资优先
|
||||
* 从右到左填充空位"). recompute() runs it, so it follows every change that frees a slot: a sale, a deployment from the
|
||||
* hand, a merge that consumed hand copies, an item equipped / destroyed / used from the hand, a summon stack removed
|
||||
* with its owner, an effect's destroyPiece… A piece that leaves temp is no longer due (tempDue). Returns the number
|
||||
* moved.
|
||||
* @returns {number}
|
||||
*/
|
||||
_fillHandFromTemp() {
|
||||
let moved = 0;
|
||||
for (let j = this.temp.length - 1; j >= 0; j--) {
|
||||
const p = this.temp[j];
|
||||
if (!p) continue;
|
||||
const i = freeSlot(this.hand);
|
||||
if (i < 0) break;
|
||||
this._detach({ piece: p, area: 'temp', idx: j });
|
||||
this.hand[i] = p;
|
||||
moved++;
|
||||
}
|
||||
return moved;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
|
||||
@@ -16,6 +16,11 @@ export class PlayerPrep {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* g.reward {idx}: the pick of the first queued offer (promotion reward, a strategy's special refresh, an item offer).
|
||||
* A pick is a 招募 like a purchase: a full regular hand refuses it, also when it would complete a merge (PRTS
|
||||
* 卫戍协议/帮助 §手牌区, see buy; GitHub #82).
|
||||
*/
|
||||
pickReward(idx) {
|
||||
const g = this._gate(); if (g) return g;
|
||||
const offer = this.offers[0];
|
||||
@@ -26,14 +31,14 @@ export class PlayerPrep {
|
||||
const handFull = freeSlot(this.hand) < 0;
|
||||
if (slot.kind === 'item') {
|
||||
if (!this.gd.item(slot.id)) return fail(ERR.BAD_TARGET);
|
||||
if (handFull && !this.completesItemMerge(slot.id)) return fail(ERR.HAND_FULL);
|
||||
if (handFull) return fail(ERR.HAND_FULL);
|
||||
} else {
|
||||
const rec = this.gd.chess(slot.id);
|
||||
if (!rec) return fail(ERR.BAD_TARGET);
|
||||
const base = this.gd.baseIdOf(slot.id);
|
||||
const need = rec.isGolden ? this.gd.goldenCopies : 1;
|
||||
if (this.m.pool.has(base) && this.m.pool.left(base) < need) return fail(ERR.SOLD_OUT);
|
||||
if (handFull && !this.completesChessMerge(slot.id)) return fail(ERR.HAND_FULL);
|
||||
if (handFull) return fail(ERR.HAND_FULL);
|
||||
}
|
||||
const price = Number.isFinite(slot.price) && slot.price > 0 ? Math.trunc(slot.price) : 0;
|
||||
if (price > this.funds) return fail(ERR.NO_FUNDS);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// server/match/player/round.js — PlayerState methods: the round lifecycle the match calls — startRound (income, pending
|
||||
// coins, the shop, summon stacks topped up), endPrep, eliminate (every copy back to the pool), recompute (legality,
|
||||
// out-of-range summons, bonds), the bond views — and battleInput (the player's PlayerBattleInput: board units with
|
||||
// their loadout, carried 联防 state, the reached layers).
|
||||
// out-of-range summons, temp pieces into free hand slots, bonds), the bond views — and battleInput (the player's
|
||||
// PlayerBattleInput: board units with their loadout, carried 联防 state, the reached layers).
|
||||
// Installed on PlayerState.prototype by server/match/PlayerState.js (a method container: never instantiated; `this` is
|
||||
// the player state).
|
||||
|
||||
@@ -74,6 +74,8 @@ export class PlayerRound {
|
||||
this.deployMap(); // a change of the deploy field (a boss round's prep) marks the legality stale
|
||||
if (this._legalityStale) this._evictIllegal();
|
||||
this._liftOutOfRange();
|
||||
// a free regular hand slot pulls a temp piece in (PRTS 卫戍协议/帮助 §手牌区 "常规手牌区出现空位时自动移入")
|
||||
this._fillHandFromTemp();
|
||||
this.bonds = computeBonds(this.gd, this);
|
||||
this.dirty();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user