2714 lines
64 KiB
HTML
2714 lines
64 KiB
HTML
<!doctype html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Editable Star Map</title>
|
|
<style>
|
|
:root {
|
|
--panel: #111827;
|
|
--panel-2: #0b1220;
|
|
--line: #d6b21e;
|
|
--line-alt: #3487d9;
|
|
--line-muted: #7f8fa6;
|
|
--text: #eef6ff;
|
|
--muted: #9ba9bd;
|
|
--accent: #69b7ff;
|
|
--danger: #ef4444;
|
|
--ok: #30c48d;
|
|
--border: rgba(255, 255, 255, 0.16);
|
|
font-family: Inter, Segoe UI, Roboto, Arial, sans-serif;
|
|
}
|
|
|
|
* {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
margin: 0;
|
|
min-height: 100vh;
|
|
background: #05080f;
|
|
color: var(--text);
|
|
overflow: hidden;
|
|
}
|
|
|
|
button,
|
|
input,
|
|
textarea,
|
|
select {
|
|
font: inherit;
|
|
}
|
|
|
|
.app {
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 1fr) 320px;
|
|
grid-template-rows: auto minmax(0, 1fr);
|
|
height: 100vh;
|
|
}
|
|
|
|
.toolbar {
|
|
grid-column: 1 / -1;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 10px 12px;
|
|
background: linear-gradient(180deg, #121a2a, #0d1422);
|
|
border-bottom: 1px solid var(--border);
|
|
min-width: 0;
|
|
}
|
|
|
|
.brand {
|
|
font-weight: 800;
|
|
margin-right: 8px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.tool-group {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding-right: 10px;
|
|
border-right: 1px solid var(--border);
|
|
min-width: 0;
|
|
}
|
|
|
|
.tool-button,
|
|
.action-button {
|
|
border: 1px solid var(--border);
|
|
color: var(--text);
|
|
background: #172033;
|
|
padding: 8px 10px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
min-height: 36px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.tool-button:hover,
|
|
.action-button:hover {
|
|
border-color: rgba(105, 183, 255, 0.65);
|
|
}
|
|
|
|
.tool-button.is-active {
|
|
background: #1d4f7a;
|
|
border-color: #72c6ff;
|
|
box-shadow: 0 0 0 2px rgba(105, 183, 255, 0.18) inset;
|
|
}
|
|
|
|
.danger {
|
|
background: #35151c;
|
|
color: #ffe8ec;
|
|
border-color: rgba(239, 68, 68, 0.45);
|
|
}
|
|
|
|
.background-label {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 7px;
|
|
color: var(--muted);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
input[type="color"] {
|
|
width: 36px;
|
|
height: 34px;
|
|
padding: 2px;
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
background: #172033;
|
|
}
|
|
|
|
.map-wrap {
|
|
position: relative;
|
|
min-width: 0;
|
|
min-height: 0;
|
|
background: #05080f;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.map-wrap::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 0;
|
|
background:
|
|
radial-gradient(circle at 19% 42%, rgba(38, 126, 255, 0.26), transparent 23%),
|
|
radial-gradient(circle at 52% 76%, rgba(241, 147, 28, 0.2), transparent 22%),
|
|
radial-gradient(circle at 88% 27%, rgba(23, 190, 165, 0.22), transparent 24%),
|
|
radial-gradient(circle at 7% 82%, rgba(151, 46, 122, 0.16), transparent 19%);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.map {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 100%;
|
|
display: block;
|
|
touch-action: none;
|
|
background-color: var(--map-bg, #05080f);
|
|
}
|
|
|
|
.sector-border {
|
|
fill: none;
|
|
stroke: rgba(145, 170, 204, 0.42);
|
|
stroke-width: 2;
|
|
stroke-dasharray: 8 4;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.grid-line {
|
|
stroke: rgba(145, 170, 204, 0.16);
|
|
stroke-width: 1;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.grid-label {
|
|
fill: rgba(220, 235, 255, 0.45);
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
text-anchor: middle;
|
|
pointer-events: none;
|
|
user-select: none;
|
|
}
|
|
|
|
.connection {
|
|
stroke: var(--line);
|
|
stroke-width: 1.5;
|
|
opacity: 0.7;
|
|
cursor: pointer;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.connection.trade {
|
|
stroke: var(--line-alt);
|
|
opacity: 0.85;
|
|
}
|
|
|
|
.connection.neutral {
|
|
stroke: var(--line-muted);
|
|
opacity: 0.75;
|
|
}
|
|
|
|
.connection-hit {
|
|
stroke: transparent;
|
|
stroke-width: 28;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.connection.is-selected {
|
|
stroke: #ffffff;
|
|
stroke-width: 3;
|
|
opacity: 1;
|
|
}
|
|
|
|
.system {
|
|
cursor: grab;
|
|
}
|
|
|
|
.system:active {
|
|
cursor: grabbing;
|
|
}
|
|
|
|
.system-hit {
|
|
fill: transparent;
|
|
stroke: transparent;
|
|
pointer-events: all;
|
|
}
|
|
|
|
.system-core {
|
|
fill: #d9f0ff;
|
|
stroke: #81c8ff;
|
|
stroke-width: 2;
|
|
filter: drop-shadow(0 0 7px #62b9ff) drop-shadow(0 0 16px rgba(100, 179, 255, 0.8));
|
|
}
|
|
|
|
.system.is-selected .system-core {
|
|
stroke: #ffffff;
|
|
stroke-width: 4;
|
|
filter: drop-shadow(0 0 9px #ffffff) drop-shadow(0 0 20px #55baff);
|
|
}
|
|
|
|
.system.is-pending .system-core {
|
|
stroke: #ffd65a;
|
|
stroke-width: 4;
|
|
}
|
|
|
|
.system-label {
|
|
fill: #ffffff;
|
|
stroke: rgba(0, 0, 0, 0.88);
|
|
stroke-width: 4;
|
|
paint-order: stroke;
|
|
font-size: 19px;
|
|
font-weight: 800;
|
|
text-anchor: middle;
|
|
pointer-events: auto;
|
|
user-select: none;
|
|
cursor: default;
|
|
}
|
|
|
|
.sidebar {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 14px;
|
|
min-width: 0;
|
|
min-height: 0;
|
|
padding: 14px;
|
|
background: var(--panel);
|
|
border-left: 1px solid var(--border);
|
|
overflow: auto;
|
|
}
|
|
|
|
.section {
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
background: var(--panel-2);
|
|
padding: 12px;
|
|
}
|
|
|
|
.section h2 {
|
|
margin: 0 0 10px;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.field {
|
|
display: grid;
|
|
gap: 6px;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.field label {
|
|
color: var(--muted);
|
|
font-size: 13px;
|
|
}
|
|
|
|
.text-input,
|
|
textarea,
|
|
select {
|
|
width: 100%;
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
background: #09101d;
|
|
color: var(--text);
|
|
padding: 9px 10px;
|
|
outline: none;
|
|
}
|
|
|
|
textarea {
|
|
min-height: 140px;
|
|
resize: vertical;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.hint,
|
|
.selection-meta {
|
|
color: var(--muted);
|
|
font-size: 13px;
|
|
line-height: 1.45;
|
|
}
|
|
|
|
.selection-meta {
|
|
margin: 0;
|
|
}
|
|
|
|
.hidden {
|
|
display: none;
|
|
}
|
|
|
|
.action-row {
|
|
display: flex;
|
|
gap: 8px;
|
|
flex-wrap: wrap;
|
|
margin-top: 12px;
|
|
}
|
|
|
|
.status {
|
|
margin-left: auto;
|
|
color: var(--muted);
|
|
font-size: 13px;
|
|
min-width: 160px;
|
|
text-align: right;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.mobile-note {
|
|
display: none;
|
|
}
|
|
|
|
@media (max-width: 880px) {
|
|
body {
|
|
overflow: auto;
|
|
}
|
|
|
|
.app {
|
|
grid-template-columns: 1fr;
|
|
grid-template-rows: auto 68vh auto;
|
|
min-height: 100vh;
|
|
height: auto;
|
|
}
|
|
|
|
.toolbar {
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.sidebar {
|
|
border-left: 0;
|
|
border-top: 1px solid var(--border);
|
|
}
|
|
|
|
.status {
|
|
text-align: left;
|
|
margin-left: 0;
|
|
flex-basis: 100%;
|
|
}
|
|
|
|
.mobile-note {
|
|
display: block;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="app">
|
|
<header class="toolbar">
|
|
<div class="brand">Star Map Editor</div>
|
|
<div class="tool-group" role="group" aria-label="Werkzeuge">
|
|
<button class="tool-button is-active" data-mode="select" type="button" title="Auswaehlen und verschieben">Auswahl</button>
|
|
<button class="tool-button" data-mode="add" type="button" title="In die Karte klicken, um ein System anzulegen">Punkt</button>
|
|
<button class="tool-button" data-mode="connect" type="button" title="Zwei Systeme anklicken, um sie zu verbinden">Verbindung</button>
|
|
</div>
|
|
<div class="tool-group">
|
|
<button class="action-button danger" id="deleteSelected" type="button">Loeschen</button>
|
|
<button class="action-button" id="resetMap" type="button">Vorlage laden</button>
|
|
</div>
|
|
<div class="tool-group" role="group" aria-label="Zoom">
|
|
<button class="action-button" id="zoomOut" type="button" title="Rauszoomen">-</button>
|
|
<button class="action-button" id="resetZoom" type="button" title="Zoom zuruecksetzen">1:1</button>
|
|
<button class="action-button" id="zoomIn" type="button" title="Ranzoomen">+</button>
|
|
</div>
|
|
<div class="tool-group">
|
|
<label class="background-label" for="backgroundColor">Hintergrund <input id="backgroundColor" type="color" value="#05080f"></label>
|
|
<span class="background-label">Raster aktiv</span>
|
|
</div>
|
|
<div class="status" id="status">Bereit</div>
|
|
</header>
|
|
|
|
<main class="map-wrap" id="mapWrap">
|
|
<svg class="map" id="map" viewBox="0 0 1296 1296" aria-label="Editierbare Sternenkarte">
|
|
<defs>
|
|
<radialGradient id="nodeGlow" cx="50%" cy="50%" r="55%">
|
|
<stop offset="0%" stop-color="#ffffff"></stop>
|
|
<stop offset="42%" stop-color="#9bd5ff"></stop>
|
|
<stop offset="100%" stop-color="#2b6dad"></stop>
|
|
</radialGradient>
|
|
</defs>
|
|
<g id="gridLayer"></g>
|
|
<rect x="55" y="59" width="984" height="984" class="sector-border"></rect>
|
|
<g id="connections"></g>
|
|
<g id="connectionHits"></g>
|
|
<g id="systems"></g>
|
|
</svg>
|
|
</main>
|
|
|
|
<aside class="sidebar">
|
|
<section class="section">
|
|
<h2>Auswahl</h2>
|
|
<p class="selection-meta" id="selectionMeta">Kein Objekt ausgewaehlt.</p>
|
|
<div id="systemEditor" class="hidden">
|
|
<div class="field">
|
|
<label for="systemName">Systemname</label>
|
|
<input class="text-input" id="systemName" type="text" autocomplete="off">
|
|
</div>
|
|
<div class="field">
|
|
<label for="systemColor">Farbe</label>
|
|
<input id="systemColor" type="color" value="#d9f0ff">
|
|
</div>
|
|
<div class="field">
|
|
<label for="systemNote">Notiz</label>
|
|
<textarea id="systemNote" placeholder="Notiz fuer dieses System"></textarea>
|
|
</div>
|
|
</div>
|
|
<div id="connectionEditor" class="hidden">
|
|
<div class="field">
|
|
<label for="connectionType">Linientyp</label>
|
|
<select id="connectionType">
|
|
<option value="standard">Gold</option>
|
|
<option value="trade">Blau</option>
|
|
<option value="neutral">Grau</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="action-row">
|
|
<button class="action-button" id="centerSelection" type="button">Zentrieren</button>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<h2>Bearbeitung</h2>
|
|
<p class="hint">Auswahl: System anklicken, Name/Notiz bearbeiten oder Punkt ziehen. Punkt: freie Stelle anklicken. Verbindung: zwei Systeme nacheinander anklicken. Neue und verschobene Systeme rasten immer auf die naechste Rasterzelle ein.</p>
|
|
<p class="hint mobile-note">Auf Touchscreens klappt Verschieben am besten im Auswahlmodus mit kurzem Halten und Ziehen.</p>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<h2>Daten</h2>
|
|
<div class="action-row">
|
|
<button class="action-button" id="saveRemote" type="button">Speichern</button>
|
|
<button class="action-button" id="loadRemote" type="button">Laden</button>
|
|
<button class="action-button" id="exportJson" type="button">Export</button>
|
|
<button class="action-button" id="importJson" type="button">Import</button>
|
|
</div>
|
|
<div class="field">
|
|
<label for="dataBox">JSON</label>
|
|
<textarea id="dataBox" placeholder="Exportierte Karte oder JSON zum Importieren"></textarea>
|
|
</div>
|
|
</section>
|
|
</aside>
|
|
</div>
|
|
|
|
<script type="application/json" id="initial-map-data">
|
|
{
|
|
"source": "universe.ini",
|
|
"background": "#05080f",
|
|
"grid": {
|
|
"minCol": 0,
|
|
"minRow": 0,
|
|
"maxCol": 16,
|
|
"maxRow": 16,
|
|
"cellSize": 72,
|
|
"margin": 72
|
|
},
|
|
"viewBox": {
|
|
"x": 0,
|
|
"y": 0,
|
|
"width": 1296,
|
|
"height": 1296
|
|
},
|
|
"systems": [
|
|
{
|
|
"id": "li05",
|
|
"nickname": "Li05",
|
|
"name": "Alaska",
|
|
"file": "systems\\Li05\\Li05.ini",
|
|
"gridX": 9,
|
|
"gridY": 7,
|
|
"stridName": 196613,
|
|
"navMapScale": 1.5,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 720,
|
|
"y": 576
|
|
},
|
|
{
|
|
"id": "iw01",
|
|
"nickname": "Iw01",
|
|
"name": "Bering",
|
|
"file": "systems\\Iw01\\Iw01.ini",
|
|
"gridX": 9,
|
|
"gridY": 9,
|
|
"stridName": 196643,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 720,
|
|
"y": 720
|
|
},
|
|
{
|
|
"id": "li02",
|
|
"nickname": "Li02",
|
|
"name": "California",
|
|
"file": "systems\\Li02\\Li02.ini",
|
|
"gridX": 6,
|
|
"gridY": 9,
|
|
"stridName": 196610,
|
|
"navMapScale": 1,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 504,
|
|
"y": 720
|
|
},
|
|
{
|
|
"id": "br02",
|
|
"nickname": "Br02",
|
|
"name": "Cambridge",
|
|
"file": "systems\\Br02\\Br02.ini",
|
|
"gridX": 4,
|
|
"gridY": 10,
|
|
"stridName": 196615,
|
|
"navMapScale": 1,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 360,
|
|
"y": 792
|
|
},
|
|
{
|
|
"id": "ku06",
|
|
"nickname": "Ku06",
|
|
"name": "Chugoku",
|
|
"file": "systems\\Ku06\\Ku06.ini",
|
|
"gridX": 7,
|
|
"gridY": 2,
|
|
"stridName": 196631,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 576,
|
|
"y": 216
|
|
},
|
|
{
|
|
"id": "li03",
|
|
"nickname": "Li03",
|
|
"name": "Colorado",
|
|
"file": "systems\\Li03\\Li03.ini",
|
|
"gridX": 7,
|
|
"gridY": 7,
|
|
"stridName": 196611,
|
|
"navMapScale": 1,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 576,
|
|
"y": 576
|
|
},
|
|
{
|
|
"id": "iw04",
|
|
"nickname": "Iw04",
|
|
"name": "Cortez",
|
|
"file": "systems\\Iw04\\Iw04.ini",
|
|
"gridX": 5,
|
|
"gridY": 9,
|
|
"stridName": 196646,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 432,
|
|
"y": 720
|
|
},
|
|
{
|
|
"id": "rh05",
|
|
"nickname": "Rh05",
|
|
"name": "Dresden",
|
|
"file": "systems\\Rh05\\Rh05.ini",
|
|
"gridX": 11,
|
|
"gridY": 12,
|
|
"stridName": 196624,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 864,
|
|
"y": 936
|
|
},
|
|
{
|
|
"id": "br06",
|
|
"nickname": "Br06",
|
|
"name": "Dublin",
|
|
"file": "systems\\Br06\\Br06.ini",
|
|
"gridX": 0,
|
|
"gridY": 10,
|
|
"stridName": 196619,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 72,
|
|
"y": 792
|
|
},
|
|
{
|
|
"id": "br05",
|
|
"nickname": "Br05",
|
|
"name": "Edinburgh",
|
|
"file": "systems\\Br05\\Br05.ini",
|
|
"gridX": 1,
|
|
"gridY": 13,
|
|
"stridName": 196618,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 144,
|
|
"y": 1008
|
|
},
|
|
{
|
|
"id": "rh02",
|
|
"nickname": "Rh02",
|
|
"name": "Frankfurt",
|
|
"file": "systems\\Rh02\\Rh02.ini",
|
|
"gridX": 10,
|
|
"gridY": 10,
|
|
"stridName": 196621,
|
|
"navMapScale": 1,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 792,
|
|
"y": 792
|
|
},
|
|
{
|
|
"id": "iw06",
|
|
"nickname": "Iw06",
|
|
"name": "Galileo",
|
|
"file": "systems\\Iw06\\Iw06.ini",
|
|
"gridX": 8,
|
|
"gridY": 6,
|
|
"stridName": 196648,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 648,
|
|
"y": 504
|
|
},
|
|
{
|
|
"id": "rh04",
|
|
"nickname": "Rh04",
|
|
"name": "Hamburg",
|
|
"file": "systems\\Rh04\\Rh04.ini",
|
|
"gridX": 12,
|
|
"gridY": 10,
|
|
"stridName": 196623,
|
|
"navMapScale": 1,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 936,
|
|
"y": 792
|
|
},
|
|
{
|
|
"id": "ku05",
|
|
"nickname": "Ku05",
|
|
"name": "Hokkaido",
|
|
"file": "systems\\Ku05\\Ku05.ini",
|
|
"gridX": 7,
|
|
"gridY": 3,
|
|
"stridName": 196630,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 576,
|
|
"y": 288
|
|
},
|
|
{
|
|
"id": "ku04",
|
|
"nickname": "Ku04",
|
|
"name": "Honshu",
|
|
"file": "systems\\Ku04\\Ku04.ini",
|
|
"gridX": 8,
|
|
"gridY": 4,
|
|
"stridName": 196629,
|
|
"navMapScale": 1,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 648,
|
|
"y": 360
|
|
},
|
|
{
|
|
"id": "iw02",
|
|
"nickname": "Iw02",
|
|
"name": "Hudson",
|
|
"file": "systems\\Iw02\\Iw02.ini",
|
|
"gridX": 9,
|
|
"gridY": 10,
|
|
"stridName": 196644,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 720,
|
|
"y": 792
|
|
},
|
|
{
|
|
"id": "iw05",
|
|
"nickname": "Iw05",
|
|
"name": "Kepler",
|
|
"file": "systems\\Iw05\\Iw05.ini",
|
|
"gridX": 6,
|
|
"gridY": 6,
|
|
"stridName": 196647,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 504,
|
|
"y": 504
|
|
},
|
|
{
|
|
"id": "ku03",
|
|
"nickname": "Ku03",
|
|
"name": "Kyushu",
|
|
"file": "systems\\Ku03\\Ku03.ini",
|
|
"gridX": 6,
|
|
"gridY": 4,
|
|
"stridName": 196628,
|
|
"navMapScale": 1,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 504,
|
|
"y": 360
|
|
},
|
|
{
|
|
"id": "br04",
|
|
"nickname": "Br04",
|
|
"name": "Leeds",
|
|
"file": "systems\\Br04\\Br04.ini",
|
|
"gridX": 2,
|
|
"gridY": 9,
|
|
"stridName": 196617,
|
|
"navMapScale": 1,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 216,
|
|
"y": 720
|
|
},
|
|
{
|
|
"id": "iw03",
|
|
"nickname": "Iw03",
|
|
"name": "Magellan",
|
|
"file": "systems\\Iw03\\Iw03.ini",
|
|
"gridX": 5,
|
|
"gridY": 10,
|
|
"stridName": 196645,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 432,
|
|
"y": 792
|
|
},
|
|
{
|
|
"id": "br03",
|
|
"nickname": "Br03",
|
|
"name": "Manchester",
|
|
"file": "systems\\Br03\\Br03.ini",
|
|
"gridX": 3,
|
|
"gridY": 12,
|
|
"stridName": 196616,
|
|
"navMapScale": 1,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 288,
|
|
"y": 936
|
|
},
|
|
{
|
|
"id": "rh01",
|
|
"nickname": "Rh01",
|
|
"name": "New Berlin",
|
|
"file": "systems\\Rh01\\Rh01.ini",
|
|
"gridX": 11,
|
|
"gridY": 11,
|
|
"stridName": 196620,
|
|
"navMapScale": 1,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 864,
|
|
"y": 864
|
|
},
|
|
{
|
|
"id": "br01",
|
|
"nickname": "Br01",
|
|
"name": "New London",
|
|
"file": "systems\\Br01\\Br01.ini",
|
|
"gridX": 2,
|
|
"gridY": 11,
|
|
"stridName": 196614,
|
|
"navMapScale": 1,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 216,
|
|
"y": 864
|
|
},
|
|
{
|
|
"id": "li06",
|
|
"nickname": "LI06",
|
|
"name": "New Nevada",
|
|
"file": "systems\\LI06\\LI06.ini",
|
|
"gridX": 6,
|
|
"gridY": 8,
|
|
"stridName": 450000,
|
|
"navMapScale": 1.82,
|
|
"visit": 1,
|
|
"note": "",
|
|
"x": 504,
|
|
"y": 648
|
|
},
|
|
{
|
|
"id": "ku01",
|
|
"nickname": "Ku01",
|
|
"name": "New Tokyo",
|
|
"file": "systems\\Ku01\\Ku01.ini",
|
|
"gridX": 7,
|
|
"gridY": 4,
|
|
"stridName": 196626,
|
|
"navMapScale": 1,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 576,
|
|
"y": 360
|
|
},
|
|
{
|
|
"id": "li01",
|
|
"nickname": "Li01",
|
|
"name": "New York",
|
|
"file": "Systems\\Li01\\Li01.ini",
|
|
"gridX": 7,
|
|
"gridY": 8,
|
|
"stridName": 196609,
|
|
"navMapScale": 1,
|
|
"visit": 1,
|
|
"note": "",
|
|
"x": 576,
|
|
"y": 648
|
|
},
|
|
{
|
|
"id": "bw04",
|
|
"nickname": "Bw04",
|
|
"name": "Omega-11",
|
|
"file": "systems\\Bw04\\Bw04.ini",
|
|
"gridX": 8,
|
|
"gridY": 13,
|
|
"stridName": 196636,
|
|
"navMapScale": 2.5,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 648,
|
|
"y": 1008
|
|
},
|
|
{
|
|
"id": "bw01",
|
|
"nickname": "Bw01",
|
|
"name": "Omega-3",
|
|
"file": "systems\\Bw01\\Bw01.ini",
|
|
"gridX": 5,
|
|
"gridY": 12,
|
|
"stridName": 196633,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 432,
|
|
"y": 936
|
|
},
|
|
{
|
|
"id": "bw05",
|
|
"nickname": "Bw05",
|
|
"name": "Omega-41",
|
|
"file": "systems\\Bw05\\Bw05.ini",
|
|
"gridX": 11,
|
|
"gridY": 6,
|
|
"stridName": 196637,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 864,
|
|
"y": 504
|
|
},
|
|
{
|
|
"id": "bw02",
|
|
"nickname": "Bw02",
|
|
"name": "Omega-5",
|
|
"file": "systems\\Bw02\\Bw02.ini",
|
|
"gridX": 5,
|
|
"gridY": 14,
|
|
"stridName": 196634,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 432,
|
|
"y": 1080
|
|
},
|
|
{
|
|
"id": "bw03",
|
|
"nickname": "Bw03",
|
|
"name": "Omega-7",
|
|
"file": "systems\\Bw03\\Bw03.ini",
|
|
"gridX": 7,
|
|
"gridY": 12,
|
|
"stridName": 196635,
|
|
"navMapScale": 3,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 576,
|
|
"y": 936
|
|
},
|
|
{
|
|
"id": "ew02",
|
|
"nickname": "Ew02",
|
|
"name": "Omicron Alpha",
|
|
"file": "systems\\Ew02\\Ew02.ini",
|
|
"gridX": 10,
|
|
"gridY": 1,
|
|
"stridName": 196650,
|
|
"navMapScale": 4,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 792,
|
|
"y": 144
|
|
},
|
|
{
|
|
"id": "hi02",
|
|
"nickname": "Hi02",
|
|
"name": "Omicron Beta",
|
|
"file": "systems\\Hi02\\Hi02.ini",
|
|
"gridX": 13,
|
|
"gridY": 14,
|
|
"stridName": 196654,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 1008,
|
|
"y": 1080
|
|
},
|
|
{
|
|
"id": "st02",
|
|
"nickname": "St02",
|
|
"name": "Omicron Beta",
|
|
"file": "systems\\St02\\St02.ini",
|
|
"gridX": 13,
|
|
"gridY": 4,
|
|
"stridName": 196656,
|
|
"navMapScale": 2,
|
|
"visit": 128,
|
|
"note": "",
|
|
"x": 1008,
|
|
"y": 360
|
|
},
|
|
{
|
|
"id": "ew03",
|
|
"nickname": "Ew03",
|
|
"name": "Omicron Gamma",
|
|
"file": "systems\\Ew03\\Ew03.ini",
|
|
"gridX": 10,
|
|
"gridY": 14,
|
|
"stridName": 196651,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 792,
|
|
"y": 1080
|
|
},
|
|
{
|
|
"id": "st04",
|
|
"nickname": "ST04",
|
|
"name": "Omicron Lambda",
|
|
"file": "systems\\St04\\St04.ini",
|
|
"gridX": 15,
|
|
"gridY": 5,
|
|
"stridName": 198004,
|
|
"navMapScale": 2.125,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 1152,
|
|
"y": 432
|
|
},
|
|
{
|
|
"id": "fp7_system",
|
|
"nickname": "FP7_system",
|
|
"name": "Omicron Major",
|
|
"file": "systems\\FP7\\FP7_system.ini",
|
|
"gridX": 16,
|
|
"gridY": 6,
|
|
"stridName": 196657,
|
|
"navMapScale": 3,
|
|
"visit": 128,
|
|
"note": "",
|
|
"x": 1224,
|
|
"y": 504
|
|
},
|
|
{
|
|
"id": "st03",
|
|
"nickname": "St03",
|
|
"name": "Omicron Major",
|
|
"file": "systems\\St03\\St03.ini",
|
|
"gridX": 14,
|
|
"gridY": 3,
|
|
"stridName": 196657,
|
|
"navMapScale": 2,
|
|
"visit": 128,
|
|
"note": "",
|
|
"x": 1080,
|
|
"y": 288
|
|
},
|
|
{
|
|
"id": "st03b",
|
|
"nickname": "St03b",
|
|
"name": "Omicron Major",
|
|
"file": "systems\\St03b\\St03b.ini",
|
|
"gridX": 15,
|
|
"gridY": 3,
|
|
"stridName": 196657,
|
|
"navMapScale": 2,
|
|
"visit": 128,
|
|
"note": "",
|
|
"x": 1152,
|
|
"y": 288
|
|
},
|
|
{
|
|
"id": "st01",
|
|
"nickname": "St01",
|
|
"name": "Omicron Minor",
|
|
"file": "systems\\St01\\St01.ini",
|
|
"gridX": 14,
|
|
"gridY": 5,
|
|
"stridName": 196655,
|
|
"navMapScale": 2,
|
|
"visit": 128,
|
|
"note": "",
|
|
"x": 1080,
|
|
"y": 432
|
|
},
|
|
{
|
|
"id": "st05",
|
|
"nickname": "St05",
|
|
"name": "Omicron Prime",
|
|
"file": "systems\\ST05\\st05.ini",
|
|
"gridX": 12,
|
|
"gridY": 16,
|
|
"stridName": 454900,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 936,
|
|
"y": 1224
|
|
},
|
|
{
|
|
"id": "ew04",
|
|
"nickname": "Ew04",
|
|
"name": "Omicron Theta",
|
|
"file": "systems\\Ew04\\Ew04.ini",
|
|
"gridX": 15,
|
|
"gridY": 12,
|
|
"stridName": 196652,
|
|
"navMapScale": 2.5,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 1152,
|
|
"y": 936
|
|
},
|
|
{
|
|
"id": "li07",
|
|
"nickname": "LI07",
|
|
"name": "Puerto Rico",
|
|
"file": "systems\\LI07\\LI07.ini",
|
|
"gridX": 4,
|
|
"gridY": 7,
|
|
"stridName": 450100,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 360,
|
|
"y": 576
|
|
},
|
|
{
|
|
"id": "ku02",
|
|
"nickname": "Ku02",
|
|
"name": "Shikoku",
|
|
"file": "systems\\Ku02\\Ku02.ini",
|
|
"gridX": 7,
|
|
"gridY": 5,
|
|
"stridName": 196627,
|
|
"navMapScale": 1,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 576,
|
|
"y": 432
|
|
},
|
|
{
|
|
"id": "bw11",
|
|
"nickname": "Bw11",
|
|
"name": "Sigma 2",
|
|
"file": "systems\\Bw11\\Bw11.ini",
|
|
"gridX": 10,
|
|
"gridY": 7,
|
|
"stridName": 196608,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 792,
|
|
"y": 576
|
|
},
|
|
{
|
|
"id": "bw06",
|
|
"nickname": "Bw06",
|
|
"name": "Sigma-13",
|
|
"file": "systems\\Bw06\\Bw06.ini",
|
|
"gridX": 12,
|
|
"gridY": 6,
|
|
"stridName": 196638,
|
|
"navMapScale": 2.5,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 936,
|
|
"y": 504
|
|
},
|
|
{
|
|
"id": "bw07",
|
|
"nickname": "Bw07",
|
|
"name": "Sigma-17",
|
|
"file": "systems\\Bw07\\Bw07.ini",
|
|
"gridX": 11,
|
|
"gridY": 4,
|
|
"stridName": 196639,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 864,
|
|
"y": 360
|
|
},
|
|
{
|
|
"id": "bw08",
|
|
"nickname": "Bw08",
|
|
"name": "Sigma-19",
|
|
"file": "systems\\Bw08\\Bw08.ini",
|
|
"gridX": 2,
|
|
"gridY": 5,
|
|
"stridName": 196640,
|
|
"navMapScale": 3,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 216,
|
|
"y": 432
|
|
},
|
|
{
|
|
"id": "rh03",
|
|
"nickname": "Rh03",
|
|
"name": "Stuttgart",
|
|
"file": "systems\\Rh03\\Rh03.ini",
|
|
"gridX": 10,
|
|
"gridY": 11,
|
|
"stridName": 196622,
|
|
"navMapScale": 1,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 792,
|
|
"y": 864
|
|
},
|
|
{
|
|
"id": "bw10",
|
|
"nickname": "Bw10",
|
|
"name": "Tau-29",
|
|
"file": "systems\\Bw10\\Bw10.ini",
|
|
"gridX": 2,
|
|
"gridY": 7,
|
|
"stridName": 196642,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 216,
|
|
"y": 576
|
|
},
|
|
{
|
|
"id": "bw09",
|
|
"nickname": "Bw09",
|
|
"name": "Tau-31",
|
|
"file": "systems\\Bw09\\Bw09.ini",
|
|
"gridX": 3,
|
|
"gridY": 6,
|
|
"stridName": 196641,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 288,
|
|
"y": 504
|
|
},
|
|
{
|
|
"id": "ew01",
|
|
"nickname": "Ew01",
|
|
"name": "Tau-37",
|
|
"file": "systems\\Ew01\\Ew01.ini",
|
|
"gridX": 4,
|
|
"gridY": 1,
|
|
"stridName": 196649,
|
|
"navMapScale": 3.5,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 360,
|
|
"y": 144
|
|
},
|
|
{
|
|
"id": "li04",
|
|
"nickname": "Li04",
|
|
"name": "Texas",
|
|
"file": "systems\\Li04\\Li04.ini",
|
|
"gridX": 8,
|
|
"gridY": 9,
|
|
"stridName": 196612,
|
|
"navMapScale": 1,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 648,
|
|
"y": 720
|
|
},
|
|
{
|
|
"id": "ku07",
|
|
"nickname": "Ku07",
|
|
"name": "Tohoku",
|
|
"file": "systems\\Ku07\\Ku07.ini",
|
|
"gridX": 9,
|
|
"gridY": 2,
|
|
"stridName": 196632,
|
|
"navMapScale": 1.7,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 720,
|
|
"y": 216
|
|
},
|
|
{
|
|
"id": "hi01",
|
|
"nickname": "Hi01",
|
|
"name": "Unknown Alpha",
|
|
"file": "systems\\Hi01\\Hi01.ini",
|
|
"gridX": 8,
|
|
"gridY": 0,
|
|
"stridName": 196653,
|
|
"navMapScale": 2,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 648,
|
|
"y": 72
|
|
},
|
|
{
|
|
"id": "st02c",
|
|
"nickname": "St02c",
|
|
"name": "Unknown Beta",
|
|
"file": "systems\\St02c\\St02c.ini",
|
|
"gridX": 13,
|
|
"gridY": 1,
|
|
"stridName": 196657,
|
|
"navMapScale": 3,
|
|
"visit": 128,
|
|
"note": "",
|
|
"x": 1008,
|
|
"y": 144
|
|
},
|
|
{
|
|
"id": "ew06",
|
|
"nickname": "Ew06",
|
|
"name": "Unknown Delta",
|
|
"file": "systems\\Ew06\\Ew06.ini",
|
|
"gridX": 15,
|
|
"gridY": 9,
|
|
"stridName": 198011,
|
|
"navMapScale": 3,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 1152,
|
|
"y": 720
|
|
},
|
|
{
|
|
"id": "ew05",
|
|
"nickname": "Ew05",
|
|
"name": "Unknown Gamma",
|
|
"file": "systems\\Ew05\\Ew05.ini",
|
|
"gridX": 15,
|
|
"gridY": 0,
|
|
"stridName": 198010,
|
|
"navMapScale": 3,
|
|
"visit": 0,
|
|
"note": "",
|
|
"x": 1152,
|
|
"y": 72
|
|
}
|
|
],
|
|
"connections": [
|
|
{
|
|
"id": "c-br01-br02",
|
|
"from": "br01",
|
|
"to": "br02",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-br01-br03",
|
|
"from": "br01",
|
|
"to": "br03",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-br01-br04",
|
|
"from": "br01",
|
|
"to": "br04",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-br01-br05",
|
|
"from": "br01",
|
|
"to": "br05",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-br02-br04",
|
|
"from": "br02",
|
|
"to": "br04",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-br02-iw03",
|
|
"from": "br02",
|
|
"to": "iw03",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-br02-iw04",
|
|
"from": "br02",
|
|
"to": "iw04",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-br03-br04",
|
|
"from": "br03",
|
|
"to": "br04",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-br03-bw01",
|
|
"from": "br03",
|
|
"to": "bw01",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-br03-bw02",
|
|
"from": "br03",
|
|
"to": "bw02",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-br04-br05",
|
|
"from": "br04",
|
|
"to": "br05",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-br04-br06",
|
|
"from": "br04",
|
|
"to": "br06",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-br04-bw10",
|
|
"from": "br04",
|
|
"to": "bw10",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-br04-iw03",
|
|
"from": "br04",
|
|
"to": "iw03",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-br06-bw10",
|
|
"from": "br06",
|
|
"to": "bw10",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw01-bw02",
|
|
"from": "bw01",
|
|
"to": "bw02",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw01-bw03",
|
|
"from": "bw01",
|
|
"to": "bw03",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-bw02-bw03",
|
|
"from": "bw02",
|
|
"to": "bw03",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw02-bw04",
|
|
"from": "bw02",
|
|
"to": "bw04",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw02-ew03",
|
|
"from": "bw02",
|
|
"to": "ew03",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw03-bw04",
|
|
"from": "bw03",
|
|
"to": "bw04",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw03-rh03",
|
|
"from": "bw03",
|
|
"to": "rh03",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-bw04-ew03",
|
|
"from": "bw04",
|
|
"to": "ew03",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw04-rh03",
|
|
"from": "bw04",
|
|
"to": "rh03",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-bw04-rh05",
|
|
"from": "bw04",
|
|
"to": "rh05",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw05-bw06",
|
|
"from": "bw05",
|
|
"to": "bw06",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw05-bw07",
|
|
"from": "bw05",
|
|
"to": "bw07",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw05-bw11",
|
|
"from": "bw05",
|
|
"to": "bw11",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw05-ku04",
|
|
"from": "bw05",
|
|
"to": "ku04",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-bw05-ku06",
|
|
"from": "bw05",
|
|
"to": "ku06",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw05-rh01",
|
|
"from": "bw05",
|
|
"to": "rh01",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw05-rh04",
|
|
"from": "bw05",
|
|
"to": "rh04",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-bw06-bw07",
|
|
"from": "bw06",
|
|
"to": "bw07",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw06-ew04",
|
|
"from": "bw06",
|
|
"to": "ew04",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw07-ew02",
|
|
"from": "bw07",
|
|
"to": "ew02",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw07-ku04",
|
|
"from": "bw07",
|
|
"to": "ku04",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-bw08-bw09",
|
|
"from": "bw08",
|
|
"to": "bw09",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw08-bw10",
|
|
"from": "bw08",
|
|
"to": "bw10",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-bw08-ew01",
|
|
"from": "bw08",
|
|
"to": "ew01",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw08-ku03",
|
|
"from": "bw08",
|
|
"to": "ku03",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-bw09-bw10",
|
|
"from": "bw09",
|
|
"to": "bw10",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-bw09-ku03",
|
|
"from": "bw09",
|
|
"to": "ku03",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-bw11-li05",
|
|
"from": "bw11",
|
|
"to": "li05",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ew01-hi01",
|
|
"from": "ew01",
|
|
"to": "hi01",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ew01-ku06",
|
|
"from": "ew01",
|
|
"to": "ku06",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ew02-hi01",
|
|
"from": "ew02",
|
|
"to": "hi01",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ew03-ew04",
|
|
"from": "ew03",
|
|
"to": "ew04",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ew03-hi02",
|
|
"from": "ew03",
|
|
"to": "hi02",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ew03-rh05",
|
|
"from": "ew03",
|
|
"to": "rh05",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ew03-st05",
|
|
"from": "ew03",
|
|
"to": "st05",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ew04-hi01",
|
|
"from": "ew04",
|
|
"to": "hi01",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ew04-hi02",
|
|
"from": "ew04",
|
|
"to": "hi02",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ew05-hi01",
|
|
"from": "ew05",
|
|
"to": "hi01",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ew05-st04",
|
|
"from": "ew05",
|
|
"to": "st04",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ew06-hi02",
|
|
"from": "ew06",
|
|
"to": "hi02",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ew06-st04",
|
|
"from": "ew06",
|
|
"to": "st04",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-iw01-iw02",
|
|
"from": "iw01",
|
|
"to": "iw02",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-iw01-li04",
|
|
"from": "iw01",
|
|
"to": "li04",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-iw01-rh02",
|
|
"from": "iw01",
|
|
"to": "rh02",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-iw02-li04",
|
|
"from": "iw02",
|
|
"to": "li04",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-iw02-rh02",
|
|
"from": "iw02",
|
|
"to": "rh02",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-iw03-iw04",
|
|
"from": "iw03",
|
|
"to": "iw04",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-iw03-li01",
|
|
"from": "iw03",
|
|
"to": "li01",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-iw03-li02",
|
|
"from": "iw03",
|
|
"to": "li02",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-iw04-li02",
|
|
"from": "iw04",
|
|
"to": "li02",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-iw04-li06",
|
|
"from": "iw04",
|
|
"to": "li06",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-iw05-iw06",
|
|
"from": "iw05",
|
|
"to": "iw06",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-iw05-ku02",
|
|
"from": "iw05",
|
|
"to": "ku02",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-iw05-li03",
|
|
"from": "iw05",
|
|
"to": "li03",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-iw06-ku02",
|
|
"from": "iw06",
|
|
"to": "ku02",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-iw06-li03",
|
|
"from": "iw06",
|
|
"to": "li03",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-ku01-ku02",
|
|
"from": "ku01",
|
|
"to": "ku02",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-ku01-ku03",
|
|
"from": "ku01",
|
|
"to": "ku03",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-ku01-ku04",
|
|
"from": "ku01",
|
|
"to": "ku04",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-ku01-ku05",
|
|
"from": "ku01",
|
|
"to": "ku05",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-ku02-ku03",
|
|
"from": "ku02",
|
|
"to": "ku03",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ku03-ku05",
|
|
"from": "ku03",
|
|
"to": "ku05",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ku04-ku06",
|
|
"from": "ku04",
|
|
"to": "ku06",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ku05-ku06",
|
|
"from": "ku05",
|
|
"to": "ku06",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ku05-ku07",
|
|
"from": "ku05",
|
|
"to": "ku07",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-ku06-ku07",
|
|
"from": "ku06",
|
|
"to": "ku07",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-li01-li02",
|
|
"from": "li01",
|
|
"to": "li02",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-li01-li03",
|
|
"from": "li01",
|
|
"to": "li03",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-li01-li04",
|
|
"from": "li01",
|
|
"to": "li04",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-li01-li05",
|
|
"from": "li01",
|
|
"to": "li05",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-li01-li06",
|
|
"from": "li01",
|
|
"to": "li06",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-li02-li04",
|
|
"from": "li02",
|
|
"to": "li04",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-li02-li06",
|
|
"from": "li02",
|
|
"to": "li06",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-li06-li07",
|
|
"from": "li06",
|
|
"to": "li07",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-rh01-rh02",
|
|
"from": "rh01",
|
|
"to": "rh02",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-rh01-rh03",
|
|
"from": "rh01",
|
|
"to": "rh03",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-rh01-rh04",
|
|
"from": "rh01",
|
|
"to": "rh04",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-rh01-rh05",
|
|
"from": "rh01",
|
|
"to": "rh05",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-rh02-rh04",
|
|
"from": "rh02",
|
|
"to": "rh04",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-rh03-rh05",
|
|
"from": "rh03",
|
|
"to": "rh05",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-rh04-rh05",
|
|
"from": "rh04",
|
|
"to": "rh05",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-st01-st02",
|
|
"from": "st01",
|
|
"to": "st02",
|
|
"type": "neutral"
|
|
},
|
|
{
|
|
"id": "c-st01-st03",
|
|
"from": "st01",
|
|
"to": "st03",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-st02-st02c",
|
|
"from": "st02",
|
|
"to": "st02c",
|
|
"type": "standard"
|
|
},
|
|
{
|
|
"id": "c-st03-st03b",
|
|
"from": "st03",
|
|
"to": "st03b",
|
|
"type": "standard"
|
|
}
|
|
]
|
|
}
|
|
</script>
|
|
|
|
<script>
|
|
const svg = document.getElementById("map");
|
|
const gridLayer = document.getElementById("gridLayer");
|
|
const sectorBorder = document.querySelector(".sector-border");
|
|
const systemLayer = document.getElementById("systems");
|
|
const connectionLayer = document.getElementById("connections");
|
|
const connectionHitLayer = document.getElementById("connectionHits");
|
|
const toolButtons = Array.from(document.querySelectorAll(".tool-button"));
|
|
const statusEl = document.getElementById("status");
|
|
const selectionMeta = document.getElementById("selectionMeta");
|
|
const systemEditor = document.getElementById("systemEditor");
|
|
const connectionEditor = document.getElementById("connectionEditor");
|
|
const systemName = document.getElementById("systemName");
|
|
const systemColor = document.getElementById("systemColor");
|
|
const systemNote = document.getElementById("systemNote");
|
|
const connectionType = document.getElementById("connectionType");
|
|
const deleteSelected = document.getElementById("deleteSelected");
|
|
const backgroundColor = document.getElementById("backgroundColor");
|
|
const zoomIn = document.getElementById("zoomIn");
|
|
const zoomOut = document.getElementById("zoomOut");
|
|
const resetZoom = document.getElementById("resetZoom");
|
|
const exportJson = document.getElementById("exportJson");
|
|
const importJson = document.getElementById("importJson");
|
|
const saveRemote = document.getElementById("saveRemote");
|
|
const loadRemote = document.getElementById("loadRemote");
|
|
const dataBox = document.getElementById("dataBox");
|
|
const resetMap = document.getElementById("resetMap");
|
|
const centerSelection = document.getElementById("centerSelection");
|
|
const storageKey = "editable-star-map-v1";
|
|
const initialData = JSON.parse(document.getElementById("initial-map-data").textContent);
|
|
|
|
let state = loadState();
|
|
let mode = "select";
|
|
let selected = null;
|
|
let pendingConnectionFrom = null;
|
|
let drag = null;
|
|
let suppressClick = false;
|
|
let currentViewBox = null;
|
|
|
|
function clone(value) {
|
|
return JSON.parse(JSON.stringify(value));
|
|
}
|
|
|
|
function loadState() {
|
|
try {
|
|
const saved = localStorage.getItem(storageKey);
|
|
if (saved) {
|
|
return JSON.parse(saved);
|
|
}
|
|
} catch (error) {
|
|
console.warn("Could not load saved map", error);
|
|
}
|
|
return clone(initialData);
|
|
}
|
|
|
|
function saveState() {
|
|
localStorage.setItem(storageKey, JSON.stringify(state));
|
|
}
|
|
|
|
function canUseServerStorage() {
|
|
return location.protocol === "http:" || location.protocol === "https:";
|
|
}
|
|
|
|
function validateMapData(next) {
|
|
if (!next || !Array.isArray(next.systems) || !Array.isArray(next.connections)) {
|
|
throw new Error("systems/connections fehlen");
|
|
}
|
|
return {
|
|
source: next.source,
|
|
background: next.background || "#05080f",
|
|
grid: next.grid,
|
|
viewBox: next.viewBox,
|
|
systems: next.systems,
|
|
connections: next.connections
|
|
};
|
|
}
|
|
|
|
function mapBounds() {
|
|
const viewBox = state.viewBox || {};
|
|
return {
|
|
x: Number.isFinite(viewBox.x) ? viewBox.x : 0,
|
|
y: Number.isFinite(viewBox.y) ? viewBox.y : 0,
|
|
width: Number.isFinite(viewBox.width) ? viewBox.width : 1174,
|
|
height: Number.isFinite(viewBox.height) ? viewBox.height : 1061
|
|
};
|
|
}
|
|
|
|
function clampViewBox(viewBox) {
|
|
const bounds = mapBounds();
|
|
const width = Math.max(160, Math.min(bounds.width, viewBox.width));
|
|
const height = Math.max(160, Math.min(bounds.height, viewBox.height));
|
|
return {
|
|
x: Math.max(bounds.x, Math.min(bounds.x + bounds.width - width, viewBox.x)),
|
|
y: Math.max(bounds.y, Math.min(bounds.y + bounds.height - height, viewBox.y)),
|
|
width,
|
|
height
|
|
};
|
|
}
|
|
|
|
function setMapViewBox(viewBox) {
|
|
currentViewBox = clampViewBox(viewBox);
|
|
svg.setAttribute("viewBox", `${currentViewBox.x} ${currentViewBox.y} ${currentViewBox.width} ${currentViewBox.height}`);
|
|
}
|
|
|
|
function resetMapViewBox() {
|
|
setMapViewBox(mapBounds());
|
|
}
|
|
|
|
function zoomMap(factor, centerPoint) {
|
|
if (!currentViewBox) {
|
|
resetMapViewBox();
|
|
}
|
|
const center = centerPoint || {
|
|
x: currentViewBox.x + currentViewBox.width / 2,
|
|
y: currentViewBox.y + currentViewBox.height / 2
|
|
};
|
|
const nextWidth = currentViewBox.width * factor;
|
|
const nextHeight = currentViewBox.height * factor;
|
|
const scaleX = nextWidth / currentViewBox.width;
|
|
const scaleY = nextHeight / currentViewBox.height;
|
|
setMapViewBox({
|
|
x: center.x - (center.x - currentViewBox.x) * scaleX,
|
|
y: center.y - (center.y - currentViewBox.y) * scaleY,
|
|
width: nextWidth,
|
|
height: nextHeight
|
|
});
|
|
}
|
|
|
|
function gridToPoint(gridX, gridY) {
|
|
const grid = state.grid;
|
|
if (!grid) {
|
|
return { x: gridX, y: gridY };
|
|
}
|
|
return {
|
|
x: grid.margin + (gridX - grid.minCol) * grid.cellSize,
|
|
y: grid.margin + (gridY - grid.minRow) * grid.cellSize
|
|
};
|
|
}
|
|
|
|
function snapPointToGrid(point) {
|
|
const grid = state.grid;
|
|
if (!grid) {
|
|
return point;
|
|
}
|
|
const gridX = Math.max(grid.minCol, Math.min(grid.maxCol, Math.round((point.x - grid.margin) / grid.cellSize + grid.minCol)));
|
|
const gridY = Math.max(grid.minRow, Math.min(grid.maxRow, Math.round((point.y - grid.margin) / grid.cellSize + grid.minRow)));
|
|
return { ...gridToPoint(gridX, gridY), gridX, gridY };
|
|
}
|
|
|
|
function clampPoint(point) {
|
|
const bounds = mapBounds();
|
|
return {
|
|
...point,
|
|
x: Math.max(bounds.x, Math.min(bounds.x + bounds.width, Math.round(point.x))),
|
|
y: Math.max(bounds.y, Math.min(bounds.y + bounds.height, Math.round(point.y)))
|
|
};
|
|
}
|
|
|
|
function findSystem(id) {
|
|
return state.systems.find((system) => system.id === id);
|
|
}
|
|
|
|
function findConnection(id) {
|
|
return state.connections.find((connection) => connection.id === id);
|
|
}
|
|
|
|
function setStatus(message) {
|
|
statusEl.textContent = message;
|
|
}
|
|
|
|
function svgPointFromEvent(event) {
|
|
const point = svg.createSVGPoint();
|
|
point.x = event.clientX;
|
|
point.y = event.clientY;
|
|
const transformed = point.matrixTransform(svg.getScreenCTM().inverse());
|
|
return clampPoint({ x: transformed.x, y: transformed.y });
|
|
}
|
|
|
|
function setMode(nextMode) {
|
|
mode = nextMode;
|
|
pendingConnectionFrom = null;
|
|
toolButtons.forEach((button) => {
|
|
button.classList.toggle("is-active", button.dataset.mode === mode);
|
|
});
|
|
if (mode === "add") {
|
|
setStatus("Punktmodus: freie Stelle anklicken.");
|
|
} else if (mode === "connect") {
|
|
setStatus("Verbindungsmodus: erstes System anklicken.");
|
|
} else {
|
|
setStatus("Auswahlmodus.");
|
|
}
|
|
render();
|
|
}
|
|
|
|
function makeSystemId(name) {
|
|
const base = name.toLowerCase()
|
|
.replace(/[^a-z0-9]+/g, "-")
|
|
.replace(/^-|-$/g, "") || "system";
|
|
let id = base;
|
|
let index = 2;
|
|
while (findSystem(id)) {
|
|
id = `${base}-${index}`;
|
|
index += 1;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
function makeConnectionId(from, to) {
|
|
const base = `c-${from}-${to}`;
|
|
let id = base;
|
|
let index = 2;
|
|
while (findConnection(id)) {
|
|
id = `${base}-${index}`;
|
|
index += 1;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
function createSystem(x, y) {
|
|
const number = state.systems.length + 1;
|
|
const point = snapPointToGrid({ x, y });
|
|
const system = {
|
|
id: makeSystemId(`Neues System ${number}`),
|
|
name: `Neues System ${number}`,
|
|
x: point.x,
|
|
y: point.y,
|
|
gridX: point.gridX,
|
|
gridY: point.gridY,
|
|
color: "#d9f0ff",
|
|
note: ""
|
|
};
|
|
state.systems.push(system);
|
|
selected = { kind: "system", id: system.id };
|
|
saveState();
|
|
render();
|
|
updateInspector();
|
|
setStatus(`${system.name} erstellt.`);
|
|
systemName.focus();
|
|
systemName.select();
|
|
return system;
|
|
}
|
|
|
|
function createConnection(from, to, type = "standard") {
|
|
if (from === to) {
|
|
setStatus("Ein System kann nicht mit sich selbst verbunden werden.");
|
|
return null;
|
|
}
|
|
const exists = state.connections.some((connection) => {
|
|
return (connection.from === from && connection.to === to) ||
|
|
(connection.from === to && connection.to === from);
|
|
});
|
|
if (exists) {
|
|
setStatus("Diese Verbindung existiert bereits.");
|
|
return null;
|
|
}
|
|
const connection = {
|
|
id: makeConnectionId(from, to),
|
|
from,
|
|
to,
|
|
type
|
|
};
|
|
state.connections.push(connection);
|
|
selected = { kind: "connection", id: connection.id };
|
|
pendingConnectionFrom = null;
|
|
saveState();
|
|
render();
|
|
updateInspector();
|
|
setStatus("Verbindung erstellt.");
|
|
return connection;
|
|
}
|
|
|
|
function deleteSelection() {
|
|
if (!selected) {
|
|
setStatus("Nichts ausgewaehlt.");
|
|
return;
|
|
}
|
|
if (selected.kind === "system") {
|
|
state.systems = state.systems.filter((system) => system.id !== selected.id);
|
|
state.connections = state.connections.filter((connection) => {
|
|
return connection.from !== selected.id && connection.to !== selected.id;
|
|
});
|
|
setStatus("System und zugehoerige Verbindungen geloescht.");
|
|
} else {
|
|
state.connections = state.connections.filter((connection) => connection.id !== selected.id);
|
|
setStatus("Verbindung geloescht.");
|
|
}
|
|
selected = null;
|
|
pendingConnectionFrom = null;
|
|
saveState();
|
|
render();
|
|
updateInspector();
|
|
}
|
|
|
|
function selectSystem(id) {
|
|
if (mode === "connect") {
|
|
if (!pendingConnectionFrom) {
|
|
pendingConnectionFrom = id;
|
|
selected = { kind: "system", id };
|
|
setStatus("Jetzt zweites System anklicken.");
|
|
render();
|
|
updateInspector();
|
|
return;
|
|
}
|
|
createConnection(pendingConnectionFrom, id);
|
|
return;
|
|
}
|
|
selected = { kind: "system", id };
|
|
pendingConnectionFrom = null;
|
|
render();
|
|
updateInspector();
|
|
}
|
|
|
|
function selectConnection(id) {
|
|
selected = { kind: "connection", id };
|
|
pendingConnectionFrom = null;
|
|
render();
|
|
updateInspector();
|
|
}
|
|
|
|
function clearSelection() {
|
|
selected = null;
|
|
pendingConnectionFrom = null;
|
|
render();
|
|
updateInspector();
|
|
}
|
|
|
|
function renderConnections() {
|
|
connectionLayer.textContent = "";
|
|
connectionHitLayer.textContent = "";
|
|
|
|
for (const connection of state.connections) {
|
|
const from = findSystem(connection.from);
|
|
const to = findSystem(connection.to);
|
|
if (!from || !to) {
|
|
continue;
|
|
}
|
|
|
|
const hit = document.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
hit.setAttribute("x1", from.x);
|
|
hit.setAttribute("y1", from.y);
|
|
hit.setAttribute("x2", to.x);
|
|
hit.setAttribute("y2", to.y);
|
|
hit.setAttribute("class", "connection-hit");
|
|
hit.dataset.id = connection.id;
|
|
hit.addEventListener("click", (event) => {
|
|
event.stopPropagation();
|
|
selectConnection(connection.id);
|
|
});
|
|
connectionHitLayer.appendChild(hit);
|
|
|
|
const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
line.setAttribute("x1", from.x);
|
|
line.setAttribute("y1", from.y);
|
|
line.setAttribute("x2", to.x);
|
|
line.setAttribute("y2", to.y);
|
|
line.setAttribute("class", `connection ${connection.type || "standard"}`);
|
|
if (selected?.kind === "connection" && selected.id === connection.id) {
|
|
line.classList.add("is-selected");
|
|
}
|
|
connectionLayer.appendChild(line);
|
|
}
|
|
}
|
|
|
|
function beginSystemDrag(system, event) {
|
|
if (mode !== "select") {
|
|
return;
|
|
}
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
const point = svgPointFromEvent(event);
|
|
drag = {
|
|
id: system.id,
|
|
startX: point.x,
|
|
startY: point.y,
|
|
systemX: system.x,
|
|
systemY: system.y,
|
|
pointerId: event.pointerId,
|
|
moved: false
|
|
};
|
|
selected = { kind: "system", id: system.id };
|
|
render();
|
|
updateInspector();
|
|
}
|
|
|
|
function handleDragMove(event) {
|
|
if (!drag) {
|
|
return;
|
|
}
|
|
const system = findSystem(drag.id);
|
|
if (!system) {
|
|
drag = null;
|
|
return;
|
|
}
|
|
|
|
event.preventDefault();
|
|
const point = svgPointFromEvent(event);
|
|
const dx = point.x - drag.startX;
|
|
const dy = point.y - drag.startY;
|
|
if (Math.abs(dx) + Math.abs(dy) > 2) {
|
|
drag.moved = true;
|
|
suppressClick = true;
|
|
}
|
|
|
|
const snappedPoint = snapPointToGrid(clampPoint({
|
|
x: drag.systemX + dx,
|
|
y: drag.systemY + dy
|
|
}));
|
|
system.x = snappedPoint.x;
|
|
system.y = snappedPoint.y;
|
|
system.gridX = snappedPoint.gridX;
|
|
system.gridY = snappedPoint.gridY;
|
|
render();
|
|
}
|
|
|
|
function handleDragEnd(event) {
|
|
if (!drag) {
|
|
return;
|
|
}
|
|
event.preventDefault();
|
|
const system = findSystem(drag.id);
|
|
if (system && drag.moved) {
|
|
selected = { kind: "system", id: system.id };
|
|
saveState();
|
|
updateInspector();
|
|
setStatus(`${system.name} verschoben.`);
|
|
}
|
|
drag = null;
|
|
window.setTimeout(() => {
|
|
suppressClick = false;
|
|
}, 0);
|
|
}
|
|
|
|
function stopLabelEvent(event) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
}
|
|
|
|
function renderSystems() {
|
|
systemLayer.textContent = "";
|
|
|
|
for (const system of state.systems) {
|
|
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
group.setAttribute("class", "system");
|
|
group.setAttribute("transform", `translate(${system.x} ${system.y})`);
|
|
group.dataset.id = system.id;
|
|
if (selected?.kind === "system" && selected.id === system.id) {
|
|
group.classList.add("is-selected");
|
|
}
|
|
if (pendingConnectionFrom === system.id) {
|
|
group.classList.add("is-pending");
|
|
}
|
|
|
|
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
circle.setAttribute("r", "8");
|
|
circle.setAttribute("class", "system-core");
|
|
circle.style.fill = system.color || "#d9f0ff";
|
|
|
|
const label = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
label.setAttribute("class", "system-label");
|
|
label.setAttribute("y", "-22");
|
|
label.textContent = system.name;
|
|
label.addEventListener("pointerdown", stopLabelEvent);
|
|
label.addEventListener("click", stopLabelEvent);
|
|
label.addEventListener("dblclick", stopLabelEvent);
|
|
|
|
const hit = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
hit.setAttribute("r", "24");
|
|
hit.setAttribute("class", "system-hit");
|
|
|
|
group.append(hit, circle, label);
|
|
group.addEventListener("pointerdown", (event) => {
|
|
beginSystemDrag(system, event);
|
|
});
|
|
group.addEventListener("click", (event) => {
|
|
event.stopPropagation();
|
|
if (suppressClick) {
|
|
return;
|
|
}
|
|
selectSystem(system.id);
|
|
});
|
|
|
|
systemLayer.appendChild(group);
|
|
}
|
|
}
|
|
|
|
function render() {
|
|
if (!currentViewBox) {
|
|
currentViewBox = mapBounds();
|
|
}
|
|
svg.setAttribute("viewBox", `${currentViewBox.x} ${currentViewBox.y} ${currentViewBox.width} ${currentViewBox.height}`);
|
|
document.documentElement.style.setProperty("--map-bg", state.background || "#05080f");
|
|
backgroundColor.value = state.background || "#05080f";
|
|
renderGrid();
|
|
renderConnections();
|
|
renderSystems();
|
|
}
|
|
|
|
function renderGrid() {
|
|
gridLayer.textContent = "";
|
|
const bounds = mapBounds();
|
|
sectorBorder.setAttribute("x", bounds.x + 8);
|
|
sectorBorder.setAttribute("y", bounds.y + 8);
|
|
sectorBorder.setAttribute("width", Math.max(0, bounds.width - 16));
|
|
sectorBorder.setAttribute("height", Math.max(0, bounds.height - 16));
|
|
|
|
const grid = state.grid;
|
|
if (!grid) {
|
|
return;
|
|
}
|
|
|
|
for (let col = grid.minCol; col <= grid.maxCol; col += 1) {
|
|
const point = gridToPoint(col, grid.minRow);
|
|
const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
line.setAttribute("x1", point.x);
|
|
line.setAttribute("y1", bounds.y + 20);
|
|
line.setAttribute("x2", point.x);
|
|
line.setAttribute("y2", bounds.y + bounds.height - 20);
|
|
line.setAttribute("class", "grid-line");
|
|
gridLayer.appendChild(line);
|
|
|
|
const label = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
label.setAttribute("x", point.x);
|
|
label.setAttribute("y", bounds.y + 32);
|
|
label.setAttribute("class", "grid-label");
|
|
label.textContent = col;
|
|
gridLayer.appendChild(label);
|
|
}
|
|
|
|
for (let row = grid.minRow; row <= grid.maxRow; row += 1) {
|
|
const point = gridToPoint(grid.minCol, row);
|
|
const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
line.setAttribute("x1", bounds.x + 20);
|
|
line.setAttribute("y1", point.y);
|
|
line.setAttribute("x2", bounds.x + bounds.width - 20);
|
|
line.setAttribute("y2", point.y);
|
|
line.setAttribute("class", "grid-line");
|
|
gridLayer.appendChild(line);
|
|
|
|
const label = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
label.setAttribute("x", bounds.x + 32);
|
|
label.setAttribute("y", point.y + 4);
|
|
label.setAttribute("class", "grid-label");
|
|
label.textContent = row;
|
|
gridLayer.appendChild(label);
|
|
}
|
|
}
|
|
|
|
function updateInspector() {
|
|
systemEditor.classList.add("hidden");
|
|
connectionEditor.classList.add("hidden");
|
|
|
|
if (!selected) {
|
|
selectionMeta.textContent = "Kein Objekt ausgewaehlt.";
|
|
return;
|
|
}
|
|
|
|
if (selected.kind === "system") {
|
|
const system = findSystem(selected.id);
|
|
if (!system) {
|
|
clearSelection();
|
|
return;
|
|
}
|
|
const gridInfo = Number.isFinite(system.gridX) && Number.isFinite(system.gridY)
|
|
? ` Raster ${system.gridX}, ${system.gridY}`
|
|
: ` ${system.x}, ${system.y}`;
|
|
selectionMeta.textContent = `System: ${system.name} (${gridInfo})`;
|
|
systemName.value = system.name;
|
|
systemColor.value = system.color || "#d9f0ff";
|
|
systemNote.value = system.note || "";
|
|
systemEditor.classList.remove("hidden");
|
|
}
|
|
|
|
if (selected.kind === "connection") {
|
|
const connection = findConnection(selected.id);
|
|
if (!connection) {
|
|
clearSelection();
|
|
return;
|
|
}
|
|
const from = findSystem(connection.from);
|
|
const to = findSystem(connection.to);
|
|
selectionMeta.textContent = `Verbindung: ${from?.name || connection.from} -> ${to?.name || connection.to}`;
|
|
connectionType.value = connection.type || "standard";
|
|
connectionEditor.classList.remove("hidden");
|
|
}
|
|
}
|
|
|
|
function exportMap() {
|
|
dataBox.value = JSON.stringify(state, null, 2);
|
|
dataBox.focus();
|
|
dataBox.select();
|
|
setStatus("Karte exportiert.");
|
|
}
|
|
|
|
function importMap() {
|
|
try {
|
|
state = validateMapData(JSON.parse(dataBox.value));
|
|
selected = null;
|
|
pendingConnectionFrom = null;
|
|
currentViewBox = mapBounds();
|
|
saveState();
|
|
render();
|
|
updateInspector();
|
|
setStatus("Karte importiert.");
|
|
} catch (error) {
|
|
setStatus(`Import fehlgeschlagen: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
async function saveRemoteMap() {
|
|
if (!canUseServerStorage()) {
|
|
setStatus("Server-Speichern braucht den Node-Server.");
|
|
exportMap();
|
|
return;
|
|
}
|
|
try {
|
|
const response = await fetch("/api/map", {
|
|
method: "PUT",
|
|
headers: { "content-type": "application/json" },
|
|
body: JSON.stringify(state)
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}`);
|
|
}
|
|
setStatus("Karte dauerhaft gespeichert.");
|
|
} catch (error) {
|
|
setStatus(`Speichern fehlgeschlagen: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
async function loadRemoteMap(options = {}) {
|
|
if (!canUseServerStorage()) {
|
|
if (!options.silent) {
|
|
setStatus("Server-Laden braucht den Node-Server.");
|
|
}
|
|
return;
|
|
}
|
|
try {
|
|
const response = await fetch("/api/map", { headers: { accept: "application/json" } });
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}`);
|
|
}
|
|
state = validateMapData(await response.json());
|
|
selected = null;
|
|
pendingConnectionFrom = null;
|
|
currentViewBox = mapBounds();
|
|
saveState();
|
|
render();
|
|
updateInspector();
|
|
if (!options.silent) {
|
|
setStatus("Gespeicherte Karte geladen.");
|
|
}
|
|
} catch (error) {
|
|
if (!options.silent) {
|
|
setStatus(`Laden fehlgeschlagen: ${error.message}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
function centerOnSelection() {
|
|
if (!selected || selected.kind !== "system") {
|
|
setStatus("Zum Zentrieren bitte ein System auswaehlen.");
|
|
return;
|
|
}
|
|
const system = findSystem(selected.id);
|
|
if (!system) {
|
|
return;
|
|
}
|
|
const width = 420;
|
|
const height = 320;
|
|
setMapViewBox({
|
|
x: system.x - width / 2,
|
|
y: system.y - height / 2,
|
|
width,
|
|
height
|
|
});
|
|
setStatus(`${system.name} zentriert. Doppelklick auf Hintergrund setzt Ansicht zurueck.`);
|
|
}
|
|
|
|
toolButtons.forEach((button) => {
|
|
button.addEventListener("click", () => setMode(button.dataset.mode));
|
|
});
|
|
|
|
svg.addEventListener("click", (event) => {
|
|
if (event.target !== svg) {
|
|
return;
|
|
}
|
|
if (mode === "add") {
|
|
const point = svgPointFromEvent(event);
|
|
createSystem(point.x, point.y);
|
|
return;
|
|
}
|
|
clearSelection();
|
|
});
|
|
|
|
svg.addEventListener("dblclick", () => {
|
|
resetMapViewBox();
|
|
setStatus("Ansicht zurueckgesetzt.");
|
|
});
|
|
|
|
systemName.addEventListener("input", () => {
|
|
if (selected?.kind !== "system") {
|
|
return;
|
|
}
|
|
const system = findSystem(selected.id);
|
|
if (!system) {
|
|
return;
|
|
}
|
|
system.name = systemName.value.trim() || "Unbenannt";
|
|
saveState();
|
|
render();
|
|
updateInspector();
|
|
});
|
|
|
|
systemColor.addEventListener("input", () => {
|
|
if (selected?.kind !== "system") {
|
|
return;
|
|
}
|
|
const system = findSystem(selected.id);
|
|
if (!system) {
|
|
return;
|
|
}
|
|
system.color = systemColor.value;
|
|
saveState();
|
|
render();
|
|
});
|
|
|
|
systemNote.addEventListener("input", () => {
|
|
if (selected?.kind !== "system") {
|
|
return;
|
|
}
|
|
const system = findSystem(selected.id);
|
|
if (!system) {
|
|
return;
|
|
}
|
|
system.note = systemNote.value;
|
|
saveState();
|
|
});
|
|
|
|
connectionType.addEventListener("change", () => {
|
|
if (selected?.kind !== "connection") {
|
|
return;
|
|
}
|
|
const connection = findConnection(selected.id);
|
|
if (!connection) {
|
|
return;
|
|
}
|
|
connection.type = connectionType.value;
|
|
saveState();
|
|
render();
|
|
updateInspector();
|
|
});
|
|
|
|
backgroundColor.addEventListener("input", () => {
|
|
state.background = backgroundColor.value;
|
|
saveState();
|
|
render();
|
|
});
|
|
|
|
zoomIn.addEventListener("click", () => zoomMap(0.78));
|
|
zoomOut.addEventListener("click", () => zoomMap(1.28));
|
|
resetZoom.addEventListener("click", () => {
|
|
resetMapViewBox();
|
|
setStatus("Zoom zurueckgesetzt.");
|
|
});
|
|
|
|
deleteSelected.addEventListener("click", deleteSelection);
|
|
exportJson.addEventListener("click", exportMap);
|
|
importJson.addEventListener("click", importMap);
|
|
saveRemote.addEventListener("click", saveRemoteMap);
|
|
loadRemote.addEventListener("click", () => loadRemoteMap());
|
|
centerSelection.addEventListener("click", centerOnSelection);
|
|
resetMap.addEventListener("click", () => {
|
|
state = clone(initialData);
|
|
selected = null;
|
|
pendingConnectionFrom = null;
|
|
currentViewBox = mapBounds();
|
|
saveState();
|
|
render();
|
|
updateInspector();
|
|
setStatus("Vorlage neu geladen.");
|
|
});
|
|
|
|
window.addEventListener("keydown", (event) => {
|
|
if (event.key === "Delete" || event.key === "Backspace") {
|
|
const tag = document.activeElement?.tagName;
|
|
if (tag === "INPUT" || tag === "TEXTAREA") {
|
|
return;
|
|
}
|
|
deleteSelection();
|
|
}
|
|
if (event.key === "Escape") {
|
|
clearSelection();
|
|
setMode("select");
|
|
}
|
|
});
|
|
window.addEventListener("pointermove", handleDragMove);
|
|
window.addEventListener("pointerup", handleDragEnd);
|
|
window.addEventListener("pointercancel", handleDragEnd);
|
|
svg.addEventListener("wheel", (event) => {
|
|
event.preventDefault();
|
|
zoomMap(event.deltaY < 0 ? 0.84 : 1.18, svgPointFromEvent(event));
|
|
}, { passive: false });
|
|
|
|
render();
|
|
updateInspector();
|
|
loadRemoteMap({ silent: true });
|
|
</script>
|
|
</body>
|
|
</html>
|