Add Tianshui advance sortie flow

This commit is contained in:
2026-06-24 03:12:06 +09:00
parent 9644e3798c
commit 3c18072c1e
11 changed files with 834 additions and 72 deletions

View File

@@ -7,6 +7,7 @@ import fiftySecondBattleMapUrl from '../../assets/images/battle/fifty-second-bat
import fiftyThirdBattleMapUrl from '../../assets/images/battle/fifty-third-battle-map.svg';
import fiftyFourthBattleMapUrl from '../../assets/images/battle/fifty-fourth-battle-map.svg';
import fiftyFifthBattleMapUrl from '../../assets/images/battle/fifty-fifth-battle-map.svg';
import fiftySixthBattleMapUrl from '../../assets/images/battle/fifty-sixth-battle-map.svg';
import fifthBattleMapUrl from '../../assets/images/battle/fifth-battle-map.svg';
import fifteenthBattleMapUrl from '../../assets/images/battle/fifteenth-battle-map.svg';
import firstBattleMapUrl from '../../assets/images/battle/first-battle-map.png';
@@ -109,5 +110,6 @@ export const battleMapAssets: Record<string, string> = {
'battle-map-fifty-second': fiftySecondBattleMapUrl,
'battle-map-fifty-third': fiftyThirdBattleMapUrl,
'battle-map-fifty-fourth': fiftyFourthBattleMapUrl,
'battle-map-fifty-fifth': fiftyFifthBattleMapUrl
'battle-map-fifty-fifth': fiftyFifthBattleMapUrl,
'battle-map-fifty-sixth': fiftySixthBattleMapUrl
};

View File

