feedback7 R22-LATE: 画卷 never puts the copied items on the copy — they are gained unequipped into the hand / temp like any item; golden: unchanged
PRTS 卫戍协议:盟约 PRTS盟约记录, 法术编纂集, 画卷 备注: 「选择策略<夕>时可获得<br>使用后销毁,获得的装备为未装备状态」. Without a promotion the 画卷 handler still equipped every copied item that did not merge (an advanced item, a 变形同构体) onto the copy in the hand (the promotion case was fixed for #389). Now each copied item is only gained (ctx.grantItem → acquireItem): the hand, overflow temp, and with both full it is destroyed with the usual toast 「整备区已满,获得的装备已销毁」, as for any gained item. A copied normal item that completes a pair still merges at once (the golden in the hand), and the copy itself still follows the chess gain rules (a full hand and temp refuse the scroll: HAND_FULL, the scroll kept). Full-hand check: a full hand with 3 free temp slots holds the copy and both copied advanced items; with 2 free slots the copy and one item fit and the other is destroyed with the toast (before, both rode on the copy). Docs: META.md (Arts list), research/04-items.md (画卷 copy). The content test that pinned the old behaviour (test/content/items.test.js: 变形同构体 "the copy wears it too") now expects it in the hand. Tests: test/match/scroll-copy-merge.test.js +3 (no promotion, normal and elite targets with advanced items; the full hand at 3 / 2 free temp slots) — all 3 fail on the parent; every test mentioning 画卷: 177 pass. Golden unchanged.
This commit is contained in:
1 parent
7c56335c06
commit
ac4231846f
5 files changed
+56
-17
No files matched your search
+1
-1
@@ -458,7 +458,7 @@ bilibili BV1vzyVBuEN9 ≈ 8:24 / BV1Qkw1zMEoR ≈ 7:25, pointed out in PR #2 —
|
|||||||
is not consumed: PRTS 下半 记录 备注 "生效时,原干员销毁,获得一名高一阶的随机初始干员(最高六阶)", PRTS 卫戍协议/帮助 "佩戴的
|
is not consumed: PRTS 下半 记录 备注 "生效时,原干员销毁,获得一名高一阶的随机初始干员(最高六阶)", PRTS 卫戍协议/帮助 "佩戴的
|
||||||
装备无法手动卸除,在失去该干员(干员出售、销毁、合并等)…时自动卸除", players re-inject it every round; player feedback after
|
装备无法手动卸除,在失去该干员(干员出售、销毁、合并等)…时自动卸除", players re-inject it every round; player feedback after
|
||||||
0.1.0), 画卷 (copy the operator in range with its
|
0.1.0), 画卷 (copy the operator in range with its
|
||||||
items), 教鞭 / “神秘顾客” (a random bounty is added).
|
items — the copied items arrive unequipped in the hand, PRTS 画卷 备注 "获得的装备为未装备状态"), 教鞭 / “神秘顾客” (a random bounty is added).
|
||||||
|
|
||||||
**教鞭 / “神秘顾客” stay a random bounty (deliberate).** The official Arts open a personal 悬赏 choice
|
**教鞭 / “神秘顾客” stay a random bounty (deliberate).** The official Arts open a personal 悬赏 choice
|
||||||
("选择一项(特殊)悬赏任务进行挑战", `choice_event hunter_band_1`). The server applies a random bounty instead: content
|
("选择一项(特殊)悬赏任务进行挑战", `choice_event hunter_band_1`). The server applies a random bounty instead: content
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ Combo check: `equip_chess_id` in blackboard is the partner's id WITHOUT the `_a/
|
|||||||
|
|
||||||
- Buff keys: 画卷 `trap_copy_front_char{}`; 教鞭 `trap_create_self_choice{choice_event=hunter_band_1}`; 神秘顾客 additionally `trap_disney_special{count=1}`.
|
- Buff keys: 画卷 `trap_copy_front_char{}`; 教鞭 `trap_create_self_choice{choice_event=hunter_band_1}`; 神秘顾客 additionally `trap_disney_special{count=1}`.
|
||||||
- `hunter_band_1` (choiceType PERSONAL_CHOOSE, "悬赏决策") pool is server-side. [ASSUMED] offer 3 random `enemyeffect_b_*` bounties (e.g. 碎骨·悬赏 +1, 萨卡兹百夫长·悬赏 +2, 泥岩·悬赏 +4, 锏·悬赏 +5, “复仇者”·悬赏 +6 funds; buff `add_enemy_kill_gain_coin{count,enemy_id,coin,round=1}`): adds 1 enemy to your next battle, killer gets `coin` funds.
|
- `hunter_band_1` (choiceType PERSONAL_CHOOSE, "悬赏决策") pool is server-side. [ASSUMED] offer 3 random `enemyeffect_b_*` bounties (e.g. 碎骨·悬赏 +1, 萨卡兹百夫长·悬赏 +2, 泥岩·悬赏 +4, 锏·悬赏 +5, “复仇者”·悬赏 +6 funds; buff `add_enemy_kill_gain_coin{count,enemy_id,coin,round=1}`): adds 1 enemy to your next battle, killer gets `coin` funds.
|
||||||
- 画卷 copy: duplicates the operator (same chess id incl. elite `_b`) AND clones its equipped items into the copy [DATA desc "包括精锐状态和装备"]; copy goes to hand [ASSUMED]; if hand full -> temporary hand.
|
- 画卷 copy: duplicates the operator (same chess id incl. elite `_b`) AND its equipped items [DATA desc "包括精锐状态和装备"]; the copied items arrive unequipped (PRTS 画卷 备注 "使用后销毁,获得的装备为未装备状态"), gained like any item — the hand, overflow temp (destroyed with the usual toast when both are full); copy goes to hand [ASSUMED]; if hand full -> temporary hand.
|
||||||
|
|
||||||
## 8. Item sources other than the shop [DATA]
|
## 8. Item sources other than the shop [DATA]
|
||||||
|
|
||||||
|
|||||||
@@ -116,12 +116,14 @@ export function registerMeta(registry) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 画卷 — trap_copy_front_char: copy the operator in range (elite status included) with its equipment. A copied
|
// 画卷 — trap_copy_front_char: copy the operator in range (elite status included) and its equipment. The copied items
|
||||||
// normal item that completes a pair with an owned one merges at once and the golden stays in the hand (research 04
|
// are gained unequipped, like any gained item — the hand, overflow temp, destroyed with the usual toast when both are
|
||||||
// addendum "两个同名道具(无论是否被装备)会自动合并…并自动返回整备区"); the built-in equipped that golden on the copy.
|
// full — never put on the copy (PRTS 画卷 备注 "使用后销毁,获得的装备为未装备状态"; until 0.2.2 a copied item that did
|
||||||
// A copy that completes a three-copy merge (GitHub #389, PR #390): the elite wears nothing — the promotion returns the
|
// not merge was equipped onto the copy). A copied normal item that completes a pair with an owned one merges at once,
|
||||||
// copies' equipment to the hand (PRTS 帮助 "在失去该干员(…合并等)…时自动卸除") — and the copied items stay in the hand
|
// the golden in the hand (research 04 addendum "两个同名道具(无论是否被装备)会自动合并…并自动返回整备区"). A copy that
|
||||||
// too (PRTS 画卷 备注 "获得的装备为未装备状态"), a copied normal item merging with the returned original.
|
// completes a three-copy merge (GitHub #389, PR #390): the promotion returns the copies' equipment to the hand too
|
||||||
|
// (PRTS 帮助 "在失去该干员(…合并等)…时自动卸除"), so the elite wears nothing and a copied normal item merges with the
|
||||||
|
// returned original.
|
||||||
wrap(registry, 'chess_item_6_02_m', () => ({
|
wrap(registry, 'chess_item_6_02_m', () => ({
|
||||||
onArt(ctx, ev) {
|
onArt(ctx, ev) {
|
||||||
const target = (ev.targets || []).find((p) => p && p.kind === 'chess');
|
const target = (ev.targets || []).find((p) => p && p.kind === 'chess');
|
||||||
@@ -132,12 +134,7 @@ export function registerMeta(registry) {
|
|||||||
const itemIds = (target.items || []).map((it) => it.id);
|
const itemIds = (target.items || []).map((it) => it.id);
|
||||||
const copy = ctx.grantChess(target.id, { requirePool: false, source: 'item:chess_item_6_02_m' });
|
const copy = ctx.grantChess(target.id, { requirePool: false, source: 'item:chess_item_6_02_m' });
|
||||||
if (!copy) { ev.error = 'HAND_FULL'; return; }
|
if (!copy) { ev.error = 'HAND_FULL'; return; }
|
||||||
const promoted = copy.id !== target.id;
|
for (const itemId of itemIds) ctx.grantItem(itemId, { source: 'item:chess_item_6_02_m' });
|
||||||
for (const itemId of itemIds) {
|
|
||||||
const got = ctx.grantItem(itemId, { source: 'item:chess_item_6_02_m' });
|
|
||||||
const holder = got && !promoted ? ctx.piece(copy.uid) : null;
|
|
||||||
if (got && got.id === itemId && holder && holder.kind === 'chess') ctx.equipDirect(got.uid, holder.uid);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -1121,10 +1121,11 @@ test('画卷 (Art): copies the operator on the tile / in front with its elite st
|
|||||||
const copy = ps.hand.find((p) => p && p.kind === 'chess');
|
const copy = ps.hand.find((p) => p && p.kind === 'chess');
|
||||||
assert.equal(copy && copy.id, DATA.chess[cid].goldenId, 'elite copy in the hand');
|
assert.equal(copy && copy.id, DATA.chess[cid].goldenId, 'elite copy in the hand');
|
||||||
// the copied 激光发射器 pairs with the original's (items merge "无论是否被装备") ⇒ the golden goes to the hand;
|
// the copied 激光发射器 pairs with the original's (items merge "无论是否被装备") ⇒ the golden goes to the hand;
|
||||||
// 变形同构体 never merges ⇒ the copy wears it too
|
// 变形同构体 never merges ⇒ its copy waits in the hand too, unequipped (PRTS 画卷 备注 "获得的装备为未装备状态";
|
||||||
assert.deepEqual(copy.items.map((x) => x.id), [A('6_09')]);
|
// until 0.2.2 the copy wore it)
|
||||||
|
assert.deepEqual(copy.items.map((x) => x.id), [], 'the copy wears nothing');
|
||||||
assert.deepEqual(ps.find(target.uid).piece.items.map((x) => x.id), [A('6_09')]);
|
assert.deepEqual(ps.find(target.uid).piece.items.map((x) => x.id), [A('6_09')]);
|
||||||
assert.deepEqual(handIds(ps, 'item'), [B('3_03')], 'merged golden in the hand (regression: was equipped on the copy)');
|
assert.deepEqual(handIds(ps, 'item').slice().sort(), [B('3_03'), A('6_09')].sort(), 'the merged golden and the copied 变形同构体 in the hand');
|
||||||
const art2 = giveItem(m, ps, 'chess_item_6_02_m');
|
const art2 = giveItem(m, ps, 'chess_item_6_02_m');
|
||||||
assert.equal(m.handle('p_0', { t: 'g.art', itemUid: art2.uid, row: 11, col: 7 }).error, 'BAD_TARGET', 'no operator in range');
|
assert.equal(m.handle('p_0', { t: 'g.art', itemUid: art2.uid, row: 11, col: 7 }).error, 'BAD_TARGET', 'no operator in range');
|
||||||
cover('chess_item_6_02_m');
|
cover('chess_item_6_02_m');
|
||||||
|
|||||||
@@ -111,3 +111,44 @@ test('Picture Scroll: a full hand and temp without promotion keeps the refused s
|
|||||||
assert.deepEqual(ps.privateView(), before);
|
assert.deepEqual(ps.privateView(), before);
|
||||||
h.invariants();
|
h.invariants();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// PRTS 画卷 备注 「使用后销毁,获得的装备为未装备状态」: without a promotion too, the copied items are gained like any item —
|
||||||
|
// the hand, overflow temp — and never put on the copy (until 0.2.2 a copy that did not merge was equipped onto it)
|
||||||
|
const ADV = [HAMMER, SHIELD].map((id) => id.replace(/_a$/, '_b'));
|
||||||
|
const looseItems = (ps) => [...ps.hand, ...ps.temp].filter((p) => p?.kind === 'item').map((p) => p.id).sort();
|
||||||
|
for (const golden of [false, true]) {
|
||||||
|
test(`Picture Scroll: one ${golden ? 'elite' : 'normal'} target — the copied (advanced) items arrive unequipped in the hand`, (t) => {
|
||||||
|
const { h, m, ps, target, cast } = prep(t, { copies: 1, golden, items: ADV });
|
||||||
|
assert.deepEqual(cast(), OK);
|
||||||
|
const copy = ps.allChess().find((p) => p.id === target.id && p !== target);
|
||||||
|
assert.ok(copy, 'the copy was gained');
|
||||||
|
assert.deepEqual(copy.items, [], 'the copy wears nothing');
|
||||||
|
assert.deepEqual(target.items.map((p) => p.id).sort(), [...ADV].sort(), 'the target keeps its own');
|
||||||
|
assert.deepEqual(looseItems(ps), [...ADV].sort(), 'the copied items wait in the hand');
|
||||||
|
assert.equal(m.gd.chess(copy.id).isGolden, golden);
|
||||||
|
h.invariants();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
test('Picture Scroll: a full hand — the copy and its items overflow into temp; an item with no slot left is destroyed like any gained item', (t) => {
|
||||||
|
for (const free of [3, 2]) {
|
||||||
|
const { h, ps, target, cast } = prep(t, { copies: 1, items: ADV });
|
||||||
|
while (ps.hand.some((p) => p == null)) assert.ok(ps.acquireItem(FILLER));
|
||||||
|
while (ps.temp.filter((p) => p == null).length > free) assert.ok(ps.acquireItem(FILLER));
|
||||||
|
const toasts = () => h.allTo('p_0', 'm.toast').map((x) => JSON.stringify(x));
|
||||||
|
const before = toasts().length;
|
||||||
|
assert.deepEqual(cast(), OK);
|
||||||
|
const copy = ps.allChess().find((p) => p.id === target.id && p !== target);
|
||||||
|
const where = copy ? ps.find(copy.uid)?.area : null;
|
||||||
|
assert.ok(where === 'temp' || where === 'hand', `free ${free}: the copy was gained into temp (the hand fills from temp once the scroll left)`);
|
||||||
|
assert.deepEqual(copy.items, [], `free ${free}: the copy wears nothing`);
|
||||||
|
assert.deepEqual(target.items.map((p) => p.id).sort(), [...ADV].sort());
|
||||||
|
const got = looseItems(ps).filter((id) => id !== FILLER);
|
||||||
|
if (free === 3) assert.deepEqual(got, [...ADV].sort(), 'room for the copy and both items');
|
||||||
|
else {
|
||||||
|
assert.equal(got.length, 1, 'room for the copy and one item: the other is destroyed');
|
||||||
|
assert.ok(toasts().slice(before).some((s) => s.includes('整备区已满,获得的装备已销毁')), 'with the usual toast');
|
||||||
|
}
|
||||||
|
h.invariants();
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in new issue
Block a user