Skip to content

Commit

Permalink
feat(map): add system-level map modes (population and fleet power)
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelMakesGames committed Sep 1, 2024
1 parent 8b22dea commit 482acf6
Show file tree
Hide file tree
Showing 10 changed files with 423 additions and 13 deletions.
5 changes: 5 additions & 0 deletions src/renderer/src/intl/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@ export default {
map_mode: {
default: 'Empires',
wars: 'Wars',
population: 'Population',
populationByCountry: 'Population (Country Color)',
populationSpecies: 'Species Population',
fleetPowerAlliedAndHostile: 'Fleet Power (Allied and Hostile)',
},
},
// labels and tooltips for various settings
Expand All @@ -256,6 +260,7 @@ export default {
},
mapMode: 'Map Mode',
mapModePointOfView: 'Point of View',
mapModeSpecies: 'Species',
borderStroke: 'Country Borders',
borderColor: 'Country Border Color',
borderFillColor: 'Country Fill Color',
Expand Down
18 changes: 18 additions & 0 deletions src/renderer/src/lib/GameState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const galacticObjectSchema = z
)
.optional(),
planet: z.number().optional(),
fleet_presence: z.array(z.number()).default([]),
$multiKeys: z
.object({
planet: preprocessedArray(z.number()).optional(),
Expand All @@ -85,6 +86,9 @@ const planetSchema = z.object({
controller: z.number().optional(),
owner: z.number().optional(),
num_sapient_pops: z.number().optional(),
species_information: z
.record(z.string(), z.object({ num_pops: z.number(), num_enslaved: z.number().optional() }))
.optional(),
});

/**
Expand Down Expand Up @@ -153,6 +157,7 @@ const countrySchema = z.object({
})
.optional(),
capital: z.number().optional(),
founder_species_ref: z.number().optional(),
subjects: preprocessedArray(z.number()),
overlord: z.number().optional(),
federation: z.number().optional(),
Expand All @@ -163,6 +168,7 @@ const countrySchema = z.object({
owned_fleets: preprocessedArray(z.object({ fleet: z.number() })),
})
.optional(),
sensor_range_fleets: z.array(z.number()).default([]),
terra_incognita: z
.object({
systems: preprocessedArray(z.number()),
Expand Down Expand Up @@ -226,6 +232,7 @@ export type Ship = WithId<z.infer<typeof shipSchema>>;

const fleetSchema = z.object({
station: z.boolean().optional(),
military_power: z.number(),
});

/**
Expand Down Expand Up @@ -305,6 +312,16 @@ const nebulaSchema = z
*/
export type Nebula = z.infer<typeof nebulaSchema>;

const speciesSchema = z.object({
base_ref: z.number().optional(),
name: localizedTextSchema,
});

/**
* @public
*/
export type Species = WithId<z.infer<typeof speciesSchema>>;

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 @@ -330,6 +347,7 @@ export const gameStateSchema = z
player: preprocessedArray(z.object({ name: z.coerce.string(), country: z.number() })),
galaxy: z.object({ shape: z.string(), core_radius: z.number() }),
planets: z.object({ planet: stellarisDb(planetSchema) }).default({}),
species_db: stellarisDb(speciesSchema),
nebula: nebulaSchema.optional(),
$multiKeys: z.object({ nebula: preprocessedArray(nebulaSchema).optional() }).optional(),
})
Expand Down
35 changes: 31 additions & 4 deletions src/renderer/src/lib/Sidebar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
validateAndResetMapSettings,
type SavedMapSettings,
} from './settings';
import { speciesOptions } from './settings/options/speciesOptions';
import type { StellarisSaveMetadata } from './stellarMapsApi';
import stellarMapsApi from './stellarMapsApi';
import { saveToWindow, timeIt, timeItAsync, toastError, wait } from './utils';
Expand Down Expand Up @@ -91,12 +92,35 @@
);
promise.then(async (gameState) => {
Promise.all(
Object.entries(gameState.country)
.filter(([_id, country]) => country.type === 'default')
.map(([id, country]) =>
localizeText(country.name).then((name) => ({ id, literalName: name })),
Object.values(gameState.country)
.filter((country) => country.type === 'default')
.map((country) =>
localizeText(country.name).then((name) => ({
id: country.id.toString(),
literalName: name,
})),
),
).then(countryOptions.set);
const speciesWithPopulation = new Set(
Object.values(gameState.planets.planet).flatMap((planet) =>
Object.keys(planet.species_information ?? {}).map((id) => parseInt(id)),
),
);
Promise.all(
Object.values(gameState.species_db)
.filter(
(species) =>
speciesWithPopulation.has(species.id) &&
species.base_ref == null &&
species.name.key !== 'UNKNOWN',
)
.map((species) =>
localizeText(species.name).then((name) => ({
id: species.id.toString(),
literalName: name,
})),
),
).then(speciesOptions.set);
});
gameStatePromise.set(promise);
Expand All @@ -105,16 +129,19 @@
...prev,
terraIncognitaPerspectiveCountry: 'player',
mapModePointOfView: 'player',
mapModeSpecies: 'player',
}));
mapSettings.update((prev) => ({
...prev,
terraIncognitaPerspectiveCountry: 'player',
mapModePointOfView: 'player',
mapModeSpecies: 'player',
}));
lastProcessedMapSettings.update((prev) => ({
...prev,
terraIncognitaPerspectiveCountry: 'player',
mapModePointOfView: 'player',
mapModeSpecies: 'player',
}));
promise.catch(
Expand Down
72 changes: 72 additions & 0 deletions src/renderer/src/lib/map/SystemIcons.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script lang="ts">
import * as d3Shape from 'd3-shape';
import {
mapSettings,
mapSettingsConfig,
Expand Down Expand Up @@ -125,8 +126,79 @@
if ($mapSettings.systemNames === 'colonized' && system.isColonized) return true;
return false;
}
function getMapModeIcons(systems: MapData['systems']) {
const max = Math.max(...systems.map((s) => s.mapModeTotalValue ?? 0));
const scale = 100 / max;
return systems
.filter((system) => system.mapModeTotalValue)
.map((system) => {
const total = system.mapModeTotalValue ?? 0;
const r = Math.sqrt(total * scale);
let startAngle = 0;
const arcs = (system.mapModeValues ?? [])
.filter((value) => value.value)
.map((value) => {
const angle = (value.value / total) * Math.PI * 2;
const start = startAngle;
const end = start + angle;
startAngle = end;
return {
...value,
start,
end,
};
});
return { r, arcs, ...system };
})
.sort((a, b) => b.r - a.r);
}
</script>

{#each getMapModeIcons(data.systems) as system}
{#if system.arcs.length <= 1}
<circle
cx={system.x}
cy={system.y}
r={system.r}
{...getFillColorAttributes({
mapSettings: $mapSettings,
colors,
countryColors: system,
colorStack: [
system.arcs[0]?.color ?? { color: 'black', colorAdjustments: [] },
$mapSettings.borderFillColor,
],
})}
/>
{:else}
{#each system.arcs as arc}
<path
d={d3Shape.arc()({
innerRadius: 0,
outerRadius: system.r,
startAngle: arc.start,
endAngle: arc.end,
})}
transform="translate({system.x},{system.y})"
{...getFillColorAttributes({
mapSettings: $mapSettings,
colors,
countryColors: system,
colorStack: [arc.color, $mapSettings.borderFillColor],
})}
/>
{/each}
{/if}
<circle
cx={system.x}
cy={system.y}
r={system.r + 0.25}
stroke-width="0.5"
fill="none"
stroke={colors.black}
/>
{/each}
{#each data.systems
.filter((s) => s.systemIsKnown || !$mapSettings.terraIncognita)
.map((s) => getSystemIcons(s, $mapSettings)) as systemIcons}
Expand Down
Loading

0 comments on commit 482acf6

Please sign in to comment.