diff --git a/src/game/scenes/CampScene.ts b/src/game/scenes/CampScene.ts index a257def..907ac40 100644 --- a/src/game/scenes/CampScene.ts +++ b/src/game/scenes/CampScene.ts @@ -183,6 +183,7 @@ type SortieChecklistItem = { label: string; complete: boolean; detail: string; + priority?: 'required' | 'recommended'; }; type SortieUnitTacticalSummary = { @@ -11265,9 +11266,10 @@ export class CampScene extends Phaser.Scene { const y = Math.floor((this.scale.height - height) / 2); const flow = this.currentSortieFlow(); const isBattleSortie = Boolean(flow.nextBattleId); + const checklist = this.sortieChecklist(); const prepTitle = 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)); @@ -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))); title.setDepth(depth + 2); 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); this.renderSortieHeaderSummary(x + 690, y + 26, 464, 58, 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.renderSortieFormationPlan(x + 442, y + 292, 350, 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); } - 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)); bg.setOrigin(0); bg.setDepth(depth); bg.setStrokeStyle(1, palette.gold, 0.5); 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 compact = rowGap < 20; items.forEach((item, index) => { const rowY = y + 42 + index * rowGap; - const color = item.complete ? '#a8ffd0' : '#ffdf7b'; - const status = item.complete ? '완료' : '주의'; + const color = item.complete ? '#a8ffd0' : item.priority === 'required' ? '#ff9d7d' : '#ffdf7b'; + 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 + 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'))); @@ -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() { return getSortieFlow(this.campaign?.latestBattleId, this.campaign?.step); } @@ -12571,47 +12584,56 @@ export class CampScene extends Phaser.Scene { { label: requiredLabel, complete: requiredComplete, - detail: requiredDetail + detail: requiredDetail, + priority: 'required' }, { label: '부상 장수 확인', 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: '출전 구성', 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: '추천 병종', complete: recommendedClassComplete, - detail: recommendedClassLine + detail: recommendedClassLine, + priority: 'recommended' }, { label: '전열 역할', complete: rolesReady, - detail: `전열 ${roleCounts.front} · 돌파 ${roleCounts.flank} · 후원 ${roleCounts.support}` + detail: `전열 ${roleCounts.front} · 돌파 ${roleCounts.flank} · 후원 ${roleCounts.support}`, + priority: 'recommended' }, { label: '전투 보급', - complete: assignedSupplyCount > 0 || supplyCount > 0, - detail: supplyCount > 0 ? `배정 ${assignedSupplyCount}/${supplyCount}` : '정비 탭에서 보급 확인' + complete: supplyCount === 0 || assignedSupplyCount > 0, + detail: supplyCount > 0 ? `배정 ${assignedSupplyCount}/${supplyCount}` : '보급 없음', + priority: 'recommended' }, { label: '공명 대화', complete: availableDialogues.length === 0 || completedDialogues >= availableDialogues.length, - detail: `${completedDialogues}/${availableDialogues.length} 완료` + detail: `${completedDialogues}/${availableDialogues.length} 완료`, + priority: 'recommended' }, { label: '현지 방문', complete: availableVisits.length === 0 || completedVisits > 0, - detail: availableVisits.length === 0 ? '방문 없음' : `${completedVisits}/${availableVisits.length} 완료` + detail: availableVisits.length === 0 ? '방문 없음' : `${completedVisits}/${availableVisits.length} 완료`, + priority: 'recommended' }, { label: '장비 상태', 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() { + const sortieChecklist = this.sortieChecklist(); return { scene: this.scene.key, activeTab: this.activeTab, @@ -14262,6 +14285,8 @@ export class CampScene extends Phaser.Scene { sortieDeploymentPreview: this.sortieDeploymentPreviewDebug(), sortieFocusedUnitId: this.sortieFocusedUnitId, sortieFocusedUnit: this.sortieFocusedUnitSummary(), + sortieChecklist, + sortieNextAction: this.sortiePrepSubtitle(sortieChecklist), reserveTrainingFocus: this.reserveTrainingFocusDefinition(), reserveTrainingFocusOptions: campaignReserveTrainingFocusDefinitions.map((focus) => ({ ...focus })), sortieRosterView: this.sortieRosterViewState(this.sortieAllies().length),