From 1ab0ba03b6ff4526f30778a9c49dde2a9a5ce3ec Mon Sep 17 00:00:00 2001 From: Michael Moore Date: Sat, 7 Sep 2024 23:17:34 -0500 Subject: [PATCH] feat: add all map modes to tooltips and system-level map modes to legend --- src/renderer/src/intl/en-US.ts | 38 +++++++-- src/renderer/src/lib/map/Legend.svelte | 16 ++++ src/renderer/src/lib/map/MapContainer.svelte | 9 +- src/renderer/src/lib/map/MapTooltip.svelte | 83 ++++++++++++++++--- src/renderer/src/lib/map/data/mapModes.ts | 70 +++++++++++++--- .../src/lib/map/data/processLegend.ts | 62 +++++++++++--- .../src/lib/map/data/processMapData.ts | 3 +- .../src/lib/map/data/processSystems.ts | 29 ++++++- .../src/lib/settings/mapSettingsConfig.ts | 10 +-- 9 files changed, 267 insertions(+), 53 deletions(-) diff --git a/src/renderer/src/intl/en-US.ts b/src/renderer/src/intl/en-US.ts index 0e1e8ff..59a096a 100644 --- a/src/renderer/src/intl/en-US.ts +++ b/src/renderer/src/intl/en-US.ts @@ -25,9 +25,13 @@ export default { loading: 'This could take a few seconds', error: 'Something has gone wrong', click_to_view_system: 'Click to open map', + tooltip: { + colonies: 'Colonies', + }, }, // various generic messages generic: { + NEVER: 'THIS IS A BUG', // this message will never be displayed enabled: 'Enabled', disabled: 'Disabled', back_button: 'Back', @@ -234,14 +238,6 @@ export default { colonized: 'Colonized Systems', all: 'All Systems', }, - map_mode: { - default: 'Empires', - wars: 'Wars', - population: 'Population', - populationByCountry: 'Population (Country Color)', - populationSpecies: 'Species Population', - fleetPowerAlliedAndHostile: 'Fleet Power (Allied and Hostile)', - }, system_map_label_position: { top: 'Top', bottom: 'Bottom', @@ -446,12 +442,38 @@ export default { common: { selected_country: 'Selected Country', }, + default: { + name: 'Empires', + }, wars: { + name: 'Wars', + tooltip_label: 'War Status', hostile: 'Hostile', ally: 'Ally in Active War', at_war: 'At War (Uninvolved)', at_peace: 'At Peace', }, + population: { + name_total: 'Total Population', + name_by_country: 'Population by Country', + name_species: 'Species Population', + tooltip_label: 'Population', + total: 'Total Population', + country: 'Population (Colored by Country)', + free_species: 'Free {species}', + enslaved_species: 'Enslaved {species}', + other_species: 'Other Species', + }, + fleet_power: { + name_allied_and_hostile: 'Allied and Hostile Fleet Power', + tooltip_label: 'Fleet Power', + own_fleet: 'Own Fleet', + own_station: 'Own Station', + allied_fleet: 'Allied Fleet', + allied_station: 'Allied Station', + hostile_fleet: 'Hostile Fleet', + hostile_station: 'Hostile Station', + }, }, legend: { fully_occupied: 'Fully Occupied', diff --git a/src/renderer/src/lib/map/Legend.svelte b/src/renderer/src/lib/map/Legend.svelte index 84aeb45..c2cb1e1 100644 --- a/src/renderer/src/lib/map/Legend.svelte +++ b/src/renderer/src/lib/map/Legend.svelte @@ -1,6 +1,7 @@
- {#await localizeText(system.name)} - {$t('generic.loading')} - {:then name} - {name} - {/await} - {#if planets} -
    + + {#await localizeText(system.name)} + {$t('generic.loading')} + {:then name} + {name} + {/await} + + {#if processedSystem?.mapModeCountryLabel} +
    + + {$t(mapModes[$mapSettings.mapMode]?.tooltipLabel ?? 'generic.NEVER')}: + + {$t(processedSystem.mapModeCountryLabel)} +
    + {/if} + {#if processedSystem?.mapModeValues?.length} + + {$t(mapModes[$mapSettings.mapMode]?.tooltipLabel ?? 'generic.NEVER')} + +
      + {#each processedSystem.mapModeValues.filter((v) => v.value) as systemValue} +
    • + + + + + {#await localizeValueLabel(systemValue)} + {$t('generic.loading')} + {:then label} + {label} + {/await} + + + {new Intl.NumberFormat($locale, { + notation: 'compact', + maximumFractionDigits: 1, + }).format(systemValue.value)} + +
    • + {/each} +
    + {/if} + {#if planets?.length} + {$t('map.tooltip.colonies')} +
      {#each planets as planet}
    • @@ -92,7 +155,7 @@ {/each}
    {/if} - +
    {$t('map.click_to_view_system')} - +
diff --git a/src/renderer/src/lib/map/data/mapModes.ts b/src/renderer/src/lib/map/data/mapModes.ts index 998afda..0c8f987 100644 --- a/src/renderer/src/lib/map/data/mapModes.ts +++ b/src/renderer/src/lib/map/data/mapModes.ts @@ -1,17 +1,20 @@ import type { MessageID } from '../../../intl'; -import type { Country, GalacticObject, GameState, Species } from '../../GameState'; +import type { Country, GalacticObject, GameState, LocalizedText, Species } from '../../GameState'; import type { ColorSetting, MapSettings } from '../../settings'; import { isDefined, parseNumberEntry } from '../../utils'; import { getUnionLeaderId } from './utils'; interface MapMode { + id: string; + name: MessageID; + tooltipLabel?: MessageID; country?: MapModeBorder[]; system?: MapModeSystem; } interface MapModeBorder { label: MessageID | null; - showInLegend: 'always' | 'never' | 'exists'; + showInLegend: 'always' | 'never'; primaryColor: string; secondaryColor?: string; matches: (gameState: GameState, countryId: number, povCountryId?: number) => boolean; @@ -26,15 +29,24 @@ interface MapModeSystem { ) => MapModeSystemValue[]; } -interface MapModeSystemValue { +export interface MapModeSystemValue { value: number; color: ColorSetting; - // TODO tooltip - // TODO legend + legendColor?: ColorSetting; + legendIndex: number; + legendLabel: MessageID | LocalizedText; + legendLabelData?: Record; } export const mapModes: Record = { + default: { + id: 'default', + name: 'map_mode.default.name', + }, wars: { + id: 'wars', + name: 'map_mode.wars.name', + tooltipLabel: 'map_mode.wars.tooltip_label', country: [ { label: 'map_mode.common.selected_country', @@ -81,6 +93,9 @@ export const mapModes: Record = { ], }, population: { + id: 'population', + name: 'map_mode.population.name_total', + tooltipLabel: 'map_mode.population.tooltip_label', country: [ { label: 'map_mode.common.selected_country', @@ -102,12 +117,17 @@ export const mapModes: Record = { { value: population, color: { color: 'intense_blue', colorAdjustments: [{ type: 'OPACITY', value: 0.5 }] }, + legendIndex: 0, + legendLabel: 'map_mode.population.total', }, ]; }, }, }, populationByCountry: { + id: 'populationByCountry', + name: 'map_mode.population.name_by_country', + tooltipLabel: 'map_mode.population.tooltip_label', country: [ { label: null, @@ -142,6 +162,9 @@ export const mapModes: Record = { { type: 'OPACITY', value: 0.75 }, ], }, + legendColor: { color: 'white', colorAdjustments: [] }, + legendIndex: 0, + legendLabel: 'map_mode.population.country', }; }) .sort((a, b) => b.value - a.value); @@ -149,6 +172,9 @@ export const mapModes: Record = { }, }, populationSpecies: { + id: 'populationSpecies', + name: 'map_mode.population.name_species', + tooltipLabel: 'map_mode.population.tooltip_label', country: [ { label: null, @@ -193,20 +219,31 @@ export const mapModes: Record = { { value: freePopulation, color: { color: 'intense_blue', colorAdjustments: [] }, + legendIndex: 0, + legendLabel: 'map_mode.population.free_species', + legendLabelData: { species: selectedSpecies?.name ?? { key: 'UNKNOWN' } }, }, { value: enslavedPopulation, color: { color: 'intense_red', colorAdjustments: [] }, + legendIndex: 1, + legendLabel: 'map_mode.population.enslaved_species', + legendLabelData: { species: selectedSpecies?.name ?? { key: 'UNKNOWN' } }, }, { value: population - freePopulation - enslavedPopulation, color: { color: 'dark_grey', colorAdjustments: [] }, + legendIndex: 2, + legendLabel: 'map_mode.population.other_species', }, ]; }, }, }, fleetPowerAlliedAndHostile: { + id: 'fleetPowerAlliedAndHostile', + name: 'map_mode.fleet_power.name_allied_and_hostile', + tooltipLabel: 'map_mode.fleet_power.tooltip_label', country: [ { label: null, @@ -266,26 +303,38 @@ export const mapModes: Record = { { value: ownMobileFleetPower, color: { color: 'dark_teal', colorAdjustments: [{ type: 'LIGHTEN', value: 0.0 }] }, + legendIndex: 0, + legendLabel: 'map_mode.fleet_power.own_fleet', }, { value: ownStationFleetPower, color: { color: 'dark_teal', colorAdjustments: [{ type: 'DARKEN', value: 0.2 }] }, + legendIndex: 1, + legendLabel: 'map_mode.fleet_power.own_station', }, { value: alliedMobileFleetPower, color: { color: 'intense_blue', colorAdjustments: [{ type: 'LIGHTEN', value: 0.0 }] }, + legendIndex: 2, + legendLabel: 'map_mode.fleet_power.allied_fleet', }, { value: alliedStationFleetPower, color: { color: 'intense_blue', colorAdjustments: [{ type: 'DARKEN', value: 0.2 }] }, + legendIndex: 3, + legendLabel: 'map_mode.fleet_power.allied_station', }, { value: hostileMobileFleetPower, color: { color: 'intense_red', colorAdjustments: [{ type: 'LIGHTEN', value: 0.0 }] }, + legendIndex: 4, + legendLabel: 'map_mode.fleet_power.hostile_fleet', }, { value: hostileStationFleetPower, color: { color: 'intense_red', colorAdjustments: [{ type: 'DARKEN', value: 0.2 }] }, + legendIndex: 5, + legendLabel: 'map_mode.fleet_power.hostile_station', }, ]; }, @@ -293,14 +342,13 @@ export const mapModes: Record = { }, }; -interface CountryMapModeInfo { +export interface MapModeCountryInfo { primaryColor: string; secondaryColor: string; - mapModeIndex?: number; - // TODO tooltip + mapModeCountryLabel?: MessageID; } -export const defaultCountryMapModeInfo: CountryMapModeInfo = { +export const defaultCountryMapModeInfo: MapModeCountryInfo = { primaryColor: 'black', secondaryColor: 'black', }; @@ -318,7 +366,7 @@ export function getCountryMapModeInfo( | 'unionSubjects' | 'unionFederationsColor' >, -): CountryMapModeInfo { +): MapModeCountryInfo { const povCountryId = settings.mapModePointOfView === 'player' ? gameState.player.filter((p) => gameState.country[p.country])[0]?.country @@ -332,6 +380,7 @@ export function getCountryMapModeInfo( return { primaryColor: match.primaryColor, secondaryColor: match.secondaryColor ?? match.primaryColor, + mapModeCountryLabel: match.showInLegend === 'always' ? match.label ?? undefined : undefined, }; } else { return defaultCountryMapModeInfo; @@ -341,7 +390,6 @@ export function getCountryMapModeInfo( gameState.country[ getUnionLeaderId(countryId, gameState, settings, ['joinedBorders', 'separateBorders']) ]?.flag?.colors; - // TODO tooltips including country name, federation status, subject status return { primaryColor: colors?.[0] ?? 'black', secondaryColor: colors?.[1] ?? 'black' }; } } diff --git a/src/renderer/src/lib/map/data/processLegend.ts b/src/renderer/src/lib/map/data/processLegend.ts index 66f505a..dd64143 100644 --- a/src/renderer/src/lib/map/data/processLegend.ts +++ b/src/renderer/src/lib/map/data/processLegend.ts @@ -2,13 +2,20 @@ import { get } from 'svelte/store'; import { t } from '../../../intl'; import type { GameState } from '../../GameState'; -import type { MapSettings } from '../../settings'; -import { mapModes } from './mapModes'; +import type { ColorSetting, MapSettings } from '../../settings'; +import { localizeText } from './locUtils'; +import { mapModes, type MapModeSystemValue } from './mapModes'; import type processBorders from './processBorders'; +import type processSystems from './processSystems'; import { getTextAspectRatio } from './utils'; interface LegendItem { symbol: + | { + type: 'icon'; + icon: string; + color: ColorSetting; + } | { type: 'border'; primaryColor: string; @@ -36,22 +43,18 @@ export const processLegendDeps = [ 'occupation', ] satisfies (keyof MapSettings)[]; -export default function processLegend( +export default async function processLegend( gameState: GameState, settings: Pick, borders: ReturnType, -): LegendData { + systems: ReturnType, +): Promise { const mapMode = mapModes[settings.mapMode]; - const mapModeLegendItems: LegendItem[] = + const countryMapModeLegendItems: LegendItem[] = mapMode?.country == null ? [] : mapMode.country - .filter( - (mapModeCountry, index) => - mapModeCountry.showInLegend === 'always' || - (mapModeCountry.showInLegend === 'exists' && - borders.some((b) => b.mapModeIndex === index)), - ) + .filter((mapModeCountry) => mapModeCountry.showInLegend === 'always') .map((mapModeCountry) => ({ label: mapModeCountry.label ? get(t)(mapModeCountry.label) : '', symbol: { @@ -60,6 +63,36 @@ export default function processLegend( secondaryColor: mapModeCountry.secondaryColor ?? mapModeCountry.primaryColor, }, })); + + const systemMapModeLegendItems: LegendItem[] = await Promise.all( + Object.values( + systems.reduce>((acc, cur) => { + for (const value of cur.mapModeValues ?? []) { + acc[value.legendIndex] = value; + } + return acc; + }, {}), + ) + .sort((a, b) => a.legendIndex - b.legendIndex) + .map>(async (value) => { + const values: Record = {}; + for (const [k, v] of Object.entries(value.legendLabelData ?? {})) { + values[k] = await localizeText(v); + } + return { + label: + typeof value.legendLabel === 'string' + ? get(t)(value.legendLabel, values) + : await localizeText(value.legendLabel), + symbol: { + type: 'icon', + icon: 'icon-circle', + color: value.legendColor ?? value.color, + }, + }; + }), + ); + const occupationLegendItems: LegendItem[] = settings.occupation ? [ { @@ -78,7 +111,12 @@ export default function processLegend( }, ] : []; - const items = insertHrBetweenGroups([mapModeLegendItems, occupationLegendItems]); + + const items = insertHrBetweenGroups([ + countryMapModeLegendItems, + systemMapModeLegendItems, + occupationLegendItems, + ]); return { items, maxLabelWidth: Math.max( diff --git a/src/renderer/src/lib/map/data/processMapData.ts b/src/renderer/src/lib/map/data/processMapData.ts index 4a46be8..7e12509 100644 --- a/src/renderer/src/lib/map/data/processMapData.ts +++ b/src/renderer/src/lib/map/data/processMapData.ts @@ -200,12 +200,13 @@ export default async function processMapData( knownWormholes, getSystemCoordinates, ); - const legend = timeIt( + const legend = await timeItAsync( 'legend', cached(processLegend), gameState, pickSettings(settings, processLegendDeps), borders, + systems, ); const emblems = await emblemsPromise; diff --git a/src/renderer/src/lib/map/data/processSystems.ts b/src/renderer/src/lib/map/data/processSystems.ts index 9726c58..f687876 100644 --- a/src/renderer/src/lib/map/data/processSystems.ts +++ b/src/renderer/src/lib/map/data/processSystems.ts @@ -1,9 +1,34 @@ import type { GameState } from '../../GameState'; import type { MapSettings } from '../../settings'; import { isDefined } from '../../utils'; -import { defaultCountryMapModeInfo, getCountryMapModeInfo, mapModes } from './mapModes'; +import { + defaultCountryMapModeInfo, + getCountryMapModeInfo, + type MapModeCountryInfo, + mapModes, + type MapModeSystemValue, +} from './mapModes'; import type processSystemOwnership from './processSystemOwnership'; +export interface ProcessedSystem extends MapModeCountryInfo { + id: number; + isColonized: boolean; + isSectorCapital: boolean; + isCountryCapital: boolean; + isOwned: boolean; + ownerIsKnown: boolean; + systemIsKnown: boolean; + hasWormhole: boolean; + hasGateway: boolean; + hasLGate: boolean; + hasShroudTunnel: boolean; + x: number; + y: number; + name: string; + mapModeValues?: MapModeSystemValue[]; + mapModeTotalValue?: number; +} + export const processSystemsDeps = [ 'unionMode', 'unionFederations', @@ -39,7 +64,7 @@ export default function processSystems( const selectedSpecies = selectedSpeciesId == null ? null : gameState.species_db[selectedSpeciesId]; - const systems = Object.values(gameState.galactic_object).map((system) => { + const systems = Object.values(gameState.galactic_object).map((system) => { const countryId = systemIdToCountry[system.id]; const country = countryId != null ? gameState.country[countryId] : null; const mapModeInfo = diff --git a/src/renderer/src/lib/settings/mapSettingsConfig.ts b/src/renderer/src/lib/settings/mapSettingsConfig.ts index 799d611..c1a7375 100644 --- a/src/renderer/src/lib/settings/mapSettingsConfig.ts +++ b/src/renderer/src/lib/settings/mapSettingsConfig.ts @@ -1,3 +1,4 @@ +import { mapModes } from '../map/data/mapModes'; import { countryOptions } from './options/countryOptions'; import { fontOptions } from './options/fontOptions'; import { glyphOptions } from './options/glyphOptions'; @@ -14,14 +15,7 @@ export const mapSettingsConfig: MapSettingConfigGroup[] = [ { id: 'mapMode', type: 'select', - options: [ - { id: 'default', name: 'option.map_mode.default' }, - { id: 'wars', name: 'option.map_mode.wars' }, - { id: 'population', name: 'option.map_mode.population' }, - { id: 'populationByCountry', name: 'option.map_mode.populationByCountry' }, - { id: 'populationSpecies', name: 'option.map_mode.populationSpecies' }, - { id: 'fleetPowerAlliedAndHostile', name: 'option.map_mode.fleetPowerAlliedAndHostile' }, - ], + options: Object.values(mapModes), requiresReprocessing: true, }, {