Skip to content

Commit

Permalink
feat(map): add trade, authority, and relations map modes
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelMakesGames committed Sep 10, 2024
1 parent a33fa32 commit d33a810
Show file tree
Hide file tree
Showing 12 changed files with 404 additions and 35 deletions.
19 changes: 19 additions & 0 deletions src/renderer/src/intl/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,10 @@ export default {
},
mapMode: 'Map Mode',
mapModePointOfView: 'Point of View',
mapModePointOfView_tooltip: `<ul>
<li><strong>WARNING</strong>: changing this can reveal information you normally could not see. Avoid if you want a "pure" experience.</li>
<li>Tip: <strong>shift+click</strong> a country on the map to change to their Point of View.</li>
</ul>`,
mapModeSpecies: 'Species',
borderStroke: 'Country Borders',
borderColor: 'Country Border Color',
Expand Down Expand Up @@ -477,6 +481,21 @@ export default {
hostile_fleet: 'Hostile Fleet',
hostile_station: 'Hostile Station',
},
trade_routes: {
name: 'Trade Routes',
tooltip_label: 'Trade Value',
collected: 'Collected Value',
pass_through: 'Passed Through Value',
pirated: 'Pirated Value',
},
authority: {
name: 'Authority',
tooltip_label: 'Authority',
},
relations: {
name: 'Relations',
tooltip_label: 'Relations',
},
},
legend: {
fully_occupied: 'Fully Occupied',
Expand Down
63 changes: 63 additions & 0 deletions src/renderer/src/lib/GameState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ const galacticObjectSchema = z
.optional(),
planet: z.number().optional(),
fleet_presence: z.array(z.number()).default([]),
trade_hub: z.object({
collected: z.number().optional(), // this does NOT include value delivered by other trade routes
destination: z.number().optional(), // trade_route ID
collected_from: z.array(z.number()).optional(), // galactic_object IDs
sources: z.array(z.number()).optional(), // trade_route IDs
}),
asteroid_belts: z
.array(
z.object({
Expand Down Expand Up @@ -172,6 +178,11 @@ const countrySchema = z.object({
.optional(),
})
.optional(),
government: z
.object({
authority: z.string(),
})
.optional(),
capital: z.number().optional(),
founder_species_ref: z.number().optional(),
subjects: preprocessedArray(z.number()),
Expand Down Expand Up @@ -338,6 +349,25 @@ const speciesSchema = z.object({
*/
export type Species = WithId<z.infer<typeof speciesSchema>>;

const tradeRouteSchema = z.object({
owner: z.number(),
from: z.number(),
to: z.number(),
delivered: z.number(),
path: z.array(
z.object({
id: z.number(),
collected: z.number(),
delivered: z.number(),
}),
),
});

/**
* @public
*/
export type TradeRoute = WithId<z.infer<typeof tradeRouteSchema>>;

function addIds<T>(db: Record<number, T>): Record<number, WithId<T>> {
return Object.fromEntries(
Object.entries(db).map(([id, obj]) => [id, { ...obj, id: parseInt(id) }]),
Expand All @@ -364,6 +394,7 @@ export const gameStateSchema = z
galaxy: z.object({ shape: z.string(), core_radius: z.number() }),
planets: z.object({ planet: stellarisDb(planetSchema) }).default({}),
species_db: stellarisDb(speciesSchema),
trade_routes: stellarisDb(tradeRouteSchema),
nebula: nebulaSchema.optional(),
$multiKeys: z.object({ nebula: preprocessedArray(nebulaSchema).optional() }).optional(),
})
Expand Down Expand Up @@ -426,3 +457,35 @@ function convertSchemaToGameStateFilter(schema: z.ZodType): boolean | Record<str
}
}
export const gameStateFilter = convertSchemaToGameStateFilter(gameStateSchema);

// trade
// galactic_object:
// - trade_collection { targets: { target: GalacticObjectID; distance: number }[] }
// - - targets is length 1; the ID it points to will have a trade_hub with this gal_obj in its collected_from
// - trade_hub {
// collected: number; // this does NOT include value delivered by other trade routes
// destination: TradeRouteID;
// collected_from: GalacticObjectID[]
// sources: TradeRouteID[]
// }
// - trade_piracy {
// active: number; ???
// max: number; ???
// total: number; un-pirated value
// throughput: number; pirate + un-pirated value
// used: number; pirated value
// targets: { target: GalacticObjectID; distance: number }[] ???
// }
// trade route:
// - delivered: number;
// - from: GalacticObjectID
// - to: GalacticObjectID
// - owner: CountryID
// - path: {
// id: GalacticObjectID
// collected: number; value entered, same as that obj's trade_piracy.throughput
// delivered: number; value left, same as that obj's trade_piracy.total
// }[]
// - - first entry in path is same ID as from
// - - last entry in path is same ID as to
// - - last entry delivered is the same as route delivered
4 changes: 2 additions & 2 deletions src/renderer/src/lib/map/Legend.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@
<text
fill="#FFFFFF"
x={symbolSize + symbolLabelGap}
y={symbolSize / 2}
dominant-baseline="central"
y={$mapSettings.legendFontSize}
dominant-baseline="auto"
font-size={$mapSettings.legendFontSize}
font-family={SYSTEM_FONTS}
>
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/src/lib/map/Map.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
import BypassLinks from './BypassLinks.svelte';
import CountryBorders from './CountryBorders.svelte';
import CountryLabels from './CountryLabels.svelte';
import type { MapData } from './data/processMapData';
import Hyperlanes from './Hyperlanes.svelte';
import Icons from './Icons.svelte';
import MapModePaths from './MapModePaths.svelte';
import OccupationPatternDefs from './OccupationPatternDefs.svelte';
import SystemIcons from './SystemIcons.svelte';
import TerraIncognita from './TerraIncognita.svelte';
import TerraIncognitaDefs from './TerraIncognitaDefs.svelte';
import type { MapData } from './data/processMapData';
export let id: string = '';
export let data: null | MapData;
Expand Down Expand Up @@ -77,6 +78,7 @@
<Hyperlanes {data} {colors} />
<TerraIncognita {data} />
<BypassLinks {data} {colors} />
<MapModePaths {data} {colors} />
<SystemIcons {data} {colors} />
<CountryLabels {data} />
{#if $debug}
Expand Down
11 changes: 6 additions & 5 deletions src/renderer/src/lib/map/MapContainer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@
function onMapClick(e: MouseEvent) {
if (e.shiftKey) {
document.getSelection()?.removeAllRanges();
if (!mapModes[$mapSettings.mapMode]?.hasPov) return;
const countryId = tooltip?.countryId;
if (countryId != null) {
Expand Down Expand Up @@ -425,16 +426,16 @@
</div>
{/if}
<div class="absolute right-3 top-3 flex gap-3">
{#if openedSystem}
<button type="button" class="variant-filled btn" on:click={closeSystemMap}>
{$t('generic.back_button')}
</button>
{/if}
{#if transform != null && !openedSystem}
<button type="button" class="variant-filled btn-icon" transition:fade on:click={resetZoom}>
<HeroiconArrowsPointingOut />
</button>
{/if}
{#if openedSystem}
<button type="button" class="variant-filled btn" on:click={closeSystemMap}>
{$t('generic.back_button')}
</button>
{/if}
{#if dataOrNull}
<button type="button" class="variant-filled btn" transition:fade on:click={openExportModal}>
{$t('export.button')}
Expand Down
50 changes: 50 additions & 0 deletions src/renderer/src/lib/map/MapModePaths.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script lang="ts">
import { mapSettings } from '../settings';
import { mapModes } from './data/mapModes';
import type { MapData } from './data/processMapData';
import { getStrokeColorAttributes } from './mapUtils';
export let data: MapData;
export let colors: Record<string, string>;
function getMapModePaths(systems: MapData['systems']) {
const max = Math.max(...systems.map((s) => s.mapModeTotalValue ?? 0));
const scale = (100 / max) * (mapModes[$mapSettings.mapMode]?.system?.scale ?? 1);
return systems
.filter((system) => system.mapModeValues?.some((v) => v.directedValues?.size))
.flatMap((system) => {
return (
system.mapModeValues?.flatMap((systemValue) =>
Array.from(systemValue.directedValues?.entries() ?? []).map(([to, value]) => ({
from: {
x: system.x,
y: system.y,
},
to: {
x: data.systems.find((s) => s.id === to)?.x ?? 0,
y: data.systems.find((s) => s.id === to)?.y ?? 0,
},
width: Math.sqrt(value * scale),
color: systemValue.color,
})),
) ?? []
);
});
}
</script>

{#each getMapModePaths(data.systems.filter((s) => s.systemIsKnown || !$mapSettings.terraIncognita)) as path}
<line
x1={path.from.x}
y1={path.from.y}
x2={path.to.x}
y2={path.to.y}
stroke-width={Math.max(0.5, path.width)}
stroke-dasharray={path.width === 0 ? '0.5 1' : undefined}
{...getStrokeColorAttributes({
mapSettings: $mapSettings,
colors,
colorStack: [path.color, $mapSettings.borderFillColor],
})}
/>
{/each}
29 changes: 18 additions & 11 deletions src/renderer/src/lib/map/MapTooltip.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<script lang="ts">
import { arrow, autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';
import { onDestroy, onMount } from 'svelte';
import { locale, t } from '../../intl';
import type { GalacticObject, GameState } from '../GameState';
import { locale, t, type MessageID } from '../../intl';
import type { GalacticObject, GameState, LocalizedText } from '../GameState';
import HeroiconUserMicro from '../icons/HeroiconUserMicro.svelte';
import { mapSettings } from '../settings';
import { isDefined } from '../utils';
import { localizeText } from './data/locUtils';
import { mapModes, type MapModeSystemValue } from './data/mapModes';
import { mapModes } from './data/mapModes';
import type { ProcessedSystem } from './data/processSystems';
import { resolveColor } from './mapUtils';
Expand Down Expand Up @@ -60,14 +60,15 @@
.filter(isDefined)
.sort((a, b) => (b?.num_sapient_pops ?? 0) - (a?.num_sapient_pops ?? 0));
async function localizeValueLabel(value: MapModeSystemValue) {
async function localizeValueLabel(
message: MessageID | LocalizedText,
data: Record<string, LocalizedText> = {},
) {
const values: Record<string, string> = {};
for (const [k, v] of Object.entries(value.legendLabelData ?? {})) {
for (const [k, v] of Object.entries(data)) {
values[k] = await localizeText(v);
}
return typeof value.legendLabel === 'string'
? $t(value.legendLabel, values)
: await localizeText(value.legendLabel);
return typeof message === 'string' ? $t(message, values) : await localizeText(message);
}
</script>

Expand All @@ -89,15 +90,21 @@
{#await localizeText(system.name)}
{$t('generic.loading')}
{:then name}
{name}
{name} {system.id}
{/await}
</strong>
{#if processedSystem?.mapModeCountryLabel}
<div class="flex flex-row justify-between gap-1 text-sm">
<span>
{$t(mapModes[$mapSettings.mapMode]?.tooltipLabel ?? 'generic.NEVER')}:
</span>
<strong>{$t(processedSystem.mapModeCountryLabel)}</strong>
<strong>
{#await localizeValueLabel(processedSystem.mapModeCountryLabel)}
{$t('generic.loading')}
{:then label}
{label}
{/await}
</strong>
</div>
{/if}
{#if processedSystem?.mapModeValues?.filter((v) => v.value).length}
Expand All @@ -120,7 +127,7 @@
stroke-width="0.125"
/>
</svg>
{#await localizeValueLabel(systemValue)}
{#await localizeValueLabel(systemValue.legendLabel, systemValue.legendLabelData)}
{$t('generic.loading')}
{:then label}
{label}
Expand Down
7 changes: 4 additions & 3 deletions src/renderer/src/lib/map/SystemIcons.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
type MapSettings,
type SettingConfigIcon,
} from '../settings';
import { mapModes } from './data/mapModes';
import type { MapData } from './data/processMapData';
import type { ProcessedSystem } from './data/processSystems';
import { getFillColorAttributes } from './mapUtils';
export let data: MapData;
export let colors: Record<string, string>;
type SystemData = MapData['systems'][number];
interface IconSettingMetadata {
systemProperty?: keyof SystemData;
systemProperty?: keyof ProcessedSystem;
mustKnowOwner?: boolean;
}
const metadata: Record<IconMapSettings, IconSettingMetadata> = {
Expand Down Expand Up @@ -129,7 +130,7 @@
function getMapModeIcons(systems: MapData['systems']) {
const max = Math.max(...systems.map((s) => s.mapModeTotalValue ?? 0));
const scale = 100 / max;
const scale = (100 / max) * (mapModes[$mapSettings.mapMode]?.system?.scale ?? 1);
return systems
.filter((system) => system.mapModeTotalValue)
.map((system) => {
Expand Down
Loading

0 comments on commit d33a810

Please sign in to comment.