Add Changsha veteran chapter

This commit is contained in:
2026-06-23 08:30:43 +09:00
parent 98876673a8
commit 2402e1c38f
11 changed files with 1217 additions and 31 deletions

View File

@@ -1634,6 +1634,76 @@ export const twentyFifthBattleVictoryPages: StoryPage[] = [
}
];
export const twentySixthBattleIntroPages: StoryPage[] = [
{
id: 'twenty-sixth-changsha-old-general',
bgm: 'story-dark',
chapter: '장사의 노장',
background: 'story-three-heroes',
speaker: '공지',
text: '장사에는 황충이라는 노장이 있습니다. 나이는 들었으나 활은 여전히 매섭고, 성 안 병사들은 그 이름을 믿고 버팁니다.'
},
{
id: 'twenty-sixth-han-xuan-suspicion',
bgm: 'battle-prep',
chapter: '의심 많은 태수',
background: 'story-militia',
speaker: '이적',
text: '한현은 황충의 명망도 의심한다고 합니다. 성을 무너뜨리기보다 황충의 체면을 지키면, 장사 사람들의 마음도 흔들릴 것입니다.'
},
{
id: 'twenty-sixth-wei-yan-rumor',
bgm: 'battle-prep',
chapter: '성문 안의 불씨',
background: 'story-liu-bei',
speaker: '마량',
text: '위연이라는 장수는 성 안의 부당함을 참지 못한다고 들었습니다. 백성을 해치지 않고 한현의 의심을 꺾으면, 성 안에서 길이 열릴 수 있습니다.'
},
{
id: 'twenty-sixth-changsha-sortie',
bgm: 'battle-prep',
chapter: '장사 노장 대면전',
background: 'story-sortie',
speaker: '유비',
portrait: 'liuBei',
text: '오늘은 노장의 이름을 꺾으러 가는 것이 아니오. 예를 갖추고 한현의 의심을 물리쳐, 장사의 사람을 얻어야 하오.'
}
];
export const twentySixthBattleVictoryPages: StoryPage[] = [
{
id: 'twenty-sixth-victory-changsha-open',
bgm: 'militia-theme',
chapter: '장사 성문이 열리다',
background: 'story-sortie',
text: '한현의 의심이 전장 위에서 무너지자, 장사 성문은 크게 흔들렸습니다. 유비군은 황충의 명예를 욕보이지 않고 병사들에게 길을 열었습니다.'
},
{
id: 'twenty-sixth-huang-zhong-joins',
bgm: 'battle-prep',
chapter: '황충의 합류',
background: 'story-militia',
speaker: '황충',
text: '늙은 몸이라 하나, 아직 활은 뜻을 향해 날아갈 수 있습니다. 장사를 예로 대하신 주공의 군에 제 활을 맡기겠습니다.'
},
{
id: 'twenty-sixth-wei-yan-joins',
bgm: 'battle-prep',
chapter: '위연의 결심',
background: 'story-liu-bei',
speaker: '위연',
text: '의심과 두려움으로 닫힌 성문을 더는 따를 수 없습니다. 제 창은 닫힌 문을 여는 데 쓰겠습니다.'
},
{
id: 'twenty-sixth-next-yi-province',
bgm: 'story-dark',
chapter: '형주 남부의 끝',
background: 'story-three-heroes',
speaker: '제갈량',
text: '장사까지 열리면 형주 남부의 뿌리가 굳어집니다. 이제 익주로 이어질 더 큰 길을 바라볼 때가 다가옵니다.'
}
];
export const firstBattleMap: BattleMap = {
width: 20,
height: 18,
@@ -1929,6 +1999,12 @@ export const twentyFifthBattleMap: BattleMap = {
terrain: createTwentyFifthBattleTerrain()
};
export const twentySixthBattleMap: BattleMap = {
width: 52,
height: 34,
terrain: createTwentySixthBattleTerrain()
};
export const firstBattleUnits: UnitData[] = [
{
id: 'liu-bei',
@@ -3957,6 +4033,51 @@ export const wulingRecruitUnits: UnitData[] = [
}
];
export const changshaRecruitUnits: UnitData[] = [
{
id: 'huang-zhong',
name: '황충',
faction: 'ally',
className: '노장 궁기병',
classKey: 'archer',
level: 15,
exp: 0,
hp: 44,
maxHp: 44,
attack: 15,
move: 4,
stats: { might: 88, intelligence: 72, leadership: 86, agility: 78, luck: 76 },
equipment: {
weapon: { itemId: 'short-bow', level: 3, exp: 12 },
armor: { itemId: 'lamellar-armor', level: 2, exp: 14 },
accessory: { itemId: 'bravery-token', level: 1, exp: 0 }
},
x: 8,
y: 23
},
{
id: 'wei-yan',
name: '위연',
faction: 'ally',
className: '장사 돌격장',
classKey: 'infantry',
level: 15,
exp: 0,
hp: 48,
maxHp: 48,
attack: 16,
move: 4,
stats: { might: 90, intelligence: 64, leadership: 82, agility: 82, luck: 70 },
equipment: {
weapon: { itemId: 'iron-spear', level: 3, exp: 10 },
armor: { itemId: 'reinforced-lamellar', level: 2, exp: 10 },
accessory: { itemId: 'bravery-token', level: 1, exp: 0 }
},
x: 9,
y: 24
}
];
const eighthBattleAllyPositions: Record<string, { x: number; y: number }> = {
'liu-bei': { x: 2, y: 17 },
'guan-yu': { x: 3, y: 17 },
@@ -10058,6 +10179,411 @@ export const twentyFifthBattleUnits: UnitData[] = [
}
];
const twentySixthBattleAllyPositions: Record<string, { x: number; y: number }> = {
'liu-bei': { x: 4, y: 27 },
'guan-yu': { x: 6, y: 25 },
'zhang-fei': { x: 6, y: 29 },
'jian-yong': { x: 3, y: 30 },
'mi-zhu': { x: 4, y: 31 },
'sun-qian': { x: 8, y: 28 },
'zhao-yun': { x: 8, y: 25 },
'zhuge-liang': { x: 5, y: 27 },
'ma-liang': { x: 7, y: 30 },
'yi-ji': { x: 9, y: 27 },
'gong-zhi': { x: 9, y: 30 }
};
export const twentySixthBattleUnits: UnitData[] = [
...[
...firstBattleUnits.filter((unit) => unit.faction === 'ally'),
...xuzhouRecruitUnits,
...caoBreakRecruitUnits,
...liuBiaoRecruitUnits,
...zhugeRecruitUnits,
...jingzhouRecruitUnits,
...guiyangRecruitUnits,
...wulingRecruitUnits
].map((unit) => placeScenarioUnit(unit, twentySixthBattleAllyPositions[unit.id] ?? { x: unit.x, y: unit.y })),
{
id: 'changsha-scout-a',
name: '장사 척후',
faction: 'enemy',
className: '장사 척후',
classKey: 'bandit',
level: 37,
exp: 56,
hp: 100,
maxHp: 100,
attack: 38,
move: 4,
stats: { might: 124, intelligence: 90, leadership: 100, agility: 118, luck: 82 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 4, exp: 36 },
armor: { itemId: 'rebel-vest', level: 4, exp: 22 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 14,
y: 25
},
{
id: 'changsha-scout-b',
name: '장사 척후',
faction: 'enemy',
className: '서문 척후',
classKey: 'bandit',
level: 37,
exp: 56,
hp: 100,
maxHp: 100,
attack: 38,
move: 4,
stats: { might: 124, intelligence: 90, leadership: 100, agility: 118, luck: 82 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 4, exp: 36 },
armor: { itemId: 'rebel-vest', level: 4, exp: 22 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 17,
y: 30
},
{
id: 'changsha-infantry-a',
name: '장사 보병',
faction: 'enemy',
className: '장사 보병',
classKey: 'yellowTurban',
level: 38,
exp: 58,
hp: 130,
maxHp: 130,
attack: 42,
move: 3,
stats: { might: 138, intelligence: 90, leadership: 116, agility: 98, luck: 82 },
equipment: {
weapon: { itemId: 'iron-spear', level: 4, exp: 50 },
armor: { itemId: 'lamellar-armor', level: 4, exp: 34 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 23,
y: 24
},
{
id: 'changsha-infantry-b',
name: '장사 보병',
faction: 'enemy',
className: '마을 보병',
classKey: 'yellowTurban',
level: 38,
exp: 58,
hp: 130,
maxHp: 130,
attack: 42,
move: 3,
stats: { might: 138, intelligence: 90, leadership: 116, agility: 98, luck: 82 },
equipment: {
weapon: { itemId: 'iron-spear', level: 4, exp: 50 },
armor: { itemId: 'lamellar-armor', level: 4, exp: 34 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 26,
y: 28
},
{
id: 'changsha-infantry-c',
name: '장사 보병',
faction: 'enemy',
className: '성문 보병',
classKey: 'yellowTurban',
level: 39,
exp: 60,
hp: 136,
maxHp: 136,
attack: 43,
move: 3,
stats: { might: 140, intelligence: 90, leadership: 118, agility: 98, luck: 82 },
equipment: {
weapon: { itemId: 'iron-spear', level: 4, exp: 54 },
armor: { itemId: 'reinforced-lamellar', level: 4, exp: 38 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 34,
y: 20
},
{
id: 'changsha-infantry-d',
name: '장사 보병',
faction: 'enemy',
className: '성내 보병',
classKey: 'yellowTurban',
level: 39,
exp: 60,
hp: 136,
maxHp: 136,
attack: 43,
move: 3,
stats: { might: 140, intelligence: 90, leadership: 118, agility: 98, luck: 82 },
equipment: {
weapon: { itemId: 'iron-spear', level: 4, exp: 54 },
armor: { itemId: 'reinforced-lamellar', level: 4, exp: 38 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 39,
y: 17
},
{
id: 'changsha-archer-a',
name: '장사 궁병',
faction: 'enemy',
className: '장사 궁병',
classKey: 'archer',
level: 38,
exp: 58,
hp: 98,
maxHp: 98,
attack: 39,
move: 3,
stats: { might: 96, intelligence: 98, leadership: 104, agility: 112, luck: 84 },
equipment: {
weapon: { itemId: 'short-bow', level: 4, exp: 46 },
armor: { itemId: 'cloth-armor', level: 4, exp: 28 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 28,
y: 20
},
{
id: 'changsha-archer-b',
name: '장사 궁병',
faction: 'enemy',
className: '성루 궁병',
classKey: 'archer',
level: 38,
exp: 58,
hp: 98,
maxHp: 98,
attack: 39,
move: 3,
stats: { might: 96, intelligence: 98, leadership: 104, agility: 112, luck: 84 },
equipment: {
weapon: { itemId: 'short-bow', level: 4, exp: 46 },
armor: { itemId: 'cloth-armor', level: 4, exp: 28 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 37,
y: 13
},
{
id: 'changsha-archer-c',
name: '장사 궁병',
faction: 'enemy',
className: '동문 궁병',
classKey: 'archer',
level: 39,
exp: 60,
hp: 102,
maxHp: 102,
attack: 40,
move: 3,
stats: { might: 98, intelligence: 100, leadership: 106, agility: 114, luck: 84 },
equipment: {
weapon: { itemId: 'short-bow', level: 4, exp: 50 },
armor: { itemId: 'cloth-armor', level: 4, exp: 30 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 44,
y: 19
},
{
id: 'changsha-cavalry-a',
name: '장사 기병',
faction: 'enemy',
className: '장사 기병',
classKey: 'cavalry',
level: 39,
exp: 60,
hp: 138,
maxHp: 138,
attack: 44,
move: 5,
stats: { might: 140, intelligence: 90, leadership: 116, agility: 118, luck: 84 },
equipment: {
weapon: { itemId: 'iron-spear', level: 4, exp: 56 },
armor: { itemId: 'lamellar-armor', level: 4, exp: 36 },
accessory: { itemId: 'bravery-token', level: 1, exp: 0 }
},
x: 31,
y: 29
},
{
id: 'changsha-cavalry-b',
name: '장사 기병',
faction: 'enemy',
className: '동문 기병',
classKey: 'cavalry',
level: 39,
exp: 60,
hp: 138,
maxHp: 138,
attack: 44,
move: 5,
stats: { might: 140, intelligence: 90, leadership: 116, agility: 118, luck: 84 },
equipment: {
weapon: { itemId: 'iron-spear', level: 4, exp: 56 },
armor: { itemId: 'lamellar-armor', level: 4, exp: 36 },
accessory: { itemId: 'bravery-token', level: 1, exp: 0 }
},
x: 42,
y: 28
},
{
id: 'changsha-guard-a',
name: '한현 친위대',
faction: 'enemy',
className: '장사 친위대',
classKey: 'yellowTurban',
level: 40,
exp: 62,
hp: 144,
maxHp: 144,
attack: 45,
move: 3,
stats: { might: 144, intelligence: 92, leadership: 122, agility: 100, luck: 84 },
equipment: {
weapon: { itemId: 'iron-spear', level: 4, exp: 62 },
armor: { itemId: 'reinforced-lamellar', level: 4, exp: 42 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 45,
y: 15
},
{
id: 'changsha-guard-b',
name: '한현 친위대',
faction: 'enemy',
className: '장사 친위대',
classKey: 'yellowTurban',
level: 40,
exp: 62,
hp: 144,
maxHp: 144,
attack: 45,
move: 3,
stats: { might: 144, intelligence: 92, leadership: 122, agility: 100, luck: 84 },
equipment: {
weapon: { itemId: 'iron-spear', level: 4, exp: 62 },
armor: { itemId: 'reinforced-lamellar', level: 4, exp: 42 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 47,
y: 18
},
{
id: 'changsha-strategist-a',
name: '장사 책사',
faction: 'enemy',
className: '장사 책사',
classKey: 'archer',
level: 39,
exp: 60,
hp: 102,
maxHp: 102,
attack: 40,
move: 3,
stats: { might: 90, intelligence: 116, leadership: 108, agility: 96, luck: 84 },
equipment: {
weapon: { itemId: 'short-bow', level: 4, exp: 50 },
armor: { itemId: 'cloth-armor', level: 4, exp: 30 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 41,
y: 14
},
{
id: 'changsha-strategist-b',
name: '장사 책사',
faction: 'enemy',
className: '의심 많은 책사',
classKey: 'archer',
level: 39,
exp: 60,
hp: 102,
maxHp: 102,
attack: 40,
move: 3,
stats: { might: 90, intelligence: 116, leadership: 108, agility: 96, luck: 84 },
equipment: {
weapon: { itemId: 'short-bow', level: 4, exp: 50 },
armor: { itemId: 'cloth-armor', level: 4, exp: 30 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 43,
y: 16
},
{
id: 'changsha-veteran-huang-zhong',
name: '황충',
faction: 'enemy',
className: '장사 노장',
classKey: 'archer',
level: 41,
exp: 66,
hp: 156,
maxHp: 156,
attack: 48,
move: 4,
stats: { might: 150, intelligence: 96, leadership: 128, agility: 108, luck: 88 },
equipment: {
weapon: { itemId: 'short-bow', level: 5, exp: 18 },
armor: { itemId: 'reinforced-lamellar', level: 4, exp: 48 },
accessory: { itemId: 'bravery-token', level: 1, exp: 0 }
},
x: 38,
y: 17
},
{
id: 'changsha-officer-wei-yan',
name: '위연',
faction: 'enemy',
className: '장사 돌격장',
classKey: 'yellowTurban',
level: 40,
exp: 64,
hp: 152,
maxHp: 152,
attack: 47,
move: 4,
stats: { might: 148, intelligence: 82, leadership: 120, agility: 112, luck: 80 },
equipment: {
weapon: { itemId: 'iron-spear', level: 5, exp: 12 },
armor: { itemId: 'reinforced-lamellar', level: 4, exp: 44 },
accessory: { itemId: 'bravery-token', level: 1, exp: 0 }
},
x: 36,
y: 23
},
{
id: 'changsha-leader-han-xuan',
name: '한현',
faction: 'enemy',
className: '장사 태수',
classKey: 'rebelLeader',
level: 41,
exp: 66,
hp: 190,
maxHp: 190,
attack: 46,
move: 4,
stats: { might: 138, intelligence: 104, leadership: 130, agility: 100, luck: 84 },
equipment: {
weapon: { itemId: 'leader-axe', level: 5, exp: 10 },
armor: { itemId: 'reinforced-lamellar', level: 5, exp: 8 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 48,
y: 16
}
];
export const firstBattleBonds: BattleBond[] = [
{
id: 'liu-bei__guan-yu',
@@ -10266,6 +10792,41 @@ export const wulingRecruitBonds: BattleBond[] = [
}
];
export const changshaRecruitBonds: BattleBond[] = [
{
id: 'liu-bei__huang-zhong',
unitIds: ['liu-bei', 'huang-zhong'],
title: '노장을 예우하다',
level: 38,
exp: 0,
description: '유비의 예우와 황충의 노련한 활이 만나, 오래 버틴 장수의 마음을 군의 힘으로 바꿉니다.'
},
{
id: 'guan-yu__huang-zhong',
unitIds: ['guan-yu', 'huang-zhong'],
title: '무인의 존중',
level: 34,
exp: 0,
description: '관우와 황충은 서로의 무예와 체면을 인정하며 전열과 원거리 압박을 함께 세웁니다.'
},
{
id: 'liu-bei__wei-yan',
unitIds: ['liu-bei', 'wei-yan'],
title: '닫힌 문을 여는 창',
level: 32,
exp: 0,
description: '유비의 큰 뜻과 위연의 거친 돌파가 만나, 막힌 성문과 전열을 과감히 흔듭니다.'
},
{
id: 'huang-zhong__wei-yan',
unitIds: ['huang-zhong', 'wei-yan'],
title: '장사의 전환',
level: 30,
exp: 0,
description: '황충의 노련함과 위연의 결단이 장사의 흔들린 군심을 새 방향으로 묶어 냅니다.'
}
];
export const secondBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario);
export const thirdBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario);
export const fourthBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario);
@@ -10296,6 +10857,7 @@ export const twentySecondBattleBonds: BattleBond[] = [...twentyFirstBattleBonds]
export const twentyThirdBattleBonds: BattleBond[] = [...twentySecondBattleBonds].map(cloneBattleBondForScenario);
export const twentyFourthBattleBonds: BattleBond[] = [...twentyThirdBattleBonds, ...jingzhouRecruitBonds].map(cloneBattleBondForScenario);
export const twentyFifthBattleBonds: BattleBond[] = [...twentyFourthBattleBonds, ...guiyangRecruitBonds].map(cloneBattleBondForScenario);
export const twentySixthBattleBonds: BattleBond[] = [...twentyFifthBattleBonds, ...wulingRecruitBonds].map(cloneBattleBondForScenario);
function createEighthBattleTerrain(): TerrainType[][] {
return Array.from({ length: 22 }, (_, y) =>
@@ -11212,6 +11774,55 @@ function createTwentyFifthBattleTerrain(): TerrainType[][] {
);
}
function createTwentySixthBattleTerrain(): TerrainType[][] {
return Array.from({ length: 34 }, (_, y) =>
Array.from({ length: 52 }, (_, x): TerrainType => {
if (x <= 3 && y >= 26) {
return 'camp';
}
if ((x >= 42 && x <= 50 && y >= 11 && y <= 20) || (x >= 37 && x <= 43 && y >= 8 && y <= 13)) {
return 'fort';
}
if ((x >= 21 && x <= 24 && y >= 26 && y <= 28) || (x >= 30 && x <= 32 && y >= 19 && y <= 21) || (x >= 15 && x <= 17 && y >= 21 && y <= 23)) {
return 'village';
}
if (
(y >= 27 && x >= 3 && x <= 18) ||
(x >= 18 && x <= 29 && y >= 23 && y <= 27) ||
(x >= 29 && x <= 38 && y >= 19 && y <= 23) ||
(x >= 37 && x <= 45 && y >= 15 && y <= 19) ||
(x >= 44 && x <= 49 && y >= 13 && y <= 17)
) {
return 'road';
}
if ((x === 11 || x === 12) && y >= 0 && y <= 24) {
return 'river';
}
if ((x === 33 || x === 34) && y >= 22 && y <= 33) {
return 'river';
}
if (
(x >= 4 && x <= 14 && y >= 7 && y <= 18) ||
(x >= 18 && x <= 30 && y >= 8 && y <= 17) ||
(x >= 35 && x <= 47 && y >= 23 && y <= 33)
) {
return 'forest';
}
if (
(x >= 0 && x <= 9 && y <= 10) ||
(x >= 21 && x <= 32 && y >= 1 && y <= 9) ||
(x >= 46 && x <= 51 && y >= 0 && y <= 10)
) {
return 'hill';
}
if ((x <= 1 && y <= 25) || (x >= 50 && y >= 21) || (x >= 39 && x <= 41 && y >= 0 && y <= 7)) {
return 'cliff';
}
return 'plain';
})
);
}
function placeScenarioUnit(unit: UnitData, position: { x: number; y: number }): UnitData {
return {
...cloneUnitForScenario(unit),