From 22793c29a99d966dcec14aaecb12753bccb86156 Mon Sep 17 00:00:00 2001 From: Wickedness Date: Sat, 4 Jul 2026 20:38:35 +0900 Subject: [PATCH] Refine Jiameng Ma Chao encounter flow --- src/game/data/battles.ts | 90 ++++++++++++++++++++++++++++++----- src/game/data/campaignFlow.ts | 2 +- src/game/data/scenario.ts | 34 ++++++------- src/game/scenes/CampScene.ts | 24 ++++++++-- 4 files changed, 117 insertions(+), 33 deletions(-) diff --git a/src/game/data/battles.ts b/src/game/data/battles.ts index 05d83b7..9951525 100644 --- a/src/game/data/battles.ts +++ b/src/game/data/battles.ts @@ -4937,14 +4937,81 @@ export const thirtyFourthBattleScenario: BattleScenarioDefinition = { openingObjectiveLines: [ '가맹관은 익주와 한중을 잇는 목입니다. 이 길을 열면 촉한 건국 이후 북방 전선의 첫 기반이 마련됩니다.', '황권은 이번 전장이 첫 출전입니다. 한중 길과 익주 내정의 균형을 잡으려면 황권을 지켜야 합니다.', - '마초의 돌격을 받아내고 방덕의 친위대를 제압하면, 서량 기병을 유비군으로 끌어들일 말길이 열립니다.' + '마초의 첫 돌격을 받아내고 서량 선봉을 끊으면, 서량 기병을 유비군으로 끌어들일 말길이 열립니다.' ], + tacticalGuide: { + summary: + '가맹관 마초 대면전은 마초를 꺾어 버리는 전투가 아니라, 서량 기병의 첫 돌격을 받아내고 유비군의 군율과 포용을 보여 주는 전장입니다. 황권의 길 해석을 지키며 산길 척후선과 보급로를 안정시키면 마초가 장로 진영에 머물 이유가 약해집니다.', + route: '서남 유비군 진영 → 산길 척후선 확인 → 가맹관 보급로 보전 → 서량 기병 돌격선 차단 → 마초 돌격 저지', + focus: + '황권은 한중 길과 익주 북문을 읽는 첫 출전 참모입니다. 조운과 장비는 마초의 돌격을 받아 내고, 법정은 마초가 장로에게 마음을 다 주지 못한 틈을 설득의 명분으로 바꿉니다.', + roles: [ + '유비: 마초를 받아들일 명분과 패배 조건의 중심', + '황권: 가맹관 길과 한중 정세를 해석하는 새 합류 참모', + '조운: 서량 기병 돌격을 기동으로 받아 내는 후속 전열', + '장비: 정면 돌파선을 막아 서량 기세를 꺾는 전열', + '법정: 마초와 장로 사이의 균열을 읽는 설득 책사', + '황충: 산허리 궁병과 관문 사격선을 견제하는 원거리 축' + ], + events: { + 'village-approach': { + title: '가맹관 말길', + lines: [ + '황권: 이 길은 성도와 한중을 잇는 북문입니다. 보급 창고와 산길을 보전해야 마초에게도 머물 곳을 보여 줄 수 있습니다.', + '유비: 창끝만 꺾어서는 장수를 얻지 못하오. 돌격을 받아 내되, 병사와 말길은 함부로 짓밟지 맙시다.' + ] + }, + 'cavalry-approach': { + title: '서량 돌격선', + lines: [ + '조운: 적 기병은 길이 열리면 한 번에 깊이 들어옵니다. 전열을 넓히지 말고 돌아 나갈 길을 먼저 끊겠습니다.', + '장비: 좋다. 정면은 내가 막을 테니, 빠져나가는 말발굽은 조운이 끊어라.' + ] + }, + 'leader-wavering': { + title: '마초의 시선이 흔들리다', + lines: [ + '서량 선봉이 끊기고 황권이 전열을 지키자, 마초는 유비군이 약탈로 길을 여는 군대가 아님을 보게 됩니다.', + '마초: 돌격을 막고도 보급을 불태우지 않는군. 저 깃발 아래라면 서량의 창도 다른 길을 찾을 수 있겠소.' + ] + } + } + }, + sortie: { + sortieLimit: 6, + requiredUnits: ['liu-bei', 'huang-quan'], + recommendedUnits: [ + { unitId: 'liu-bei', role: 'reserve', reason: '마초를 받아들일 명분과 패배 조건의 중심입니다.' }, + { unitId: 'huang-quan', role: 'support', reason: '황권의 첫 출전입니다. 익주 북문과 한중 길을 해석합니다.' }, + { unitId: 'zhao-yun', role: 'flank', reason: '서량 기병의 돌격을 기동으로 받아 내고 후속을 끊습니다.' }, + { unitId: 'zhang-fei', role: 'front', reason: '마초의 정면 돌격을 받아 내는 강한 전열 압박입니다.' }, + { unitId: 'fa-zheng', role: 'support', reason: '마초가 장로에게 마음을 다 주지 못한 틈을 읽습니다.' }, + { unitId: 'huang-zhong', role: 'support', reason: '산허리 궁병과 관문 사격선을 안정적으로 견제합니다.' } + ], + recommendedClasses: [ + { label: '포용 명분', classKeys: ['lord'], reason: '유비가 살아 있어야 마초 합류의 명분이 유지됩니다.' }, + { label: '길 해석', classKeys: ['strategist'], reason: '황권과 법정이 가맹관 길과 마초의 속내를 읽습니다.' }, + { label: '돌격 저지', classKeys: ['spearman', 'infantry'], reason: '장비 같은 전열 무장이 서량 기병의 첫 충돌을 받아 냅니다.' }, + { label: '기병 회수', classKeys: ['cavalry'], reason: '조운이 깊게 들어온 기병의 후속과 퇴로를 끊습니다.' }, + { label: '사격 견제', classKeys: ['archer'], reason: '황충이 관문 궁병과 고지 사격선을 누릅니다.' } + ], + deploymentSlots: [ + { x: 4, y: 44, role: 'reserve', unitId: 'liu-bei', label: '포용 명분' }, + { x: 14, y: 44, role: 'support', unitId: 'huang-quan', label: '길 해석' }, + { x: 8, y: 43, role: 'flank', unitId: 'zhao-yun', label: '기병 회수' }, + { x: 4, y: 46, role: 'front', unitId: 'zhang-fei', label: '돌격 저지' }, + { x: 11, y: 45, role: 'support', unitId: 'fa-zheng', label: '설득 책략' }, + { x: 7, y: 44, role: 'support', unitId: 'huang-zhong', label: '사격 견제' } + ], + note: + '가맹관 마초 대면전은 서량 기병을 완전히 몰살하는 싸움이 아니라, 첫 돌격을 막고 보급로와 말길을 보전해 마초가 유비군을 인정하게 만드는 전투입니다.' + }, map: thirtyFourthBattleMap, units: thirtyFourthBattleUnits, bonds: thirtyFourthBattleBonds, mapTextureKey: 'battle-map-thirty-fourth', leaderUnitId: 'jiameng-leader-ma-chao', - quickVictoryTurnLimit: 50, + quickVictoryTurnLimit: 76, baseVictoryGold: 5660, objectives: [ { @@ -4968,19 +5035,12 @@ export const thirtyFourthBattleScenario: BattleScenarioDefinition = { rewardGold: 1360, unitId: 'huang-quan' }, - { - id: 'jiameng-storehouse', - kind: 'secure-terrain', - label: '가맹관 보급 창고 확보', - rewardGold: 1680, - terrain: 'village' - }, { id: 'quick', kind: 'quick-victory', - label: '50턴 이내 승리', + label: '76턴 이내 승리', rewardGold: 1300, - maxTurn: 50 + maxTurn: 76 } ], defeatConditions: [ @@ -4988,6 +5048,14 @@ export const thirtyFourthBattleScenario: BattleScenarioDefinition = { { kind: 'unit-defeated', unitId: 'huang-quan' } ], itemRewards: ['콩 22', '상처약 16', '탁주 8', '서량 기병 전술서 +1', '마초 합류'], + campaignReward: { + supplies: ['콩 22', '상처약 16', '탁주 8'], + reputation: ['서량 기병 전술서 +1', '가맹관 말길 보전 +1'], + recruits: ['ma-chao'], + unlockBattleId: 'thirty-fifth-battle-yangping-scout', + unlockLabel: '양평관 정찰전 개방', + note: '가맹관에서 서량 돌격을 받아 내고 마초가 합류했습니다. 보급로와 말길을 보전한 덕분에 양평관 정찰을 시작할 수 있습니다.' + }, victoryPages: thirtyFourthBattleVictoryPages, nextCampScene: 'CampScene' }; diff --git a/src/game/data/campaignFlow.ts b/src/game/data/campaignFlow.ts index c2168f2..b84d22c 100644 --- a/src/game/data/campaignFlow.ts +++ b/src/game/data/campaignFlow.ts @@ -576,7 +576,7 @@ const sortieFlows: Record = { eyebrow: '다음 전장', title: thirtyFifthBattleScenario.title, description: - '가맹관에서 마초가 합류했습니다. 이제 양평관으로 정찰대를 보내 한중 전선의 첫 지형과 보급 창고를 확인하고, 흩어진 서량 기병을 규합할 차례입니다.', + '가맹관에서 보급 창고와 말길을 보전한 채 마초가 합류했습니다. 이제 양평관으로 정찰대를 보내 한중 전선의 첫 지형과 보급 창고를 확인하고, 흩어진 서량 기병을 규합할 차례입니다.', rewardHint: `예상 보상: ${thirtyFifthBattleScenario.title} 개방 / 마대 합류`, nextBattleId: thirtyFifthBattleScenario.id, campaignStep: 'thirty-fifth-battle', diff --git a/src/game/data/scenario.ts b/src/game/data/scenario.ts index b8a2d8b..7f8594e 100644 --- a/src/game/data/scenario.ts +++ b/src/game/data/scenario.ts @@ -2458,7 +2458,7 @@ export const thirtyFourthBattleIntroPages: StoryPage[] = [ chapter: '한중으로 향하는 길', background: 'story-yizhou-luo-road', speaker: '제갈량', - text: '성도는 안정되었으나 북쪽 한중은 아직 장로와 조조의 그늘 아래 흔들립니다. 촉한의 이름을 세우려면 북문을 지킬 길부터 열어야 합니다.' + text: '성도는 안정되었으나 북쪽 한중은 아직 장로와 조조의 그늘 아래 흔들립니다. 가맹관의 말길과 보급 창고를 보전해야 촉한의 북문을 열 수 있습니다.' }, { id: 'thirty-fourth-huang-quan-first-counsel', @@ -2466,7 +2466,7 @@ export const thirtyFourthBattleIntroPages: StoryPage[] = [ chapter: '황권의 첫 출전', background: 'story-yizhou-luo-road', speaker: '황권', - text: '가맹관은 익주와 한중을 잇는 목입니다. 그곳에 마초의 기병이 나타났다면, 싸움만이 아니라 그가 누구의 길을 택할지도 보아야 합니다.' + text: '가맹관은 익주와 한중을 잇는 목입니다. 서량 기병이 이 길에서 날뛰면 성도의 안정도 흔들립니다. 먼저 산길 척후와 보급선을 정리해야 합니다.' }, { id: 'thirty-fourth-ma-chao-appears', @@ -2474,7 +2474,7 @@ export const thirtyFourthBattleIntroPages: StoryPage[] = [ chapter: '서량의 기병', background: 'story-yizhou-luo-road', speaker: '법정', - text: '마초는 조조에게 쫓기고 장로에게도 마음을 다 주지 못했습니다. 그의 돌격을 막아 내고, 유비군이 서량의 창을 품을 그릇임을 보여 주십시오.' + text: '마초는 조조에게 쫓기고 장로에게도 마음을 다 주지 못했습니다. 첫 돌격을 받아 내고 선봉 기병을 끊으면, 그가 깃발을 다시 볼 틈이 생깁니다.' }, { id: 'thirty-fourth-sortie', @@ -2483,7 +2483,7 @@ export const thirtyFourthBattleIntroPages: StoryPage[] = [ background: 'story-yizhou-luo-road', speaker: '유비', portrait: 'liuBei', - text: '마초의 용맹을 꺾으려 하지 말고, 그 창끝이 향할 뜻을 바꾸어야 하오. 황권과 법정은 길을 열고, 장수들은 기병의 돌파를 받아내시오.' + text: '마초의 용맹을 꺾으려 하지 말고, 그 창끝이 향할 뜻을 바꾸어야 하오. 황권은 길을 읽고, 조운과 장비는 돌격을 받아 내며, 보급 창고는 반드시 보전하시오.' } ]; @@ -2493,7 +2493,7 @@ export const thirtyFourthBattleVictoryPages: StoryPage[] = [ bgm: 'militia-theme', chapter: '서량 돌격을 막다', background: 'story-yizhou-luo-road', - text: '가맹관의 좁은 길에서 서량 기병의 첫 돌격은 멈췄습니다. 마초는 패배보다도 유비군의 군율과 익주 장수들의 안정된 태도를 더 오래 바라보았습니다.' + text: '가맹관의 좁은 길에서 서량 기병의 첫 돌격은 멈췄습니다. 산길 척후선과 보급로가 무너지지 않자, 마초는 유비군의 군율을 다시 보게 되었습니다.' }, { id: 'thirty-fourth-ma-chao-joins', @@ -2501,7 +2501,7 @@ export const thirtyFourthBattleVictoryPages: StoryPage[] = [ chapter: '마초의 합류', background: 'story-yizhou-luo-road', speaker: '마초', - text: '창을 맡길 주군을 찾지 못해 오래 떠돌았습니다. 유비공이 백성을 보전하고 장수를 예우한다면, 서량의 창 또한 그 깃발 아래 서겠습니다.' + text: '돌격을 막고도 말길을 짓밟지 않는 군대라면, 내 창도 더는 원한만 좇지 않겠소. 유비공의 깃발 아래에서 북쪽 길을 열겠습니다.' }, { id: 'thirty-fourth-next-hanzhong', @@ -2509,7 +2509,7 @@ export const thirtyFourthBattleVictoryPages: StoryPage[] = [ chapter: '한중 전선의 개막', background: 'story-yizhou-luo-road', speaker: '제갈량', - text: '마초가 합류하자 북쪽 길이 한층 넓어졌습니다. 이제 촉한의 기반은 성도 안쪽에서 끝나지 않고, 한중을 향한 전선으로 나아가게 됩니다.' + text: '마초가 합류하고 가맹관의 말길이 보전되자 북쪽 전선이 한층 넓어졌습니다. 이제 양평관을 살펴 한중으로 들어갈 첫 발판을 마련해야 합니다.' } ]; @@ -16129,9 +16129,9 @@ export const thirtyFourthBattleUnits: UnitData[] = [ ...chengduPressureRecruitUnits, ...chengduSurrenderRecruitUnits ].map((unit) => placeScenarioUnit(unit, thirtyFourthBattleAllyPositions[unit.id] ?? { x: unit.x, y: unit.y })), - createThirtyFirstEnemyUnit('jiameng-scout-a', '한중 척후', '산길 척후', 'bandit', 64, 198, 64, 18, 43), - createThirtyFirstEnemyUnit('jiameng-scout-b', '한중 척후', '관문 척후', 'bandit', 64, 198, 64, 26, 41), - createThirtyFirstEnemyUnit('jiameng-scout-c', '한중 척후', '수로 척후', 'bandit', 65, 206, 66, 34, 39), + createThirtyFirstEnemyUnit('jiameng-scout-a', '한중 척후', '산길 척후', 'bandit', 64, 138, 32, 18, 43), + createThirtyFirstEnemyUnit('jiameng-scout-b', '한중 척후', '관문 척후', 'bandit', 64, 138, 32, 26, 41), + createThirtyFirstEnemyUnit('jiameng-scout-c', '한중 척후', '수로 척후', 'bandit', 65, 146, 34, 34, 39), createThirtyFirstEnemyUnit('jiameng-infantry-a', '장로 보병', '외곽 보병', 'yellowTurban', 65, 288, 78, 33, 36), createThirtyFirstEnemyUnit('jiameng-infantry-b', '장로 보병', '외곽 보병', 'yellowTurban', 65, 288, 78, 38, 35), createThirtyFirstEnemyUnit('jiameng-infantry-c', '가맹관 보병', '관문 보병', 'yellowTurban', 66, 300, 80, 45, 30), @@ -16139,18 +16139,18 @@ export const thirtyFourthBattleUnits: UnitData[] = [ createThirtyFirstEnemyUnit('jiameng-archer-a', '한중 궁병', '산허리 궁병', 'archer', 65, 206, 66, 36, 32), createThirtyFirstEnemyUnit('jiameng-archer-b', '한중 궁병', '관문 궁병', 'archer', 66, 214, 68, 47, 24), createThirtyFirstEnemyUnit('jiameng-archer-c', '한중 궁병', '고지 궁병', 'archer', 66, 214, 68, 55, 19), - createThirtyFirstEnemyUnit('jiameng-cavalry-a', '서량 기병', '좌익 기병', 'cavalry', 66, 304, 68, 30, 45), - createThirtyFirstEnemyUnit('jiameng-cavalry-b', '서량 기병', '우익 기병', 'cavalry', 66, 304, 68, 44, 40), - createThirtyFirstEnemyUnit('jiameng-cavalry-c', '서량 기병', '돌격 기병', 'cavalry', 67, 316, 70, 56, 33), - createThirtyFirstEnemyUnit('jiameng-cavalry-d', '서량 기병', '친위 기병', 'cavalry', 67, 316, 70, 61, 26), + createThirtyFirstEnemyUnit('jiameng-cavalry-a', '서량 기병', '좌익 기병', 'cavalry', 66, 176, 34, 30, 45), + createThirtyFirstEnemyUnit('jiameng-cavalry-b', '서량 기병', '우익 기병', 'cavalry', 66, 176, 34, 44, 40), + createThirtyFirstEnemyUnit('jiameng-cavalry-c', '서량 기병', '돌격 기병', 'cavalry', 67, 176, 34, 56, 33), + createThirtyFirstEnemyUnit('jiameng-cavalry-d', '서량 기병', '친위 기병', 'cavalry', 67, 176, 34, 61, 26), createThirtyFirstEnemyUnit('jiameng-guard-a', '마초 친위대', '돌격 친위대', 'yellowTurban', 67, 326, 72, 53, 24), createThirtyFirstEnemyUnit('jiameng-guard-b', '마초 친위대', '관문 친위대', 'yellowTurban', 67, 326, 72, 58, 23), createThirtyFirstEnemyUnit('jiameng-strategist-a', '한중 책사', '장로 사자', 'strategist', 66, 214, 68, 50, 20), createThirtyFirstEnemyUnit('jiameng-strategist-b', '한중 책사', '관문 책사', 'strategist', 67, 222, 70, 60, 18), - createThirtyFirstEnemyUnit('jiameng-ambusher-a', '한중 복병', '남령 복병', 'bandit', 65, 206, 66, 39, 43), - createThirtyFirstEnemyUnit('jiameng-ambusher-b', '한중 복병', '동령 복병', 'bandit', 65, 206, 66, 52, 37), + createThirtyFirstEnemyUnit('jiameng-ambusher-a', '한중 복병', '남령 복병', 'bandit', 65, 126, 32, 39, 43), + createThirtyFirstEnemyUnit('jiameng-ambusher-b', '한중 복병', '동령 복병', 'bandit', 65, 126, 32, 52, 37), createThirtyFirstEnemyUnit('jiameng-officer-pang-de', '방덕', '서량 부장', 'rebelLeader', 68, 344, 74, 59, 25), - createThirtyFirstEnemyUnit('jiameng-leader-ma-chao', '마초', '서량 기병장', 'rebelLeader', 69, 336, 76, 63, 20) + createThirtyFirstEnemyUnit('jiameng-leader-ma-chao', '마초', '서량 기병장', 'rebelLeader', 69, 166, 38, 24, 42) ]; const thirtyFifthBattleAllyPositions: Record = { diff --git a/src/game/scenes/CampScene.ts b/src/game/scenes/CampScene.ts index 97e7bec..5a1a8e0 100644 --- a/src/game/scenes/CampScene.ts +++ b/src/game/scenes/CampScene.ts @@ -1188,15 +1188,31 @@ const sortieRulesByBattleId: Partial