feat: connect sortie recommendations to battle results
This commit is contained in:
@@ -59,6 +59,12 @@ import {
|
||||
sortiePerformanceRoleContributionLine,
|
||||
type SortiePerformanceReview
|
||||
} from '../data/sortiePerformanceReview';
|
||||
import {
|
||||
evaluateSortieRecommendationOutcome,
|
||||
sortieRecommendationContributionCompactLabel,
|
||||
sortieRecommendationDisplayUnitIds,
|
||||
type SortieRecommendationOutcome
|
||||
} from '../data/sortieRecommendationOutcome';
|
||||
import {
|
||||
sortieOrderDefinition,
|
||||
sortieOrderDefinitions,
|
||||
@@ -174,6 +180,7 @@ import {
|
||||
type CampaignSortieFormationPresets,
|
||||
type CampaignSortiePerformanceSnapshot,
|
||||
type CampaignSortiePresetId,
|
||||
type CampaignSortieRecommendationSnapshot,
|
||||
type FirstBattleReport
|
||||
} from '../state/campaignState';
|
||||
import { palette } from '../ui/palette';
|
||||
@@ -621,6 +628,13 @@ type SortieOrderBrowserView = {
|
||||
type ReportFormationReviewView = {
|
||||
closeButton: Phaser.GameObjects.Rectangle;
|
||||
presetButtons: Partial<Record<CampaignSortiePresetId, Phaser.GameObjects.Rectangle>>;
|
||||
recommendationPanel?: Phaser.GameObjects.Rectangle;
|
||||
recommendationRows: {
|
||||
unitId: string;
|
||||
background: Phaser.GameObjects.Rectangle;
|
||||
nameText: Phaser.GameObjects.Text;
|
||||
statusText: Phaser.GameObjects.Text;
|
||||
}[];
|
||||
};
|
||||
|
||||
type ReportFormationHistoryRecord = {
|
||||
@@ -16994,7 +17008,7 @@ export class CampScene extends Phaser.Scene {
|
||||
this.sortieRosterScroll = 0;
|
||||
this.sortiePortraitRosterPage = 0;
|
||||
this.sortiePlanFeedback = `${projection.label} 적용 완료 · 장비·보급 재점검`;
|
||||
this.persistSortieSelection();
|
||||
this.persistSortieSelection(this.createSortieRecommendationSnapshot(projection));
|
||||
this.sortieRecommendationUndoState = {
|
||||
recommendationId,
|
||||
beforeCoreBondId,
|
||||
@@ -17221,6 +17235,55 @@ export class CampScene extends Phaser.Scene {
|
||||
return runtime === expected && saved === expected;
|
||||
}
|
||||
|
||||
private createSortieRecommendationSnapshot(
|
||||
projection: SortieRecommendationProjection
|
||||
): CampaignSortieRecommendationSnapshot | undefined {
|
||||
const scenario = this.nextSortieScenario();
|
||||
if (!scenario) {
|
||||
return undefined;
|
||||
}
|
||||
const sortieOrderId = this.currentSortieOrderId();
|
||||
const sortieResonanceBondId = this.currentSortieResonanceBondId(scenario);
|
||||
return {
|
||||
version: 1,
|
||||
battleId: scenario.id,
|
||||
planId: projection.recommendationId,
|
||||
label: projection.label,
|
||||
summary: projection.plan.summary,
|
||||
...(sortieOrderId ? { sortieOrderId } : {}),
|
||||
...(sortieResonanceBondId ? { sortieResonanceBondId } : {}),
|
||||
selectedUnitIds: [...projection.selectedUnitIds],
|
||||
formationAssignments: { ...projection.formationAssignments },
|
||||
unitReasons: { ...projection.plan.unitReasons }
|
||||
};
|
||||
}
|
||||
|
||||
private sortieRecommendationSelectionMatchesCurrent(selection?: CampaignSortieRecommendationSnapshot) {
|
||||
const scenario = this.nextSortieScenario();
|
||||
return Boolean(
|
||||
selection &&
|
||||
scenario?.id === selection.battleId &&
|
||||
JSON.stringify(selection.selectedUnitIds) === JSON.stringify(this.selectedSortieUnitIds) &&
|
||||
selection.selectedUnitIds.every((unitId) => (
|
||||
selection.formationAssignments[unitId] === this.sortieFormationAssignments[unitId]
|
||||
)) &&
|
||||
selection.sortieOrderId === this.currentSortieOrderId() &&
|
||||
selection.sortieResonanceBondId === this.currentSortieResonanceBondId(scenario)
|
||||
);
|
||||
}
|
||||
|
||||
private currentSortieRecommendationSelection() {
|
||||
const selection = this.campaign?.sortieRecommendationSelection;
|
||||
return this.sortieRecommendationSelectionMatchesCurrent(selection)
|
||||
? {
|
||||
...selection!,
|
||||
selectedUnitIds: [...selection!.selectedUnitIds],
|
||||
formationAssignments: { ...selection!.formationAssignments },
|
||||
unitReasons: { ...selection!.unitReasons }
|
||||
}
|
||||
: undefined;
|
||||
}
|
||||
|
||||
private confirmPinnedSortieSwap() {
|
||||
const preview = this.sortieFormationComparisonPreview();
|
||||
if (
|
||||
@@ -18510,6 +18573,7 @@ export class CampScene extends Phaser.Scene {
|
||||
const sortieResonanceBondId = this.campaign?.sortieResonanceSelection?.battleId === flow.nextBattleId
|
||||
? this.campaign.sortieResonanceSelection.bondId
|
||||
: undefined;
|
||||
const sortieRecommendation = this.currentSortieRecommendationSelection();
|
||||
if (this.skipIntroStoryForSortie) {
|
||||
void startLazyScene(this, 'BattleScene', {
|
||||
battleId: flow.nextBattleId,
|
||||
@@ -18517,7 +18581,8 @@ export class CampScene extends Phaser.Scene {
|
||||
sortieFormationAssignments,
|
||||
sortieItemAssignments,
|
||||
sortieOrderId,
|
||||
sortieResonanceBondId
|
||||
sortieResonanceBondId,
|
||||
sortieRecommendation
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -18531,7 +18596,8 @@ export class CampScene extends Phaser.Scene {
|
||||
sortieFormationAssignments,
|
||||
sortieItemAssignments,
|
||||
sortieOrderId,
|
||||
sortieResonanceBondId
|
||||
sortieResonanceBondId,
|
||||
sortieRecommendation
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -18838,7 +18904,7 @@ export class CampScene extends Phaser.Scene {
|
||||
)
|
||||
);
|
||||
|
||||
const sortieOrderResult = this.campaign?.battleHistory[report.battleId]?.sortieOrder ?? report.sortieOrder;
|
||||
const sortieOrderResult = report.sortieOrder ?? this.campaign?.battleHistory[report.battleId]?.sortieOrder;
|
||||
if (sortieOrderResult?.command) {
|
||||
const commandRecord = this.sortieOrderCommandRecordLine(sortieOrderResult.command);
|
||||
this.track(
|
||||
@@ -18866,11 +18932,12 @@ export class CampScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
const review = this.reportFormationReview();
|
||||
const recommendationOutcome = review ? this.reportSortieRecommendationOutcome(review) : undefined;
|
||||
const historyRecords = this.reportFormationHistoryRecords();
|
||||
const actions: { label: string; action: () => void; assign: (button: Phaser.GameObjects.Rectangle) => void }[] = [];
|
||||
if (review) {
|
||||
actions.push({
|
||||
label: `최근 평가 ${review.grade}`,
|
||||
label: recommendationOutcome ? `추천 검증 ${recommendationOutcome.statusLabel}` : `최근 평가 ${review.grade}`,
|
||||
action: () => this.showReportFormationReview(),
|
||||
assign: (button) => { this.reportFormationReviewToggleButton = button; }
|
||||
});
|
||||
@@ -18902,27 +18969,28 @@ export class CampScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
const settlement = this.campaign?.battleHistory[report.battleId];
|
||||
const snapshot = settlement?.sortiePerformance ?? report.sortiePerformance;
|
||||
const snapshot = report.sortiePerformance ?? settlement?.sortiePerformance;
|
||||
if (!snapshot) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const endUnits = settlement
|
||||
? settlement.units.map((unit) => ({ id: unit.unitId, hp: unit.hp, maxHp: unit.maxHp }))
|
||||
: report.units
|
||||
.filter((unit) => unit.faction === 'ally')
|
||||
.map((unit) => ({ id: unit.id, hp: unit.hp, maxHp: unit.maxHp }));
|
||||
const reportEndUnits = report.units
|
||||
.filter((unit) => unit.faction === 'ally')
|
||||
.map((unit) => ({ id: unit.id, hp: unit.hp, maxHp: unit.maxHp }));
|
||||
const endUnits = reportEndUnits.length > 0
|
||||
? reportEndUnits
|
||||
: settlement?.units.map((unit) => ({ id: unit.unitId, hp: unit.hp, maxHp: unit.maxHp })) ?? [];
|
||||
const endUnitIds = new Set(endUnits.map((unit) => unit.id));
|
||||
if (!snapshot.selectedUnitIds.some((unitId) => endUnitIds.has(unitId))) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const selectedIds = new Set(snapshot.selectedUnitIds);
|
||||
const storedReview = settlement?.sortieReview ?? report.sortieReview;
|
||||
const storedReview = report.sortieReview ?? settlement?.sortieReview;
|
||||
const activeBondCount = storedReview?.activeBondCount ?? report.bonds.filter((bond) => bond.unitIds.every((unitId) => selectedIds.has(unitId))).length;
|
||||
const review = evaluateSortiePerformanceReview({
|
||||
outcome: settlement?.outcome ?? report.outcome,
|
||||
objectives: settlement?.objectives ?? report.objectives,
|
||||
outcome: report.outcome,
|
||||
objectives: report.objectives,
|
||||
enemyCount: storedReview?.enemyCount ?? report.totalEnemies,
|
||||
activeBondCount,
|
||||
endUnits,
|
||||
@@ -18943,6 +19011,38 @@ export class CampScene extends Phaser.Scene {
|
||||
};
|
||||
}
|
||||
|
||||
private reportSortieRecommendationOutcome(
|
||||
review: SortiePerformanceReview,
|
||||
report = this.report
|
||||
): SortieRecommendationOutcome | undefined {
|
||||
if (!report) {
|
||||
return undefined;
|
||||
}
|
||||
const settlement = this.campaign?.battleHistory[report.battleId];
|
||||
const recommendation = report.sortieRecommendation ?? settlement?.sortieRecommendation;
|
||||
if (!recommendation) {
|
||||
return undefined;
|
||||
}
|
||||
return evaluateSortieRecommendationOutcome({
|
||||
planId: recommendation.planId,
|
||||
selectedUnitIds: recommendation.selectedUnitIds,
|
||||
formationAssignments: recommendation.formationAssignments,
|
||||
unitReasons: recommendation.unitReasons,
|
||||
performance: review.snapshot,
|
||||
outcome: report.outcome,
|
||||
objectives: report.objectives,
|
||||
activeBondCount: review.activeBondCount,
|
||||
sortieOrder: report.sortieOrder ?? settlement?.sortieOrder
|
||||
});
|
||||
}
|
||||
|
||||
private reportSortieRecommendationSnapshot(report = this.report) {
|
||||
if (!report) {
|
||||
return undefined;
|
||||
}
|
||||
return report.sortieRecommendation ?? this.campaign?.battleHistory[report.battleId]?.sortieRecommendation;
|
||||
}
|
||||
|
||||
private reportFormationSourcePresetIds(snapshot: CampaignSortiePerformanceSnapshot) {
|
||||
const presets = this.sortieFormationPresets();
|
||||
return campaignSortiePresetIds.filter((presetId) => sortiePerformanceMatchesPreset(snapshot, presets[presetId]));
|
||||
@@ -19271,6 +19371,8 @@ export class CampScene extends Phaser.Scene {
|
||||
this.reportFormationReviewObjects.forEach((object) => object.destroy());
|
||||
this.reportFormationReviewObjects = [];
|
||||
this.reportFormationReviewView = undefined;
|
||||
const recommendationOutcome = this.reportSortieRecommendationOutcome(review, report);
|
||||
const recommendationSnapshot = this.reportSortieRecommendationSnapshot(report);
|
||||
const depth = 52;
|
||||
const width = 980;
|
||||
const height = 420;
|
||||
@@ -19337,12 +19439,19 @@ export class CampScene extends Phaser.Scene {
|
||||
});
|
||||
|
||||
const analysisY = y + 184;
|
||||
this.renderReportFormationAnalysisPanel(
|
||||
'전술 판단',
|
||||
[
|
||||
`강점 · ${review.strengths.join(' · ')}`,
|
||||
`보완 · ${this.compactText(review.improvement, 62)}`
|
||||
],
|
||||
const recommendationPanel = this.renderReportFormationAnalysisPanel(
|
||||
recommendationOutcome && recommendationSnapshot
|
||||
? `추천안 검증 · ${recommendationSnapshot.label}`
|
||||
: '전술 판단',
|
||||
recommendationOutcome
|
||||
? [
|
||||
`${recommendationOutcome.statusLabel} ${recommendationOutcome.score}점 · ${recommendationOutcome.kpis.map((kpi) => `${kpi.label} ${kpi.status === 'hit' ? '적중' : kpi.status === 'partial' ? '부분' : '미달'}`).join(' · ')}`,
|
||||
`다음 조정 · ${this.compactText(recommendationOutcome.improvement, 58)}`
|
||||
]
|
||||
: [
|
||||
`강점 · ${review.strengths.join(' · ')}`,
|
||||
`보완 · ${this.compactText(review.improvement, 62)}`
|
||||
],
|
||||
x + 18,
|
||||
analysisY,
|
||||
596,
|
||||
@@ -19354,7 +19463,7 @@ export class CampScene extends Phaser.Scene {
|
||||
'역할·공명 기여',
|
||||
[
|
||||
`${sortiePerformanceRoleContributionLine(review)} · 역할 ${review.roleCoverage}/3`,
|
||||
`공명 ${review.activeBondCount}개 · 연장 ${review.contributionTotals.extendedBondTriggers}회 · 파훼 ${review.contributionTotals.counterplays}`
|
||||
`공명 ${review.activeBondCount}개 · 추격 ${review.contributionTotals.extendedBondTriggers}회 · 파훼 ${review.contributionTotals.counterplays}`
|
||||
],
|
||||
x + 624,
|
||||
analysisY,
|
||||
@@ -19365,35 +19474,61 @@ export class CampScene extends Phaser.Scene {
|
||||
);
|
||||
|
||||
const rosterY = y + 264;
|
||||
const hiddenUnitCount = Math.max(0, review.snapshot.selectedUnitIds.length - 8);
|
||||
const contributionByUnitId = new Map(recommendationOutcome?.unitContributions.map((entry) => [entry.unitId, entry]) ?? []);
|
||||
const actualUnitIds = review.snapshot.selectedUnitIds;
|
||||
const displayUnitIds = sortieRecommendationDisplayUnitIds(actualUnitIds, recommendationOutcome?.unitContributions ?? []);
|
||||
const hiddenUnitCount = Math.max(0, displayUnitIds.length - 8);
|
||||
this.trackReportFormationReview(
|
||||
this.add.text(
|
||||
x + 18,
|
||||
rosterY,
|
||||
`출전 명단·역할${hiddenUnitCount > 0 ? ` · 외 ${hiddenUnitCount}명` : ''}`,
|
||||
`${recommendationOutcome ? '추천 무장별 판정' : '출전 명단·역할'}${hiddenUnitCount > 0 ? ` · 외 ${hiddenUnitCount}명` : ''}`,
|
||||
this.textStyle(13, '#f2e3bf', true)
|
||||
)
|
||||
).setDepth(depth + 2);
|
||||
const visibleUnitIds = review.snapshot.selectedUnitIds.slice(0, 8);
|
||||
const visibleUnitIds = displayUnitIds.slice(0, 8);
|
||||
const recommendationRows: ReportFormationReviewView['recommendationRows'] = [];
|
||||
const chipGap = 6;
|
||||
const chipWidth = Math.floor((width - 36 - chipGap * Math.max(0, visibleUnitIds.length - 1)) / Math.max(1, visibleUnitIds.length));
|
||||
const settlement = this.campaign?.battleHistory[report.battleId];
|
||||
visibleUnitIds.forEach((unitId, index) => {
|
||||
const settledUnit = settlement?.units.find((unit) => unit.unitId === unitId);
|
||||
const reportUnit = report.units.find((unit) => unit.id === unitId);
|
||||
const hp = settledUnit?.hp ?? reportUnit?.hp ?? 0;
|
||||
const maxHp = settledUnit?.maxHp ?? reportUnit?.maxHp ?? 0;
|
||||
const role = review.snapshot.formationAssignments[unitId];
|
||||
const hp = reportUnit?.hp ?? settledUnit?.hp ?? 0;
|
||||
const maxHp = reportUnit?.maxHp ?? settledUnit?.maxHp ?? 0;
|
||||
const contribution = contributionByUnitId.get(unitId);
|
||||
const role = contribution?.role ?? review.snapshot.formationAssignments[unitId];
|
||||
const roleLabel = role === 'front' ? '전' : role === 'flank' ? '돌' : role === 'support' ? '후' : '예';
|
||||
const statusColor = contribution
|
||||
? this.reportSortieRecommendationOutcomeColor(contribution.status)
|
||||
: recommendationOutcome
|
||||
? { label: '변경', tone: palette.red, text: '#ffb6a6' }
|
||||
: undefined;
|
||||
const chipX = x + 18 + index * (chipWidth + chipGap);
|
||||
const chip = this.trackReportFormationReview(this.add.rectangle(chipX, rosterY + 23, chipWidth, 38, hp > 0 ? 0x172a22 : 0x2a1b18, 0.94));
|
||||
chip.setOrigin(0);
|
||||
chip.setDepth(depth + 2);
|
||||
chip.setStrokeStyle(1, hp > 0 ? palette.green : palette.red, 0.58);
|
||||
this.trackReportFormationReview(this.add.text(chipX + 7, rosterY + 29, `${roleLabel} ${this.compactText(reportUnit?.name ?? this.unitName(unitId), 7)}`, this.textStyle(10, '#f2e3bf', true)))
|
||||
.setDepth(depth + 3);
|
||||
this.trackReportFormationReview(this.add.text(chipX + 7, rosterY + 45, `HP ${hp}/${maxHp}`, this.textStyle(9, hp > 0 ? '#a8ffd0' : '#ffb6a6', true)))
|
||||
chip.setStrokeStyle(1, statusColor?.tone ?? (hp > 0 ? palette.green : palette.red), 0.68);
|
||||
const nameText = this.trackReportFormationReview(this.add.text(chipX + 7, rosterY + 29, `${roleLabel} ${this.compactText(reportUnit?.name ?? this.unitName(unitId), 7)}`, {
|
||||
...this.textStyle(10, '#f2e3bf', true),
|
||||
fixedWidth: chipWidth - 14
|
||||
}))
|
||||
.setDepth(depth + 3);
|
||||
const metric = contribution ? this.reportSortieRecommendationContributionCompact(contribution) : recommendationOutcome ? '추천 외' : undefined;
|
||||
const statusText = this.trackReportFormationReview(this.add.text(
|
||||
chipX + 7,
|
||||
rosterY + 45,
|
||||
recommendationOutcome
|
||||
? `${statusColor?.label} · ${metric}`
|
||||
: `HP ${hp}/${maxHp}`,
|
||||
{
|
||||
...this.textStyle(8, statusColor?.text ?? (hp > 0 ? '#a8ffd0' : '#ffb6a6'), true),
|
||||
fixedWidth: chipWidth - 14
|
||||
}
|
||||
)).setDepth(depth + 3);
|
||||
if (recommendationOutcome) {
|
||||
recommendationRows.push({ unitId, background: chip, nameText, statusText });
|
||||
}
|
||||
});
|
||||
|
||||
const presetTitleY = y + 337;
|
||||
@@ -19440,7 +19575,12 @@ export class CampScene extends Phaser.Scene {
|
||||
this.trackReportFormationReview(this.add.text(x + 18, y + height - 3, this.compactText(feedback, 78), this.textStyle(10, feedbackColor, true)))
|
||||
.setOrigin(0, 1)
|
||||
.setDepth(depth + 2);
|
||||
this.reportFormationReviewView = { closeButton, presetButtons };
|
||||
this.reportFormationReviewView = {
|
||||
closeButton,
|
||||
presetButtons,
|
||||
recommendationPanel: recommendationOutcome ? recommendationPanel : undefined,
|
||||
recommendationRows
|
||||
};
|
||||
}
|
||||
|
||||
private hideReportFormationReview(resetFeedback = true) {
|
||||
@@ -19504,6 +19644,22 @@ export class CampScene extends Phaser.Scene {
|
||||
.setDepth(depth + 1);
|
||||
}
|
||||
|
||||
private reportSortieRecommendationOutcomeColor(status: SortieRecommendationOutcome['status']) {
|
||||
if (status === 'hit') {
|
||||
return { label: '적중', tone: palette.green, text: '#a8ffd0' };
|
||||
}
|
||||
if (status === 'partial') {
|
||||
return { label: '부분', tone: palette.gold, text: '#ffdf7b' };
|
||||
}
|
||||
return { label: '미달', tone: palette.red, text: '#ffb6a6' };
|
||||
}
|
||||
|
||||
private reportSortieRecommendationContributionCompact(
|
||||
contribution: SortieRecommendationOutcome['unitContributions'][number]
|
||||
) {
|
||||
return sortieRecommendationContributionCompactLabel(contribution);
|
||||
}
|
||||
|
||||
private renderReportFormationAnalysisPanel(
|
||||
title: string,
|
||||
lines: string[],
|
||||
@@ -19523,6 +19679,7 @@ export class CampScene extends Phaser.Scene {
|
||||
this.trackReportFormationReview(this.add.text(x + 12, y + 30 + index * 18, this.compactText(line, width > 500 ? 72 : 42), this.textStyle(10, index === 0 ? '#d4dce6' : '#ffdf7b', true)))
|
||||
.setDepth(depth + 1);
|
||||
});
|
||||
return background;
|
||||
}
|
||||
|
||||
private addReportFormationReviewButton(
|
||||
@@ -21574,7 +21731,7 @@ export class CampScene extends Phaser.Scene {
|
||||
return true;
|
||||
}
|
||||
|
||||
private persistSortieSelection() {
|
||||
private persistSortieSelection(recommendationSelection?: CampaignSortieRecommendationSnapshot) {
|
||||
this.reportFormationHistoryUndoState = undefined;
|
||||
this.sortieSwapUndoState = undefined;
|
||||
this.sortiePinnedSwapCandidateUnitId = undefined;
|
||||
@@ -21591,6 +21748,17 @@ export class CampScene extends Phaser.Scene {
|
||||
campaign.selectedSortieUnitIds = [...this.selectedSortieUnitIds];
|
||||
campaign.sortieFormationAssignments = { ...this.sortieFormationAssignments };
|
||||
campaign.sortieItemAssignments = this.cloneSortieItemAssignments(this.sortieItemAssignments);
|
||||
const activeRecommendation = recommendationSelection ?? campaign.sortieRecommendationSelection;
|
||||
if (this.sortieRecommendationSelectionMatchesCurrent(activeRecommendation)) {
|
||||
campaign.sortieRecommendationSelection = {
|
||||
...activeRecommendation!,
|
||||
selectedUnitIds: [...activeRecommendation!.selectedUnitIds],
|
||||
formationAssignments: { ...activeRecommendation!.formationAssignments },
|
||||
unitReasons: { ...activeRecommendation!.unitReasons }
|
||||
};
|
||||
} else {
|
||||
delete campaign.sortieRecommendationSelection;
|
||||
}
|
||||
this.campaign = saveCampaignState(campaign);
|
||||
}
|
||||
|
||||
@@ -21948,6 +22116,10 @@ export class CampScene extends Phaser.Scene {
|
||||
const sortieOrderSummaries = this.sortieOrderSummaries();
|
||||
const sortieOrderBattleResults = sortieScenario ? this.campaign?.sortieOrderHistory[sortieScenario.id] : undefined;
|
||||
const reportFormationReview = this.reportFormationReview();
|
||||
const reportRecommendationOutcome = reportFormationReview
|
||||
? this.reportSortieRecommendationOutcome(reportFormationReview)
|
||||
: undefined;
|
||||
const reportRecommendationSnapshot = this.reportSortieRecommendationSnapshot();
|
||||
const reportFormationMatchingPresetIds = reportFormationReview
|
||||
? this.reportFormationSourcePresetIds(reportFormationReview.snapshot)
|
||||
: [];
|
||||
@@ -22027,6 +22199,7 @@ export class CampScene extends Phaser.Scene {
|
||||
};
|
||||
})
|
||||
},
|
||||
sortieRecommendationSelection: this.currentSortieRecommendationSelection() ?? null,
|
||||
reportOperationOrder: reportSortieOrder
|
||||
? {
|
||||
available: true,
|
||||
@@ -22088,6 +22261,53 @@ export class CampScene extends Phaser.Scene {
|
||||
matchingPresetIds: [...reportFormationMatchingPresetIds],
|
||||
overwriteConfirmId: this.reportFormationPresetOverwriteConfirmId ?? null,
|
||||
feedback: this.reportFormationReviewFeedback,
|
||||
recommendationFeedback: reportRecommendationOutcome && reportRecommendationSnapshot
|
||||
? {
|
||||
planId: reportRecommendationOutcome.planId,
|
||||
label: reportRecommendationSnapshot.label,
|
||||
status: reportRecommendationOutcome.status,
|
||||
statusLabel: reportRecommendationOutcome.statusLabel,
|
||||
score: reportRecommendationOutcome.score,
|
||||
summary: reportRecommendationOutcome.summary,
|
||||
improvement: reportRecommendationOutcome.improvement,
|
||||
kpis: reportRecommendationOutcome.kpis.map((kpi) => ({ ...kpi })),
|
||||
counts: {
|
||||
hit: reportRecommendationOutcome.unitContributions.filter((entry) => entry.status === 'hit').length,
|
||||
partial: reportRecommendationOutcome.unitContributions.filter((entry) => entry.status === 'partial').length,
|
||||
miss: reportRecommendationOutcome.unitContributions.filter((entry) => entry.status === 'miss').length
|
||||
},
|
||||
missingUnitIds: reportRecommendationOutcome.unitContributions.filter((entry) => !entry.deployed).map((entry) => entry.unitId),
|
||||
unexpectedUnitIds: (reportFormationReview?.snapshot.selectedUnitIds ?? []).filter(
|
||||
(unitId) => !reportRecommendationOutcome.unitContributions.some((entry) => entry.unitId === unitId)
|
||||
),
|
||||
displayedUnitIds: this.reportFormationReviewView?.recommendationRows.map((row) => row.unitId) ?? [],
|
||||
panelBounds: this.sortieObjectBoundsDebug(this.reportFormationReviewView?.recommendationPanel),
|
||||
rows: reportRecommendationOutcome.unitContributions.map((entry) => {
|
||||
const rowView = this.reportFormationReviewView?.recommendationRows.find((row) => row.unitId === entry.unitId);
|
||||
const settlementUnit = this.report && this.campaign?.battleHistory[this.report.battleId]?.units.find((unit) => unit.unitId === entry.unitId);
|
||||
const reportUnit = this.report?.units.find((unit) => unit.id === entry.unitId);
|
||||
return {
|
||||
unitId: entry.unitId,
|
||||
name: reportUnit?.name ?? settlementUnit?.name ?? this.unitName(entry.unitId),
|
||||
role: entry.role,
|
||||
actualRole: entry.actualRole ?? null,
|
||||
fullReason: entry.reason,
|
||||
status: entry.status,
|
||||
score: entry.score,
|
||||
headline: entry.headline,
|
||||
metrics: entry.metrics.map((metric) => ({ ...metric })),
|
||||
strategyUses: entry.strategyUses,
|
||||
signatureStrategyUses: entry.signatureStrategyUses,
|
||||
hp: reportUnit?.hp ?? settlementUnit?.hp ?? 0,
|
||||
maxHp: reportUnit?.maxHp ?? settlementUnit?.maxHp ?? 0,
|
||||
cardBounds: this.sortieObjectBoundsDebug(rowView?.background),
|
||||
nameBounds: this.sortieTextBoundsDebug(rowView?.nameText),
|
||||
statusBounds: this.sortieTextBoundsDebug(rowView?.statusText),
|
||||
displayText: rowView?.statusText.text ?? null
|
||||
};
|
||||
})
|
||||
}
|
||||
: null,
|
||||
toggleButtonBounds: this.sortieObjectBoundsDebug(this.reportFormationReviewToggleButton),
|
||||
closeButtonBounds: this.sortieObjectBoundsDebug(this.reportFormationReviewView?.closeButton),
|
||||
presetCards: sortiePresetDefinitions.map((definition) => ({
|
||||
@@ -22107,6 +22327,7 @@ export class CampScene extends Phaser.Scene {
|
||||
matchingPresetIds: [],
|
||||
overwriteConfirmId: null,
|
||||
feedback: '',
|
||||
recommendationFeedback: null,
|
||||
toggleButtonBounds: null,
|
||||
closeButtonBounds: null,
|
||||
presetCards: []
|
||||
|
||||
Reference in New Issue
Block a user