feat: preview sortie lineup swaps
This commit is contained in:
@@ -337,6 +337,46 @@ type SortieUnitSynergyPreview = {
|
||||
comparison?: SortieSynergyComparison;
|
||||
};
|
||||
|
||||
type SortieComparisonMetric = {
|
||||
key: 'attack' | 'intelligence' | 'move' | 'terrain';
|
||||
label: string;
|
||||
current: number;
|
||||
projected: number;
|
||||
delta: number;
|
||||
};
|
||||
|
||||
type SortieFormationComparisonPreview = {
|
||||
source: 'focus' | 'hover-add' | 'hover-swap' | 'hover-blocked';
|
||||
mode: SortieUnitSynergyPreview['mode'] | 'swap';
|
||||
unitId: string;
|
||||
incomingUnitId: string | null;
|
||||
incomingUnitName: string | null;
|
||||
outgoingUnitId: string | null;
|
||||
outgoingUnitName: string | null;
|
||||
selectedUnitIds: string[];
|
||||
projectedSelectedUnitIds: string[];
|
||||
headline: string;
|
||||
detail: string;
|
||||
metrics: SortieComparisonMetric[];
|
||||
current: SortieSynergySnapshot;
|
||||
projected?: SortieSynergySnapshot;
|
||||
comparison?: SortieSynergyComparison;
|
||||
};
|
||||
|
||||
type SortieComparisonPanelView = {
|
||||
background: Phaser.GameObjects.Rectangle;
|
||||
title: Phaser.GameObjects.Text;
|
||||
summary: Phaser.GameObjects.Text;
|
||||
headline: Phaser.GameObjects.Text;
|
||||
metrics: {
|
||||
background: Phaser.GameObjects.Rectangle;
|
||||
label: Phaser.GameObjects.Text;
|
||||
value: Phaser.GameObjects.Text;
|
||||
}[];
|
||||
impact: Phaser.GameObjects.Text;
|
||||
detail: Phaser.GameObjects.Text;
|
||||
};
|
||||
|
||||
type CampaignTimelineChapter = {
|
||||
id: string;
|
||||
title: string;
|
||||
@@ -10784,6 +10824,7 @@ export class CampScene extends Phaser.Scene {
|
||||
private sortieItemAssignments: CampaignSortieItemAssignments = {};
|
||||
private sortieFocusedUnitId = 'liu-bei';
|
||||
private sortieHoveredUnitId?: string;
|
||||
private sortieComparisonPanelView?: SortieComparisonPanelView;
|
||||
private sortieRosterScroll = 0;
|
||||
private sortiePlanFeedback = '';
|
||||
private sortiePrepStep: SortiePrepStep = 'briefing';
|
||||
@@ -10804,6 +10845,7 @@ export class CampScene extends Phaser.Scene {
|
||||
this.contentObjects = [];
|
||||
this.dialogueObjects = [];
|
||||
this.sortieObjects = [];
|
||||
this.sortieComparisonPanelView = undefined;
|
||||
this.saveSlotObjects = [];
|
||||
this.saveSlotConfirmObjects = [];
|
||||
this.pendingSaveSlot = undefined;
|
||||
@@ -12762,6 +12804,7 @@ export class CampScene extends Phaser.Scene {
|
||||
);
|
||||
card.on('pointerover', () => {
|
||||
this.sortieHoveredUnitId = unit.id;
|
||||
this.refreshCampaignSortieComparisonPanel();
|
||||
card.setFillStyle(!availability.available ? 0x15191e : selected ? 0x20362a : 0x1d2731, 0.98);
|
||||
card.setStrokeStyle(2, palette.gold, 0.98);
|
||||
accent.setFillStyle(palette.gold, 0.98);
|
||||
@@ -12774,6 +12817,7 @@ export class CampScene extends Phaser.Scene {
|
||||
if (this.sortieHoveredUnitId === unit.id) {
|
||||
this.sortieHoveredUnitId = undefined;
|
||||
}
|
||||
this.refreshCampaignSortieComparisonPanel();
|
||||
card.setFillStyle(fill, !availability.available ? 0.62 : 0.96);
|
||||
card.setStrokeStyle(restingStrokeWidth, availability.available ? accentColor : 0x53606c, restingStrokeAlpha);
|
||||
accent.setFillStyle(accentColor, focused || selected ? 0.94 : recommendation ? 0.72 : 0.34);
|
||||
@@ -12822,46 +12866,170 @@ export class CampScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
private renderCampaignSortieComparisonPanel(x: number, y: number, width: number, height: number, depth: number) {
|
||||
const focused = this.sortieFocusedUnit();
|
||||
const synergy = this.sortieSynergySnapshot();
|
||||
const plan = this.sortiePlanSummary();
|
||||
const bg = this.trackSortie(this.add.rectangle(x, y, width, height, 0x151f2a, 0.96));
|
||||
bg.setOrigin(0);
|
||||
bg.setDepth(depth);
|
||||
bg.setStrokeStyle(1, synergy.coveredRoleCount === 3 ? palette.green : palette.gold, 0.58);
|
||||
this.trackSortie(this.add.text(x + 18, y + 12, focused ? `편성 변화 · ${focused.name}` : '편성 변화', this.textStyle(16, '#f2e3bf', true))).setDepth(depth + 1);
|
||||
this.trackSortie(
|
||||
this.add.text(
|
||||
x + width - 18,
|
||||
y + 14,
|
||||
`공명 ${synergy.activeBondCount} · 역할 ${synergy.coveredRoleCount}/3 · 지형 ${synergy.terrainGrade}`,
|
||||
this.textStyle(10, '#d8b15f', true)
|
||||
)
|
||||
).setOrigin(1, 0).setDepth(depth + 1);
|
||||
const title = this.trackSortie(this.add.text(x + 18, y + 9, '편성 변화', this.textStyle(15, '#f2e3bf', true)));
|
||||
title.setDepth(depth + 1);
|
||||
const summary = this.trackSortie(this.add.text(x + width - 18, y + 11, '', this.textStyle(9, '#d8b15f', true)));
|
||||
summary.setOrigin(1, 0);
|
||||
summary.setDepth(depth + 1);
|
||||
const headline = this.trackSortie(this.add.text(x + 18, y + 31, '', this.textStyle(11, '#c8d2dd', true)));
|
||||
headline.setDepth(depth + 1);
|
||||
|
||||
if (!focused) {
|
||||
this.trackSortie(this.add.text(x + 18, y + 44, '왼쪽 초상 카드를 선택하면 합류·해제 전후가 표시됩니다.', this.textStyle(12, '#9fb0bf'))).setDepth(depth + 1);
|
||||
const metricGap = 6;
|
||||
const metricWidth = Math.floor((width - 36 - metricGap * 3) / 4);
|
||||
const metricLabels: SortieComparisonMetric['label'][] = ['공격', '지력', '기동', '지형'];
|
||||
const metrics = metricLabels.map((label, index) => {
|
||||
const metricX = x + 18 + index * (metricWidth + metricGap);
|
||||
const background = this.trackSortie(this.add.rectangle(metricX, y + 51, metricWidth, 32, 0x111922, 0.9));
|
||||
background.setOrigin(0);
|
||||
background.setDepth(depth + 1);
|
||||
background.setStrokeStyle(1, 0x53606c, 0.34);
|
||||
const labelText = this.trackSortie(this.add.text(metricX + 7, y + 55, label, this.textStyle(8, '#9fb0bf', true)));
|
||||
labelText.setDepth(depth + 2);
|
||||
const value = this.trackSortie(this.add.text(metricX + metricWidth - 7, y + 65, '-', this.textStyle(10, '#c8d2dd', true)));
|
||||
value.setOrigin(1, 0);
|
||||
value.setDepth(depth + 2);
|
||||
return { background, label: labelText, value };
|
||||
});
|
||||
const impact = this.trackSortie(this.add.text(x + 18, y + 89, '', this.textStyle(10, '#d4dce6', true)));
|
||||
impact.setDepth(depth + 1);
|
||||
const detail = this.trackSortie(this.add.text(x + 18, y + 106, '', this.textStyle(9, '#9fb0bf')));
|
||||
detail.setDepth(depth + 1);
|
||||
|
||||
this.sortieComparisonPanelView = {
|
||||
background: bg,
|
||||
title,
|
||||
summary,
|
||||
headline,
|
||||
metrics,
|
||||
impact,
|
||||
detail
|
||||
};
|
||||
this.refreshCampaignSortieComparisonPanel();
|
||||
}
|
||||
|
||||
private refreshCampaignSortieComparisonPanel() {
|
||||
const view = this.sortieComparisonPanelView;
|
||||
if (!view) {
|
||||
return;
|
||||
}
|
||||
const preview = this.sortieUnitSynergyPreview(focused);
|
||||
const previewColor = preview.mode === 'remove'
|
||||
? '#ff9d7d'
|
||||
: preview.mode === 'add' || preview.mode === 'current'
|
||||
? '#a8ffd0'
|
||||
|
||||
const preview = this.sortieFormationComparisonPreview();
|
||||
const current = preview?.current ?? this.sortieSynergySnapshot();
|
||||
const projected = preview?.projected ?? current;
|
||||
const showMetricTransition = Boolean(preview?.projected);
|
||||
const comparison = preview?.comparison;
|
||||
const isHoverPreview = preview?.source === 'hover-add' || preview?.source === 'hover-swap';
|
||||
const lostRole = Boolean(comparison?.lostRoles.length);
|
||||
const completeFormation = projected.coveredRoleCount === coreSortieSynergyRoles.length;
|
||||
view.background.setStrokeStyle(
|
||||
lostRole ? 2 : 1,
|
||||
lostRole ? palette.red : isHoverPreview ? palette.gold : completeFormation ? palette.green : palette.gold,
|
||||
lostRole ? 0.78 : isHoverPreview ? 0.72 : 0.58
|
||||
);
|
||||
|
||||
if (!preview) {
|
||||
view.title.setText('편성 변화');
|
||||
view.summary.setText(`공명 ${current.activeBondCount} · 역할 ${current.coveredRoleCount}/3 · 지형 ${current.terrainGrade}`);
|
||||
view.headline.setText('출전 무장을 클릭해 교체 기준으로 고정하십시오.').setColor('#9fb0bf');
|
||||
view.metrics.forEach((metric) => {
|
||||
metric.background.setFillStyle(0x111922, 0.9).setStrokeStyle(1, 0x53606c, 0.34);
|
||||
metric.value.setText('-').setColor('#77818c');
|
||||
});
|
||||
view.impact.setText('후보 초상에 마우스를 올리면 역할·공명 변화가 표시됩니다.').setColor('#d4dce6');
|
||||
view.detail.setText('미리보기는 실제 출전 명단과 보급을 변경하지 않습니다.').setColor('#9fb0bf');
|
||||
return;
|
||||
}
|
||||
|
||||
const title = preview.source === 'hover-swap'
|
||||
? `가상 교체 · ${preview.outgoingUnitName} → ${preview.incomingUnitName}`
|
||||
: preview.source === 'hover-add'
|
||||
? `가상 합류 · ${preview.incomingUnitName}`
|
||||
: `편성 변화 · ${this.unitName(preview.unitId)}`;
|
||||
view.title.setText(this.compactText(title, 28));
|
||||
view.summary.setText(
|
||||
this.compactText(
|
||||
`공명 ${current.activeBondCount}→${projected.activeBondCount} · 역할 ${current.coveredRoleCount}/3→${projected.coveredRoleCount}/3 · 지형 ${projected.terrainGrade}`,
|
||||
38
|
||||
)
|
||||
);
|
||||
const headlineColor = preview.source === 'hover-swap' || preview.mode === 'add'
|
||||
? '#a8ffd0'
|
||||
: preview.mode === 'remove'
|
||||
? '#ff9d7d'
|
||||
: preview.mode === 'waiting'
|
||||
? '#ffdf7b'
|
||||
: '#87919c';
|
||||
this.trackSortie(this.add.text(x + 18, y + 39, this.compactText(preview.headline, 54), this.textStyle(12, previewColor, true))).setDepth(depth + 1);
|
||||
this.trackSortie(this.add.text(x + 18, y + 62, this.compactText(preview.detail, 66), this.textStyle(10, '#c8d2dd'))).setDepth(depth + 1);
|
||||
const feedback = this.sortiePlanFeedback || plan.warningLine;
|
||||
this.trackSortie(
|
||||
this.add.text(
|
||||
x + 18,
|
||||
y + height - 27,
|
||||
this.compactText(feedback, 64),
|
||||
this.textStyle(10, this.sortiePlanFeedback ? '#a8ffd0' : plan.warnings.length > 0 ? '#ffdf7b' : '#9fb0bf', true)
|
||||
: preview.mode === 'blocked'
|
||||
? '#87919c'
|
||||
: '#d4dce6';
|
||||
view.headline.setText(this.compactText(preview.headline, 64)).setColor(headlineColor);
|
||||
|
||||
view.metrics.forEach((metricView, index) => {
|
||||
const metric = preview.metrics[index];
|
||||
if (!metric) {
|
||||
metricView.background.setFillStyle(0x111922, 0.9).setStrokeStyle(1, 0x53606c, 0.34);
|
||||
metricView.value.setText('-').setColor('#77818c');
|
||||
return;
|
||||
}
|
||||
const positive = metric.delta > 0;
|
||||
const negative = metric.delta < 0;
|
||||
const tone = positive ? palette.green : negative ? palette.red : 0x53606c;
|
||||
const fill = positive ? 0x172a22 : negative ? 0x2a1b18 : 0x111922;
|
||||
const color = positive ? '#a8ffd0' : negative ? '#ff9d7d' : '#c8d2dd';
|
||||
const delta = metric.delta > 0 ? `+${metric.delta}` : `${metric.delta}`;
|
||||
metricView.label.setText(metric.label);
|
||||
metricView.background.setFillStyle(fill, 0.92).setStrokeStyle(1, tone, positive || negative ? 0.58 : 0.34);
|
||||
metricView.value
|
||||
.setText(showMetricTransition ? `${metric.current}→${metric.projected} ${delta}` : `${metric.current}`)
|
||||
.setColor(color);
|
||||
});
|
||||
|
||||
if (!comparison) {
|
||||
view.impact
|
||||
.setText(this.compactText(preview.detail, 68))
|
||||
.setColor(preview.mode === 'blocked' ? '#87919c' : preview.mode === 'waiting' ? '#ffdf7b' : '#d4dce6');
|
||||
view.detail
|
||||
.setText('미리보기 · 실제 출전 명단·역할·보급은 변경되지 않습니다.')
|
||||
.setColor('#9fb0bf');
|
||||
return;
|
||||
}
|
||||
|
||||
const gainedRoleLabels = comparison?.gainedRoles.map((role) => this.sortieFormationRoleLabel(role)) ?? [];
|
||||
const lostRoleLabels = comparison?.lostRoles.map((role) => this.sortieFormationRoleLabel(role)) ?? [];
|
||||
const roleChanges = [
|
||||
lostRoleLabels.length > 0 ? `공백 ${lostRoleLabels.join('·')}` : '',
|
||||
gainedRoleLabels.length > 0 ? `보강 ${gainedRoleLabels.join('·')}` : ''
|
||||
].filter(Boolean);
|
||||
const roleChange = roleChanges.join(' · ') || '역할 유지';
|
||||
const bondDelta = comparison?.activeBondDelta ?? 0;
|
||||
const signedBondDelta = bondDelta > 0 ? `+${bondDelta}` : `${bondDelta}`;
|
||||
view.impact
|
||||
.setText(
|
||||
this.compactText(
|
||||
`역할 ${current.coveredRoleCount}/3→${projected.coveredRoleCount}/3 · 공명 ${current.activeBondCount}→${projected.activeBondCount} (${signedBondDelta}) · ${roleChange}`,
|
||||
68
|
||||
)
|
||||
)
|
||||
).setDepth(depth + 1);
|
||||
.setColor(lostRoleLabels.length > 0 ? '#ff9d7d' : gainedRoleLabels.length > 0 || bondDelta > 0 ? '#a8ffd0' : '#d4dce6');
|
||||
|
||||
const gainedBonds = comparison?.gainedBonds.map((bond) => `+${bond.title}`) ?? [];
|
||||
const lostBonds = comparison?.lostBonds.map((bond) => `-${bond.title}`) ?? [];
|
||||
const bondLine = [gainedBonds[0], lostBonds[0], ...gainedBonds.slice(1), ...lostBonds.slice(1)]
|
||||
.filter((bond): bond is string => Boolean(bond))
|
||||
.slice(0, 2)
|
||||
.join(' · ') || '공명 변화 없음';
|
||||
const terrainDelta = projected.terrainScore - current.terrainScore;
|
||||
const signedTerrainDelta = terrainDelta > 0 ? `+${terrainDelta}` : `${terrainDelta}`;
|
||||
view.detail
|
||||
.setText(
|
||||
this.compactText(
|
||||
`${bondLine} · 지형 ${current.terrainGrade} ${current.terrainScore}→${projected.terrainGrade} ${projected.terrainScore} (${signedTerrainDelta}) · 실제 편성 미변경`,
|
||||
82
|
||||
)
|
||||
)
|
||||
.setColor(isHoverPreview ? '#ffdf7b' : '#9fb0bf');
|
||||
}
|
||||
|
||||
private renderFirstSortieRoleDiagram(x: number, y: number, width: number, height: number, depth: number) {
|
||||
@@ -13965,6 +14133,160 @@ export class CampScene extends Phaser.Scene {
|
||||
});
|
||||
}
|
||||
|
||||
private sortieFormationComparisonPreview(): SortieFormationComparisonPreview | undefined {
|
||||
const scenario = this.nextSortieScenario();
|
||||
const selectedUnitIds = [...this.selectedSortieUnitIds];
|
||||
const selectedIds = new Set(selectedUnitIds);
|
||||
const current = this.sortieSynergySnapshot(selectedUnitIds, scenario);
|
||||
const focused = this.sortieFocusedUnit();
|
||||
const hovered = this.sortieHoveredUnitId
|
||||
? this.sortieRosterUnits().find((unit) => unit.id === this.sortieHoveredUnitId)
|
||||
: undefined;
|
||||
|
||||
if (hovered && hovered.id !== focused?.id && !selectedIds.has(hovered.id)) {
|
||||
const availability = this.sortieUnitAvailability(hovered, scenario);
|
||||
if (!availability.available) {
|
||||
return {
|
||||
source: 'hover-blocked',
|
||||
mode: 'blocked',
|
||||
unitId: hovered.id,
|
||||
incomingUnitId: hovered.id,
|
||||
incomingUnitName: hovered.name,
|
||||
outgoingUnitId: null,
|
||||
outgoingUnitName: null,
|
||||
selectedUnitIds,
|
||||
projectedSelectedUnitIds: selectedUnitIds,
|
||||
headline: `${hovered.name} 편성 불가 · ${availability.reason}`,
|
||||
detail: '현재 출전 명단은 유지됩니다.',
|
||||
metrics: this.sortieComparisonMetrics(hovered, hovered, scenario),
|
||||
current
|
||||
};
|
||||
}
|
||||
|
||||
if (selectedUnitIds.length < this.sortieMaxUnits(scenario)) {
|
||||
const projectedSelectedUnitIds = [...selectedUnitIds, hovered.id];
|
||||
const projected = this.sortieSynergySnapshot(projectedSelectedUnitIds, scenario);
|
||||
const comparison = compareSortieSynergy(current, projected);
|
||||
return {
|
||||
source: 'hover-add',
|
||||
mode: 'add',
|
||||
unitId: hovered.id,
|
||||
incomingUnitId: hovered.id,
|
||||
incomingUnitName: hovered.name,
|
||||
outgoingUnitId: null,
|
||||
outgoingUnitName: null,
|
||||
selectedUnitIds,
|
||||
projectedSelectedUnitIds,
|
||||
headline: `IN ${hovered.name} · ${this.sortieFormationRoleLabel(this.sortieFormationRole(hovered, scenario))} 합류`,
|
||||
detail: `빈 출전 슬롯에 합류했을 때의 가상 결과입니다.`,
|
||||
metrics: this.sortieComparisonMetrics(undefined, hovered, scenario),
|
||||
current,
|
||||
projected,
|
||||
comparison
|
||||
};
|
||||
}
|
||||
|
||||
const outgoing = focused && selectedIds.has(focused.id) && !this.isRequiredSortieUnit(focused.id)
|
||||
? focused
|
||||
: undefined;
|
||||
if (!outgoing) {
|
||||
return {
|
||||
source: 'hover-blocked',
|
||||
mode: 'waiting',
|
||||
unitId: hovered.id,
|
||||
incomingUnitId: hovered.id,
|
||||
incomingUnitName: hovered.name,
|
||||
outgoingUnitId: null,
|
||||
outgoingUnitName: null,
|
||||
selectedUnitIds,
|
||||
projectedSelectedUnitIds: selectedUnitIds,
|
||||
headline: `교체 기준 필요 · 출전 중인 비필수 무장을 먼저 클릭하세요.`,
|
||||
detail: `${hovered.name} 후보는 아직 실제 편성에 반영되지 않았습니다.`,
|
||||
metrics: this.sortieComparisonMetrics(hovered, hovered, scenario),
|
||||
current
|
||||
};
|
||||
}
|
||||
|
||||
const projectedSelectedUnitIds = selectedUnitIds.map((unitId) => unitId === outgoing.id ? hovered.id : unitId);
|
||||
const projected = this.sortieSynergySnapshot(projectedSelectedUnitIds, scenario);
|
||||
const comparison = compareSortieSynergy(current, projected);
|
||||
const outgoingRole = this.sortieFormationRoleLabel(this.sortieFormationRole(outgoing, scenario));
|
||||
const incomingRole = this.sortieFormationRoleLabel(this.sortieFormationRole(hovered, scenario));
|
||||
return {
|
||||
source: 'hover-swap',
|
||||
mode: 'swap',
|
||||
unitId: hovered.id,
|
||||
incomingUnitId: hovered.id,
|
||||
incomingUnitName: hovered.name,
|
||||
outgoingUnitId: outgoing.id,
|
||||
outgoingUnitName: outgoing.name,
|
||||
selectedUnitIds,
|
||||
projectedSelectedUnitIds,
|
||||
headline: `OUT ${outgoing.name} ${outgoingRole} · IN ${hovered.name} ${incomingRole}`,
|
||||
detail: `${outgoing.name} 대신 ${hovered.name}을 투입했을 때의 가상 결과입니다.`,
|
||||
metrics: this.sortieComparisonMetrics(outgoing, hovered, scenario),
|
||||
current,
|
||||
projected,
|
||||
comparison
|
||||
};
|
||||
}
|
||||
|
||||
if (!focused) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const focusedPreview = this.sortieUnitSynergyPreview(focused);
|
||||
const focusedSelected = selectedIds.has(focused.id);
|
||||
const projected = focusedPreview.projected;
|
||||
return {
|
||||
source: 'focus',
|
||||
mode: focusedPreview.mode,
|
||||
unitId: focused.id,
|
||||
incomingUnitId: focusedSelected ? null : focused.id,
|
||||
incomingUnitName: focusedSelected ? null : focused.name,
|
||||
outgoingUnitId: focusedSelected && !this.isRequiredSortieUnit(focused.id) ? focused.id : null,
|
||||
outgoingUnitName: focusedSelected && !this.isRequiredSortieUnit(focused.id) ? focused.name : null,
|
||||
selectedUnitIds,
|
||||
projectedSelectedUnitIds: projected?.selectedUnitIds ?? selectedUnitIds,
|
||||
headline: focusedPreview.headline,
|
||||
detail: focusedPreview.detail,
|
||||
metrics: focusedPreview.mode === 'add'
|
||||
? this.sortieComparisonMetrics(undefined, focused, scenario)
|
||||
: focusedPreview.mode === 'remove'
|
||||
? this.sortieComparisonMetrics(focused, undefined, scenario)
|
||||
: this.sortieComparisonMetrics(focused, focused, scenario),
|
||||
current,
|
||||
projected,
|
||||
comparison: focusedPreview.comparison
|
||||
};
|
||||
}
|
||||
|
||||
private sortieComparisonMetrics(
|
||||
currentUnit: UnitData | undefined,
|
||||
projectedUnit: UnitData | undefined,
|
||||
scenario = this.nextSortieScenario()
|
||||
): SortieComparisonMetric[] {
|
||||
const currentAttack = currentUnit ? currentUnit.attack + this.equipmentAttackBonus(currentUnit) : 0;
|
||||
const projectedAttack = projectedUnit ? projectedUnit.attack + this.equipmentAttackBonus(projectedUnit) : 0;
|
||||
const values: Omit<SortieComparisonMetric, 'delta'>[] = [
|
||||
{ key: 'attack', label: '공격', current: currentAttack, projected: projectedAttack },
|
||||
{
|
||||
key: 'intelligence',
|
||||
label: '지력',
|
||||
current: currentUnit?.stats.intelligence ?? 0,
|
||||
projected: projectedUnit?.stats.intelligence ?? 0
|
||||
},
|
||||
{ key: 'move', label: '기동', current: currentUnit?.move ?? 0, projected: projectedUnit?.move ?? 0 },
|
||||
{
|
||||
key: 'terrain',
|
||||
label: '지형',
|
||||
current: currentUnit ? this.sortieTerrainScore(currentUnit, scenario) : 0,
|
||||
projected: projectedUnit ? this.sortieTerrainScore(projectedUnit, scenario) : 0
|
||||
}
|
||||
];
|
||||
return values.map((metric) => ({ ...metric, delta: metric.projected - metric.current }));
|
||||
}
|
||||
|
||||
private sortieUnitSynergyPreview(unit: UnitData): SortieUnitSynergyPreview {
|
||||
const scenario = this.nextSortieScenario();
|
||||
const current = this.sortieSynergySnapshot(this.selectedSortieUnitIds, scenario);
|
||||
@@ -15075,6 +15397,7 @@ export class CampScene extends Phaser.Scene {
|
||||
this.sortieObjects.forEach((object) => object.destroy());
|
||||
this.sortieObjects = [];
|
||||
this.sortieHoveredUnitId = undefined;
|
||||
this.sortieComparisonPanelView = undefined;
|
||||
}
|
||||
|
||||
private trackSortie<T extends Phaser.GameObjects.GameObject>(object: T) {
|
||||
@@ -16894,6 +17217,7 @@ export class CampScene extends Phaser.Scene {
|
||||
sortieDeploymentPreview: this.sortieDeploymentPreviewDebug(),
|
||||
sortieFocusedUnitId: this.sortieFocusedUnitId,
|
||||
sortieHoveredUnitId: this.sortieHoveredUnitId ?? null,
|
||||
sortieComparisonPreview: this.sortieFormationComparisonPreview() ?? null,
|
||||
sortieFocusedUnit: this.sortieFocusedUnitSummary(),
|
||||
sortiePlanFeedback: this.sortiePlanFeedback,
|
||||
sortieChecklist,
|
||||
|
||||
Reference in New Issue
Block a user