Skip to content

Commit

Permalink
feat(map,ui): add clickable hyperlane links to system maps
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelMakesGames committed Sep 3, 2024
1 parent 3b17dfd commit d9d4816
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 15 deletions.
1 change: 1 addition & 0 deletions src/renderer/src/intl/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ export default {
systemMapLabelPlanetsEnabled: 'Planet Names Enabled',
systemMapLabelMoonsEnabled: 'Moon Names Enabled',
systemMapLabelAsteroidsEnabled: 'Asteroid Names Enabled',
systemMapHyperlanesEnabled: 'Hyperlane Connections Enabled',
appLocale: 'StellarMaps Language',
appLocale_tooltip:
'Join the Discord server (link in the top bar) if you want to help translate!',
Expand Down
3 changes: 3 additions & 0 deletions src/renderer/src/lib/map/MapContainer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,9 @@
gameState={gameStateOrNull}
system={openedSystem}
colors={colorsOrNull}
onSystemSelected={(system) => {
openedSystem = system;
}}
/>
{/if}
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
Expand Down
122 changes: 107 additions & 15 deletions src/renderer/src/lib/map/solarSystemMap/SolarSystemMap.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { select } from 'd3-selection';
import { zoom } from 'd3-zoom';
import { zoom, zoomIdentity } from 'd3-zoom';
import { t } from '../../../intl';
import type { GalacticObject, GameState, Planet } from '../../GameState';
import { mapSettings, type MapSettings } from '../../settings';
Expand All @@ -15,20 +15,66 @@
export let id: string;
export let exportMode = false;
export let previewMode = false;
export let onSystemSelected: null | ((system: GalacticObject) => void) = null;
let svg: SVGElement;
let g: SVGGElement;
let zoomHandler = zoom().on('zoom', (e) => {
if (g) g.setAttribute('transform', e.transform.toString());
});
let zoomHandler = zoom()
.on('zoom', (e) => {
if (g) g.setAttribute('transform', e.transform.toString());
})
.filter(function filter(event: MouseEvent) {
// click and drag for middle button only
if (event.type === 'mousedown') return event.button === 1;
// this is the default implementation
return (!event.ctrlKey || event.type === 'wheel') && !event.button;
});
$: if (svg && !exportMode && !previewMode) {
select(svg).call(zoomHandler as any);
}
function resetZoom() {
if (svg) {
select(svg).call(zoomHandler.transform as any, zoomIdentity);
}
}
$: planets = system.planet
.map((planetId) => gameState.planets.planet[planetId])
.filter(isDefined);
$: systemConnections = system.hyperlane
.map((h) => {
const toSystem = gameState.galactic_object[h.to];
if (!toSystem) return null;
const theta = Math.atan2(
toSystem.coordinate.y - system.coordinate.y,
// note: inverted x value
system.coordinate.x - toSystem.coordinate.x,
);
const x = 400 * Math.cos(theta);
const y = 400 * Math.sin(theta);
const trianglePath = `
M ${x + Math.cos(theta + Math.PI / 2) * 30} ${y + Math.sin(theta + Math.PI / 2) * 30}
L ${x - Math.cos(theta + Math.PI / 2) * 30} ${y - Math.sin(theta + Math.PI / 2) * 30}
L ${x + Math.cos(theta) * 20} ${y + Math.sin(theta) * 20}
Z
`;
const textPathPoints: [[number, number], [number, number]] = [
[
x - Math.cos(theta) * 5 + Math.cos(theta + Math.PI / 2) * 500,
y - Math.sin(theta) * 5 + Math.sin(theta + Math.PI / 2) * 500,
],
[
x - Math.cos(theta) * 5 - Math.cos(theta + Math.PI / 2) * 500,
y - Math.sin(theta) * 5 - Math.sin(theta + Math.PI / 2) * 500,
],
];
if (y < 0) textPathPoints.reverse();
const textPath = `M ${textPathPoints[0][0]} ${textPathPoints[0][1]} L ${textPathPoints[1][0]} ${textPathPoints[1][1]}`;
return { x, y, system: toSystem, trianglePath, textPath };
})
.filter(isDefined);
function getPlanetColor(planet: Planet): string | undefined {
let pc = planet.planet_class;
if (pc.includes('_station')) [(pc = pc.substring(0, pc.indexOf('_station')))];
Expand Down Expand Up @@ -251,6 +297,15 @@
return Boolean(planet.is_moon);
}
function getPlanetRadius(planet: Planet, settings: MapSettings) {
return Math.sqrt(
planet.planet_size *
(settings.systemMapPlanetScale ?? 1) *
(isStar(planet) ? 2 : 1) *
(isMoon(planet) ? 0.5 : 1),
);
}
function isPlanetLabeled(planet: Planet, settings: MapSettings) {
return (
(isColony(planet) && settings.systemMapLabelColoniesEnabled) ||
Expand All @@ -265,9 +320,7 @@
}
function getPlanetLabelPathAttributes(planet: Planet, settings: MapSettings) {
const r = Math.sqrt(
planet.planet_size * (settings.systemMapPlanetScale ?? 1) * (isStar(planet) ? 2 : 1),
);
const r = getPlanetRadius(planet, settings);
let x = -planet.coordinate.x;
let y = planet.coordinate.y;
let position = settings.systemMapLabelPlanetsPosition;
Expand Down Expand Up @@ -314,9 +367,7 @@
}
function getPlanetLabelTextPathAttributes(planet: Planet, settings: MapSettings) {
const r = Math.sqrt(
planet.planet_size * (settings.systemMapPlanetScale ?? 1) * (isStar(planet) ? 2 : 1),
);
const r = getPlanetRadius(planet, settings);
let position = settings.systemMapLabelPlanetsPosition;
if (position === 'orbit' && !planet.orbit) {
position = settings.systemMapLabelPlanetsFallbackPosition;
Expand Down Expand Up @@ -363,8 +414,8 @@
}
}
function getShadowPath(planet: Planet, scale: number) {
const r = Math.sqrt(planet.planet_size * scale);
function getShadowPath(planet: Planet, settings: MapSettings) {
const r = getPlanetRadius(planet, settings);
return `
M 0 ${r}
A ${r} ${r} 0 0 0 0 ${-r}
Expand Down Expand Up @@ -468,7 +519,7 @@
<Glow enabled filterId="starGlow" let:filter>
<circle
fill={isBlackHole(planet) && filter ? '#FFFFFF' : getPlanetColor(planet)}
r={Math.sqrt(planet.planet_size * 2 * ($mapSettings.systemMapPlanetScale ?? 1))}
r={getPlanetRadius(planet, $mapSettings)}
cx={-planet.coordinate.x}
cy={planet.coordinate.y}
{filter}
Expand All @@ -477,12 +528,12 @@
{:else}
<circle
fill={getPlanetColor(planet)}
r={Math.sqrt(planet.planet_size * ($mapSettings.systemMapPlanetScale ?? 1))}
r={getPlanetRadius(planet, $mapSettings)}
cx={-planet.coordinate.x}
cy={planet.coordinate.y}
/>
<path
d={getShadowPath(planet, $mapSettings.systemMapPlanetScale ?? 1)}
d={getShadowPath(planet, $mapSettings)}
transform=" translate({-planet.coordinate.x} {planet.coordinate
.y}) rotate({getShadowRotation(planet)})"
fill="#000000"
Expand Down Expand Up @@ -514,5 +565,46 @@
</textPath>
</text>
{/each}
{#if $mapSettings.systemMapHyperlanesEnabled}
{#each systemConnections as connection}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<g
style={!previewMode && !exportMode ? 'cursor: pointer;' : undefined}
fill={colors.dark_teal}
on:click={() => {
if (previewMode || exportMode) return;
resetZoom();
onSystemSelected?.(connection.system);
}}
>
<path d={connection.trianglePath} />
<defs>
<path
id="connectionLabelPath{connection.system.id}"
pathLength="1"
d={connection.textPath}
/>
</defs>
<text
font-family={$mapSettings.systemMapLabelPlanetsFont}
font-size={$mapSettings.systemMapLabelPlanetsFontSize}
>
<textPath
href="#connectionLabelPath{connection.system.id}"
startOffset="0.5"
text-anchor="middle"
dominant-baseline={connection.y < 0 ? 'hanging' : 'auto'}
>
{#await localizeText(connection.system.name)}
{$t('generic.loading')}
{:then systemName}
{systemName}
{/await}
</textPath>
</text>
</g>
{/each}
{/if}
</g>
</svg>
2 changes: 2 additions & 0 deletions src/renderer/src/lib/settings/mapSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export type BooleanMapSettings =
| 'systemMapLabelPlanetsEnabled'
| 'systemMapLabelMoonsEnabled'
| 'systemMapLabelAsteroidsEnabled'
| 'systemMapHyperlanesEnabled'
| 'terraIncognita'
| 'unionLeaderUnderline'
| 'unionMode';
Expand Down Expand Up @@ -449,6 +450,7 @@ export const defaultMapSettings: MapSettings = {
systemMapLabelPlanetsEnabled: false,
systemMapLabelMoonsEnabled: false,
systemMapLabelAsteroidsEnabled: false,
systemMapHyperlanesEnabled: true,
};

export const mapSettings = localStorageStore('mapSettings', defaultMapSettings);
Expand Down
4 changes: 4 additions & 0 deletions src/renderer/src/lib/settings/mapSettingsConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,10 @@ export const mapSettingsConfig: MapSettingConfigGroup[] = [
id: 'systemMapLabelAsteroidsEnabled',
type: 'toggle',
},
{
id: 'systemMapHyperlanesEnabled',
type: 'toggle',
},
],
},
];

0 comments on commit d9d4816

Please sign in to comment.