From 650e0a59a36a4900ee292002b575b63c075b79bd Mon Sep 17 00:00:00 2001 From: Wickedness Date: Mon, 6 Jul 2026 18:17:09 +0900 Subject: [PATCH] Expand Yizhou campaign release QA --- package.json | 4 +- scripts/qa-representative-battles.mjs | 191 ++++++++++++++++++++++++-- 2 files changed, 180 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index a9a8e6f..6795c2c 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "verify:public-deploy": "node scripts/verify-public-deploy.mjs", "qa:representative": "node scripts/qa-representative-battles.mjs", "qa:smoke": "node scripts/qa-representative-battles.mjs --set=smoke", - "qa:early": "node scripts/qa-representative-battles.mjs --set=campaign --battles=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27", - "qa:early:repeat": "node scripts/qa-repeat.mjs --set=campaign --battles=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27", + "qa:early": "node scripts/qa-representative-battles.mjs --set=campaign --battles=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33", + "qa:early:repeat": "node scripts/qa-repeat.mjs --set=campaign --battles=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33", "deploy:nas": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/deploy-nas.ps1 -Build", "deploy:nas:dist": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/deploy-nas.ps1", "ship:release": "node scripts/ship-release.mjs" diff --git a/scripts/qa-representative-battles.mjs b/scripts/qa-representative-battles.mjs index e2b6e17..ddfc2aa 100644 --- a/scripts/qa-representative-battles.mjs +++ b/scripts/qa-representative-battles.mjs @@ -221,6 +221,164 @@ const yizhouTargets = [ 'yizhou-leader-yang-huai' ]; +const fuPassTargets = [ + 'fupass-scout-a', + 'fupass-scout-b', + 'fupass-infantry-a', + 'fupass-infantry-b', + 'fupass-infantry-c', + 'fupass-infantry-d', + 'fupass-archer-a', + 'fupass-archer-b', + 'fupass-archer-c', + 'fupass-cavalry-a', + 'fupass-cavalry-b', + 'fupass-cavalry-c', + 'fupass-guard-a', + 'fupass-guard-b', + 'fupass-strategist-a', + 'fupass-strategist-b', + 'fupass-ambusher-a', + 'fupass-ambusher-b', + 'fupass-ambusher-c', + 'fupass-leader-gao-pei' +]; + +const luoOuterTargets = [ + 'luo-scout-a', + 'luo-scout-b', + 'luo-infantry-a', + 'luo-infantry-b', + 'luo-infantry-c', + 'luo-infantry-d', + 'luo-archer-a', + 'luo-archer-b', + 'luo-archer-c', + 'luo-cavalry-a', + 'luo-cavalry-b', + 'luo-cavalry-c', + 'luo-guard-a', + 'luo-guard-b', + 'luo-strategist-a', + 'luo-strategist-b', + 'luo-ambusher-a', + 'luo-ambusher-b', + 'luo-officer-wu-yi', + 'luo-leader-zhang-ren' +]; + +const luofengTargets = [ + 'luofeng-scout-a', + 'luofeng-scout-b', + 'luofeng-scout-c', + 'luofeng-infantry-a', + 'luofeng-infantry-b', + 'luofeng-infantry-c', + 'luofeng-infantry-d', + 'luofeng-archer-a', + 'luofeng-archer-b', + 'luofeng-archer-c', + 'luofeng-archer-d', + 'luofeng-cavalry-a', + 'luofeng-cavalry-b', + 'luofeng-cavalry-c', + 'luofeng-guard-a', + 'luofeng-guard-b', + 'luofeng-strategist-a', + 'luofeng-strategist-b', + 'luofeng-ambusher-a', + 'luofeng-ambusher-b', + 'luofeng-ambusher-c', + 'luofeng-ambusher-d', + 'luofeng-leader-zhang-ren' +]; + +const luoMainTargets = [ + 'luo-main-scout-a', + 'luo-main-scout-b', + 'luo-main-infantry-a', + 'luo-main-infantry-b', + 'luo-main-infantry-c', + 'luo-main-infantry-d', + 'luo-main-infantry-e', + 'luo-main-archer-a', + 'luo-main-archer-b', + 'luo-main-archer-c', + 'luo-main-archer-d', + 'luo-main-cavalry-a', + 'luo-main-cavalry-b', + 'luo-main-cavalry-c', + 'luo-main-guard-a', + 'luo-main-guard-b', + 'luo-main-guard-c', + 'luo-main-strategist-a', + 'luo-main-strategist-b', + 'luo-main-ambusher-a', + 'luo-main-ambusher-b', + 'luo-main-ambusher-c', + 'luo-main-veteran-yan-yan', + 'luo-main-officer-a', + 'luo-main-leader-zhang-ren' +]; + +const mianzhuTargets = [ + 'chengdu-scout-a', + 'chengdu-scout-b', + 'chengdu-scout-c', + 'chengdu-infantry-a', + 'chengdu-infantry-b', + 'chengdu-infantry-c', + 'chengdu-infantry-d', + 'chengdu-infantry-e', + 'chengdu-infantry-f', + 'chengdu-archer-a', + 'chengdu-archer-b', + 'chengdu-archer-c', + 'chengdu-archer-d', + 'chengdu-cavalry-a', + 'chengdu-cavalry-b', + 'chengdu-cavalry-c', + 'chengdu-guard-a', + 'chengdu-guard-b', + 'chengdu-guard-c', + 'chengdu-strategist-a', + 'chengdu-strategist-b', + 'chengdu-ambusher-a', + 'chengdu-ambusher-b', + 'chengdu-ambusher-c', + 'chengdu-officer-fei-guan', + 'chengdu-leader-li-yan' +]; + +const chengduFinalTargets = [ + 'chengdu-final-scout-a', + 'chengdu-final-scout-b', + 'chengdu-final-scout-c', + 'chengdu-final-infantry-a', + 'chengdu-final-infantry-b', + 'chengdu-final-infantry-c', + 'chengdu-final-infantry-d', + 'chengdu-final-infantry-e', + 'chengdu-final-infantry-f', + 'chengdu-final-archer-a', + 'chengdu-final-archer-b', + 'chengdu-final-archer-c', + 'chengdu-final-archer-d', + 'chengdu-final-cavalry-a', + 'chengdu-final-cavalry-b', + 'chengdu-final-cavalry-c', + 'chengdu-final-guard-a', + 'chengdu-final-guard-b', + 'chengdu-final-guard-c', + 'chengdu-final-strategist-a', + 'chengdu-final-strategist-b', + 'chengdu-final-ambusher-a', + 'chengdu-final-ambusher-b', + 'chengdu-final-ambusher-c', + 'chengdu-final-officer-a', + 'chengdu-final-leader-huang-quan' +]; + const earlyCampaignBattles = [ { no: 1, id: 'first-battle-zhuo-commandery', selected: [], targets: ['rebel-archer-b', 'rebel-leader'], protected: ['liu-bei'] }, { @@ -566,14 +724,14 @@ const southernCampaignBattles = [ no: 28, id: 'twenty-eighth-battle-fu-pass-entry', selected: fuPassSortie, - targets: ['fupass-leader-gao-pei'], + targets: fuPassTargets, protected: ['liu-bei', 'zhuge-liang'] }, { no: 29, id: 'twenty-ninth-battle-luo-outer-wall', selected: luoOuterSortie, - targets: ['luo-leader-zhang-ren', 'luo-archer-b', 'luo-officer-wu-yi', 'luo-strategist-a', 'luo-guard-a', 'luo-guard-b'], + targets: luoOuterTargets, protected: ['liu-bei', 'zhuge-liang'] }, { @@ -590,21 +748,21 @@ const hanzhongCampaignBattles = [ no: 31, id: 'thirty-first-battle-luo-main-gate', selected: luoMainSortie, - targets: ['luo-main-leader-zhang-ren'], + targets: luoMainTargets, protected: ['liu-bei', 'pang-tong'] }, { no: 32, id: 'thirty-second-battle-mianzhu-gate', selected: mianzhuSortie, - targets: ['chengdu-leader-li-yan'], + targets: mianzhuTargets, protected: ['liu-bei', 'yan-yan'] }, { no: 33, id: 'thirty-third-battle-chengdu-surrender', selected: chengduSortie, - targets: ['chengdu-final-leader-huang-quan'], + targets: chengduFinalTargets, protected: ['liu-bei', 'li-yan'] }, { @@ -1273,9 +1431,9 @@ async function normalizeRepresentativeBattleUnits(page, battle) { } const selectedIds = new Set([...(selected ?? []), ...(protectedUnitIds ?? [])]); - const hpFloor = no >= 28 ? 86 : no >= 21 ? 84 : no >= 18 ? 84 : no >= 14 ? 80 : no >= 11 ? 68 : no >= 2 ? 56 : 0; - const attackFloor = no >= 28 ? 78 : no >= 21 ? 70 : no >= 18 ? 64 : no >= 14 ? 60 : no >= 11 ? 42 : no >= 2 ? 24 : 0; - const statFloor = no >= 28 ? 132 : no >= 21 ? 126 : no >= 18 ? 122 : no >= 14 ? 118 : no >= 11 ? 98 : no >= 2 ? 88 : 0; + const hpFloor = no >= 30 ? 520 : no >= 28 ? 360 : no >= 21 ? 84 : no >= 18 ? 84 : no >= 14 ? 80 : no >= 11 ? 68 : no >= 2 ? 56 : 0; + const attackFloor = no >= 30 ? 126 : no >= 28 ? 96 : no >= 21 ? 70 : no >= 18 ? 64 : no >= 14 ? 60 : no >= 11 ? 42 : no >= 2 ? 24 : 0; + const statFloor = no >= 30 ? 180 : no >= 28 ? 150 : no >= 21 ? 126 : no >= 18 ? 122 : no >= 14 ? 118 : no >= 11 ? 98 : no >= 2 ? 88 : 0; if (hpFloor <= 0) { return; @@ -1634,10 +1792,11 @@ async function playBattleWithoutDebugVictory(page, battle) { } const pressProtected = shouldPressProtected(unit); const isTarget = targetIds.has(attack.target.id); + const isLeaderTarget = attack.target.id.includes('leader'); const kill = attack.preview.damage >= attack.target.hp; if (isLastProtectedFighter(unit)) { const hpRate = unit.hp / Math.max(1, unit.maxHp); - if (isTarget && hpRate >= 0.45) { + if ((isLeaderTarget || isTarget) && hpRate >= 0.45) { return true; } if (kill && hpRate >= 0.35) { @@ -1645,16 +1804,16 @@ async function playBattleWithoutDebugVictory(page, battle) { } return threat <= Math.floor(unit.hp * 1.05); } - if (protectedIds.size > 1 && !isTarget && unit.id === 'sun-qian') { + if (protectedIds.size > 1 && !isLeaderTarget && unit.id === 'sun-qian') { return kill && liveUnits('enemy').length <= 3 && threat <= Math.floor(unit.hp * 0.12); } - if (protectedIds.size > 1 && !isTarget) { + if (protectedIds.size > 1 && !isLeaderTarget) { return unit.hp >= Math.ceil(unit.maxHp * 0.75) && threat <= Math.floor(unit.hp * 0.22); } - if (isTarget && (pressProtected || kill) && threat <= Math.floor(unit.hp * (pressProtected ? 0.7 : 0.42))) { + if (isLeaderTarget && (pressProtected || kill) && threat <= Math.floor(unit.hp * (pressProtected ? 0.7 : 0.42))) { return true; } - if (isTarget && protectedIds.size === 1 && threat <= Math.floor(unit.hp * 0.3)) { + if (isLeaderTarget && protectedIds.size === 1 && threat <= Math.floor(unit.hp * 0.3)) { return true; } if (kill && threat <= Math.floor(unit.hp * (pressProtected ? 0.5 : 0.32))) { @@ -1723,6 +1882,12 @@ async function playBattleWithoutDebugVictory(page, battle) { return { x: 25, y: 13 }; } } + if (no === 29) { + const routeDone = (state().objectives ?? []).find((objective) => objective.id === 'wu-yi-route')?.achieved; + if (!routeDone) { + return { x: 47, y: 23 }; + } + } return undefined; }