Polish sortie prep guidance
This commit is contained in:
@@ -183,6 +183,7 @@ type SortieChecklistItem = {
|
|||||||
label: string;
|
label: string;
|
||||||
complete: boolean;
|
complete: boolean;
|
||||||
detail: string;
|
detail: string;
|
||||||
|
priority?: 'required' | 'recommended';
|
||||||
};
|
};
|
||||||
|
|
||||||
type SortieUnitTacticalSummary = {
|
type SortieUnitTacticalSummary = {
|
||||||
@@ -11265,9 +11266,10 @@ export class CampScene extends Phaser.Scene {
|
|||||||
const y = Math.floor((this.scale.height - height) / 2);
|
const y = Math.floor((this.scale.height - height) / 2);
|
||||||
const flow = this.currentSortieFlow();
|
const flow = this.currentSortieFlow();
|
||||||
const isBattleSortie = Boolean(flow.nextBattleId);
|
const isBattleSortie = Boolean(flow.nextBattleId);
|
||||||
|
const checklist = this.sortieChecklist();
|
||||||
const prepTitle = isBattleSortie ? '출진 준비' : '군영 의정';
|
const prepTitle = isBattleSortie ? '출진 준비' : '군영 의정';
|
||||||
const prepSubtitle = isBattleSortie
|
const prepSubtitle = isBattleSortie
|
||||||
? '대화, 보급, 상인 정비를 마친 뒤 함께 출전할 무장을 선택합니다.'
|
? this.sortiePrepSubtitle(checklist)
|
||||||
: '군영 대화와 장부를 정리한 뒤 다음 장면에 함께할 무장을 선택합니다.';
|
: '군영 대화와 장부를 정리한 뒤 다음 장면에 함께할 무장을 선택합니다.';
|
||||||
|
|
||||||
const shade = this.trackSortie(this.add.rectangle(0, 0, this.scale.width, this.scale.height, 0x020406, 0.68));
|
const shade = this.trackSortie(this.add.rectangle(0, 0, this.scale.width, this.scale.height, 0x020406, 0.68));
|
||||||
@@ -11283,13 +11285,13 @@ export class CampScene extends Phaser.Scene {
|
|||||||
const title = this.trackSortie(this.add.text(x + 34, y + 28, prepTitle, this.textStyle(30, '#f2e3bf', true)));
|
const title = this.trackSortie(this.add.text(x + 34, y + 28, prepTitle, this.textStyle(30, '#f2e3bf', true)));
|
||||||
title.setDepth(depth + 2);
|
title.setDepth(depth + 2);
|
||||||
const subtitle = this.trackSortie(
|
const subtitle = this.trackSortie(
|
||||||
this.add.text(x + 34, y + 70, prepSubtitle, this.textStyle(15, '#d4dce6'))
|
this.add.text(x + 34, y + 70, this.compactText(prepSubtitle, 72), this.textStyle(15, '#d4dce6'))
|
||||||
);
|
);
|
||||||
subtitle.setDepth(depth + 2);
|
subtitle.setDepth(depth + 2);
|
||||||
this.renderSortieHeaderSummary(x + 690, y + 26, 464, 58, depth + 2);
|
this.renderSortieHeaderSummary(x + 690, y + 26, 464, 58, depth + 2);
|
||||||
|
|
||||||
this.renderSortieBriefing(x + 34, y + 104, 560, 168, depth + 2);
|
this.renderSortieBriefing(x + 34, y + 104, 560, 168, depth + 2);
|
||||||
this.renderSortieChecklist(x + 612, y + 104, 542, 168, depth + 2);
|
this.renderSortieChecklist(x + 612, y + 104, 542, 168, depth + 2, checklist);
|
||||||
this.renderSortieUnitSummary(x + 34, y + 292, 390, 286, depth + 2);
|
this.renderSortieUnitSummary(x + 34, y + 292, 390, 286, depth + 2);
|
||||||
this.renderSortieFormationPlan(x + 442, y + 292, 350, 286, depth + 2);
|
this.renderSortieFormationPlan(x + 442, y + 292, 350, 286, depth + 2);
|
||||||
this.renderSortieFocusPanel(x + 810, y + 292, 344, 286, depth + 2);
|
this.renderSortieFocusPanel(x + 810, y + 292, 344, 286, depth + 2);
|
||||||
@@ -11376,20 +11378,19 @@ export class CampScene extends Phaser.Scene {
|
|||||||
this.trackSortie(this.add.text(x + 8, y + 18, this.compactText(value, 13), this.textStyle(10, '#f2e3bf', true))).setDepth(depth + 1);
|
this.trackSortie(this.add.text(x + 8, y + 18, this.compactText(value, 13), this.textStyle(10, '#f2e3bf', true))).setDepth(depth + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderSortieChecklist(x: number, y: number, width: number, height: number, depth: number) {
|
private renderSortieChecklist(x: number, y: number, width: number, height: number, depth: number, items = this.sortieChecklist()) {
|
||||||
const bg = this.trackSortie(this.add.rectangle(x, y, width, height, 0x0d141c, 0.92));
|
const bg = this.trackSortie(this.add.rectangle(x, y, width, height, 0x0d141c, 0.92));
|
||||||
bg.setOrigin(0);
|
bg.setOrigin(0);
|
||||||
bg.setDepth(depth);
|
bg.setDepth(depth);
|
||||||
bg.setStrokeStyle(1, palette.gold, 0.5);
|
bg.setStrokeStyle(1, palette.gold, 0.5);
|
||||||
this.trackSortie(this.add.text(x + 18, y + 14, '준비 체크', this.textStyle(18, '#f2e3bf', true))).setDepth(depth + 1);
|
this.trackSortie(this.add.text(x + 18, y + 14, '준비 체크', this.textStyle(18, '#f2e3bf', true))).setDepth(depth + 1);
|
||||||
|
|
||||||
const items = this.sortieChecklist();
|
|
||||||
const rowGap = Math.max(15, Math.floor((height - 50) / Math.max(1, items.length)));
|
const rowGap = Math.max(15, Math.floor((height - 50) / Math.max(1, items.length)));
|
||||||
const compact = rowGap < 20;
|
const compact = rowGap < 20;
|
||||||
items.forEach((item, index) => {
|
items.forEach((item, index) => {
|
||||||
const rowY = y + 42 + index * rowGap;
|
const rowY = y + 42 + index * rowGap;
|
||||||
const color = item.complete ? '#a8ffd0' : '#ffdf7b';
|
const color = item.complete ? '#a8ffd0' : item.priority === 'required' ? '#ff9d7d' : '#ffdf7b';
|
||||||
const status = item.complete ? '완료' : '주의';
|
const status = item.complete ? '완료' : item.priority === 'required' ? '필수' : '권장';
|
||||||
this.trackSortie(this.add.text(x + 18, rowY, status, this.textStyle(compact ? 10 : 13, color, true))).setDepth(depth + 1);
|
this.trackSortie(this.add.text(x + 18, rowY, status, this.textStyle(compact ? 10 : 13, color, true))).setDepth(depth + 1);
|
||||||
this.trackSortie(this.add.text(x + 68, rowY, item.label, this.textStyle(compact ? 11 : 14, '#d4dce6', true))).setDepth(depth + 1);
|
this.trackSortie(this.add.text(x + 68, rowY, item.label, this.textStyle(compact ? 11 : 14, '#d4dce6', true))).setDepth(depth + 1);
|
||||||
const detail = this.trackSortie(this.add.text(x + 184, rowY, this.compactText(item.detail, compact ? 36 : 42), this.textStyle(compact ? 10 : 12, item.complete ? '#9fb0bf' : '#d8b15f')));
|
const detail = this.trackSortie(this.add.text(x + 184, rowY, this.compactText(item.detail, compact ? 36 : 42), this.textStyle(compact ? 10 : 12, item.complete ? '#9fb0bf' : '#d8b15f')));
|
||||||
@@ -12524,6 +12525,18 @@ export class CampScene extends Phaser.Scene {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sortiePrepSubtitle(checklist: SortieChecklistItem[]) {
|
||||||
|
const nextRequired = checklist.find((item) => !item.complete && item.priority === 'required');
|
||||||
|
const nextRecommended = checklist.find((item) => !item.complete);
|
||||||
|
const next = nextRequired ?? nextRecommended;
|
||||||
|
if (!next) {
|
||||||
|
return '정비가 끝났습니다. 전열과 보급을 확인한 뒤 출진하십시오.';
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefix = next.priority === 'required' ? '필수' : '권장';
|
||||||
|
return `${prefix}: ${next.label} - ${next.detail}`;
|
||||||
|
}
|
||||||
|
|
||||||
private currentSortieFlow() {
|
private currentSortieFlow() {
|
||||||
return getSortieFlow(this.campaign?.latestBattleId, this.campaign?.step);
|
return getSortieFlow(this.campaign?.latestBattleId, this.campaign?.step);
|
||||||
}
|
}
|
||||||
@@ -12571,47 +12584,56 @@ export class CampScene extends Phaser.Scene {
|
|||||||
{
|
{
|
||||||
label: requiredLabel,
|
label: requiredLabel,
|
||||||
complete: requiredComplete,
|
complete: requiredComplete,
|
||||||
detail: requiredDetail
|
detail: requiredDetail,
|
||||||
|
priority: 'required'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '부상 장수 확인',
|
label: '부상 장수 확인',
|
||||||
complete: injured.length === 0,
|
complete: injured.length === 0,
|
||||||
detail: injured.length === 0 ? '전원 완전한 병력' : `${injured.map((unit) => unit.name).join(', ')} 회복 권장`
|
detail: injured.length === 0 ? '전원 완전한 병력' : `${injured.map((unit) => unit.name).join(', ')} 회복 권장`,
|
||||||
|
priority: 'recommended'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '출전 구성',
|
label: '출전 구성',
|
||||||
complete: requiredUnitIds.every((id) => selected.some((unit) => unit.id === id)) && selected.length > 0,
|
complete: requiredUnitIds.every((id) => selected.some((unit) => unit.id === id)) && selected.length > 0,
|
||||||
detail: selected.length > 0 ? selected.map((unit) => unit.name).join(', ') : '출전 무장 선택 필요'
|
detail: selected.length > 0 ? selected.map((unit) => unit.name).join(', ') : '출전 무장 선택 필요',
|
||||||
|
priority: 'required'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '추천 병종',
|
label: '추천 병종',
|
||||||
complete: recommendedClassComplete,
|
complete: recommendedClassComplete,
|
||||||
detail: recommendedClassLine
|
detail: recommendedClassLine,
|
||||||
|
priority: 'recommended'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '전열 역할',
|
label: '전열 역할',
|
||||||
complete: rolesReady,
|
complete: rolesReady,
|
||||||
detail: `전열 ${roleCounts.front} · 돌파 ${roleCounts.flank} · 후원 ${roleCounts.support}`
|
detail: `전열 ${roleCounts.front} · 돌파 ${roleCounts.flank} · 후원 ${roleCounts.support}`,
|
||||||
|
priority: 'recommended'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '전투 보급',
|
label: '전투 보급',
|
||||||
complete: assignedSupplyCount > 0 || supplyCount > 0,
|
complete: supplyCount === 0 || assignedSupplyCount > 0,
|
||||||
detail: supplyCount > 0 ? `배정 ${assignedSupplyCount}/${supplyCount}` : '정비 탭에서 보급 확인'
|
detail: supplyCount > 0 ? `배정 ${assignedSupplyCount}/${supplyCount}` : '보급 없음',
|
||||||
|
priority: 'recommended'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '공명 대화',
|
label: '공명 대화',
|
||||||
complete: availableDialogues.length === 0 || completedDialogues >= availableDialogues.length,
|
complete: availableDialogues.length === 0 || completedDialogues >= availableDialogues.length,
|
||||||
detail: `${completedDialogues}/${availableDialogues.length} 완료`
|
detail: `${completedDialogues}/${availableDialogues.length} 완료`,
|
||||||
|
priority: 'recommended'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '현지 방문',
|
label: '현지 방문',
|
||||||
complete: availableVisits.length === 0 || completedVisits > 0,
|
complete: availableVisits.length === 0 || completedVisits > 0,
|
||||||
detail: availableVisits.length === 0 ? '방문 없음' : `${completedVisits}/${availableVisits.length} 완료`
|
detail: availableVisits.length === 0 ? '방문 없음' : `${completedVisits}/${availableVisits.length} 완료`,
|
||||||
|
priority: 'recommended'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '장비 상태',
|
label: '장비 상태',
|
||||||
complete: this.visitedTabs.has('status'),
|
complete: this.visitedTabs.has('status'),
|
||||||
detail: this.visitedTabs.has('status') ? '장수 탭 확인됨' : '장수 탭 확인 권장'
|
detail: this.visitedTabs.has('status') ? '장수 탭 확인됨' : '장수 탭 확인 권장',
|
||||||
|
priority: 'recommended'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -14249,6 +14271,7 @@ export class CampScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getDebugState() {
|
getDebugState() {
|
||||||
|
const sortieChecklist = this.sortieChecklist();
|
||||||
return {
|
return {
|
||||||
scene: this.scene.key,
|
scene: this.scene.key,
|
||||||
activeTab: this.activeTab,
|
activeTab: this.activeTab,
|
||||||
@@ -14262,6 +14285,8 @@ export class CampScene extends Phaser.Scene {
|
|||||||
sortieDeploymentPreview: this.sortieDeploymentPreviewDebug(),
|
sortieDeploymentPreview: this.sortieDeploymentPreviewDebug(),
|
||||||
sortieFocusedUnitId: this.sortieFocusedUnitId,
|
sortieFocusedUnitId: this.sortieFocusedUnitId,
|
||||||
sortieFocusedUnit: this.sortieFocusedUnitSummary(),
|
sortieFocusedUnit: this.sortieFocusedUnitSummary(),
|
||||||
|
sortieChecklist,
|
||||||
|
sortieNextAction: this.sortiePrepSubtitle(sortieChecklist),
|
||||||
reserveTrainingFocus: this.reserveTrainingFocusDefinition(),
|
reserveTrainingFocus: this.reserveTrainingFocusDefinition(),
|
||||||
reserveTrainingFocusOptions: campaignReserveTrainingFocusDefinitions.map((focus) => ({ ...focus })),
|
reserveTrainingFocusOptions: campaignReserveTrainingFocusDefinitions.map((focus) => ({ ...focus })),
|
||||||
sortieRosterView: this.sortieRosterViewState(this.sortieAllies().length),
|
sortieRosterView: this.sortieRosterViewState(this.sortieAllies().length),
|
||||||
|
|||||||
Reference in New Issue
Block a user