feat: guide early stays and remember player choices
This commit is contained in:
@@ -344,7 +344,7 @@ type CampTabButtonView = {
|
||||
|
||||
type VictoryRewardCardId = CampaignVictoryRewardCategoryId;
|
||||
|
||||
type VictoryRewardActionId = 'equipment' | 'supplies' | 'sortie' | 'close';
|
||||
type VictoryRewardActionId = 'equipment' | 'supplies' | 'guided' | 'sortie' | 'close';
|
||||
|
||||
type VictoryRewardIcon =
|
||||
| { kind: 'mark'; mark: string }
|
||||
@@ -368,6 +368,7 @@ type VictoryRewardActionView = {
|
||||
id: VictoryRewardActionId;
|
||||
label: string;
|
||||
button: Phaser.GameObjects.Rectangle;
|
||||
primary: boolean;
|
||||
};
|
||||
|
||||
type CampActionButtonView = {
|
||||
@@ -377,6 +378,23 @@ type CampActionButtonView = {
|
||||
hovered: boolean;
|
||||
};
|
||||
|
||||
type FirstCampGuidedAction =
|
||||
| {
|
||||
kind: 'visit';
|
||||
label: '북문 정찰막 둘러보기';
|
||||
targetId: typeof firstPursuitScoutVisitId;
|
||||
}
|
||||
| {
|
||||
kind: 'dialogue';
|
||||
label: string;
|
||||
targetId: string;
|
||||
}
|
||||
| {
|
||||
kind: 'sortie';
|
||||
label: '출진 준비';
|
||||
targetId: null;
|
||||
};
|
||||
|
||||
type EquipmentSwapRequest = {
|
||||
unitId: string;
|
||||
slot: EquipmentSlot;
|
||||
@@ -11517,6 +11535,9 @@ export class CampScene extends Phaser.Scene {
|
||||
private reportFormationHistoryUndoState?: ReportFormationHistoryUndoState;
|
||||
private tabButtons: CampTabButtonView[] = [];
|
||||
private sortieCommandButton?: Phaser.GameObjects.Rectangle;
|
||||
private sortieCommandLabel?: Phaser.GameObjects.Text;
|
||||
private sortieBypassButton?: Phaser.GameObjects.Rectangle;
|
||||
private sortieBypassLabel?: Phaser.GameObjects.Text;
|
||||
private sortiePrimaryActionButton?: CampActionButtonView;
|
||||
private sortieNextActionGuideBackground?: Phaser.GameObjects.Rectangle;
|
||||
private sortieNextActionGuideText?: Phaser.GameObjects.Text;
|
||||
@@ -11672,6 +11693,10 @@ export class CampScene extends Phaser.Scene {
|
||||
this.reportFormationHistoryView = undefined;
|
||||
this.reportFormationHistoryUndoState = undefined;
|
||||
this.tabButtons = [];
|
||||
this.sortieCommandButton = undefined;
|
||||
this.sortieCommandLabel = undefined;
|
||||
this.sortieBypassButton = undefined;
|
||||
this.sortieBypassLabel = undefined;
|
||||
this.sortiePrimaryActionButton = undefined;
|
||||
this.sortieNextActionGuideBackground = undefined;
|
||||
this.sortieNextActionGuideText = undefined;
|
||||
@@ -13025,6 +13050,85 @@ export class CampScene extends Phaser.Scene {
|
||||
);
|
||||
}
|
||||
|
||||
private firstCampGuidedAction(): FirstCampGuidedAction | undefined {
|
||||
if (
|
||||
!this.isFirstSortiePrepSlice() ||
|
||||
this.currentCampBattleId() !== campBattleIds.first
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
if (!this.completedCampVisits().includes(firstPursuitScoutVisitId)) {
|
||||
return {
|
||||
kind: 'visit',
|
||||
label: '북문 정찰막 둘러보기',
|
||||
targetId: firstPursuitScoutVisitId
|
||||
};
|
||||
}
|
||||
const followup = this.firstBattleCampFollowup();
|
||||
if (
|
||||
followup &&
|
||||
!this.completedCampDialogues().includes(followup.targetDialogueId)
|
||||
) {
|
||||
return {
|
||||
kind: 'dialogue',
|
||||
label: `${followup.mentorName}와 핵심 회고`,
|
||||
targetId: followup.targetDialogueId
|
||||
};
|
||||
}
|
||||
return {
|
||||
kind: 'sortie',
|
||||
label: '출진 준비',
|
||||
targetId: null
|
||||
};
|
||||
}
|
||||
|
||||
private runFirstCampGuidedAction() {
|
||||
const action = this.firstCampGuidedAction();
|
||||
if (!action || action.kind === 'sortie') {
|
||||
this.showRequestedSortiePrep();
|
||||
return;
|
||||
}
|
||||
if (action.kind === 'visit') {
|
||||
this.activeTab = 'visit';
|
||||
this.selectedVisitId = action.targetId;
|
||||
this.render();
|
||||
this.startCampNavigation('CampVisitExplorationScene', { visitId: action.targetId });
|
||||
return;
|
||||
}
|
||||
this.activeTab = 'dialogue';
|
||||
this.selectedDialogueId = action.targetId;
|
||||
this.render();
|
||||
}
|
||||
|
||||
private campPrimaryActionLabel(flow = this.currentSortieFlow()) {
|
||||
if (this.isFinalEpilogueFlow(flow) && this.campaign?.step === flow.campaignStep) {
|
||||
return '엔딩 보기';
|
||||
}
|
||||
const guidedAction = this.firstCampGuidedAction();
|
||||
return guidedAction?.kind === 'visit'
|
||||
? '정찰막 둘러보기'
|
||||
: guidedAction?.label ?? '출진 준비';
|
||||
}
|
||||
|
||||
private refreshCampPrimaryActionPresentation() {
|
||||
const label = this.campPrimaryActionLabel();
|
||||
const guidedAction = this.firstCampGuidedAction();
|
||||
const bypassVisible = Boolean(guidedAction && guidedAction.kind !== 'sortie');
|
||||
this.sortieCommandLabel
|
||||
?.setText(label)
|
||||
.setFontSize(
|
||||
bypassVisible
|
||||
? Array.from(label).length > 7 ? 12 : 14
|
||||
: Array.from(label).length > 8 ? 13 : 16
|
||||
);
|
||||
[this.sortieBypassButton, this.sortieBypassLabel].forEach((object) => {
|
||||
object?.setVisible(bypassVisible);
|
||||
if (object?.input) {
|
||||
object.input.enabled = bypassVisible;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private hasPendingFirstBattleCamaraderieMemory() {
|
||||
const dialogue = this.firstBattleCamaraderieDialogue();
|
||||
return Boolean(
|
||||
@@ -13102,16 +13206,36 @@ export class CampScene extends Phaser.Scene {
|
||||
soundDirector.playSelect();
|
||||
this.showCampSaveSlotPanel();
|
||||
});
|
||||
const flow = this.currentSortieFlow();
|
||||
const storyButtonLabel = this.isFinalEpilogueFlow(flow) && this.campaign?.step === flow.campaignStep ? '엔딩 보기' : '출진 준비';
|
||||
this.sortieCommandButton = this.addCommandButton(storyButtonLabel, 1152, 38, 142, () => {
|
||||
const guidedAction = this.firstCampGuidedAction();
|
||||
const splitSortieCommand = Boolean(guidedAction && guidedAction.kind !== 'sortie');
|
||||
const sortieCommand = this.addCommandButton(
|
||||
this.campPrimaryActionLabel(),
|
||||
splitSortieCommand ? 1132 : 1152,
|
||||
38,
|
||||
splitSortieCommand ? 116 : 142,
|
||||
() => {
|
||||
soundDirector.playSelect();
|
||||
const flow = this.currentSortieFlow();
|
||||
if (this.isFinalEpilogueFlow(flow)) {
|
||||
this.startVictoryStory();
|
||||
return;
|
||||
}
|
||||
this.showSortiePrep();
|
||||
}, true);
|
||||
this.runFirstCampGuidedAction();
|
||||
},
|
||||
true
|
||||
);
|
||||
this.sortieCommandButton = sortieCommand.background;
|
||||
this.sortieCommandLabel = sortieCommand.label;
|
||||
if (splitSortieCommand) {
|
||||
const sortieBypass = this.addCommandButton('바로 출진', 1230, 38, 72, () => {
|
||||
soundDirector.playSelect();
|
||||
this.showRequestedSortiePrep();
|
||||
});
|
||||
sortieBypass.label.setFontSize(13);
|
||||
this.sortieBypassButton = sortieBypass.background;
|
||||
this.sortieBypassLabel = sortieBypass.label;
|
||||
}
|
||||
this.refreshCampPrimaryActionPresentation();
|
||||
}
|
||||
|
||||
private showCampSaveSlotPanel() {
|
||||
@@ -13472,6 +13596,9 @@ export class CampScene extends Phaser.Scene {
|
||||
const panelY = 90;
|
||||
const panelWidth = 884;
|
||||
const panelHeight = 546;
|
||||
const firstCampGuidedAction = report.battleId === campBattleIds.first
|
||||
? this.firstCampGuidedAction()
|
||||
: undefined;
|
||||
|
||||
const shade = this.trackVictoryReward(
|
||||
this.add.rectangle(0, 0, campLegacyCanvasWidth, campLegacyCanvasHeight, 0x020406, 0.78)
|
||||
@@ -13683,15 +13810,24 @@ export class CampScene extends Phaser.Scene {
|
||||
);
|
||||
noteText.setDepth(depth + 2);
|
||||
|
||||
const actionDefinitions: Array<{ id: VictoryRewardActionId; label: string; primary?: boolean }> = [
|
||||
{ id: 'equipment', label: '장비 확인' },
|
||||
{ id: 'supplies', label: '보급 확인' },
|
||||
{ id: 'sortie', label: '출진 준비', primary: true },
|
||||
{ id: 'close', label: '닫기' }
|
||||
];
|
||||
const buttonWidth = 158;
|
||||
const actionDefinitions: Array<{ id: VictoryRewardActionId; label: string; primary?: boolean }> =
|
||||
firstCampGuidedAction
|
||||
? [
|
||||
{ id: 'equipment', label: '장비 확인' },
|
||||
{ id: 'supplies', label: '보급 확인' },
|
||||
{ id: 'guided', label: firstCampGuidedAction.label, primary: true },
|
||||
{ id: 'sortie', label: '바로 출진' },
|
||||
{ id: 'close', label: '닫기' }
|
||||
]
|
||||
: [
|
||||
{ id: 'equipment', label: '장비 확인' },
|
||||
{ id: 'supplies', label: '보급 확인' },
|
||||
{ id: 'sortie', label: '출진 준비', primary: true },
|
||||
{ id: 'close', label: '닫기' }
|
||||
];
|
||||
const buttonWidth = firstCampGuidedAction ? 156 : 158;
|
||||
const buttonHeight = 48;
|
||||
const actionGap = 14;
|
||||
const actionGap = firstCampGuidedAction ? 8 : 14;
|
||||
const totalActionWidth = actionDefinitions.length * buttonWidth + (actionDefinitions.length - 1) * actionGap;
|
||||
const actionStartX = panelX + Math.floor((panelWidth - totalActionWidth) / 2);
|
||||
actionDefinitions.forEach((definition, index) => {
|
||||
@@ -13708,7 +13844,16 @@ export class CampScene extends Phaser.Scene {
|
||||
button.setInteractive({ useHandCursor: true });
|
||||
|
||||
const label = this.trackVictoryReward(
|
||||
this.add.text(buttonX + buttonWidth / 2, buttonY + buttonHeight / 2, definition.label, this.textStyle(14, definition.primary ? '#fff2b8' : '#f2e3bf', true))
|
||||
this.add.text(
|
||||
buttonX + buttonWidth / 2,
|
||||
buttonY + buttonHeight / 2,
|
||||
definition.label,
|
||||
this.textStyle(
|
||||
definition.id === 'guided' && Array.from(definition.label).length > 8 ? 12 : 14,
|
||||
definition.primary ? '#fff2b8' : '#f2e3bf',
|
||||
true
|
||||
)
|
||||
)
|
||||
);
|
||||
label.setOrigin(0.5);
|
||||
label.setDepth(depth + 3);
|
||||
@@ -13723,7 +13868,12 @@ export class CampScene extends Phaser.Scene {
|
||||
target.on('pointerout', () => button.setFillStyle(restingFill, 0.99));
|
||||
target.on('pointerdown', run);
|
||||
});
|
||||
this.victoryRewardActions.push({ id: definition.id, label: definition.label, button });
|
||||
this.victoryRewardActions.push({
|
||||
id: definition.id,
|
||||
label: definition.label,
|
||||
button,
|
||||
primary: Boolean(definition.primary)
|
||||
});
|
||||
});
|
||||
soundDirector.playRewardRevealCue();
|
||||
}
|
||||
@@ -13759,6 +13909,10 @@ export class CampScene extends Phaser.Scene {
|
||||
this.render();
|
||||
return;
|
||||
}
|
||||
if (action === 'guided') {
|
||||
this.runFirstCampGuidedAction();
|
||||
return;
|
||||
}
|
||||
if (action === 'sortie' || (action === 'close' && this.openSortiePrepOnCreate)) {
|
||||
this.showRequestedSortiePrep();
|
||||
}
|
||||
@@ -14110,7 +14264,7 @@ export class CampScene extends Phaser.Scene {
|
||||
target.on('pointerout', () => setHovered(false));
|
||||
target.on('pointerdown', action);
|
||||
});
|
||||
return bg;
|
||||
return { background: bg, label: text };
|
||||
}
|
||||
|
||||
private render() {
|
||||
@@ -14124,6 +14278,7 @@ export class CampScene extends Phaser.Scene {
|
||||
this.acknowledgePendingVictoryRewardCategories(['supplies']);
|
||||
}
|
||||
this.visitedTabs.add(this.activeTab);
|
||||
this.refreshCampPrimaryActionPresentation();
|
||||
this.updateTabButtons();
|
||||
this.renderUnitColumn();
|
||||
this.renderReportPanel();
|
||||
@@ -26042,6 +26197,7 @@ export class CampScene extends Phaser.Scene {
|
||||
: false;
|
||||
const cityEquipmentSortieStatus = this.cityEquipmentSortieStatus();
|
||||
const firstBattleCampFollowup = this.firstBattleCampFollowup();
|
||||
const firstCampGuidedAction = this.firstCampGuidedAction();
|
||||
const firstBattleCamaraderieMemory = this.firstBattleCamaraderieMemory();
|
||||
const firstBattleCamaraderieDialogue = this.firstBattleCamaraderieDialogue();
|
||||
const thirdBattlePriorityReturn = this.thirdBattlePriorityReturnDialogue();
|
||||
@@ -26090,6 +26246,7 @@ export class CampScene extends Phaser.Scene {
|
||||
actions: this.victoryRewardActions.map((action) => ({
|
||||
id: action.id,
|
||||
label: action.label,
|
||||
primary: action.primary,
|
||||
bounds: this.sortieObjectBoundsDebug(action.button),
|
||||
interactive: Boolean(action.button.input?.enabled)
|
||||
}))
|
||||
@@ -26148,8 +26305,28 @@ export class CampScene extends Phaser.Scene {
|
||||
lineWidth: button.bg.lineWidth
|
||||
})),
|
||||
sortieCommand: {
|
||||
label: this.sortieCommandLabel?.text ?? null,
|
||||
guidedKind: firstCampGuidedAction?.kind ?? null,
|
||||
targetId: firstCampGuidedAction?.targetId ?? null,
|
||||
bounds: this.sortieObjectBoundsDebug(this.sortieCommandButton),
|
||||
interactive: Boolean(this.sortieCommandButton?.input?.enabled)
|
||||
interactive: Boolean(
|
||||
this.sortieCommandButton?.input?.enabled &&
|
||||
this.sortieCommandLabel?.input?.enabled
|
||||
),
|
||||
bypass:
|
||||
this.sortieBypassButton?.visible &&
|
||||
this.sortieBypassLabel?.visible
|
||||
? {
|
||||
label: this.sortieBypassLabel.text,
|
||||
bounds: this.sortieObjectBoundsDebug(
|
||||
this.sortieBypassButton
|
||||
),
|
||||
interactive: Boolean(
|
||||
this.sortieBypassButton.input?.enabled &&
|
||||
this.sortieBypassLabel.input?.enabled
|
||||
)
|
||||
}
|
||||
: null
|
||||
},
|
||||
firstVictoryFollowup: firstBattleCampFollowup
|
||||
? {
|
||||
|
||||
Reference in New Issue
Block a user