Polish Wuzhang ending QA flow
This commit is contained in:
@@ -466,7 +466,7 @@ const campaignTimelineChapters: CampaignTimelineChapter[] = [
|
||||
'sixty-fifth-battle-weishui-northbank',
|
||||
'sixty-sixth-battle-wuzhang-final'
|
||||
],
|
||||
nextHints: ['한중 창고 정비', '출전 무장 재편', '기산 출진로', '천수·가정 방면', '강유 합류', '진창 공성 재계산', '무도·음평 확보', '한중 우로 방어', '기산 재출정', '노성 추격', '위수 진영', '위수 북안']
|
||||
nextHints: ['한중 창고 정비', '출전 무장 재편', '기산 출진로', '천수·가정 방면', '강유 합류', '진창 공성 재계산', '무도·음평 확보', '한중 우로 방어', '기산 재출정', '노성 추격', '위수 진영', '위수 북안', '오장원 최종전']
|
||||
}
|
||||
];
|
||||
|
||||
@@ -10007,6 +10007,7 @@ export class CampScene extends Phaser.Scene {
|
||||
private visitedTabs = new Set<CampTab>();
|
||||
private selectedSortieUnitIds: string[] = [];
|
||||
private sortieFocusedUnitId = 'liu-bei';
|
||||
private sortieRosterScroll = 0;
|
||||
private terrainCountCache = new Map<BattleScenarioId, SortieTerrainCounts>();
|
||||
|
||||
constructor() {
|
||||
@@ -11260,16 +11261,35 @@ export class CampScene extends Phaser.Scene {
|
||||
const allies = this.sortieAllies();
|
||||
const selectedCount = this.selectedSortieUnitIds.length;
|
||||
const maxUnits = this.sortieMaxUnits();
|
||||
const denseRoster = allies.length > 12;
|
||||
const rowGap = denseRoster ? 17 : allies.length > 7 ? 30 : allies.length > 5 ? 38 : allies.length > 4 ? 43 : 48;
|
||||
const rowHeight = denseRoster ? 15 : allies.length > 7 ? 24 : rowGap - 6;
|
||||
const rosterView = this.sortieRosterViewState(allies.length, height);
|
||||
const { denseRoster, rowGap, rowHeight, listTopOffset, listFooterHeight, maxVisibleRows, maxScroll } = rosterView;
|
||||
this.sortieRosterScroll = Phaser.Math.Clamp(this.sortieRosterScroll, 0, maxScroll);
|
||||
const visibleAllies = allies.slice(this.sortieRosterScroll, this.sortieRosterScroll + maxVisibleRows);
|
||||
const handleRosterWheel = (
|
||||
_pointer: Phaser.Input.Pointer,
|
||||
_deltaX: number,
|
||||
deltaY: number,
|
||||
_deltaZ: number,
|
||||
event?: { stopPropagation: () => void }
|
||||
) => {
|
||||
event?.stopPropagation();
|
||||
const direction = Math.sign(deltaY);
|
||||
if (direction === 0 || maxScroll <= 0) {
|
||||
return;
|
||||
}
|
||||
this.updateSortieRosterScroll(this.sortieRosterScroll + direction * 3, maxScroll);
|
||||
};
|
||||
if (maxScroll > 0) {
|
||||
bg.setInteractive({ useHandCursor: false });
|
||||
bg.on('wheel', handleRosterWheel);
|
||||
}
|
||||
this.trackSortie(
|
||||
this.add.text(x + 18, y + 14, `출전 무장 선택 ${selectedCount}/${maxUnits}`, this.textStyle(18, '#f2e3bf', true))
|
||||
).setDepth(depth + 1);
|
||||
this.trackSortie(this.add.text(x + width - 18, y + 17, '클릭해서 분석 및 출전/대기 전환', this.textStyle(12, '#9fb0bf'))).setOrigin(1, 0).setDepth(depth + 1);
|
||||
|
||||
allies.forEach((unit, index) => {
|
||||
const rowY = y + (denseRoster ? 48 : 50) + index * rowGap;
|
||||
visibleAllies.forEach((unit, index) => {
|
||||
const rowY = y + listTopOffset + index * rowGap;
|
||||
const selected = this.isSortieSelected(unit.id);
|
||||
const required = this.isRequiredSortieUnit(unit.id);
|
||||
const recruited = !foundingSortieUnitIds.has(unit.id);
|
||||
@@ -11280,6 +11300,7 @@ export class CampScene extends Phaser.Scene {
|
||||
row.setDepth(depth + 1);
|
||||
row.setStrokeStyle(1, selected ? palette.green : 0x53606c, selected ? 0.62 : 0.38);
|
||||
row.setInteractive({ useHandCursor: true });
|
||||
row.on('wheel', handleRosterWheel);
|
||||
row.on('pointerover', () => {
|
||||
this.sortieFocusedUnitId = unit.id;
|
||||
});
|
||||
@@ -11303,6 +11324,63 @@ export class CampScene extends Phaser.Scene {
|
||||
this.trackSortie(this.add.text(x + 610, rowY + 18, this.compactText(summary.bondLine, 15), this.textStyle(11, selected ? '#a8ffd0' : '#68727e', selected))).setDepth(depth + 2);
|
||||
}
|
||||
});
|
||||
|
||||
if (maxScroll > 0) {
|
||||
const rangeEnd = this.sortieRosterScroll + visibleAllies.length;
|
||||
const info = this.trackSortie(
|
||||
this.add.text(
|
||||
x + width - 18,
|
||||
y + height - 20,
|
||||
`${this.sortieRosterScroll + 1}-${rangeEnd}/${allies.length} · 휠로 이동`,
|
||||
this.textStyle(11, '#9fb0bf', true)
|
||||
)
|
||||
);
|
||||
info.setOrigin(1, 0);
|
||||
info.setDepth(depth + 1);
|
||||
|
||||
const trackX = x + width - 12;
|
||||
const trackY = y + listTopOffset;
|
||||
const trackHeight = height - listFooterHeight;
|
||||
const scrollTrack = this.trackSortie(this.add.rectangle(trackX, trackY, 4, trackHeight, 0x05070a, 0.78));
|
||||
scrollTrack.setOrigin(0, 0);
|
||||
scrollTrack.setDepth(depth + 1);
|
||||
const thumbHeight = Math.max(24, trackHeight * (maxVisibleRows / allies.length));
|
||||
const thumbY = trackY + (trackHeight - thumbHeight) * (this.sortieRosterScroll / maxScroll);
|
||||
const scrollThumb = this.trackSortie(this.add.rectangle(trackX, thumbY, 4, thumbHeight, palette.gold, 0.84));
|
||||
scrollThumb.setOrigin(0, 0);
|
||||
scrollThumb.setDepth(depth + 2);
|
||||
}
|
||||
}
|
||||
|
||||
private updateSortieRosterScroll(nextScroll: number, maxScroll: number) {
|
||||
const clamped = Phaser.Math.Clamp(Math.round(nextScroll), 0, maxScroll);
|
||||
if (clamped === this.sortieRosterScroll) {
|
||||
return;
|
||||
}
|
||||
this.sortieRosterScroll = clamped;
|
||||
this.showSortiePrep();
|
||||
}
|
||||
|
||||
private sortieRosterViewState(totalUnits: number, height = 286) {
|
||||
const denseRoster = totalUnits > 12;
|
||||
const rowGap = denseRoster ? 17 : totalUnits > 7 ? 30 : totalUnits > 5 ? 38 : totalUnits > 4 ? 43 : 48;
|
||||
const rowHeight = denseRoster ? 15 : totalUnits > 7 ? 24 : rowGap - 6;
|
||||
const listTopOffset = denseRoster ? 48 : 50;
|
||||
const listFooterHeight = denseRoster ? 74 : 68;
|
||||
const naturalVisibleRows = Math.max(1, Math.floor((height - listTopOffset - 10 - rowHeight) / rowGap) + 1);
|
||||
const scrollVisibleRows = Math.max(1, Math.floor((height - listFooterHeight) / rowGap));
|
||||
const maxVisibleRows = totalUnits <= naturalVisibleRows ? totalUnits : scrollVisibleRows;
|
||||
const maxScroll = Math.max(0, totalUnits - maxVisibleRows);
|
||||
return {
|
||||
denseRoster,
|
||||
rowGap,
|
||||
rowHeight,
|
||||
listTopOffset,
|
||||
listFooterHeight,
|
||||
maxVisibleRows,
|
||||
maxScroll,
|
||||
scroll: Phaser.Math.Clamp(this.sortieRosterScroll, 0, maxScroll)
|
||||
};
|
||||
}
|
||||
|
||||
private renderSortieFormationPlan(x: number, y: number, width: number, height: number, depth: number) {
|
||||
@@ -13201,6 +13279,7 @@ export class CampScene extends Phaser.Scene {
|
||||
sortieFocusedUnit: this.sortieFocusedUnitSummary(),
|
||||
reserveTrainingFocus: this.reserveTrainingFocusDefinition(),
|
||||
reserveTrainingFocusOptions: campaignReserveTrainingFocusDefinitions.map((focus) => ({ ...focus })),
|
||||
sortieRosterView: this.sortieRosterViewState(this.sortieAllies().length),
|
||||
sortieRoster: this.sortieAllies().map((unit) => {
|
||||
const summary = this.sortieUnitTacticalSummary(unit);
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user