// QA: 5 张卡技能生效 RED 基线红测(当前线上 v1.1.0 引擎,name 字符串匹配) // 目的:在宋子沐卡ID修复前,实测证实 5 张卡技能因 name 不匹配全部失效(红测复现) // 回合归属:turnNo 奇数为 P0(房主),偶数为 P1(对手)—— 与前端一致 ObjC.import('Foundation'); const V11_ROOT = '/Users/pamper/idbots/project/bots/11/2026-08-13/game-regression/v11-online/extracted'; function readFile(p) { const s = $.NSString.stringWithContentsOfFileEncodingError(p, $.NSUTF8StringEncoding, $()); return ObjC.unwrap(s) || ''; } function makeSandbox() { const w = {}; w.window = w; w.console = { log: function(){}, error: function(){} }; w.addEventListener = function(){}; return w; } function loadScript(w, code) { const fn = new Function('window', 'console', code + '\n;return window;'); return fn(w.window, w.console); } const s = makeSandbox(); loadScript(s, readFile(V11_ROOT + '/data/agent-cards.js')); loadScript(s, readFile(V11_ROOT + '/engine/agent-duel-core.js')); const C = s.BantangCore; const RNG_HI = () => 0.9; // 封锁 50% 概率技能干扰 const room = { roomId: 'qaskill', mode: 'ai', protocolVersion: '1.1', creatorMetaID: 'idq-a', joinerMetaID: 'idq-b', deckHash: 'dh' }; const mk = (turnNo, cardId, player, target) => ({ op: 'move', roomId: 'qaskill', turnNo, player, cardId, target: target || '', actionHash: C.computeActionHash(turnNo, cardId, target || '', player), ts: 1786600000 + turnNo, }); let pass = 0, fail = 0; function ok(name, cond, extra) { if (cond) { pass++; console.log(' ✅', name); } else { fail++; console.log(' ❌', name, extra !== undefined ? JSON.stringify(extra) : ''); } } // P0 出牌(奇数回合)/ P1 出牌(偶数回合) const mv = (st, n, who) => C.applyMove(st, mk(n, 'agent-jiangyichen', who), { rng: RNG_HI }); function gameWith(handA) { return C.startBattle(room, { nameA: '房主', nameB: '对手', handA: handA.slice(), handB: ['agent-jiangyichen'], rng: RNG_HI }); } console.log('═══ 5 张卡技能 RED 基线(线上 v1.1.0,name 匹配)═══'); const probe = gameWith(['agent-jiangyichen']); console.log('P0 name=' + probe.players[0].name + ' / P1 name=' + probe.players[1].name + '(前端实际值,非卡牌名)'); // TC-01 江亦辰·节点加固(ondamage 减伤1):P0 持卡,P1 t2 攻击 atk4 // 正确期望:P0 26→23(4-1);当前 bug:减伤不生效 → 22 { const st = gameWith(['agent-jiangyichen']); mv(st, 1, 'idq-a'); mv(st, 2, 'idq-b'); const after = st.players[0].hp; ok('TC-01 节点加固生效:P0 hp=23(受击减伤1)', after === 23, { after, expected: 23, redObserved: 22 }); } // TC-02 数据管家·数据冗余(ondamage 减伤2):P0 持卡,P1 t2 攻击 atk4 // 正确期望:P0 26→24;当前 bug → 22 { const st = gameWith(['agent-data']); mv(st, 1, 'idq-a'); mv(st, 2, 'idq-b'); const after = st.players[0].hp; ok('TC-02 数据冗余生效:P0 hp=24(受击减伤2)', after === 24, { after, expected: 24, redObserved: 22 }); } // TC-03 沈知珩·架构兜底(turnend 回血2):P0 持卡;t1末+2→28,P1 t2攻-4→24,P0 t3末+2→26 // 当前 bug:两回合末均不回复 → 22 { const st = gameWith(['agent-shenzhiheng']); mv(st, 1, 'idq-a'); mv(st, 2, 'idq-b'); mv(st, 3, 'idq-a'); const after = st.players[0].hp; ok('TC-03 架构兜底生效:P0 hp=26(回合末各回2,28→24→26)', after === 26, { after, expected: 26, redObserved: 22 }); } // TC-04 节点守望者·状态同步(turnend 回血1):P0 持卡;t1末+1→27,P1 t2攻-4→23,P0 t3末+1→24 // 当前 bug → 22 { const st = gameWith(['agent-node']); mv(st, 1, 'idq-a'); mv(st, 2, 'idq-b'); mv(st, 3, 'idq-a'); const after = st.players[0].hp; ok('TC-04 状态同步生效:P0 hp=24(回合末各回1,27→23→24)', after === 24, { after, expected: 24, redObserved: 22 }); } // TC-05 预言机守卫·实时喂价(每3回合末 atkBonus+1):P0 持卡,跑 t1/t3/t5/t7/t9 出牌 // 正确期望:多回合后 atkBonus≥1;当前 bug → 恒 0 { const st = gameWith(['agent-oracle']); mv(st, 1, 'idq-a'); mv(st, 2, 'idq-b'); mv(st, 3, 'idq-a'); mv(st, 4, 'idq-b'); mv(st, 5, 'idq-a'); mv(st, 6, 'idq-b'); mv(st, 7, 'idq-a'); mv(st, 8, 'idq-b'); mv(st, 9, 'idq-a'); const atkBonus = st.players[0].skillFlags.atkBonus; ok('TC-05 实时喂价生效:P0 累计 atkBonus≥1', atkBonus >= 1, { atkBonus, expected: '>=1', redObserved: 0 }); } console.log('\nRESULT pass=' + pass + ' fail=' + fail + '(RED 基线:全部 fail = 5 卡技能在线上确实失效,bug 复现成立)');