feedback1 WA: return the Mutation Cell before summon cards; bots keep it
Review follow-ups for reports #1 and #4. - transformChess: the returned equipment is stowed before the new operator's summon stack is granted (as in _mergeChess); when the transformation completes a merge, the carrier's equipment goes through _mergeChess, which also returns it before the elite's summons. A summon card no longer pushes the returned 突变细胞 into temp, where it would be lost at the deadline. - bot: a 突变细胞 left in temp with a full hand gets a hand slot (sell the weakest single hand chess, else destroy the cheapest other hand item, else sell any hand chess) instead of being destroyed. - 拉普兰德's per-piece counter key is module-prefixed; the re-bought copy case is marked [ASSUMED] in the code and docs/META.md. - .dhint--rule gets a style; sources quote PRTS 卫戍协议/帮助 exactly ("在失去该干员(干员出售、销毁、合并等)…时自动卸除").
This commit is contained in:
1 parent
f8e35d5055
commit
85d8bd40b4
10 files changed
+154
-36
No files matched your search
+19
-13
@@ -34,7 +34,8 @@
|
||||
// (拉普兰德: 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].
|
||||
// * Transformations (transformChess, 突变细胞): the new chess keeps the tile when legal; `returnItems` sends the old
|
||||
// piece's equipment to the hand (overflow temp) like a destroyed operator's.
|
||||
// piece's equipment to the hand (overflow temp) like a destroyed operator's — before the new piece's summon stack,
|
||||
// as in a merge, so a summon card never pushes the returned 突变细胞 into temp.
|
||||
// * Items: equip max 2 (a 3rd replaces the equipped item the player picks — g.equip replaceUid, the oldest when
|
||||
// absent; equipped items are otherwise locked: g.destroy refuses them),
|
||||
// 2 identical normal items (hand/temp/equipped) merge into the golden item in the hand, items are never sold
|
||||
@@ -585,7 +586,9 @@ export class PlayerState {
|
||||
* cell itself comes back, player feedback after 0.1.0), overflowing into temp; with both full an item stays on the new
|
||||
* piece up to its equipPerChess slots (any further one is destroyed with a log warning, as in a merge). Pool copies are
|
||||
* swapped; completes a merge when possible — a deployed piece's tile then counts as a consumed copy's for the elite
|
||||
* (_mergeChess fromKey, when legal for it).
|
||||
* (_mergeChess fromKey, when legal for it), and the merge returns the equipment itself. A new piece on the board gets
|
||||
* its summon stack only after the equipment came back (like _mergeChess: a summon card must not push the returned
|
||||
* equipment — 突变细胞 — into temp, where it would be lost at the deadline).
|
||||
* @param {{ returnItems?: boolean }} [opts]
|
||||
*/
|
||||
transformChess(piece, newId, { returnItems = false } = {}) {
|
||||
@@ -600,23 +603,26 @@ export class PlayerState {
|
||||
const base = this.gd.baseIdOf(newId);
|
||||
const taken = this.m.pool.take(base, rec.isGolden ? this.gd.goldenCopies : 1);
|
||||
let np = this.newPiece('chess', newId, { poolCopies: taken });
|
||||
np.items = returnItems ? [] : items;
|
||||
if (!rec.isGolden && this.completesChessMerge(newId)) {
|
||||
const merging = !rec.isGolden && this.completesChessMerge(newId);
|
||||
// a merge returns the consumed copies' equipment (this one's too) before the elite's summons (_mergeChess)
|
||||
const pending = returnItems && !merging ? items : [];
|
||||
np.items = returnItems && !merging ? [] : items;
|
||||
let deployed = false;
|
||||
if (merging) {
|
||||
np = this._mergeChess(base, np, loc.area === 'board' ? { fromKey: loc.key, fromDir: pieceDir(piece) } : undefined);
|
||||
if (!np && !returnItems) { this.recompute(); return null; }
|
||||
} else if (loc.area === 'board') {
|
||||
const [r, c] = parseKey(loc.key);
|
||||
if (this._legal(np, r, c)) { np.dir = pieceDir(piece); this.board.set(loc.key, np); this.grantTokensFor(np); } else if (!this.stow(np, { allowTemp: true })) { this.returnCopies(np); np = null; }
|
||||
if (this._legal(np, r, c)) { np.dir = pieceDir(piece); this.board.set(loc.key, np); deployed = true; } else if (!this.stow(np, { allowTemp: true })) { this.returnCopies(np); np = null; }
|
||||
} else if (!this._putBack(loc, np)) { this.returnCopies(np); np = null; }
|
||||
// after the new piece took its place (a hand piece keeps its slot), its predecessor's equipment comes off
|
||||
if (returnItems) {
|
||||
for (const it of items) {
|
||||
if (this.stow(it, { allowTemp: true })) continue;
|
||||
if (np && np.items.length < this.gd.equipPerChess) { np.items.push(it); continue; }
|
||||
this.m.log.warn?.(`[match ${this.m.roomCode}] ${this.playerId}: returned item ${it.id} destroyed (no space)`);
|
||||
}
|
||||
this.checkItemMerges();
|
||||
for (const it of pending) {
|
||||
if (this.stow(it, { allowTemp: true })) continue;
|
||||
if (np && np.items.length < this.gd.equipPerChess) { np.items.push(it); continue; }
|
||||
this.m.log.warn?.(`[match ${this.m.roomCode}] ${this.playerId}: returned item ${it.id} destroyed (no space)`);
|
||||
}
|
||||
if (returnItems) this.checkItemMerges();
|
||||
// deployed in its predecessor's place: its manually deployable summons join the hand, after the returned equipment
|
||||
if (deployed) this.grantTokensFor(np);
|
||||
this.recompute();
|
||||
if (np) this.m.dispatch(this, 'onGain', { piece: np, kind: 'chess', source: 'transform' });
|
||||
return np;
|
||||
|
||||
+27
-4
@@ -38,7 +38,8 @@
|
||||
// 5. equip items on the strongest deployed damage dealers (consume-on-equip items / Arts only with a handler);
|
||||
// 突变细胞 (it comes back after every transformation) on the least valuable single normal operator below 6阶 —
|
||||
// never an elite or one of a merge pair (cellTarget)
|
||||
// 6. resolve the temp slots, keep one hand slot free, then Ready.
|
||||
// 6. resolve the temp slots (a 突变细胞 left there gets a hand slot made for it — makeHandRoom — instead of being
|
||||
// destroyed), keep one hand slot free, then Ready.
|
||||
// Placement quality (tools/matchrun sweeps, research-faithful waves): the planner beats random layouts by ≈ 8 points
|
||||
// of kill rate and rehearsal adds ≈ 5 more; see docs/META.md §1.5.
|
||||
|
||||
@@ -806,12 +807,15 @@ function context(m, ps) {
|
||||
return { owned, focus: focusBond(m, ps, owned), roles: roles(m, ps), fly: model.flyTotal, model };
|
||||
}
|
||||
|
||||
/** Sell the weakest bench chess that is not part of a merge pair (or anything when keepPairs is false). */
|
||||
function sellWeakestHand(m, ps, { keepPairs = true, below = Infinity } = {}) {
|
||||
/**
|
||||
* Sell the weakest bench chess (temp and hand; the hand only with `handOnly`) that is not part of a merge pair (or
|
||||
* anything when keepPairs is false).
|
||||
*/
|
||||
function sellWeakestHand(m, ps, { keepPairs = true, below = Infinity, handOnly = false } = {}) {
|
||||
const ctx = context(m, ps);
|
||||
let worst = null;
|
||||
let worstV = Infinity;
|
||||
for (const p of [...ps.temp, ...ps.hand]) {
|
||||
for (const p of handOnly ? ps.hand : [...ps.temp, ...ps.hand]) {
|
||||
if (!p || p.kind !== 'chess') continue;
|
||||
const base = m.gd.baseIdOf(p.id);
|
||||
if (keepPairs && !m.gd.isGolden(p.id) && ps.countCopies(base) >= 2) continue;
|
||||
@@ -1215,6 +1219,20 @@ function equipItems(m, ps) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a hand slot for an item worth keeping (突变细胞): sell the weakest single hand chess, else destroy the cheapest
|
||||
* other hand item, else sell the weakest hand chess even of a pair. false: the hand stays full (summon cards only).
|
||||
*/
|
||||
function makeHandRoom(m, ps) {
|
||||
if (freeSlot(ps.hand) >= 0) return true;
|
||||
if (sellWeakestHand(m, ps, { handOnly: true }) && freeSlot(ps.hand) >= 0) return true;
|
||||
const gd = m.gd;
|
||||
const junk = ps.hand.filter((p) => p && p.kind === 'item' && !isMutationCell(gd, p.id))
|
||||
.sort((a, b) => ((gd.item(a.id) || {}).price || 0) - ((gd.item(b.id) || {}).price || 0) || a.uid - b.uid)[0];
|
||||
if (junk && tryDo(() => ps.destroy(junk.uid)) && freeSlot(ps.hand) >= 0) return true;
|
||||
return sellWeakestHand(m, ps, { handOnly: true, keepPairs: false }) && freeSlot(ps.hand) >= 0;
|
||||
}
|
||||
|
||||
function resolveTemp(m, ps) {
|
||||
for (let i = 0; i < ps.temp.length; i++) {
|
||||
const p = ps.temp[i];
|
||||
@@ -1228,6 +1246,11 @@ function resolveTemp(m, ps) {
|
||||
if (j >= 0) tryDo(() => ps.move(p.uid, { area: 'hand', idx: j }));
|
||||
}
|
||||
} else if (p.kind === 'item') {
|
||||
// 突变细胞 comes back after every transformation (昆图斯's strategy item): make room in the hand rather than lose it
|
||||
if (isMutationCell(m.gd, p.id) && makeHandRoom(m, ps)) {
|
||||
const j = freeSlot(ps.hand);
|
||||
if (j >= 0 && tryDo(() => ps.move(p.uid, { area: 'hand', idx: j }))) continue;
|
||||
}
|
||||
tryDo(() => ps.destroy(p.uid));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,9 +179,10 @@ const ITEM_HANDLERS = {
|
||||
},
|
||||
// 突变细胞 "战斗结束后,装备者替换为高一阶的随机干员": the carrier is replaced by a random NORMAL operator one tier higher
|
||||
// (at most 6; an elite carrier too — PRTS 卫戍协议:盟约 下半/PRTS盟约记录 备注 "生效时,原干员销毁,获得一名高一阶的随机
|
||||
// 初始干员(最高六阶)"); the destroyed operator's equipment, the cell included, returns to the hand (the official text
|
||||
// never says 销毁 — unlike every consumable item —, and players re-inject it every round: "之后就是一直打针,扎到核心卡
|
||||
// …就换人扎", bilibili cv47000418; player feedback after 0.1.0). The new operator keeps the tile when legal.
|
||||
// 初始干员(最高六阶)"); the destroyed operator's equipment, the cell included, returns to the hand (PRTS 卫戍协议/帮助
|
||||
// "佩戴的装备无法手动卸除,在失去该干员(干员出售、销毁、合并等)或装备合并为进阶品质时自动卸除"; the official text never
|
||||
// says 销毁 for the cell — unlike every consumable item —, and players re-inject it every round: "之后就是一直打针,扎到
|
||||
// 核心卡…就换人扎", bilibili cv47000418; player feedback after 0.1.0). The new operator keeps the tile when legal.
|
||||
char_chess_transformation_equip: {
|
||||
onBattleResult(ctx) {
|
||||
const { piece, holder } = ctx.source;
|
||||
|
||||
Reference in new issue
Block a user