@@ -23,6 +23,10 @@ import {
fiftyFifthBattleMap,
fiftyFifthBattleUnits,
fiftyFifthBattleVictoryPages,
fiftySixthBattleBonds,
fiftySixthBattleMap,
fiftySixthBattleUnits,
fiftySixthBattleVictoryPages,
firstBattleBonds,
fifteenthBattleBonds,
fifteenthBattleMap,
@@ -280,7 +284,8 @@ export type BattleScenarioId =
| 'fifty-second-battle-meng-huo-fifth-capture'
| 'fifty-third-battle-meng-huo-sixth-capture'
| 'fifty-fourth-battle-meng-huo-final-capture'
| 'fifty-fifth-battle-northern-qishan-road';
| 'fifty-fifth-battle-northern-qishan-road'
| 'fifty-sixth-battle-tianshui-advance';
export type BattleObjectiveKind = 'defeat-leader' | 'pacify-leader' | 'keep-unit-alive' | 'secure-terrain' | 'quick-victory';
@@ -3607,6 +3612,80 @@ export const fiftyFifthBattleScenario: BattleScenarioDefinition = {
nextCampScene: 'CampScene'
};
export const fiftySixthBattleScenario: BattleScenarioDefinition = {
id: 'fifty-sixth-battle-tianshui-advance',
title: '천수 진군로',
victoryConditionLabel: '천수 길목 돌파와 곽회 방어선 격퇴',
defeatConditionLabel: '제갈량 퇴각',
openingObjectiveLines: [
'기산 출진로를 열었지만 위군은 천수와 가정 사이에 새 방어선을 폈습니다. 곽회가 본영을 잡고, 강유라는 젊은 장수가 산길의 허점을 살피고 있습니다.',
'이번 전투는 적장을 베는 것만으로 끝나지 않습니다. 천수 길목, 물길, 마을, 가정으로 이어지는 고개를 함께 보아야 북벌이 끊기지 않습니다.',
'곽회를 물러나게 하고 강유의 움직임을 확인하십시오. 다음 전선에서 그 이름이 촉한에 어떤 의미가 될지 여기서 처음 드러납니다.'
],
map: fiftySixthBattleMap,
units: fiftySixthBattleUnits,
bonds: fiftySixthBattleBonds,
mapTextureKey: 'battle-map-fifty-sixth',
leaderUnitId: 'northern-second-leader-guo-huai',
quickVictoryTurnLimit: 112,
baseVictoryGold: 15480,
objectives: [
{
id: 'repel-guo-huai',
kind: 'defeat-leader',
label: '곽회 방어선 격퇴',
rewardGold: 10380,
unitId: 'northern-second-leader-guo-huai'
},
{
id: 'test-jiang-wei',
kind: 'defeat-leader',
label: '강유의 전술 확인',
rewardGold: 4800,
unitId: 'northern-second-officer-jiang-wei'
},
{
id: 'zhuge-liang',
kind: 'keep-unit-alive',
label: '제갈량 생존',
rewardGold: 2720,
unitId: 'zhuge-liang'
},
{
id: 'tianshui-road',
kind: 'secure-terrain',
label: '천수 진군로 확보',
rewardGold: 5400,
terrain: 'road'
},
{
id: 'jieting-forts',
kind: 'secure-terrain',
label: '가정 고개 요새 압박',
rewardGold: 5060,
terrain: 'fort'
},
{
id: 'waterline-villages',
kind: 'secure-terrain',
label: '물길 마을 안정',
rewardGold: 4740,
terrain: 'village'
},
{
id: 'quick',
kind: 'quick-victory',
label: '112턴 이내 천수로 확보',
rewardGold: 3480,
maxTurn: 112
}
],
defeatConditions: [{ kind: 'unit-defeated', unitId: 'zhuge-liang' }],
itemRewards: ['콩 90', '상처약 74', '탁주 36', '천수 진군 장부 1', '가정 지형 표식 1'],
victoryPages: fiftySixthBattleVictoryPages,
nextCampScene: 'CampScene'
};
export const defaultBattleScenarioId: BattleScenarioId = firstBattleScenario.id;
export const battleScenarios: Record<BattleScenarioId, BattleScenarioDefinition> = {
@@ -3664,7 +3743,8 @@ export const battleScenarios: Record<BattleScenarioId, BattleScenarioDefinition>
'fifty-second-battle-meng-huo-fifth-capture': fiftySecondBattleScenario,
'fifty-third-battle-meng-huo-sixth-capture': fiftyThirdBattleScenario,
'fifty-fourth-battle-meng-huo-final-capture': fiftyFourthBattleScenario,
'fifty-fifth-battle-northern-qishan-road': fiftyFifthBattleScenario
'fifty-fifth-battle-northern-qishan-road': fiftyFifthBattleScenario,
'fifty-sixth-battle-tianshui-advance': fiftySixthBattleScenario
};
export const defaultBattleScenario = battleScenarios[defaultBattleScenarioId];

View File

@@ -10,6 +10,7 @@ import {
fiftyThirdBattleScenario,
fiftyFourthBattleScenario,
fiftyFifthBattleScenario,
fiftySixthBattleScenario,
fortiethBattleScenario,
fortyFirstBattleScenario,
fortySecondBattleScenario,
@@ -77,6 +78,8 @@ import {
fiftyFourthBattleVictoryPages,
fiftyFifthBattleIntroPages,
fiftyFifthBattleVictoryPages,
fiftySixthBattleIntroPages,
fiftySixthBattleVictoryPages,
fortiethBattleIntroPages,
fortiethBattleVictoryPages,
fortyFirstBattleIntroPages,
@@ -813,13 +816,24 @@ const sortieFlows: Record<string, SortieFlow> = {
},
[fiftyFifthBattleScenario.id]: {
afterBattleId: fiftyFifthBattleScenario.id,
eyebrow: '다음 장 준비',
title: '기산 출진로 이후',
eyebrow: '다음 장',
title: fiftySixthBattleScenario.title,
description:
'기산 산길과 한중 보급 거점을 확보했습니다. 다음 북벌 전장은 더 넓은 위군 방어선과 천수 방면의 움직임으로 이어질 예정입니다.',
rewardHint: '다음 목표: 천수 방면 진군 준비 / 북벌 장기전 설계',
pages: fiftyFifthBattleVictoryPages,
unavailableNotice: '천수 방면의 다음 북벌 전투는 이어지는 작업에서 확장합니다. 현재는 기산 출진로 후 군영에서 무장 대화와 보급 정비를 진행할 수 있습니다.'
'기산 산길과 한중 보급 거점을 확보했습니다. 이제 천수와 가정 사이에서 곽회와 강유의 대응을 확인하며 북벌의 전선을 넓혀야 합니다.',
rewardHint: `예상 보상: ${fiftySixthBattleScenario.title} 개방 / 천수 진군 장부`,
nextBattleId: fiftySixthBattleScenario.id,
campaignStep: 'fifty-sixth-battle',
pages: [...fiftyFifthBattleVictoryPages, ...fiftySixthBattleIntroPages]
},
[fiftySixthBattleScenario.id]: {
afterBattleId: fiftySixthBattleScenario.id,
eyebrow: '다음 장 준비',
title: '천수 진군로 이후',
description:
'천수 방면의 방어선을 밀어냈고 강유의 이름이 제갈량의 장부에 남았습니다. 다음 북벌 전장은 가정 방면의 배치 판단과 더 큰 위군 반격으로 이어질 예정입니다.',
rewardHint: '다음 목표: 가정 방면 배치 준비 / 강유 접촉 단서 정리',
pages: fiftySixthBattleVictoryPages,
unavailableNotice: '가정 방면의 다음 북벌 전투는 이어지는 작업에서 확장합니다. 현재는 천수 진군로 후 군영에서 무장 대화와 보급 정비를 진행할 수 있습니다.'
},
'northern-campaign-prep-camp-legacy': {
afterBattleId: fiftyFourthBattleScenario.id,

View File

@@ -3589,6 +3589,59 @@ export const fiftyFifthBattleVictoryPages: StoryPage[] = [
}
];
export const fiftySixthBattleIntroPages: StoryPage[] = [
{
id: 'fifty-sixth-tianshui-rumor',
bgm: 'story-dark',
chapter: '천수 방면',
background: 'story-militia',
speaker: '마량',
text: '기산 길목을 확보하자 천수와 가정 사이의 성들이 급히 봉화를 올렸습니다. 위군은 조진의 전초대가 물러난 틈을 곽회에게 맡기고, 젊은 장수 강유가 산길의 허점을 살피고 있다고 합니다.'
},
{
id: 'fifty-sixth-qishan-council',
bgm: 'battle-prep',
chapter: '진군로 재편',
background: 'story-sortie',
speaker: '제갈량',
text: '북벌은 빠르게 밀어붙이는 것보다 끊기지 않는 것이 중요합니다. 조운은 우회로를 보고, 황권과 마량은 마을과 창고를 묶고, 왕평은 가정으로 이어지는 고개를 살펴 주십시오.'
},
{
id: 'fifty-sixth-jiang-wei-shadow',
bgm: 'battle-prep',
chapter: '젊은 장수의 그림자',
background: 'story-three-heroes',
speaker: '조운',
text: '강유라는 장수는 평범한 수비장이 아닙니다. 정면에서만 치면 곽회의 방어선에 묶이고, 너무 깊이 들어가면 가정 방면의 퇴로가 끊깁니다. 이번 싸움은 적장 하나보다 전선 전체를 읽는 싸움이 되겠습니다.'
}
];
export const fiftySixthBattleVictoryPages: StoryPage[] = [
{
id: 'fifty-sixth-victory-tianshui-road',
bgm: 'militia-theme',
chapter: '천수 진군로 확보',
background: 'story-sortie',
text: '곽회의 방어선은 물러났고, 천수로 향하는 길에는 촉한군의 표식이 세워졌습니다. 그러나 강유의 움직임은 끝내 가볍지 않았고, 위군은 가정 방면으로 더 넓은 방어선을 펼치기 시작했습니다.'
},
{
id: 'fifty-sixth-victory-jiang-wei',
bgm: 'story-dark',
chapter: '강유의 이름',
background: 'story-militia',
speaker: '제갈량',
text: '강유는 아직 우리의 사람이 아니지만, 그 재능은 전장 위에 분명히 드러났습니다. 억지로 꺾기보다 길을 남겨 두어야 합니다. 다음 전선에서는 사람을 얻는 일과 땅을 얻는 일이 함께 시험받을 것입니다.'
},
{
id: 'fifty-sixth-next-jieting',
bgm: 'story-dark',
chapter: '가정의 불안',
background: 'story-three-heroes',
speaker: '왕평',
text: '가정으로 이어지는 고개는 생각보다 좁고 물길도 멀리 있습니다. 진영을 어디에 세우느냐에 따라 북벌 전체가 흔들릴 수 있습니다. 다음 전장은 승리보다 배치의 판단이 더 무겁게 다가올 것입니다.'
}
];
export const firstBattleMap: BattleMap = {
width: 20,
height: 18,
@@ -4064,6 +4117,12 @@ export const fiftyFifthBattleMap: BattleMap = {
terrain: createFiftyFifthBattleTerrain()
};
export const fiftySixthBattleMap: BattleMap = {
width: 112,
height: 94,
terrain: createFiftySixthBattleTerrain()
};
export const firstBattleUnits: UnitData[] = [
{
id: 'liu-bei',
@@ -16806,6 +16865,95 @@ export const fiftyFifthBattleUnits: UnitData[] = [
createThirtyFirstEnemyUnit('northern-first-leader-cao-zhen', '조진', '위군 대장', 'rebelLeader', 178, 1240, 296, 102, 24)
];
const fiftySixthBattleAllyPositions: Record<string, { x: number; y: number }> = {
'guan-yu': { x: 16, y: 80 },
'zhang-fei': { x: 22, y: 82 },
'jian-yong': { x: 10, y: 86 },
'mi-zhu': { x: 14, y: 88 },
'sun-qian': { x: 19, y: 88 },
'zhao-yun': { x: 29, y: 76 },
'zhuge-liang': { x: 17, y: 75 },
'ma-liang': { x: 21, y: 72 },
'yi-ji': { x: 9, y: 90 },
'gong-zhi': { x: 25, y: 88 },
'huang-zhong': { x: 34, y: 75 },
'wei-yan': { x: 40, y: 79 },
'pang-tong': { x: 14, y: 70 },
'fa-zheng': { x: 25, y: 70 },
'wu-yi': { x: 41, y: 86 },
'yan-yan': { x: 45, y: 79 },
'li-yan': { x: 32, y: 69 },
'huang-quan': { x: 11, y: 74 },
'ma-chao': { x: 48, y: 75 },
'ma-dai': { x: 52, y: 82 },
'wang-ping': { x: 36, y: 70 }
};
export const fiftySixthBattleUnits: UnitData[] = [
...[
...firstBattleUnits.filter((unit) => unit.faction === 'ally' && unit.id !== 'liu-bei'),
...xuzhouRecruitUnits,
...caoBreakRecruitUnits,
...liuBiaoRecruitUnits,
...zhugeRecruitUnits,
...jingzhouRecruitUnits,
...guiyangRecruitUnits,
...wulingRecruitUnits,
...changshaRecruitUnits,
...yizhouRecruitUnits,
...fuPassRecruitUnits,
...luoCastleRecruitUnits,
...luoCastleProperRecruitUnits,
...chengduPressureRecruitUnits,
...chengduSurrenderRecruitUnits,
...hanzhongOpeningRecruitUnits,
...hanzhongScoutRecruitUnits,
...hanzhongMainRecruitUnits
].map((unit) => placeScenarioUnit(unit, fiftySixthBattleAllyPositions[unit.id] ?? { x: unit.x, y: unit.y })),
createThirtyFirstEnemyUnit('northern-second-scout-a', '천수 척후병', '서쪽 사면 정찰', 'bandit', 176, 824, 238, 48, 72),
createThirtyFirstEnemyUnit('northern-second-scout-b', '천수 척후병', '하곡 정찰', 'bandit', 176, 824, 238, 57, 69),
createThirtyFirstEnemyUnit('northern-second-scout-c', '천수 척후병', '중앙 마을 감시', 'bandit', 177, 836, 240, 68, 62),
createThirtyFirstEnemyUnit('northern-second-scout-d', '천수 척후병', '동쪽 물목 감시', 'bandit', 177, 836, 240, 79, 55),
createThirtyFirstEnemyUnit('northern-second-scout-e', '천수 척후병', '가정 고개 정찰', 'bandit', 178, 848, 242, 91, 48),
createThirtyFirstEnemyUnit('northern-second-scout-f', '천수 척후병', '곽회 본영 정찰', 'bandit', 178, 848, 242, 101, 36),
createThirtyFirstEnemyUnit('northern-second-infantry-a', '위군 천수 보병', '서쪽 보병', 'yellowTurban', 177, 1140, 282, 52, 66),
createThirtyFirstEnemyUnit('northern-second-infantry-b', '위군 천수 보병', '하곡 보병', 'yellowTurban', 177, 1140, 282, 60, 62),
createThirtyFirstEnemyUnit('northern-second-infantry-c', '위군 천수 보병', '마을 길목 보병', 'yellowTurban', 178, 1160, 286, 70, 57),
createThirtyFirstEnemyUnit('northern-second-infantry-d', '위군 천수 보병', '중앙 고개 보병', 'yellowTurban', 178, 1160, 286, 78, 50),
createThirtyFirstEnemyUnit('northern-second-infantry-e', '위군 천수 보병', '동쪽 보병', 'yellowTurban', 179, 1180, 290, 88, 45),
createThirtyFirstEnemyUnit('northern-second-infantry-f', '위군 천수 보병', '가정로 보병', 'yellowTurban', 179, 1180, 290, 96, 38),
createThirtyFirstEnemyUnit('northern-second-infantry-g', '위군 천수 보병', '곽회 본영 보병', 'yellowTurban', 180, 1200, 294, 104, 30),
createThirtyFirstEnemyUnit('northern-second-infantry-h', '위군 천수 보병', '보급 차단 보병', 'yellowTurban', 180, 1200, 294, 92, 70),
createThirtyFirstEnemyUnit('northern-second-archer-a', '위군 천수 궁병', '서쪽 고지 궁병', 'archer', 176, 860, 250, 55, 57),
createThirtyFirstEnemyUnit('northern-second-archer-b', '위군 천수 궁병', '하곡 궁병', 'archer', 177, 872, 252, 64, 53),
createThirtyFirstEnemyUnit('northern-second-archer-c', '위군 천수 궁병', '중앙 마을 궁병', 'archer', 177, 872, 252, 73, 48),
createThirtyFirstEnemyUnit('northern-second-archer-d', '위군 천수 궁병', '물목 궁병', 'archer', 178, 884, 254, 84, 41),
createThirtyFirstEnemyUnit('northern-second-archer-e', '위군 천수 궁병', '가정 고개 궁병', 'archer', 178, 884, 254, 94, 34),
createThirtyFirstEnemyUnit('northern-second-archer-f', '위군 천수 궁병', '곽회 본영 궁병', 'archer', 179, 896, 256, 105, 24),
createThirtyFirstEnemyUnit('northern-second-cavalry-a', '위군 역습 기병', '서쪽 기병', 'cavalry', 177, 1148, 288, 47, 61),
createThirtyFirstEnemyUnit('northern-second-cavalry-b', '위군 역습 기병', '하곡 기병', 'cavalry', 177, 1148, 288, 59, 58),
createThirtyFirstEnemyUnit('northern-second-cavalry-c', '위군 역습 기병', '중앙 역습 기병', 'cavalry', 178, 1170, 292, 72, 51),
createThirtyFirstEnemyUnit('northern-second-cavalry-d', '위군 역습 기병', '물목 우회 기병', 'cavalry', 179, 1192, 296, 84, 45),
createThirtyFirstEnemyUnit('northern-second-cavalry-e', '위군 역습 기병', '가정로 기병', 'cavalry', 179, 1192, 296, 96, 39),
createThirtyFirstEnemyUnit('northern-second-cavalry-f', '위군 역습 기병', '곽회 친위 기병', 'cavalry', 180, 1214, 300, 106, 32),
createThirtyFirstEnemyUnit('northern-second-jieting-guard-a', '가정 고개 수비대', '서쪽 고개 수비', 'yellowTurban', 178, 1180, 290, 43, 49),
createThirtyFirstEnemyUnit('northern-second-jieting-guard-b', '가정 고개 수비대', '중앙 고개 수비', 'yellowTurban', 178, 1180, 290, 55, 43),
createThirtyFirstEnemyUnit('northern-second-jieting-guard-c', '가정 고개 수비대', '물목 고개 수비', 'yellowTurban', 179, 1200, 294, 69, 36),
createThirtyFirstEnemyUnit('northern-second-jieting-guard-d', '가정 고개 수비대', '가정 길목 수비', 'yellowTurban', 179, 1200, 294, 82, 29),
createThirtyFirstEnemyUnit('northern-second-jieting-guard-e', '가정 고개 수비대', '곽회 영채 수비', 'yellowTurban', 180, 1220, 298, 98, 20),
createThirtyFirstEnemyUnit('northern-second-jieting-guard-f', '가정 고개 수비대', '북쪽 절벽 수비', 'yellowTurban', 180, 1220, 298, 106, 18),
createThirtyFirstEnemyUnit('northern-second-strategist-a', '위군 천수 군리', '마을 회유 군리', 'strategist', 178, 928, 262, 67, 46),
createThirtyFirstEnemyUnit('northern-second-strategist-b', '위군 천수 군리', '물목 기록 군리', 'strategist', 179, 940, 264, 82, 38),
createThirtyFirstEnemyUnit('northern-second-strategist-c', '위군 천수 군리', '가정 보급 군리', 'strategist', 179, 940, 264, 94, 52),
createThirtyFirstEnemyUnit('northern-second-strategist-d', '위군 천수 군리', '곽회 본영 군리', 'strategist', 180, 952, 266, 104, 26),
createThirtyFirstEnemyUnit('northern-second-officer-xiahou-mao', '하후무', '천수 방면 장수', 'rebelLeader', 180, 1220, 300, 74, 58),
createThirtyFirstEnemyUnit('northern-second-officer-fei-yao', '비요', '가정 지원 장수', 'rebelLeader', 180, 1220, 300, 95, 58),
createThirtyFirstEnemyUnit('northern-second-officer-jiang-wei', '강유', '천수 젊은 장수', 'rebelLeader', 181, 1260, 306, 86, 43),
createThirtyFirstEnemyUnit('northern-second-officer-ma-zun', '마준', '천수 태수', 'rebelLeader', 180, 1220, 300, 101, 64),
createThirtyFirstEnemyUnit('northern-second-officer-supply', '위군 둔전 장교', '물목 보급 장수', 'rebelLeader', 179, 1200, 296, 75, 70),
createThirtyFirstEnemyUnit('northern-second-leader-guo-huai', '곽회', '위군 북방 지휘관', 'rebelLeader', 182, 1320, 316, 104, 24)
];
export const firstBattleBonds: BattleBond[] = [
{
id: 'liu-bei__guan-yu',
@@ -17897,6 +18045,45 @@ export const fiftyFifthBattleBonds: BattleBond[] = [...fiftyFourthBattleBonds, .
cloneBattleBondForScenario
);
export const northernSecondBattleBonds: BattleBond[] = [
{
id: 'zhuge-liang__wang-ping_northern-second',
unitIds: ['zhuge-liang', 'wang-ping'],
title: '가정의 지형 판단',
level: 56,
exp: 0,
description: '제갈량의 큰 전략과 왕평의 산길 판단이 맞물리면 가정으로 이어지는 고개에서 치명적인 배치 실수를 줄일 수 있습니다.'
},
{
id: 'zhao-yun__ma-dai_northern-second',
unitIds: ['zhao-yun', 'ma-dai'],
title: '우회로의 기동',
level: 54,
exp: 0,
description: '조운의 안정된 기동과 마대의 서량 정찰 감각이 합쳐지면 천수 방면의 우회 기병을 빠르게 끊을 수 있습니다.'
},
{
id: 'huang-quan__wang-ping_northern-second',
unitIds: ['huang-quan', 'wang-ping'],
title: '물길과 보급선',
level: 52,
exp: 0,
description: '황권의 보급 장부와 왕평의 지형 판단은 물길이 먼 고개에서 군량과 진영 위치를 동시에 지키는 힘이 됩니다.'
},
{
id: 'ma-liang__fa-zheng_northern-second',
unitIds: ['ma-liang', 'fa-zheng'],
title: '천수 설득의 글',
level: 48,
exp: 0,
description: '마량의 부드러운 문장과 법정의 날카로운 판단이 더해지면 천수 장수들의 흔들리는 마음을 놓치지 않습니다.'
}
];
export const fiftySixthBattleBonds: BattleBond[] = [...fiftyFifthBattleBonds, ...northernSecondBattleBonds].map(
cloneBattleBondForScenario
);
function createEighthBattleTerrain(): TerrainType[][] {
return Array.from({ length: 22 }, (_, y) =>
Array.from({ length: 26 }, (_, x): TerrainType => {
@@ -20836,6 +21023,82 @@ function createFiftyFifthBattleTerrain(): TerrainType[][] {
);
}
function createFiftySixthBattleTerrain(): TerrainType[][] {
return Array.from({ length: 94 }, (_, y) =>
Array.from({ length: 112 }, (_, x): TerrainType => {
if (
(x >= 0 && x <= 111 && y >= 82) ||
(x >= 76 && x <= 81 && y >= 0 && y <= 93) ||
(x >= 0 && x <= 30 && y >= 73 && y <= 90) ||
(x >= 86 && x <= 107 && y >= 69 && y <= 84)
) {
return 'river';
}
if (
(x >= 8 && x <= 108 && y >= 74 && y <= 80) ||
(x >= 29 && x <= 88 && y >= 58 && y <= 66) ||
(x >= 70 && x <= 108 && y >= 50 && y <= 58) ||
(x >= 44 && x <= 51 && y >= 50 && y <= 82) ||
(x >= 86 && x <= 92 && y >= 54 && y <= 90) ||
(x >= 54 && x <= 75 && y >= 62 && y <= 69) ||
(x >= 63 && x <= 86 && y >= 43 && y <= 52) ||
(x >= 90 && x <= 108 && y >= 25 && y <= 35)
) {
return 'road';
}
if (
(x >= 11 && x <= 72 && y >= 55 && y <= 82) ||
(x >= 60 && x <= 105 && y >= 47 && y <= 76) ||
(x >= 2 && x <= 42 && y >= 61 && y <= 82) ||
(x >= 88 && x <= 111 && y >= 53 && y <= 78)
) {
return 'forest';
}
if (
(x >= 0 && x <= 42 && y <= 47) ||
(x >= 38 && x <= 80 && y <= 38) ||
(x >= 82 && x <= 111 && y <= 48) ||
(x >= 96 && x <= 111 && y >= 34 && y <= 65)
) {
return 'hill';
}
if (
(x <= 2 && y <= 82) ||
(x >= 109 && y <= 79) ||
(x >= 33 && x <= 39 && y >= 0 && y <= 53) ||
(x >= 80 && x <= 85 && y >= 0 && y <= 42) ||
(x >= 52 && x <= 58 && y >= 74 && y <= 93)
) {
return 'cliff';
}
if (
(x >= 14 && x <= 28 && y >= 70 && y <= 81) ||
(x >= 49 && x <= 63 && y >= 58 && y <= 68) ||
(x >= 84 && x <= 99 && y >= 50 && y <= 61) ||
(x >= 94 && x <= 106 && y >= 30 && y <= 41)
) {
return 'village';
}
if (
(x >= 24 && x <= 43 && y >= 64 && y <= 74) ||
(x >= 67 && x <= 84 && y >= 60 && y <= 72) ||
(x >= 88 && x <= 106 && y >= 42 && y <= 55)
) {
return 'camp';
}
if (
(x >= 58 && x <= 72 && y >= 36 && y <= 48) ||
(x >= 80 && x <= 98 && y >= 33 && y <= 47) ||
(x >= 94 && x <= 108 && y >= 45 && y <= 58) ||
(x >= 96 && x <= 109 && y >= 18 && y <= 31)
) {
return 'fort';
}
return 'plain';
})
);
}
function placeScenarioUnit(unit: UnitData, position: { x: number; y: number }): UnitData {
return {
...cloneUnitForScenario(unit),