Add Xuzhou rescue sortie flow

This commit is contained in:
2026-06-23 00:45:22 +09:00
parent 84a5606a73
commit 87b200d772
11 changed files with 929 additions and 43 deletions

View File

@@ -484,6 +484,62 @@ export const sixthBattleVictoryPages: StoryPage[] = [
}
];
export const seventhBattleIntroPages: StoryPage[] = [
{
id: 'seventh-xuzhou-envoy-arrives',
bgm: 'militia-theme',
chapter: '도겸의 사신',
background: 'story-militia',
text: '서주의 도겸이 조조군의 압박을 견디지 못하고 원군을 청했다. 공손찬의 허락을 받은 유비는 세 형제와 의용군을 이끌고 남쪽 길로 향했다.'
},
{
id: 'seventh-liu-bei-xuzhou',
bgm: 'battle-prep',
chapter: '서주로 가는 길',
background: 'story-liu-bei',
speaker: '유비',
portrait: 'liuBei',
text: '서주 백성이 위태롭다는데 어찌 머뭇거리겠는가. 도겸 공을 돕고, 백성들이 피난길에서 더 쓰러지지 않게 길을 열자.'
},
{
id: 'seventh-guan-yu-field',
bgm: 'battle-prep',
chapter: '조조군 선봉',
background: 'story-three-heroes',
speaker: '관우',
portrait: 'guanYu',
text: '조조군 선봉은 길과 촌락을 함께 누르고 있습니다. 기병은 길을 따라 빠르게 내려오고, 궁병은 강가와 마을 뒤에서 전열을 끊을 것입니다.'
},
{
id: 'seventh-zhang-fei-charge',
bgm: 'battle-prep',
chapter: '서주 구원전',
background: 'story-sortie',
speaker: '장비',
portrait: 'zhangFei',
text: '서주 백성을 울리는 놈들이라면 이름이 누구든 상관없습니다. 형님, 조조군 선봉부터 박살내고 길을 열어 봅시다!'
}
];
export const seventhBattleVictoryPages: StoryPage[] = [
{
id: 'seventh-victory-xuzhou-road',
bgm: 'militia-theme',
chapter: '서주의 문',
background: 'story-militia',
text: '조조군 선봉이 물러서자 서주 성문 앞 피난민들의 숨통이 트였다. 도겸은 유비의 군기를 맞이하며 깊이 고개를 숙였다.'
},
{
id: 'seventh-tao-qian-entrusts',
bgm: 'battle-prep',
chapter: '도겸의 부탁',
background: 'story-liu-bei',
speaker: '유비',
portrait: 'liuBei',
text: '서주를 맡아 달라는 도겸 공의 청은 너무 무거운 일이다. 그러나 백성이 의지할 곳이 없다면, 외면하는 것도 의가 아니다.'
}
];
export const firstBattleMap: BattleMap = {
width: 20,
height: 18,
@@ -644,6 +700,33 @@ export const sixthBattleMap: BattleMap = {
]
};
export const seventhBattleMap: BattleMap = {
width: 24,
height: 20,
terrain: [
['forest', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'hill', 'hill', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'fort', 'fort', 'plain', 'hill', 'forest', 'forest', 'plain', 'plain'],
['forest', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'hill', 'plain', 'plain', 'road', 'road', 'river', 'river', 'road', 'plain', 'fort', 'plain', 'plain', 'plain', 'hill', 'forest', 'plain', 'plain'],
['plain', 'plain', 'forest', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'road', 'road', 'road', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'hill', 'plain', 'plain'],
['plain', 'forest', 'plain', 'road', 'plain', 'hill', 'forest', 'plain', 'road', 'road', 'plain', 'plain', 'river', 'river', 'road', 'road', 'plain', 'hill', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain'],
['plain', 'plain', 'road', 'road', 'plain', 'hill', 'plain', 'plain', 'road', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'road', 'road', 'plain', 'village', 'plain', 'forest', 'plain', 'plain', 'hill'],
['plain', 'plain', 'road', 'plain', 'plain', 'plain', 'plain', 'forest', 'road', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'plain', 'road', 'village', 'village', 'plain', 'forest', 'forest', 'plain', 'hill'],
['forest', 'plain', 'road', 'plain', 'hill', 'hill', 'plain', 'forest', 'road', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'road', 'plain', 'village', 'plain', 'plain', 'forest', 'plain', 'plain'],
['forest', 'road', 'road', 'plain', 'plain', 'hill', 'plain', 'plain', 'road', 'forest', 'plain', 'plain', 'river', 'river', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain'],
['plain', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'hill', 'road', 'forest', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'road', 'plain', 'plain', 'hill', 'plain', 'plain', 'forest', 'plain'],
['road', 'road', 'plain', 'forest', 'forest', 'plain', 'plain', 'hill', 'road', 'road', 'road', 'road', 'road', 'road', 'river', 'river', 'road', 'road', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain'],
['road', 'plain', 'plain', 'forest', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'river', 'river', 'road', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain'],
['plain', 'road', 'forest', 'plain', 'plain', 'hill', 'plain', 'plain', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain'],
['plain', 'road', 'forest', 'forest', 'plain', 'plain', 'plain', 'hill', 'plain', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'plain', 'plain', 'plain'],
['plain', 'road', 'plain', 'plain', 'hill', 'plain', 'forest', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'hill', 'plain', 'plain'],
['camp', 'road', 'road', 'plain', 'hill', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'plain'],
['camp', 'camp', 'road', 'plain', 'forest', 'plain', 'plain', 'forest', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'plain'],
['plain', 'camp', 'road', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'hill', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'river', 'plain', 'plain'],
['plain', 'plain', 'camp', 'road', 'plain', 'forest', 'plain', 'plain', 'hill', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'plain', 'plain'],
['plain', 'plain', 'plain', 'road', 'road', 'plain', 'forest', 'forest', 'plain', 'plain', 'hill', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'plain'],
['plain', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'forest', 'forest', 'plain', 'plain', 'hill', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain']
]
};
export const firstBattleUnits: UnitData[] = [
{
id: 'liu-bei',
@@ -2219,6 +2302,270 @@ export const sixthBattleUnits: UnitData[] = [
}
];
const seventhBattleAllyPositions: Record<string, { x: number; y: number }> = {
'liu-bei': { x: 2, y: 16 },
'guan-yu': { x: 3, y: 16 },
'zhang-fei': { x: 2, y: 17 }
};
export const seventhBattleUnits: UnitData[] = [
...firstBattleUnits
.filter((unit) => unit.faction === 'ally')
.map((unit) => placeScenarioUnit(unit, seventhBattleAllyPositions[unit.id] ?? { x: unit.x, y: unit.y })),
{
id: 'xuzhou-raider-a',
name: '조조군 척후',
faction: 'enemy',
className: '조조 척후',
classKey: 'bandit',
level: 7,
exp: 42,
hp: 34,
maxHp: 34,
attack: 12,
move: 4,
stats: { might: 74, intelligence: 48, leadership: 54, agility: 70, luck: 52 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 2, exp: 18 },
armor: { itemId: 'rebel-vest', level: 2, exp: 8 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 5,
y: 13
},
{
id: 'xuzhou-infantry-a',
name: '조조 보병',
faction: 'enemy',
className: '조조 보병',
classKey: 'yellowTurban',
level: 7,
exp: 42,
hp: 39,
maxHp: 39,
attack: 12,
move: 3,
stats: { might: 77, intelligence: 48, leadership: 64, agility: 58, luck: 52 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 2, exp: 18 },
armor: { itemId: 'lamellar-armor', level: 2, exp: 8 },
accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 }
},
x: 7,
y: 12
},
{
id: 'xuzhou-archer-a',
name: '조조 궁병',
faction: 'enemy',
className: '조조 궁병',
classKey: 'archer',
level: 7,
exp: 44,
hp: 31,
maxHp: 31,
attack: 13,
move: 3,
stats: { might: 69, intelligence: 58, leadership: 58, agility: 68, luck: 52 },
equipment: {
weapon: { itemId: 'short-bow', level: 2, exp: 20 },
armor: { itemId: 'cloth-armor', level: 2, exp: 8 },
accessory: { itemId: 'wind-quiver', level: 1, exp: 0 }
},
x: 9,
y: 11
},
{
id: 'xuzhou-cavalry-a',
name: '조조 기병',
faction: 'enemy',
className: '조조 경기병',
classKey: 'cavalry',
level: 8,
exp: 46,
hp: 42,
maxHp: 42,
attack: 14,
move: 5,
stats: { might: 82, intelligence: 46, leadership: 64, agility: 82, luck: 54 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 2, exp: 22 },
armor: { itemId: 'rebel-vest', level: 2, exp: 10 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 11,
y: 9
},
{
id: 'xuzhou-raider-b',
name: '피난로 척후',
faction: 'enemy',
className: '조조 척후',
classKey: 'bandit',
level: 7,
exp: 44,
hp: 35,
maxHp: 35,
attack: 13,
move: 4,
stats: { might: 76, intelligence: 49, leadership: 55, agility: 71, luck: 53 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 2, exp: 20 },
armor: { itemId: 'rebel-vest', level: 2, exp: 8 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 6,
y: 8
},
{
id: 'xuzhou-archer-b',
name: '마을 궁병',
faction: 'enemy',
className: '조조 궁병',
classKey: 'archer',
level: 8,
exp: 46,
hp: 32,
maxHp: 32,
attack: 14,
move: 3,
stats: { might: 70, intelligence: 60, leadership: 59, agility: 69, luck: 53 },
equipment: {
weapon: { itemId: 'short-bow', level: 2, exp: 22 },
armor: { itemId: 'cloth-armor', level: 2, exp: 10 },
accessory: { itemId: 'wind-quiver', level: 1, exp: 0 }
},
x: 10,
y: 7
},
{
id: 'xuzhou-cavalry-b',
name: '조조 기병',
faction: 'enemy',
className: '조조 경기병',
classKey: 'cavalry',
level: 8,
exp: 48,
hp: 43,
maxHp: 43,
attack: 14,
move: 5,
stats: { might: 83, intelligence: 47, leadership: 65, agility: 83, luck: 54 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 2, exp: 24 },
armor: { itemId: 'rebel-vest', level: 2, exp: 10 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 14,
y: 8
},
{
id: 'xuzhou-guard-a',
name: '선봉 수비병',
faction: 'enemy',
className: '조조 보병',
classKey: 'yellowTurban',
level: 8,
exp: 48,
hp: 41,
maxHp: 41,
attack: 13,
move: 3,
stats: { might: 80, intelligence: 50, leadership: 67, agility: 60, luck: 53 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 2, exp: 24 },
armor: { itemId: 'lamellar-armor', level: 2, exp: 10 },
accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 }
},
x: 15,
y: 5
},
{
id: 'xuzhou-cavalry-c',
name: '측면 기병',
faction: 'enemy',
className: '조조 경기병',
classKey: 'cavalry',
level: 8,
exp: 50,
hp: 44,
maxHp: 44,
attack: 14,
move: 5,
stats: { might: 84, intelligence: 47, leadership: 66, agility: 84, luck: 55 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 2, exp: 26 },
armor: { itemId: 'rebel-vest', level: 2, exp: 12 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 19,
y: 6
},
{
id: 'xuzhou-guard-b',
name: '선봉 수비병',
faction: 'enemy',
className: '조조 보병',
classKey: 'yellowTurban',
level: 8,
exp: 50,
hp: 42,
maxHp: 42,
attack: 13,
move: 3,
stats: { might: 81, intelligence: 50, leadership: 68, agility: 60, luck: 54 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 2, exp: 26 },
armor: { itemId: 'lamellar-armor', level: 2, exp: 10 },
accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 }
},
x: 17,
y: 3
},
{
id: 'xuzhou-archer-c',
name: '서주 길목 궁병',
faction: 'enemy',
className: '조조 궁병',
classKey: 'archer',
level: 8,
exp: 50,
hp: 33,
maxHp: 33,
attack: 14,
move: 3,
stats: { might: 71, intelligence: 61, leadership: 60, agility: 70, luck: 54 },
equipment: {
weapon: { itemId: 'short-bow', level: 2, exp: 26 },
armor: { itemId: 'cloth-armor', level: 2, exp: 10 },
accessory: { itemId: 'wind-quiver', level: 1, exp: 0 }
},
x: 15,
y: 1
},
{
id: 'xuzhou-leader-xiahou-dun',
name: '하후돈',
faction: 'enemy',
className: '조조군 장수',
classKey: 'rebelLeader',
level: 9,
exp: 54,
hp: 58,
maxHp: 58,
attack: 16,
move: 4,
stats: { might: 88, intelligence: 66, leadership: 82, agility: 66, luck: 58 },
equipment: {
weapon: { itemId: 'leader-axe', level: 2, exp: 30 },
armor: { itemId: 'lamellar-armor', level: 2, exp: 16 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 16,
y: 1
}
];
export const firstBattleBonds: BattleBond[] = [
{
id: 'liu-bei__guan-yu',
@@ -2251,6 +2598,7 @@ export const thirdBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBo
export const fourthBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario);
export const fifthBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario);
export const sixthBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario);
export const seventhBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario);
function placeScenarioUnit(unit: UnitData, position: { x: number; y: number }): UnitData {
return {