feedback7: merge fb7-late follow-ups — 画卷's copied items always arrive unequipped (PRTS 「获得的装备为未装备状态」; hand, then temp, destroyed with the toast when both are full), 度算浪波 breaks an HP-ratio tie by the latest deployment (PRTS)

- Golden union OK (roster-033 / roster-034 again); broad node --test 5602 (5600 pass, 0 fail, 2 skipped); lint 0 errors.
This commit is contained in:
sganggs committed 2026-10-08 22:04:15 +08:00
commit cee3784321
8 files changed
+141 -73

No files matched your search

+1 -1
View File
@@ -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
+1 -1
View File
@@ -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]
+9 -12
View File
@@ -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);
}
}, },
})); }));
@@ -100,7 +100,9 @@ export default {
// (GitHub #124 「引星棘刺一技能不会在满技力时自动释放」) // (GitHub #124 「引星棘刺一技能不会在满技力时自动释放」)
trigger: { rule: 'SP_FULL' }, trigger: { rule: 'SP_FULL' },
onStart({ battle, unit }) { onStart({ battle, unit }) {
const t = battle.alliesInGrid(unit).filter((a) => a.hp > 0).sort((a, b) => a.hpRatio - b.hpRatio || b.blocking.length - a.blocking.length || dist(a, unit) - dist(b, unit) || a.id - b.id)[0]; // PRTS 备注 「※优先选择生命比例最低>最晚部署的我方单位(不含装置职业单位)」: the lowest HP ratio, a tie to the
// latest deployment (alliesInGrid has no devices); until 0.2.2 a tie went to more blocking, then the nearer
const t = battle.alliesInGrid(unit).filter((a) => a.hp > 0).sort((a, b) => a.hpRatio - b.hpRatio || b.deploySeq - a.deploySeq || a.id - b.id)[0];
if (!t) return; if (!t) return;
const key = `thorn2:guard:${unit.id}:${unit.mem.zoneSeq = (unit.mem.zoneSeq ?? 0) + 1}`; // its regen buff ("效果均可叠加") const key = `thorn2:guard:${unit.id}:${unit.mem.zoneSeq = (unit.mem.zoneSeq ?? 0) + 1}`; // its regen buff ("效果均可叠加")
const z = { type: 'guard', r: t.tileR, c: t.tileC, x: t.x, y: t.y, t: 0, acc: 0, dur: num(bb.projectile_delay_time, 6) + extend(battle, unit), key }; const z = { type: 'guard', r: t.tileR, c: t.tileC, x: t.x, y: t.y, t: 0, acc: 0, dur: num(bb.projectile_delay_time, 6) + extend(battle, unit), key };
+4 -3
View File
@@ -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');
+27
View File
@@ -1061,6 +1061,33 @@ test('引星棘刺 S1 度算浪波: two alchemy units on one ally add their DEF
done(h); done(h);
}); });
test('引星棘刺 S1 度算浪波: at an HP-ratio tie the unit goes to the latest deployed ally (PRTS 备注 「优先选择生命比例最低>最晚部署的我方单位」)', () => {
for (const id of pair('15')) {
const h = run({
defs: { chess: { t_first: ally('t_first'), t_last: ally('t_last') } },
// t_first stands nearer to her, t_last deploys after it (until 0.2.2 a tie went to more blocking, then the nearer)
units: [entry(id, 'skchr_thorn2_1', { row: 10, col: 3 }), { chessId: 't_first', row: 11, col: 4 }, { chessId: 't_last', row: 9, col: 5 }],
enemies: [],
});
const u = sel(h, id, 'skchr_thorn2_1');
const first = h.unit('t_first'), last = h.unit('t_last');
h.step();
assert.ok(last.deploySeq > first.deploySeq, 't_last is the later deployment');
first.hp = first.s.maxHp * 0.5; last.hp = last.s.maxHp * 0.5;
cast(h, u);
const z = u.mem.zones.find((x) => x.type === 'guard');
assert.deepEqual([z.r, z.c], [9, 5], 'the latest deployed of the two at 50 %');
// the lowest ratio still comes first
last.hp = last.s.maxHp; first.hp = first.s.maxHp * 0.4;
const n0 = u.skill.activations;
u.skill.gainSp(1000);
assert.ok(h.runUntil(() => u.skill.activations > n0, 15), 'cast again');
const z2 = u.mem.zones.filter((x) => x.type === 'guard').at(-1);
assert.deepEqual([z2.r, z2.c], [11, 4], 'a lower ratio beats a later deployment');
done(h);
}
});
test('引星棘刺 S1 度算浪波 (AUTO): fires as soon as its SP is full, no enemy needed (GitHub #124)', () => { test('引星棘刺 S1 度算浪波 (AUTO): fires as soon as its SP is full, no enemy needed (GitHub #124)', () => {
for (const id of pair('15')) { for (const id of pair('15')) {
const h = run({ const h = run({
+55 -55
View File
@@ -2017,64 +2017,64 @@
"round": 12, "round": 12,
"seed": 3754088384, "seed": 3754088384,
"waveId": "act1autochess_h05", "waveId": "act1autochess_h05",
"time": 214.93, "time": 216.03,
"ticks": 6448, "ticks": 6481,
"reason": "cleared", "reason": "cleared",
"killed": 54, "killed": 39,
"total": 167, "total": 167,
"unspawned": 0, "unspawned": 0,
"bossHpLeft": null, "bossHpLeft": null,
"rngDraws": 203, "rngDraws": 182,
"errors": {"count":0,"kinds":[]}, "errors": {"count":0,"kinds":[]},
"players": { "players": {
"p1": { "p1": {
"killed": 54, "killed": 39,
"total": 167, "total": 167,
"perfect": false, "perfect": false,
"leaks": 113, "leaks": 128,
"leaksUncounted": 0, "leaksUncounted": 0,
"coins": 2, "coins": 2,
"dmg": 579384, "dmg": 391926,
"bossDmg": 0, "bossDmg": 0,
"heal": 69092, "heal": 47767,
"deaths": 26, "deaths": 30,
"layerGains": {"indomShip":75} "layerGains": {"indomShip":85}
} }
}, },
"units": [ "units": [
["p1",1,"op","chess_char_5_11_a",0,null,0,0,185,6,24682,19544,18577,52,11], ["p1",1,"op","chess_char_5_11_a",0,null,2184.38,1,201,4,20958,13633,14291,46,11],
["p1",2,"op","chess_char_5_19_a",0,null,3241,1,200,8,50200,14729,7170,23,3], ["p1",2,"op","chess_char_5_19_a",0,null,2021.06,1,196,5,53968,15830,3047,30,2],
["p1",3,"op","chess_char_5_13_a",0,null,681.15,1,202,6,179758,34866,5612,46,1], ["p1",3,"op","chess_char_5_13_a",0,null,0,0,146,2,96135,31535,1666,34,1],
["p1",4,"op","chess_char_5_14_a",0,null,1331.1,1,203,0,4188,13059,3998,14,2], ["p1",4,"op","chess_char_5_14_a",0,null,1520.35,1,205,3,8189,4996,1320,24,2],
["p1",5,"op","chess_char_5_06_b",0,"uniequip_003_etlchi",2992,1,204,15,169257,19787,11958,43,10], ["p1",5,"op","chess_char_5_06_b",0,"uniequip_003_etlchi",0,0,167,10,111978,15012,7732,25,6],
["p1",6,"op","chess_char_5_17_a",0,null,723.48,1,193,1,20066,17784,192,37,9], ["p1",6,"op","chess_char_5_17_a",0,null,2250,1,204,1,13924,20073,1638,27,7],
["p1",7,"op","chess_char_5_12_a",1,null,1404,1,3,3,15355,4263,3154,15,3], ["p1",7,"op","chess_char_5_12_a",1,null,0,0,3,3,13568,3752,2348,12,2],
["p1",8,"op","chess_char_5_15_a",0,null,769.65,1,4,7,30505,713,557,126,34], ["p1",8,"op","chess_char_5_15_a",0,null,0,0,4,4,16352,2127,1201,72,19],
["p1",9,"op","chess_char_5_20_a",0,null,0,0,188,0,7281,3522,198,16,1], ["p1",9,"op","chess_char_5_20_a",0,null,0,0,188,0,7091,4808,167,15,1],
["p1",10,"op","chess_char_5_21_a",0,null,0,0,191,0,5581,7990,180,9,0], ["p1",10,"op","chess_char_5_21_a",0,null,0,0,190,0,4781,7272,227,8,0],
["p1",11,"op","chess_char_5_22_a",0,null,1237,1,5,8,69963,3136,3136,103,4], ["p1",11,"op","chess_char_5_22_a",0,null,1237,1,203,7,42434,3417,1483,67,2],
["p1",12,"op","chess_char_5_23_a",0,null,1422,1,205,0,0,8541,13622,21,7], ["p1",12,"op","chess_char_5_23_a",0,null,0,0,158,0,0,5524,12141,19,7],
["-","~1","device","trap_1105_accrate",null,null,100,1,1,0,0,0,0,0,null], ["-","~1","device","trap_1105_accrate",null,null,100,1,1,0,0,0,0,0,null],
["-","~2","device","trap_1105_accrate",null,null,100,1,2,0,0,0,0,0,null], ["-","~2","device","trap_1105_accrate",null,null,100,1,2,0,0,0,0,0,null],
["p1","~1","token","token_10015_dusk_drgn",null,null,1947.38,0,38,0,2547,787,737,8,null] ["p1","~1","token","token_10015_dusk_drgn",null,null,1716.76,0,38,0,2547,787,507,8,null]
], ],
"enemies": { "enemies": {
"enemy_1005_yokai_3": [1,0,1,5415,17555], "enemy_1005_yokai_3": [1,0,1,6177,9927],
"enemy_1009_lurker": [72,19,53,995,136536], "enemy_1009_lurker": [72,17,55,766,100625],
"enemy_10159_mntrjn": [1,0,1,0,25020], "enemy_10159_mntrjn": [1,0,1,0,25020],
"enemy_1019_jshoot_2": [51,24,27,18021,164779], "enemy_1019_jshoot_2": [51,14,37,18364,123264],
"enemy_1118_lidbox_2": [1,1,0,39,14774], "enemy_1118_lidbox_2": [1,1,0,39,14774],
"enemy_1196_msfyin": [1,1,0,0,3], "enemy_1196_msfyin": [1,1,0,0,3],
"enemy_1252_lysytb_2": [1,0,1,338,11250], "enemy_1252_lysytb_2": [1,0,1,373,12737],
"enemy_1364_spnaxe_2": [1,0,1,4584,15996], "enemy_1364_spnaxe_2": [1,0,1,10148,8334],
"enemy_1404_msnip": [36,9,27,111370,227827], "enemy_1404_msnip": [36,6,30,83483,139086],
"enemy_1516_jakill": [1,0,1,7554,10944], "enemy_1516_jakill": [1,0,1,9418,5522],
"enemy_9011_acrefr": [1,0,1,408,6100] "enemy_9011_acrefr": [1,0,1,0,3435]
}, },
"hooks": [1,205,6448,1165,1165,4391,4433,16853,80,194,85,83,0,27673,241,133,167,113,1,86,15,0,6], "hooks": [1,205,6481,1015,1015,3503,3526,10073,69,198,60,59,0,21941,201,111,167,128,1,75,17,0,4],
"events": {"atk":1276,"bounty":1,"deploy":36,"die":81,"dmg":4429,"engage":12,"fx":107,"heal":69,"layer":15,"leak":113,"skill":168,"spawn":182,"status":237}, "events": {"atk":1090,"bounty":1,"deploy":36,"die":70,"dmg":3522,"engage":12,"fx":86,"heal":51,"layer":17,"leak":128,"skill":119,"spawn":182,"status":212},
"fx": {"aoe":2,"crit":6,"dodge":6,"dollEnd":3,"garrison":15,"healAoe":11,"hpShare":1,"palsy":1,"reborn":2,"revive":11,"sp":5,"substitute":6,"summon":1,"swap":3,"zone":34}, "fx": {"aoe":2,"crit":4,"dodge":4,"dollEnd":6,"garrison":17,"healAoe":11,"hpShare":1,"palsy":1,"reborn":2,"revive":10,"sp":2,"substitute":6,"summon":1,"zone":19},
"snaps": "0092f82c 0dd327ad 53a3d810 58ba1139 90f0fe1e cb512e37 ceb2cf74 a613cd55 5ce6b6e5 d58de777 28df897a f1e4ae5f b4781ed7 3af8405e 2c626d36 ce72c9fa e19c7d08 6db15d3a b2ed2de3 dadece28 8a66c30e ce8ff9d7" "snaps": "0092f82c 48a2bc74 b6ee7f69 2bd2b1d8 2b402a77 a6de3974 8ee0fed8 13615544 a83cdaa1 f1824fc8 28b8a693 695a9e0a 42c35de0 0f609ec1 ffb4425a 3841da00 aba0f9db ff2e4a84 c0d35663 5c9e36ae 684e8e87 1a6513d1"
}, },
"roster-034": { "roster-034": {
"about": "pass 1 band 6: chess_char_5_08_b chess_char_5_11_b chess_char_5_19_b chess_char_5_13_b chess_char_5_14_b chess_char_5_07_b chess_char_5_17_b chess_char_5_10_b chess_char_5_12_b chess_char_5_15_b chess_char_5_20_b chess_char_5_21_b", "about": "pass 1 band 6: chess_char_5_08_b chess_char_5_11_b chess_char_5_19_b chess_char_5_13_b chess_char_5_14_b chess_char_5_07_b chess_char_5_17_b chess_char_5_10_b chess_char_5_12_b chess_char_5_15_b chess_char_5_20_b chess_char_5_21_b",
@@ -2092,22 +2092,22 @@
"total": 104, "total": 104,
"unspawned": 0, "unspawned": 0,
"bossHpLeft": null, "bossHpLeft": null,
"rngDraws": 565, "rngDraws": 571,
"errors": {"count":0,"kinds":[]}, "errors": {"count":0,"kinds":[]},
"players": {"p1":{"killed":90,"total":104,"perfect":false,"leaks":14,"leaksUncounted":0,"coins":8,"dmg":5110704,"bossDmg":0,"heal":120360,"deaths":1,"layerGains":{}}}, "players": {"p1":{"killed":90,"total":104,"perfect":false,"leaks":14,"leaksUncounted":0,"coins":8,"dmg":5114900,"bossDmg":0,"heal":118910,"deaths":1,"layerGains":{}}},
"units": [ "units": [
["p1",1,"op","chess_char_5_08_b",0,"uniequip_003_horn",1512.22,1,7,28,1726631,34150,947,96,84], ["p1",1,"op","chess_char_5_08_b",0,"uniequip_003_horn",2996.09,1,7,24,1724859,34441,947,96,84],
["p1",2,"op","chess_char_5_11_b",0,"uniequip_003_demkni",3477.62,1,10,0,39384,11346,66820,184,36], ["p1",2,"op","chess_char_5_11_b",0,"uniequip_003_demkni",3087.35,1,10,0,39266,12654,69512,184,38],
["p1",3,"op","chess_char_5_19_b",0,"none",3682,1,11,4,178330,6313,6313,129,5], ["p1",3,"op","chess_char_5_19_b",0,"none",3682,1,11,3,176264,6843,6948,120,4],
["p1",4,"op","chess_char_5_13_b",0,"uniequip_003_ghost2",3448.92,1,13,3,109842,29514,29300,210,5], ["p1",4,"op","chess_char_5_13_b",0,"uniequip_003_ghost2",3197.32,1,13,4,109272,27806,27516,210,5],
["p1",5,"op","chess_char_5_14_b",0,"none",3261,1,15,0,16840,13747,13949,217,20], ["p1",5,"op","chess_char_5_14_b",0,"none",3261,1,15,0,16840,11013,10956,217,19],
["p1",6,"op","chess_char_5_07_b",0,"uniequip_003_surtr",2680,1,8,7,313834,25889,788,121,56], ["p1",6,"op","chess_char_5_07_b",0,"uniequip_003_surtr",2680,1,8,7,311939,25889,788,122,55],
["p1",7,"op","chess_char_5_17_b",0,"uniequip_003_f12yin",538.04,1,97,6,252445,5368,450,89,42], ["p1",7,"op","chess_char_5_17_b",0,"uniequip_003_f12yin",875.96,1,97,6,252678,5030,450,89,42],
["p1",8,"op","chess_char_5_10_b",0,"uniequip_003_lisa",1470,1,4,5,160605,0,0,120,6], ["p1",8,"op","chess_char_5_10_b",0,"uniequip_003_lisa",1470,1,4,5,159475,0,0,120,6],
["p1",9,"op","chess_char_5_12_b",1,"uniequip_003_dusk",1667,1,5,32,1794727,0,0,91,6], ["p1",9,"op","chess_char_5_12_b",1,"uniequip_003_dusk",1667,1,5,35,1804443,0,0,91,6],
["p1",10,"op","chess_char_5_15_b",0,"none",1907.5,1,12,1,57355,0,0,166,82], ["p1",10,"op","chess_char_5_15_b",0,"none",1907.5,1,12,1,57321,0,0,166,86],
["p1",11,"op","chess_char_5_20_b",0,"uniequip_003_aglina",1352,1,14,2,213658,1794,1794,138,6], ["p1",11,"op","chess_char_5_20_b",0,"uniequip_003_aglina",1352,1,14,2,213658,1794,1794,138,6],
["p1",12,"op","chess_char_5_21_b",0,"none",3330.35,1,6,2,247052,0,0,57,3], ["p1",12,"op","chess_char_5_21_b",0,"none",3330.35,1,6,3,248885,0,0,57,3],
["-","~1","device","trap_1105_accrate",null,null,100,0,1,0,0,0,0,0,null], ["-","~1","device","trap_1105_accrate",null,null,100,0,1,0,0,0,0,0,null],
["-","~2","device","trap_1105_accrate",null,null,100,1,2,0,0,0,0,0,null], ["-","~2","device","trap_1105_accrate",null,null,100,1,2,0,0,0,0,0,null],
["-","~3","device","trap_1105_accrate",null,null,100,1,3,0,0,0,0,0,null] ["-","~3","device","trap_1105_accrate",null,null,100,1,3,0,0,0,0,0,null]
@@ -2119,16 +2119,16 @@
"enemy_1165_duhond_2": [1,1,0,114,24767], "enemy_1165_duhond_2": [1,1,0,114,24767],
"enemy_1204_msfhu": [1,1,0,0,12], "enemy_1204_msfhu": [1,1,0,0,12],
"enemy_1288_duskls": [1,1,0,1594,30964], "enemy_1288_duskls": [1,1,0,1594,30964],
"enemy_1381_winman": [27,25,2,4083,938899], "enemy_1381_winman": [27,25,2,4053,938899],
"enemy_1381_winman_2": [39,31,7,24737,1719910], "enemy_1381_winman_2": [39,31,7,24234,1723090],
"enemy_1387_winshd": [30,26,2,76127,2104525], "enemy_1387_winshd": [30,26,2,74009,2105541],
"enemy_1425_lrcmra_2": [1,1,0,11329,154793], "enemy_1425_lrcmra_2": [1,1,0,11329,154793],
"enemy_2009_csaudc": [1,1,0,2690,49534] "enemy_2009_csaudc": [1,1,0,2690,49534]
}, },
"hooks": [1,120,6900,2558,2558,3995,3996,8433,91,103,351,349,0,31469,6479,104,104,11,1,92,0,0,8], "hooks": [1,120,6900,2553,2553,3990,3990,8324,91,103,354,352,0,31724,6540,104,104,11,1,92,0,0,9],
"events": {"atk":3070,"bounty":4,"deploy":13,"die":92,"dmg":3985,"engage":12,"fx":326,"heal":36,"leak":11,"skill":700,"spawn":119,"status":807}, "events": {"atk":3068,"bounty":4,"deploy":13,"die":92,"dmg":3979,"engage":12,"fx":330,"heal":38,"leak":11,"skill":706,"spawn":119,"status":806},
"fx": {"aoe":20,"crit":14,"dodge":8,"ember":1,"healAoe":36,"hpShare":5,"sp":139,"zone":103}, "fx": {"aoe":19,"crit":12,"dodge":9,"ember":1,"healAoe":38,"hpShare":5,"sp":138,"zone":108},
"snaps": "de3bd2e2 23f28d96 7a63c0f2 28a9a6fe e6984bca 8eae402d 2b24991d c8788f23 5a001b21 0fed151a 87c013c7 b38057af 123bb336 e5b070fc 7fac01fd fb1d6e4d 2f2bc32b d3c63947 a34e6643 97d460d1 3c1349d8 783047e8 2de5f6f2" "snaps": "de3bd2e2 7b2cb2f6 387a1a50 7d4a972d 1e9785f0 bfbc181b 46e0e6f7 d028a75e 0983234c 674b285e 976ff8d6 12ccf30d acf0e4cd 0a9c1ac1 b2bbe21c 3ef3b750 153d049c ee9a46f5 39828267 5064f766 65d6e402 cb0cb3b9 3bbac53a"
}, },
"roster-035": { "roster-035": {
"about": "pass 1 band 6: chess_char_6_03_a chess_char_6_06_a chess_char_6_07_a chess_char_5_22_b chess_char_5_23_b chess_char_6_01_a chess_char_6_02_a chess_char_6_04_a chess_char_6_05_a chess_char_6_08_a chess_char_6_09_a chess_char_6_11_a token_10017_skadi2_dedant token_10030_mlyss_wtrman", "about": "pass 1 band 6: chess_char_6_03_a chess_char_6_06_a chess_char_6_07_a chess_char_5_22_b chess_char_5_23_b chess_char_6_01_a chess_char_6_02_a chess_char_6_04_a chess_char_6_05_a chess_char_6_08_a chess_char_6_09_a chess_char_6_11_a token_10017_skadi2_dedant token_10030_mlyss_wtrman",
+41
View File
@@ -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();
}
});