Canvas rewrite

-Full rewrite to use Canvas instead of SVG
-Restructured backend
This commit is contained in:
Pyro Nicampt committed 2025-03-15 18:39:46 -05:00
1 parent 856320c976
commit 45d9e8d577
13 files changed
+1604 -1048

No files matched your search

+52
View File
@@ -1,5 +1,6 @@
'use strict';
import {Color} from './js/colorlib.js';
import {SpriteBounds} from './js/spritelib.js';
export const gradeColors = {
grade_flat: '#fff',
@@ -40,3 +41,54 @@ export const sidingUsageMeanings = {
SP:'Passenger Car Storage',
D:'Service & Repair',
}
export const bezierGradeResolution = 80;
export const bezierLengthResolution = 80;
export const bezierCurvatureResolution = 80;
export const scrollZoomFactor = 1.25;
export const buttonZoomFactor = 1.5;
export const minZoomLevel = 0.01;
export const maxZoomLevel = 60;
export const defaultMapPadding = 500;
export const maxZoneLength = 700;
/** How many pixels around the view is still considered "visible", beyond this and items are culled. */
export const viewCullMargin = 60;
export const tooltipHitzone = {
default: {radius:12},
junction: {width:24, height:35},
};
export const spriteBounds = {
unknown: new SpriteBounds(0,0,20,20),
junction: new SpriteBounds(0,20,20,20),
gradeArrow: new SpriteBounds(20,20,20,20),
gradeArrowDown: new SpriteBounds(40,20,20,20),
speed_1: new SpriteBounds(20,40,20,20),
speed_2: new SpriteBounds(40,40,20,20),
speed_3: new SpriteBounds(60,40,20,20),
speed_4: new SpriteBounds(20,60,20,20),
speed_5: new SpriteBounds(40,60,20,20),
speed_6: new SpriteBounds(60,60,20,20),
speed_7: new SpriteBounds(80,60,20,20),
speed_8: new SpriteBounds(100,60,20,20),
speed_9: new SpriteBounds(120,60,20,20),
speed_10: new SpriteBounds(140,60,20,20),
speed_12: new SpriteBounds(160,60,20,20),
office: new SpriteBounds(0,80,20,20),
office_military: new SpriteBounds(20,80,20,20),
office_museum: new SpriteBounds(40,80,20,20),
water: new SpriteBounds(0,100,20,20),
coal: new SpriteBounds(20,100,20,20),
landmark: new SpriteBounds(40,100,20,20),
shop: new SpriteBounds(60,100,20,20),
garage: new SpriteBounds(80,100,20,20),
demonstrator: new SpriteBounds(100,100,20,20),
service: new SpriteBounds(0,120,20,20),
service_repair: new SpriteBounds(20,120,20,20),
service_diesel: new SpriteBounds(40,120,20,20),
service_charger: new SpriteBounds(60,120,20,20),
}
+8 -210
View File
@@ -12,218 +12,11 @@
<meta property="og:description" content="Web-based map of Derail Valley">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<script type="module" src="index.js"></script>
<script type="module" src="legend.js"></script>
</head>
<body>
<div id="mapContainer">
<div id="mapNavigator">
<svg id="map" xmlns="http://www.w3.org/2000/svg" width="1000" height="1000" viewBox="0 0 10000 10000">
<style>
.fixedScale{
transform: scale(var(--markerScale));
}
.sign, .rail, .signText, .poi{
cursor: default;
}
.signText{
fill:#000;
font: 60px Arial;
font-weight: bold;
}
.signText.gradeSign{
font-size: 30px;
stroke:#fff;
stroke-width: 6px;
paint-order: stroke fill;
}
.yardSignage{
font: 200px "Noto Sans Mono";
text-anchor: middle;
}
.rail{
fill: none;
}
.rail.grade_flat{
stroke: var(--grade_flat);
}
.rail.grade_0{
stroke: var(--grade_0);
}
.rail.grade_1{
stroke: var(--grade_1);
}
.rail.grade_2{
stroke: var(--grade_2);
}
.rail.grade_3{
stroke: var(--grade_3);
}
.rail.grade_4{
stroke: var(--grade_4);
}
.rail.grade_5{
stroke: var(--grade_5);
}
.rail.grade_6{
stroke: var(--grade_6);
}
.rail.grade_7{
stroke: var(--grade_7);
}
.rail.grade_8{
stroke: var(--grade_8);
}
.rail.grade_9{
stroke: var(--grade_9);
}
.gradeSign.grade_flat{
fill: var(--grade_flat);
}
.gradeSign.grade_0{
fill: var(--grade_0);
}
.gradeSign.grade_1{
fill: var(--grade_1);
}
.gradeSign.grade_2{
fill: var(--grade_2);
}
.gradeSign.grade_3{
fill: var(--grade_3);
}
.gradeSign.grade_4{
fill: var(--grade_4);
}
.gradeSign.grade_5{
fill: var(--grade_5);
}
.gradeSign.grade_6{
fill: var(--grade_6);
}
.gradeSign.grade_7{
fill: var(--grade_7);
}
.gradeSign.grade_8{
fill: var(--grade_8);
}
.gradeSign.grade_9{
fill: var(--grade_9);
}
</style>
<style id="zoomStyle"></style>
<defs>
<g id="gradeArrow" transform="scale(30)" viewBox="0 0 10 10" >
<path d="M 0 -7 L 3.8 4 L -3.8 4 z" fill="black"/>
<path d="M 0 -5 L 3 4 L 0 5 L -3 4 z"/>
</g>
<g id="gradeArrowDownhill" transform="rotate(180) scale(30)" viewBox="0 0 10 10" >
<path d="M 0 -7 L 3.8 4 L -3.8 4 z" fill="black"/>
<path d="M 0 -5 L 3 4 L 0 5 L -3 4 z"/>
<path d="M 0 -1.9 1.91 3.83 H -1.9 z" fill="black"/>
<path d="M 0 -0.3 1.5 4.3 h -3 z"/>
</g>
<g id="speedSign" transform="scale(15)" viewBox="0 0 10 10" style="fill:#fff; stroke:#e73819">
<circle cx="0" cy="0" r="3"/>
</g>
<g id="speedSignWarning" transform="scale(15)" viewBox="0 0 10 10" style="fill:#702012; stroke:#e73819">
<circle cx="0" cy="0" r="3"/>
</g>
<g id="speedSign_1" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSignWarning"></use>
<text class="signText" style="fill:#fff;transform:translate(-15px, 20px)">1</text>
</g>
<g id="speedSign_2" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSignWarning"></use>
<text class="signText" style="fill:#fff;transform:translate(-15px, 20px)">2</text>
</g>
<g id="speedSign_3" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSignWarning"></use>
<text class="signText" style="fill:#fff;transform:translate(-15px, 20px)">3</text>
</g>
<g id="speedSign_4" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSign"></use>
<text class="signText" style="transform:translate(-15px, 20px)">4</text>
</g>
<g id="speedSign_5" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSign"></use>
<text class="signText" style="transform:translate(-16px, 20px)">5</text>
</g>
<g id="speedSign_6" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSign"></use>
<text class="signText" style="transform:translate(-15px, 20px)">6</text>
</g>
<g id="speedSign_7" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSign"></use>
<text class="signText" style="transform:translate(-15px, 20px)">7</text>
</g>
<g id="speedSign_8" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSign"></use>
<text class="signText" style="transform:translate(-16px, 20px)">8</text>
</g>
<g id="speedSign_9" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSign"></use>
<text class="signText" style="transform:translate(-15px, 20px)">9</text>
</g>
<g id="speedSign_10" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSign"></use>
<text class="signText" style="transform:translate(-35px, 20px)">10</text>
</g>
<g id="speedSign_12" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSign"></use>
<text class="signText" style="transform:translate(-35px, 20px)">12</text>
</g>
<g id="junctionSign" transform="scale(20)" viewBox="0 0 20 20">
<g transform="translate(-10 -10)">
<rect ry="1.347" y="0.539" x="5.003" height="18.922" width="9.994" style="stroke:#ebebeb;stroke-width:0.9"></rect>
<rect ry="0.989" transform="rotate(22.53)" y="-3.763" x="12.079" height="18.335" width="1.979" style="fill:#ebebeb"></rect>
</g>
</g>
<g id="mrk_office" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="M10 3.8l5.56 6.03h-1.66v5.2h-2.51v-3.93h-2.78v3.93h-2.51v-5.2h-1.66z" fill="#fff"></path>
</g>
<g id="mrk_water" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="M10 2c-1.02 2.01-7.08 14.38 0 14.38 7.08 0 1.02-12.46 0-14.38z" fill="#fff"></path>
</g>
<g id="mrk_coal" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="M10.75 2.98 7.85 4.02 5.64 7.39 5.74 7.54 7.08 7.06 10.14 9.03 10.47 8.6 13.67 7.4 14.65 8.03 14.94 7.21 13.65 4.97ZM7.07 7.6 4.18 8.64 1.96 12.01 3.49 15.33h6.78l.99-3.5-1.3-2.25zm6.61.47-2.9 1.04-.27.4.17.13 1.22 2.21-.91 3.48h5.88l.99-3.02-1.3-2.25z" fill="#fff"></path>
</g>
<g id="mrk_landmark" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="m11.39 1.43c-.2-.09-.42 0-.51.2L4.22 16.88c-.09.2 0 .42.2.5.2.09.42 0 .51-.2L11.59 1.93c.09-.2 0-.42-.2-.51zm.53.93-2.39 5.48c.21 0 .42 0 .62.01.2.01.41.02.61.03.2.01.4.03.6.05.2.02.39.04.59.06.19.02.39.05.58.08.19.03.38.06.57.1.09.02.18.04.28.05l2.12-2.56c-.01-.01-.02-.03-.04-.05C15.31 5.46 15.18 5.3 15.05 5.14 14.91 4.99 14.78 4.83 14.64 4.68 14.5 4.53 14.36 4.39 14.22 4.24 14.08 4.1 13.93 3.96 13.79 3.82 13.64 3.68 13.49 3.55 13.34 3.42 13.27 3.35 13.19 3.29 13.12 3.23 13.04 3.16 12.96 3.1 12.88 3.04 12.81 2.98 12.73 2.92 12.65 2.86 12.57 2.8 12.49 2.74 12.41 2.69 12.33 2.63 12.25 2.57 12.16 2.52 12.08 2.47 12 2.41 11.92 2.36Zm3.37 4.06-2.12 2.56c.1.02.19.04.29.06.19.04.37.08.55.13.18.04.37.09.55.14.18.05.36.1.54.15.18.05.36.1.53.16.18.05.35.11.53.17.18.06.35.12.53.17.17.06.35.12.52.18.35.12.69.25 1.04.37C18 10.15 17.76 9.79 17.51 9.44 17.27 9.09 17.02 8.74 16.78 8.39 16.65 8.22 16.53 8.05 16.4 7.88 16.28 7.7 16.15 7.53 16.02 7.36 15.9 7.2 15.77 7.03 15.64 6.86 15.53 6.71 15.41 6.57 15.29 6.42Z" fill="#fff"></path>
</g>
<g id="mrk_shop" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="M9.25 17.9V16.35C8.29 16.26 7.75 15.91 7.12 15.31 6.49 14.71 6.14 13.86 6.06 12.75l2.2-.49c.03.65.21 1.14.54 1.49.33.35.75.52 1.24.52.47 0 .81-.13 1.03-.4.23-.27.34-.55.34-.85 0-.39-.11-.71-.34-.94-.23-.24-.52-.46-.88-.65L8.66 10.62C7.91 10.23 7.33 9.75 6.9 9.2 6.48 8.65 6.27 7.92 6.27 7.02 6.27 6.37 6.41 5.8 6.68 5.31 6.97 4.8 7.35 4.4 7.83 4.1 8.32 3.8 8.63 3.61 9.25 3.54V2.1h1.65v1.48c.91.12 1.41.49 1.94 1.1.53.6.82 1.39.88 2.36L11.65 7.47C11.62 6.87 11.45 6.41 11.15 6.08 10.86 5.75 10.5 5.58 10.07 5.58 9.66 5.58 9.33 5.7 9.08 5.94 8.84 6.17 8.72 6.48 8.72 6.88c0 .36.11.65.32.86.21.21.51.42.88.63l1.63.86c.83.45 1.43.95 1.82 1.51.39.55.58 1.23.58 2.02 0 .6-.13 1.16-.4 1.67-.25.51-.63.95-1.12 1.3-.48.33-.84.54-1.53.63v1.4z" fill="#fff"></path>
</g>
<g id="mrk_garage" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="M10 2.88 2.98 6.42 2.98 15.1 5.53 15.1 5.53 8.91 14.47 8.91 14.47 15.1 17.02 15.1 17.02 6.42 10 2.88z" fill="#fff"></path>
</g>
<g id="mrk_service" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="m8.92 1.99q3.29 0 4.68 1.13 1.39 1.1 1.39 2.74 0 2.06-1.78 3.36-.67.5-1.63.91l-.38.17q-.17.07-.34 1.01-.14.91-.14 1.32-.12.12-.7.29-.55.14-.94.14-.36 0-.55-.05-.26-.43-.26-1.87 0-1.44.12-1.85 3.79-.91 3.79-3.07 0-1.87-3.46-1.87-1.22 0-1.78.24-.14-.24-.14-1.01 0-.77.22-1.3.65-.29 1.9-.29zm-.7 15.5q-.46-.48-.46-1.15 0-.67.46-1.13.48-.48 1.15-.48.67 0 1.13.48.48.46.48 1.13 0 .67-.48 1.15-.46.46-1.13.46-.67 0-1.15-.46z" fill="#fff"></path>
</g>
<g id="mrk_service_repair" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="m12.88 2.82c-.1.01-.2.05-.28.13l-2.19 2.19c-.15.16-.17.39-.03.53l1.12 1.12c.29-.29.37.07.57.58l-1.61 1.6 1.77 1.72 1.7-1.7c.44.03.82-.06.99.11.22.22.19.59-.06.85l-.02.02c1.48 1.26 3.85-1.11 2.59-2.59l-.02.02c-.25.25-.63.28-.85.06-.3-.3.23-1.31-.61-2.15l-.1-.1c-.73-.74-2.07-.7-1.6-1.18l-1.12-1.12c-.06-.06-.16-.09-.25-.09zM5.63 3.68c-.28 0-.55.05-.83.14l2.02 2.02-1.85 1.86-2.02-2.02c-.72 2.23 1.45 4.34 3.67 3.55l7.27 7.27c.46.47 1.22.47 1.71-.02.49-.49.5-1.25.03-1.72L8.36 7.49c.69-1.94-.84-3.84-2.73-3.81zm2.41 7.72-3.36 3.35c-.47.47-.47 1.23.02 1.72.49.49 1.25.49 1.72.02l3.38-3.38z" fill="#fff"></path>
</g>
<g id="mrk_service_diesel" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="m12.67 2.93c-.5-.01-1 .09-1.46.31L9.86 3.88 9.76 3.66 7.64 4.67 7.18 4.02C7.09 3.89 6.92 3.86 6.79 3.95L5.55 4.8C5.43 4.89 5.4 5.07 5.48 5.2L5.87 5.75 5.51 6.22 2.7 10.29c-.2.3-.18.7.06.97-.4.74-.49 1.54-.3 2.26.22.86.77 1.6 1.5 2.16 1.46 1.12 3.68 1.56 5.63.5 1.07-.59 2.19-.79 2.99-.64.8.14 1.26.52 1.39 1.32.05.33.36.55.69.5.33-.05.55-.36.5-.69-.2-1.27-1.2-2.1-2.36-2.31-1.16-.21-2.51.08-3.78.77C7.53 15.95 5.8 15.6 4.69 14.74 4.14 14.32 3.76 13.77 3.62 13.23 3.52 12.83 3.53 12.42 3.73 11.99l1.39.96c.81.56 1.93.36 2.49-.46L10.41 8.46 9.88 5.6 10.53 5.29 10.42 5.05 11.76 4.42C12.2 4.21 12.7 4.17 13.17 4.32L16.71 5.44 17.1 4.2 13.56 3.08C13.27 2.99 12.97 2.94 12.67 2.93Zm4.24 3.31c-.39.63-2.7 4.48 0 4.48 2.7 0 .39-3.88 0-4.48zM6.91 7.47 8.22 7.92 7.2 8.58c-.04.03-.08.06-.11.11l-1.55 2.46c-.1.16-.05.38.11.48.16.1.38.05.48-.11L7.64 9.12 8.94 8.29c.24.29.25.71.02 1.03L7.44 11.52C6.92 12.27 5.93 12.48 5.22 11.99L4.36 11.4Z" fill="#fff"></path>
</g>
<g id="mrk_service_charger" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="m7.96 2.6h4.56l-1.97 5.8h3.02L8.61 18.36 9.6 10.25H7.26Z" fill="#fff"></path>
</g>
</defs>
</svg>
<svg id="mapMarkers" xmlns="http://www.w3.org/2000/svg" width="1000" height="1000" viewBox="0 0 10000 10000">
</svg>
</div>
<canvas id="mapCanvas"></canvas>
<div id="tooltip" class="panel" style="display:none"></div>
</div>
<div id="stats" class="orange">
@@ -254,7 +47,8 @@
<hr style="margin: 5px 0;">
Hover over tracks and objects for detailed information.
<hr style="margin: 5px 0;">
Last Updated: <span id="lastUpdate"></span>
Last update <span id="lastUpdate"></span>:
<div id="changelog"></div>
</div>
<div class="panel">
<h3>Key</h3>
@@ -277,5 +71,9 @@
<button id="legendButton" class="panel orange"><svg style="transform:rotate(180deg) translateY(0px)" viewBox="0 0 32 32"><path fill="currentColor" d="M31 16 20 31l-16 0L15 16 4 1l16 0Z"/></svg></button>
</div>
</div>
<div style="display:none">
<img id="mapSprites" src="mapSprites.svg">
<img id="mapTerrain" src="">
</div>
</body>
</html>
+290 -781
View File
File diff suppressed because it is too large. Load diff
+50
View File
@@ -1,5 +1,6 @@
'use strict';
import * as Vector from './vectorlib.js';
import * as Utils from './utillib.js';
/** A 3D vector
* @typedef {Object} Vector
* @property {number} x
@@ -139,3 +140,52 @@ export function evaluateAcceleration(p1, h1, h2, p2, t){
}
return result;
}
/** Get the min and max bounds of a bezier.
* @param {Vector} p1
* @param {Vector} h1
* @param {Vector} h2
* @param {Vector} p2
*/
export function getBounds(p1, h1, h2, p2){
// TODO: replace this with a not brute-forced version.
let pos;
let min = {x:Infinity, y:Infinity, z:Infinity};
let max = {x:-Infinity, y:-Infinity, z:-Infinity};
for(let i=0; i<=40; i++){
pos = evaluatePoint(p1, h1, h2, p2, i/40);
min.x = Math.min(pos.x, min.x);
max.x = Math.max(pos.x, max.x);
min.y = Math.min(pos.y, min.y);
max.y = Math.max(pos.y, max.y);
min.z = Math.min(pos.z, min.z);
max.z = Math.max(pos.z, max.z);
}
return {min:min, max:max};
}
/** Get the closest point on the curve to `testPoint` in 2D space, as well as the distance.
* (This means using the (x,z) coordinates of the bezier as (x,y))
* @param {Vector} p1
* @param {Vector} h1
* @param {Vector} h2
* @param {Vector} p2
* @param {*} testPoint
*/
export function getNearestPoint2D(p1, h1, h2, p2, testPoint, iterationCount = 200){
// TODO: replace this with a more accurate version
let minDistance = Infinity;
let bezEval;
let evalDist;
let closestPoint = {x:0, y:0};
for(let i=0; i<=iterationCount; i++){
bezEval = evaluatePoint(p1, h1, h2, p2, i/iterationCount);
evalDist = Math.pow(testPoint.x-bezEval.x, 2) + Math.pow(testPoint.y-bezEval.z, 2);
if(evalDist < minDistance){
minDistance = evalDist;
closestPoint.x = bezEval.x;
closestPoint.y = bezEval.y;
}
}
return {point:closestPoint, distance:Math.sqrt(minDistance)};
}
+573
View File
@@ -0,0 +1,573 @@
'use strict';
import * as Vector from './vectorlib.js';
import * as Bezier from './bezierlib.js';
import * as Utils from './utillib.js';
import {Color} from './colorlib.js';
import * as Config from './../config.js';
export let railTracks = [];
export let markers = [];
let visibleMarkers = [];
let uniqueTrackNames = {};
export let testPoint = {x:0,y:0};
export let matrix = {
initialize: () => {
matrix.minX = Infinity;
matrix.minY = Infinity;
matrix.minAlt = Infinity;
matrix.maxX = -Infinity;
matrix.maxY = -Infinity;
matrix.maxAlt = -Infinity;
for(const railTrack of railTracks){
for(const curve of railTrack.curves){
matrix.minX = Math.min(matrix.minX, curve.start.x, curve.h1.x, curve.h2.x, curve.end.x);
matrix.minY = Math.min(matrix.minY, curve.start.z, curve.h1.z, curve.h2.z, curve.end.z);
matrix.minAlt = Math.min(matrix.minAlt, curve.start.y, curve.h1.y, curve.h2.y, curve.end.y);
matrix.maxX = Math.max(matrix.maxX, curve.start.x, curve.h1.x, curve.h2.x, curve.end.x);
matrix.maxY = Math.max(matrix.maxY, curve.start.z, curve.h1.z, curve.h2.z, curve.end.z);
matrix.maxAlt = Math.max(matrix.maxAlt, curve.start.y, curve.h1.y, curve.h2.y, curve.end.y);
}
}
matrix.width = matrix.maxX-matrix.minX;
matrix.height = matrix.maxY-matrix.minY;
console.log(`(${matrix.minX}, ${matrix.minY}) to (${matrix.maxX}, ${matrix.maxY}). Altitude from ${matrix.minAlt} to ${matrix.maxAlt}`);
}
};
export let view = {
initialize: () => {
const mapContainer = document.getElementById('mapContainer');
let isLandscape = mapContainer.clientWidth > mapContainer.clientHeight;
view.scale = Math.min(mapContainer.clientWidth/(matrix.width + Config.defaultMapPadding*2), mapContainer.clientHeight/(matrix.height + Config.defaultMapPadding*2));
if(isLandscape){
view.x = mapContainer.clientWidth*0.6 - (matrix.width * 0.5 + matrix.minX)*view.scale;
view.y = (matrix.minY - Config.defaultMapPadding) * view.scale;
}else{
view.x = -(matrix.minX - Config.defaultMapPadding) * view.scale;
view.y = -(mapContainer.clientHeight*0.5 - (matrix.height * 0.5 + matrix.minY)*view.scale);
}
view.dirty = false;
view.convertX = x => {
return x * view.scale + view.x;
}
view.convertY = y => {
return (matrix.minY+matrix.maxY-y) * view.scale - view.y;
}
view.unconvertX = x => {
return (x - view.x) / view.scale;
}
view.unconvertY = y => {
return -((y + view.y) / view.scale - (matrix.minY + matrix.maxY));
}
}
}
export let layers = {};
export function reset(){
railTracks = [];
markers = [];
}
export function sortTracks(){
railTracks.sort((a,b) => {
if(a.curves && b.curves)
return a.curves[0].start.y-b.curves[b.curves.length-1].end.y;
return 0;
});
}
export function sortMarkers(){
markers.sort((a,b) => {
if(a.type == b.type){
switch(a.type){
case 'speed':
return b.value-a.value;
default:
return 0;
}
}
if(a.type == 'station') return 1;
if(b.type == 'station') return -1;
if(a.type == 'speed') return 1;
if(b.type == 'speed') return -1;
if(a.type == 'junction') return 1;
if(b.type == 'junction') return -1;
if(a.type == 'service') return 1;
if(b.type == 'service') return -1;
return 0;
});
}
export function filterToTrack(trackName, trackNum = 0){
if(typeof(trackName) == 'string'){
for(let railTrack of railTracks){
if(railTrack.name == trackName && railTrack.number == trackNum){
railTracks = [railTrack];
break;
}
}
}else if(typeof(trackName) == 'object'){
let newRailTracks = [];
for(let railTrack of railTracks){
for(let filter of trackName){
if(railTrack.name == filter.name && railTrack.number == (filter.number ?? 0)){
newRailTracks.push(railTrack);
}
}
}
railTracks = newRailTracks;
}
}
export function addTrack(trackData){
let newTrack = {
name: trackData.name,
curves: [],
bounds: {min:{x:Infinity,y:Infinity,z:Infinity},max:{x:-Infinity,y:-Infinity,z:-Infinity}},
};
if(uniqueTrackNames[trackData.name] == null){
uniqueTrackNames[trackData.name] = 0;
}else{
uniqueTrackNames[trackData.name]++;
}
newTrack.number = uniqueTrackNames[trackData.name];
let yardData = /\[(#)\]|\[(Y)\][_-]\[(.*?)\][_-]\[((.*?)[_-](.*?)[_-](.*?)|.*)\]/.exec(trackData.name);
if(yardData){
if(yardData[1]){ // track is [#], thus in yard limits, but not a specific designated yard.
newTrack.isYard = true;
}else if(yardData[2]){ // track is [Y], and is a specific yard.
newTrack.isYard = true;
newTrack.yardStation = yardData[3]; // Which station the track belongs to
newTrack.yardDesignation = yardData[4]; // Full yard designation, used on signs
newTrack.yardName = yardData[5]; // A yard, B yard, C yard, etc.
newTrack.yardSidingNumber = yardData[6]; // Which siding within the yard
newTrack.yardUsage = yardData[7]; // Outbound? Parking? Inbound? Loading? That's this.
}
}else if(/Turntable Track/.test(newTrack.name)){
newTrack.isTurntable = true;
}else if(/\[track (diverging|through)\]/.test(newTrack.name)){
newTrack.isJunction = true;
}
newTrack.randomColor = Color.random();
for(let i=0; i+1<trackData.points.length; i++){
const curveStart = trackData.points[i].position;
const curveHandle1 = trackData.points[i].h2;
const curveHandle2 = trackData.points[i+1].h1;
const curveEnd = trackData.points[i+1].position;
newTrack.curves[i] = {
start: curveStart,
h1: curveHandle1,
h2: curveHandle2,
end: curveEnd,
grade: Bezier.estimateGrade(curveStart, curveHandle1, curveHandle2, curveEnd, Config.bezierGradeResolution),
length: Bezier.estimateLength(curveStart, curveHandle1, curveHandle2, curveEnd, Config.bezierLengthResolution),
curvature: Bezier.estimateCurvature(curveStart, curveHandle1, curveHandle2, curveEnd, Config.bezierCurvatureResolution),
randomColor: Color.random(),
color: '#fff',
averageHeight: (curveStart.y+curveEnd.y) * 0.5,
bounds: Bezier.getBounds(curveStart, curveHandle1, curveHandle2, curveEnd),
}
newTrack.curves[i].gradeClass = Utils.gradeToClass(newTrack.curves[i].grade);
newTrack.curves[i].postedSpeed = Utils.radiusToSpeed(1/newTrack.curves[i].curvature);
if(newTrack.isYard || newTrack.isTurntable) newTrack.curves[i].postedSpeed = Math.min(newTrack.curves[i].postedSpeed, 50);
newTrack.curves[i].tooltip = [
`<h2>Rail</h2>"${trackData.name}"${uniqueTrackNames[trackData.name] > 0 ? ' #'+uniqueTrackNames[trackData.name] : ''} Sec${i}`,
`Max Grade: ${Math.round(newTrack.curves[i].grade*1000)/10}%`,
`Min Radius: ${Math.round(1/newTrack.curves[i].curvature)} meters`,
`Top Speed: ${Utils.radiusToSpeed(1/newTrack.curves[i].curvature)} km/h`,
`Length: ${Math.round(newTrack.curves[i].length*10)/10} meters`,
`Altitude (ASL): ${Math.round(newTrack.curves[i].averageHeight)-110} meters`,
].join('\n');
newTrack.bounds.min.x = Math.min(newTrack.bounds.min.x, newTrack.curves[i].bounds.min.x);
newTrack.bounds.max.x = Math.max(newTrack.bounds.max.x, newTrack.curves[i].bounds.max.x);
newTrack.bounds.min.y = Math.min(newTrack.bounds.min.y, newTrack.curves[i].bounds.min.y);
newTrack.bounds.max.y = Math.max(newTrack.bounds.max.y, newTrack.curves[i].bounds.max.y);
newTrack.bounds.min.z = Math.min(newTrack.bounds.min.z, newTrack.curves[i].bounds.min.z);
newTrack.bounds.max.z = Math.max(newTrack.bounds.max.z, newTrack.curves[i].bounds.max.z);
}
railTracks.push(newTrack);
}
export function addJunction(junctionData){
if(junctionData.name.startsWith('S-') || junctionData.excludeFromJunctionMap)
junctionData.minZoom = 1;
else if(junctionData.name.startsWith('W-'))
junctionData.minZoom = 0.05;
junctionData.tooltip = `<h2>Junction</h2>"${junctionData.name}" - #${junctionData.index}\n${junctionData.excludeFromJunctionMap ? 'Inv' : 'V'}isible On Map\nLinked Junctions:\n- ${junctionData.linkedJunctions.join('\n- ')}`;
junctionData.tooltipHitzone = Config.tooltipHitzone.junction;
markers.push(junctionData);
}
export function addPoi(poiData, level = 0){
let newPoi = {
type: poiData.type,
name: poiData.name,
color: poiData.color,
position: poiData.position,
};
if(level > 0) newPoi.minZoom = 0.3;
switch(poiData.type){
case 'service':
newPoi.serviceTypes = [];
if(poiData.repair) newPoi.serviceTypes.push('Repair');
if(poiData.diesel) newPoi.serviceTypes.push('Diesel');
if(poiData.charger) newPoi.serviceTypes.push('Charger');
if(newPoi.serviceTypes.length == 0){
newPoi.tooltip = '<h1>Service</h1><hr>?';
}else{
newPoi.tooltip = '<h1>Service</h1><hr>- '+newPoi.serviceTypes.join('\n- ');
}
break;
case 'station':
newPoi.maxZoom = 1;
newPoi.nickname = poiData.nickname;
newPoi.shorthand = poiData.shorthand;
newPoi.color = poiData.color;
newPoi.tooltip = `<h1>${poiData.name}</h1>`;
break;
case 'shop':
newPoi.tooltip = `<h1>Shop</h1>${poiData.stock ? '<hr>'+poiData.stock.sort().join('\n') : ''}`;
break;
case 'water':
newPoi.tooltip = '<h1>Water Tower</h1>';
break;
case 'coal':
newPoi.tooltip = '<h1>Coal Tower</h1>';
break;
case 'office':
if(poiData.museum) newPoi.tooltip = '<h1>Museum Office</h1>';
else if(poiData.military) newPoi.tooltip = '<h1>Military Office</h1>';
else newPoi.tooltip = '<h1>Office</h1>';
break;
default:
newPoi.tooltip = `<h1>${newPoi.name}</h1>${poiData.description ? '<hr>'+poiData.description : ''}`;
newPoi.minZoom = 0.07;
break;
}
markers.push(newPoi);
if(poiData.children){
for(let child of poiData.children){
addPoi(child, level+1);
}
}
}
export function generateTrackSignage(){
let yardMarkers = {};
for(const railTrack of railTracks){
let trackZoneData = {};
let lastCurve = false;
let resetTzdGrade = (i=0) => {
trackZoneData.gradeClass = railTrack.curves[i].gradeClass;
trackZoneData.gradeDirection = Math.sign(railTrack.curves[i].grade);
trackZoneData.gradeZoneStart = i;
trackZoneData.gradeZoneLength = 0;
};
let resetTzdSpeed = (i=0) => {
trackZoneData.postedSpeed = railTrack.curves[i].postedSpeed;
trackZoneData.speedZoneStart = i;
trackZoneData.speedZoneLength = 0;
};
resetTzdGrade();
resetTzdSpeed();
for(let i=0; i<railTrack.curves.length; i++){
let nextIndex = i+1;
if(nextIndex == railTrack.curves.length){
lastCurve = true;
nextIndex = i;
}
//Grade Markers
trackZoneData.gradeZoneLength += railTrack.curves[i].length;
if(railTrack.curves[nextIndex].gradeClass != trackZoneData.gradeClass
|| Math.sign(railTrack.curves[nextIndex].grade) != trackZoneData.gradeDirection
|| trackZoneData.gradeZoneLength > Config.maxZoneLength
|| lastCurve){
if(railTrack.curves[i].gradeClass != 'flat'){
let markerIndex = Math.ceil((trackZoneData.gradeZoneStart + i)/2);
let markerOffset = (trackZoneData.gradeZoneStart + i) % 2 ? 0.1 : 0.6;
let b1 = railTrack.curves[markerIndex].start;
let b2 = railTrack.curves[markerIndex].h1;
let b3 = railTrack.curves[markerIndex].h2;
let b4 = railTrack.curves[markerIndex].end;
let newMarker = {
type: 'grade',
value: railTrack.curves[markerIndex].grade,
position: Bezier.evaluatePoint(b1, b2, b3, b4, markerOffset),
gradeClass: railTrack.curves[markerIndex].gradeClass,
minZoom: Math.max(0.02, Math.min(1.0, 20/trackZoneData.gradeZoneLength)),
zoneLength: trackZoneData.gradeZoneLength,
zoneCount: i - trackZoneData.gradeZoneStart + 1,
tooltipHitzone: {radius:16},
}
let tangent = newMarker.value > 0
? Bezier.evaluateVelocity(b1, b2, b3, b4, markerOffset)
: Bezier.evaluateVelocity(b4, b3, b2, b1, markerOffset);
let tangentLength = Math.sqrt(tangent.x * tangent.x + tangent.z * tangent.z) * Math.sign(tangent.y);
newMarker.rotation = Math.atan2(tangent.x/tangentLength, tangent.z/tangentLength);
newMarker.tooltip = `<h2>Grade</h2>${Math.round(newMarker.value*1000)/10}% Grade\nUphill bearing ${Utils.mod(newMarker.rotation/(Math.PI*2)*360, 360).toFixed(0)}, ${Utils.angleToCardinalDirection(newMarker.rotation)}\nSection Length: ${newMarker.zoneLength.toFixed(1)} meters\nCovers ${newMarker.zoneCount} track sections`;
markers.push(newMarker);
}
resetTzdGrade(nextIndex);
}
//Speed Markers
trackZoneData.speedZoneLength += railTrack.curves[i].length;
if(railTrack.curves[nextIndex].postedSpeed != trackZoneData.postedSpeed
|| trackZoneData.speedZoneLength > Config.maxZoneLength
|| lastCurve){
let markerIndex = Math.ceil((trackZoneData.speedZoneStart + i)/2);
let markerOffset = (trackZoneData.speedZoneStart + i) % 2 ? 0.2 : 0.7;
let b1 = railTrack.curves[markerIndex].start;
let b2 = railTrack.curves[markerIndex].h1;
let b3 = railTrack.curves[markerIndex].h2;
let b4 = railTrack.curves[markerIndex].end;
let newMarker = {
type: 'speed',
value: railTrack.curves[markerIndex].postedSpeed,
position: Bezier.evaluatePoint(b1, b2, b3, b4, markerOffset),
zoneLength: trackZoneData.speedZoneLength,
zoneCount: i - trackZoneData.speedZoneStart + 1,
minZoom: 0.05,
}
if((railTrack.isYard || railTrack.isJunction) && newMarker.value > 30){
if(newMarker.value > 40) newMarker.minZoom = 5;
else newMarker.minZoom = 0.5;
}
if(newMarker.value > 80) newMarker.minZoom *= 6.5;
else if(newMarker.value > 60) newMarker.minZoom *= 5;
else if(newMarker.value > 40) newMarker.minZoom *= 3;
if(newMarker.zoneLength > 500) newMarker.minZoom *= 0.15;
if(newMarker.zoneLength < 35 && newMarker.value > 40) newMarker.minZoom *= 6;
newMarker.tooltip = `<h2>Speed</h2>${Math.round(newMarker.value)} km/h\nSection Length: ${newMarker.zoneLength.toFixed(1)} meters\nCovers ${newMarker.zoneCount} track sections`;
markers.push(newMarker);
resetTzdSpeed(nextIndex);
}
}
if(railTrack.yardDesignation && railTrack.yardDesignation != '--'){
if(!yardMarkers[railTrack.yardStation]) yardMarkers[railTrack.yardStation] = {};
let centerIndex = Math.floor(railTrack.curves.length/2);
let yardSignPos = railTrack.curves[centerIndex].start;
if(railTrack.curves.length % 2 == 1){
let curve = railTrack.curves[centerIndex];
yardSignPos = Bezier.evaluatePoint(curve.start, curve.h1, curve.h2, curve.end, 0.5);
}
if(railTrack.yardName){
if(!yardMarkers[railTrack.yardStation][railTrack.yardName])
yardMarkers[railTrack.yardStation][railTrack.yardName] = {accCenter:{x:0,y:0,z:0},accCount:0};
yardMarkers[railTrack.yardStation][railTrack.yardName].accCenter.x += yardSignPos.x;
yardMarkers[railTrack.yardStation][railTrack.yardName].accCenter.y += yardSignPos.y;
yardMarkers[railTrack.yardStation][railTrack.yardName].accCenter.z += yardSignPos.z;
yardMarkers[railTrack.yardStation][railTrack.yardName].accCount++;
}
let newMarker = {
type: 'yardSiding',
name: railTrack.yardDesignation,
position: yardSignPos,
minZoom: 2.5,
};
if(railTrack.yardName && railTrack.yardSidingNumber && railTrack.yardUsage){
if(!Config.sidingUsageMeanings[railTrack.yardUsage]) console.log(`Unknown siding usage for ${railTrack.yardDesignation} at ${railTrack.yardStation}`);
newMarker.tooltip = `<h2>Siding</h2>${railTrack.yardDesignation}\n${Config.sidingUsageMeanings[railTrack.yardUsage]}`;
}else{
newMarker.tooltip = `<h2>Siding</h2>${railTrack.yardDesignation}`;
}
markers.push(newMarker);
}
}
for(let station in yardMarkers){
for(let yard in yardMarkers[station]){
markers.push({
type: 'yard',
name: `${yard} Yard`,
position: {
x: yardMarkers[station][yard].accCenter.x/yardMarkers[station][yard].accCount,
y: yardMarkers[station][yard].accCenter.y/yardMarkers[station][yard].accCount,
z: yardMarkers[station][yard].accCenter.z/yardMarkers[station][yard].accCount,
},
tooltip: `<h1>${yard} Yard</h1>`,
maxZoom: 2.5,
minZoom: 0.25,
});
}
}
}
/**
* Finds the relevant tooltip for where the cursor is (if possible)
* Returns "" if there is none.
* @param {Number} cursorX
* @param {Number} cursorY
* @returns {String}
*/
export function testTooltip(cursorX, cursorY){
cursorX = view.unconvertX(cursorX);
cursorY = view.unconvertY(cursorY);
let finalTooltip = '';
let radius = 0;
let width = 0;
let height = 0;
for(const marker of markers){
if(!marker.visible) continue;
if(marker.boundsCheck != null){
if(marker.boundsCheck(cursorX, cursorY))
finalTooltip = marker.tooltip ?? finalTooltip;
}else if(marker.tooltipHitzone && marker.tooltipHitzone.width && marker.tooltipHitzone.height){
width = marker.tooltipHitzone.width * 0.5/view.scale;
height = marker.tooltipHitzone.height * 0.5/view.scale;
if( marker.position.x - cursorX + (marker.tooltipHitzone.offsetX ?? 0)/view.scale <= width
&& marker.position.x - cursorX + (marker.tooltipHitzone.offsetX ?? 0)/view.scale >= -width
&& marker.position.z - cursorY + (marker.tooltipHitzone.offsetY ?? 0)/view.scale <= height
&& marker.position.z - cursorY + (marker.tooltipHitzone.offsetY ?? 0)/view.scale >= -height){
finalTooltip = marker.tooltip ?? finalTooltip;
}
}else{
if(marker.tooltipHitzone)
radius = (marker.tooltipHitzone.radius ?? Config.tooltipHitzone.default.radius) / view.scale;
else
radius = Config.tooltipHitzone.default.radius / view.scale;
if(Math.pow(marker.position.x - cursorX, 2) + Math.pow(marker.position.z - cursorY, 2) <= radius * radius){
finalTooltip = marker.tooltip ?? finalTooltip;
}
}
}
if(finalTooltip != '') return finalTooltip;
let bestDistance = Infinity;
let test = {};
let boundsPadding = 40/view.scale;
for(const railTrack of railTracks){
if(cursorX >= railTrack.bounds.min.x - boundsPadding && cursorX <= railTrack.bounds.max.x + boundsPadding && cursorY >= railTrack.bounds.min.z - boundsPadding && cursorY <= railTrack.bounds.max.z + boundsPadding){
for(const curve of railTrack.curves){
if(cursorX >= curve.bounds.min.x - boundsPadding && cursorX <= curve.bounds.max.x + boundsPadding && cursorY >= curve.bounds.min.z - boundsPadding && cursorY <= curve.bounds.max.z + boundsPadding){
test = Bezier.getNearestPoint2D(curve.start, curve.h1, curve.h2, curve.end, {x:cursorX, y:cursorY}, Math.floor(curve.length*2));
if(test.distance < bestDistance){
bestDistance = test.distance;
finalTooltip = curve.tooltip;
testPoint = test.point;
}
}
}
}
}
return bestDistance <= Math.min(Math.max(0.9, 12/view.scale), 120) ? finalTooltip : '';
}
export function setTrackColorMode(mode){
let modeFunction;
const keyElement = document.getElementById('legendKeyColors');
let gradientString = '';
keyElement.innerHTML = '';
switch(mode){
case 'Track Type':
modeFunction = (curve, railTrack) => {
let finalColor = '#aaa';
if(railTrack.isYard && railTrack.yardDesignation) finalColor = '#4bf';
else if(railTrack.isYard) finalColor = '#4fb';
else if(railTrack.isJunction) finalColor = '#fb4';
else if(railTrack.isTurntable) finalColor = '#b4f';
curve.color = finalColor;
};
keyElement.innerHTML = `
<span class="colorKey"><span style="background:#4bf"></span> Designated Yard</span>
<span class="colorKey"><span style="background:#4fb"></span> Other Yard</span>
<span class="colorKey"><span style="background:#fb4"></span> Point</span>
<span class="colorKey"><span style="background:#b4f"></span> Turntable</span>
<span class="colorKey"><span style="background:#aaa"></span> Other Tracks</span>`;
break;
case 'Grade':
modeFunction = curve => {
curve.color = Config.gradeColors['grade_'+curve.gradeClass];
};
for(let grade in Config.gradeColors){
keyElement.innerHTML += `<span class="colorKey" style="${grade.replace('grade_','') < 6 || grade == 'grade_flat' ? 'color:#000;' : ''}padding:0px 3px;background:${Config.gradeColors[grade]}">${(Utils.classToGrade(grade.replace('grade_',''))*100).toFixed(1)}%</span>`;
}
break;
case 'Altitude':
modeFunction = (curve, railTrack) => {
curve.color = Color.blendGradient(Config.altitudeGradient, (curve.averageHeight-matrix.minAlt)/(matrix.maxAlt-matrix.minAlt)).hex;
}
gradientString = 'linear-gradient(to right';
for(let col of Config.altitudeGradient) gradientString += ', '+col.hex;
gradientString += ')';
keyElement.innerHTML = `
<div style="display:flex">
<div>${Math.floor(matrix.minAlt)}m</div>
<div class="gradientKey" style="flex-grow:1;background:${gradientString}"></div>
<div>${Math.ceil(matrix.maxAlt)}m</div>
</div>
`;
break;
case 'Speed':
modeFunction = curve => {
curve.color = Color.blendGradient(Config.speedGradient, (curve.postedSpeed+10)/110).hex;
};
gradientString = 'linear-gradient(to right';
for(let col of Config.speedGradient) gradientString += ', '+col.hex;
gradientString += ')';
keyElement.innerHTML = `
<div style="display:flex">
<div>10 kmh</div>
<div class="gradientKey" style="flex-grow:1;background:${gradientString}"></div>
<div>120 kmh</div>
</div>
`;
break;
case 'Track Random':
modeFunction = (curve, railTrack) => {
curve.color = railTrack.randomColor.hex;
};
keyElement.innerHTML = 'Random per continuous track section';
break;
case 'Curve Random':
modeFunction = curve => {
curve.color = curve.randomColor.hex;
};
keyElement.innerHTML = 'Random per individual track bezier curve';
break;
default:
modeFunction = curve => {
curve.color = '#fff';
};
break;
}
if(modeFunction != null){
for(let railTrack of railTracks){
for(let curve of railTrack.curves){
modeFunction(curve, railTrack);
}
}
}
view.dirty = true;
}
+10
View File
@@ -0,0 +1,10 @@
'use strict';
export class SpriteBounds{
constructor(x, y, width, height){
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
}
+98 -1
View File
@@ -1,5 +1,8 @@
'use strict';
import * as Config from './../config.js';
import {SpriteBounds} from './spritelib.js';
export const gradeIncrements = [
0.0498,
0.04,
@@ -11,7 +14,60 @@ export const gradeIncrements = [
0.01,
0.005,
0.001
]
];
const gradeArrowDimensions = 64;
export const gradeArrows = new OffscreenCanvas(11*gradeArrowDimensions, 2*gradeArrowDimensions);
export function generateGradeArrows(){
const ctx = gradeArrows.getContext('2d');
const mapSprites = document.getElementById('mapSprites');
for(let i=0; i<11; i++){
ctx.drawImage(
mapSprites,
Config.spriteBounds.gradeArrow.x,
Config.spriteBounds.gradeArrow.y,
Config.spriteBounds.gradeArrow.width,
Config.spriteBounds.gradeArrow.height,
i*gradeArrowDimensions,
0,
gradeArrowDimensions,
gradeArrowDimensions
);
ctx.drawImage(
mapSprites,
Config.spriteBounds.gradeArrowDown.x,
Config.spriteBounds.gradeArrowDown.y,
Config.spriteBounds.gradeArrowDown.width,
Config.spriteBounds.gradeArrowDown.height,
i*gradeArrowDimensions,
gradeArrowDimensions,
gradeArrowDimensions,
gradeArrowDimensions
);
if(i == 0){
Config.spriteBounds['gradeArrow_flat'] = new SpriteBounds(0, 0, gradeArrowDimensions, gradeArrowDimensions);
Config.spriteBounds['gradeArrowDown_flat'] = new SpriteBounds(0, gradeArrowDimensions, gradeArrowDimensions, gradeArrowDimensions);
}else{
Config.spriteBounds['gradeArrow_'+(i-1)] = new SpriteBounds(i*gradeArrowDimensions, 0, gradeArrowDimensions, gradeArrowDimensions);
Config.spriteBounds['gradeArrowDown_'+(i-1)] = new SpriteBounds(i*gradeArrowDimensions, gradeArrowDimensions, gradeArrowDimensions, gradeArrowDimensions);
}
}
let arrowLayer = gradeArrows.transferToImageBitmap();
for(let i=0; i<11; i++){
if(i == 0)
ctx.fillStyle = Config.gradeColors.grade_flat;
else
ctx.fillStyle = Config.gradeColors['grade_'+(i-1)];
ctx.fillRect(i*gradeArrowDimensions, 0, gradeArrowDimensions, gradeArrowDimensions * 2);
}
ctx.globalCompositeOperation = 'multiply';
ctx.drawImage(arrowLayer, 0, 0);
ctx.globalCompositeOperation = 'destination-in';
ctx.drawImage(arrowLayer, 0, 0);
arrowLayer.close();
}
export function randomRange(min=0.0, max=1.0){
return Math.random()*(max-min) + min;
@@ -75,3 +131,44 @@ export function radiusToSpeed(radius){
export function clamp(v, min = 0, max = 1){
return Math.max(Math.min(v, max), min);
}
export function zoomToCullLevel(zoom){
if(zoom < 1) return 0;
if(zoom < 2) return 1;
if(zoom < 3) return 2;
if(zoom < 6) return 3;
if(zoom < 12) return 4;
if(zoom < 24) return 5;
if(zoom < 60) return 6;
return 7;
}
export function lerp(a, b, t){
return (a * (1-t)) + (b*t);
}
export function mod(a, b){
return (a % b + b) % b;
}
export function titleCase(text){
text = text.toString();
if(text){
return `${text[0].toUpperCase()}${text.substring(1)}`;
}
return '';
}
const cardinalDirections = [
'North',
'Northeast',
'East',
'Southeast',
'South',
'Southwest',
'West',
'Northwest',
]
export function angleToCardinalDirection(angle){
return cardinalDirections[Math.floor(mod(angle/(2*Math.PI) + (0.5/cardinalDirections.length), 1)*cardinalDirections.length)];
}
+95 -56
View File
@@ -3,7 +3,7 @@
import * as Utils from './js/utillib.js';
import * as Config from './config.js';
import { Color } from './js/colorlib.js';
import { setTrackColorMode } from './index.js';
import * as MapData from './js/mapdata.js';
let settingEntries = [
{
@@ -18,9 +18,9 @@ let settingEntries = [
'Track Random',
'Curve Random',
],
default: 'Grade',
state: 'Grade',
func: state =>{
setTrackColorMode(state);
MapData.setTrackColorMode(state);
}
},
{
@@ -28,15 +28,17 @@ let settingEntries = [
id: 'toggle_signage',
state: true,
func: state =>{
document.getElementById('signage_container').style.display = state ? 'unset' : 'none';
MapData.layers.signage = state;
MapData.view.dirty = true;
},
children: [
{
label: 'Speed Signs',
label: 'Speed Signage',
id: 'toggle_speedSigns',
state: true,
func: state =>{
document.getElementById('signage_speed').style.display = state ? 'unset' : 'none';
MapData.layers.speedSigns = state;
MapData.view.dirty = true;
},
},
{
@@ -44,7 +46,8 @@ let settingEntries = [
id: 'toggle_gradeSigns',
state: true,
func: state =>{
document.getElementById('signage_grade').style.display = state ? 'unset' : 'none';
MapData.layers.gradeArrows = state;
MapData.view.dirty = true;
},
},
{
@@ -52,15 +55,17 @@ let settingEntries = [
id: 'toggle_yardSigns',
state: true,
func: state =>{
document.getElementById('signage_yard').style.display = state ? 'unset' : 'none';
MapData.layers.yardSigns = state;
MapData.view.dirty = true;
},
},
{
label: 'Junctions',
id: 'toggle_junctions',
state: false,
state: true,
func: state =>{
document.getElementById('signage_junction').style.display = state ? 'unset' : 'none';
MapData.layers.junctions = state;
MapData.view.dirty = true;
},
}
],
@@ -70,15 +75,26 @@ let settingEntries = [
id: 'toggle_poi',
state: true,
func: state => {
document.getElementById('poi_container').style.display = state ? 'unset' : 'none';
MapData.layers.poi = state;
MapData.view.dirty = true;
},
children: [
{
label: 'Stations',
id: 'toggle_stations',
state: true,
func: state =>{
MapData.layers.stations = state;
MapData.view.dirty = true;
}
},
{
label: 'Services',
id: 'toggle_services',
state: true,
func: state =>{
document.getElementById('poi_service').style.display = state ? 'unset' : 'none';
MapData.layers.services = state;
MapData.view.dirty = true;
},
},
{
@@ -86,7 +102,8 @@ let settingEntries = [
id: 'toggle_landmarks',
state: true,
func: state =>{
document.getElementById('poi_landmarks').style.display = state ? 'unset' : 'none';
MapData.layers.landmarks = state;
MapData.view.dirty = true;
},
},
]
@@ -98,19 +115,44 @@ let settingEntries = [
'Uphill',
'Downhill',
],
default: 'Uphill',
state: 'Uphill',
func: state =>{
let newHref = state == 'Uphill' ? '#gradeArrow' : '#gradeArrowDownhill';
for(let arrow of document.getElementsByClassName('gradeArrow')){
arrow.setAttribute('href', newHref);
}
MapData.layers.gradeDirection = state == 'Downhill' ? 'Down' : '';
MapData.view.dirty = true;
}
},
{
label: 'Terrain',
id: 'dropdown_terrain',
options: [
'None',
'Simple',
'DVRT',
],
state: 'None',
func: state =>{
let mapTerrain = document.getElementById('mapTerrain');
if(state == 'None'){
MapData.layers.terrain = false;
MapData.view.dirty = true;
}else{
mapTerrain.onload = () => {
MapData.layers.terrain = true;
MapData.view.dirty = true;
}
if(state == 'Simple'){
document.getElementById('mapTerrain').src = 'terrains/simple.jpg';
}else if(state == 'DVRT'){
document.getElementById('mapTerrain').src = 'terrains/dvrt.jpg';
}
}
}
}
];
const legendKey = document.getElementById('legendKey');
document.addEventListener('DOMContentLoaded', async () => {
export function initialize(){
const settingsPanel = document.getElementById('settingsPanel');
for(let thisSetting of settingEntries){
addSettingEntry(thisSetting, settingsPanel);
@@ -130,7 +172,7 @@ document.addEventListener('DOMContentLoaded', async () => {
if(document.body.clientWidth <= 700) legendButtonEvent();
populateKey();
setLastUpdateData();
});
};
function addSettingEntry(thisSetting, parent, indent=0){
thisSetting.divContainer = document.createElement('div');
@@ -166,7 +208,7 @@ function addSettingEntry(thisSetting, parent, indent=0){
optionElement.value = inputEntry;
optionElement.innerHTML = inputEntry;
}
if(inputEntry == thisSetting.default) optionElement.selected = true;
if(inputEntry == thisSetting.state) optionElement.selected = true;
thisSetting.inputElement.appendChild(optionElement);
}
if(thisSetting.func){
@@ -199,6 +241,10 @@ function addSettingEntry(thisSetting, parent, indent=0){
thisSetting.inputElement.style.margin = `0 0.5em 0 ${indent}em`;
thisSetting.divContainer.prepend(thisSetting.inputElement);
if(thisSetting.func){
thisSetting.func(thisSetting.state);
}
if(thisSetting.children){
for(let child of thisSetting.children){
addSettingEntry(child, parent, indent+1);
@@ -208,11 +254,16 @@ function addSettingEntry(thisSetting, parent, indent=0){
let pageLoadUpdateDate = null;
async function setLastUpdateData(){
if(!window.location.host.includes('github')) return;
let commits = await (await fetch(new Request('https://api.github.com/repos/PyroNicampt/DV-Community-Map/commits'))).json();
let lastUpdateElement = document.getElementById('lastUpdate');
if(!commits){
lastUpdateElement.innerHTML = '<a href="https://github.com/PyroNicampt/DV-Community-Map/commits/main/" title="Could not fetch commit history">Fetch Error</a>';
return;
};
let updateDate = new Date(commits[0].commit.author.date);
lastUpdateElement.innerHTML = `<a href="https://github.com/PyroNicampt/DV-Community-Map/commits/main/" title="${updateDate.toString()}">${Utils.formattedTimeBetweenDates(new Date(), updateDate)} ago</a>`;
lastUpdateElement.innerHTML = `<a href="https://github.com/PyroNicampt/DV-Community-Map/commits/main/" title="${updateDate.toString()}">${Utils.formattedTimeBetweenDates(new Date(), updateDate)} ${new Date() >= updateDate ? 'ago' : 'from now'}</a>`;
document.getElementById('changelog').innerHTML = commits[0].commit.message.replace('\n','<br>');
if(pageLoadUpdateDate == null){
pageLoadUpdateDate = updateDate;
}else{
@@ -221,38 +272,24 @@ async function setLastUpdateData(){
}
}
setTimeout(() => {setLastUpdateData()}, 900000);
setTimeout(() => {setLastUpdateData()}, 600000);
}
function populateKey(){
const getGradeValue = gradeIndex => {return Math.round(Utils.gradeIncrements[Utils.gradeIncrements.length-gradeIndex-1]*10000)/100};
for(let i=-1; i<Utils.gradeIncrements.length; i++){
let gradeValue = `< ${getGradeValue(0)}`;
let gradeIndex = 'flat';
if(i>=0){
gradeValue = `${getGradeValue(i)}`;
gradeIndex = i;
}
if(i == Utils.gradeIncrements.length-1){
gradeValue = '> '+gradeValue+'%';
}else if(i >= 0){
gradeValue += ` - ${getGradeValue(i+1)}%`;
}
addKeyEntry(`<use fill="${Config.gradeColors['grade_'+gradeIndex]}" href="#gradeArrow" transform="rotate(90)"/>`, ` ${gradeValue} Grade`);
}
addKeyEntry('<use href="#speedSign_5"/>', ' Speed Limit');
addKeyEntry('<use href="#junctionSign" fill="#de2121" transform="scale(0.75)"/>', ' Junction/Switch');
addKeyEntry('<use href="#mrk_office" fill="#c4693e" transform="scale(0.75)"/>', ' Station Office');
addKeyEntry('<use href="#mrk_shop" fill="#4f54e9" transform="scale(0.75)"/>', ' Shop');
addKeyEntry('<use href="#mrk_service_repair" fill="#239a96" transform="scale(0.75)"/>', ' Repair Service');
addKeyEntry('<use href="#mrk_service_diesel" fill="#239a96" transform="scale(0.75)"/>', ' Diesel Refuel');
addKeyEntry('<use href="#mrk_service_charger" fill="#239a96" transform="scale(0.75)"/>', ' Electric Charger');
addKeyEntry('<use href="#mrk_coal" fill="#202020" transform="scale(0.75)"/>', ' Coal Tower');
addKeyEntry('<use href="#mrk_water" fill="#3fa5ff" transform="scale(0.75)"/>', ' Water Tower');
addKeyEntry('<use href="#mrk_landmark" fill="#af5757" transform="scale(0.75)"/>', ' Landmark');
addKeyEntry('<use href="#mrk_garage" fill="#8b5dd7" transform="scale(0.75)"/>', ' Garage');
addKeyEntry('gradeArrow', 'Grade Indication')
addKeyEntry('speed_5', 'Speed Limit');
addKeyEntry('junction', 'Junction/Switch');
addKeyEntry('office', 'Station Office');
addKeyEntry('shop', 'Shop');
addKeyEntry('service_repair', 'Repair Service');
addKeyEntry('service_diesel', 'Diesel Refuel');
addKeyEntry('service_charger', 'Electric Charger');
addKeyEntry('coal', 'Coal Tower');
addKeyEntry('water', 'Water Tower');
addKeyEntry('landmark', 'Landmark');
addKeyEntry('garage', 'Garage');
//Add empty entries to make entries align right in the columns.
for(let i=0; i<2; i++){
for(let i=0; i<3; i++){
addKeyEntry();
}
}
@@ -262,15 +299,17 @@ function populateKey(){
* @param {string} keyImageHTML
* @param {string} keyLabel
*/
function addKeyEntry(keyImageHTML, keyLabel){
function addKeyEntry(keySprite, keyLabel){
if(!keyLabel) keyLabel = '&nbsp;';
let keyDiv = document.createElement('div');
if(keyImageHTML){
let keyImage = document.createElement('svg');
keyImage.innerHTML = keyImageHTML;
if(keySprite && Config.spriteBounds[keySprite]){
let keyImage = document.createElement('span');
let spriteBounds = Config.spriteBounds[keySprite];
keyImage.classList.add('inlineSvg');
keyImage.setAttribute('viewBox', '-150 -150 300 300');
keyImage.style.background = `url('mapSprites.svg') -${spriteBounds.x/spriteBounds.width}em -${spriteBounds.y/spriteBounds.height}em`;
keyImage.style.backgroundSize = '1000%';
keyDiv.appendChild(keyImage);
keyLabel = ' '+keyLabel;
}
keyDiv.innerHTML += keyLabel;
legendKey.appendChild(keyDiv);
+282
View File
@@ -0,0 +1,282 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="200"
height="200"
viewBox="0 0 200 200"
version="1.1"
id="svg1"
xml:space="preserve"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs1" /><g
id="g33"><rect
style="fill:#bb3f3f;stroke:#ebebeb;stroke-width:0.9"
id="rect32"
width="9.9940004"
height="18.922001"
x="5.0029998"
y="20.539"
ry="1.347"
rx="1.347" /><rect
style="fill:#ebebeb"
id="rect33"
width="1.979"
height="18.334999"
x="19.742342"
y="14.710581"
ry="0.98900002"
transform="rotate(22.53)"
rx="0.98900002" /></g><g
id="g34"><circle
style="opacity:1;fill:#702012;fill-opacity:1;stroke:#e73819;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="path33"
cx="10"
cy="50"
r="8.5" /><circle
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e73819;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="circle33"
cx="9.9997206"
cy="69.999718"
r="8.5" /><use
x="0"
y="0"
xlink:href="#path33"
id="use34"
transform="translate(20)" /><use
x="0"
y="0"
xlink:href="#path33"
id="use35"
transform="translate(40)" /><use
x="0"
y="0"
xlink:href="#path33"
id="use36"
transform="translate(60)" /><use
x="0"
y="0"
xlink:href="#circle33"
id="use37"
transform="translate(20.000279)" /><use
x="0"
y="0"
xlink:href="#circle33"
id="use38"
transform="translate(40.000279)" /><use
x="0"
y="0"
xlink:href="#circle33"
id="use39"
transform="translate(60.000279)" /><use
x="0"
y="0"
xlink:href="#circle33"
id="use40"
transform="translate(80.000279)" /><use
x="0"
y="0"
xlink:href="#circle33"
id="use41"
transform="translate(100.00028)" /><use
x="0"
y="0"
xlink:href="#circle33"
id="use42"
transform="translate(120.00028)" /><use
x="0"
y="0"
xlink:href="#circle33"
id="use43"
transform="translate(140.00028)" /><use
x="0"
y="0"
xlink:href="#circle33"
id="use44"
transform="translate(160.00028)" /><path
d="m 29.496942,47.783503 q -0.249023,0.226384 -0.543322,0.354669 -0.2943,0.128284 -0.694245,0.128284 -0.226385,0 -0.422584,-0.08301 -0.188654,-0.09055 -0.324485,-0.233931 -0.13583,-0.150923 -0.218838,-0.339576 -0.07546,-0.188654 -0.07546,-0.399946 0,-0.211292 0.06791,-0.362214 0.06792,-0.158469 0.188654,-0.271662 0.120738,-0.113192 0.279207,-0.203745 0.158469,-0.09055 0.339576,-0.188654 0.377308,-0.249023 0.671607,-0.505592 0.301846,-0.256568 0.535776,-0.475407 0.233931,-0.226384 0.407492,-0.384853 0.173561,-0.158469 0.301846,-0.218838 0.07546,-0.03773 0.13583,-0.06037 0.06792,-0.03019 0.135831,-0.03773 0.06791,-0.01509 0.150923,-0.01509 0.08301,-0.0075 0.211292,-0.0075 0.392399,0 0.618783,0.158469 0.233931,0.158469 0.347123,0.415038 0.120738,0.249022 0.150923,0.56596 0.03018,0.316938 0.03018,0.641422 v 7.002821 q 0,0.33203 -0.03018,0.656514 -0.03018,0.316938 -0.150923,0.565961 -0.113192,0.249023 -0.347123,0.407492 -0.226384,0.150922 -0.618783,0.150922 -0.399946,0 -0.62633,-0.150922 -0.226384,-0.158469 -0.347123,-0.407492 -0.113192,-0.249023 -0.143376,-0.565961 -0.03018,-0.324484 -0.03018,-0.656514 z"
id="text44"
style="font-size:15.4545px;line-height:1;font-family:'Concert One';-inkscape-font-specification:'Concert One, Normal';letter-spacing:-1px;word-spacing:5px;fill:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
aria-label="1" /><path
d="m 49.994806,44.512566 q 0.852716,0 1.49414,0.233931 0.64897,0.226385 1.079102,0.641424 0.430131,0.407493 0.64897,0.981001 0.218839,0.565963 0.218839,1.252664 0,0.45277 -0.128285,0.837624 -0.120739,0.384854 -0.324485,0.701793 -0.1962,0.316939 -0.45277,0.573509 -0.25657,0.256569 -0.520685,0.45277 -0.633878,0.467862 -1.426226,0.754616 -0.264115,0.105646 -0.528231,0.25657 -0.25657,0.143377 -0.460316,0.339577 -0.203747,0.188654 -0.332031,0.445224 -0.128285,0.249023 -0.128285,0.573508 v 0.467863 h 2.626065 q 0.316939,0 0.633878,0.03018 0.316939,0.03018 0.573508,0.135831 0.25657,0.105646 0.41504,0.316939 0.158469,0.203747 0.158469,0.558416 0,0.362216 -0.158469,0.573509 -0.15847,0.211292 -0.41504,0.324485 -0.256569,0.113192 -0.573508,0.143377 -0.316939,0.03019 -0.633878,0.03019 h -3.75799 q -0.377308,0 -0.626331,-0.120739 -0.241478,-0.128285 -0.384855,-0.316939 -0.143377,-0.1962 -0.203746,-0.430131 -0.05282,-0.241478 -0.05282,-0.475409 v -1.207386 q 0,-0.581055 0.188654,-1.086648 0.1962,-0.513139 0.543324,-0.928178 0.354669,-0.422585 0.837624,-0.754616 0.4905,-0.332032 1.086647,-0.565963 0.596147,-0.233931 0.950817,-0.445223 0.35467,-0.218839 0.543324,-0.422586 0.1962,-0.211292 0.25657,-0.422585 0.06037,-0.211292 0.06037,-0.430131 0,-0.392401 -0.135831,-0.618786 -0.128285,-0.226385 -0.324485,-0.332031 -0.188654,-0.113192 -0.399947,-0.135831 -0.203746,-0.03018 -0.347123,-0.03018 -0.377309,0 -0.603694,0.113193 -0.226384,0.105646 -0.347123,0.271662 -0.113193,0.166015 -0.150923,0.369762 -0.03018,0.1962 -0.03018,0.377308 0,0.2943 -0.113192,0.505593 -0.113193,0.203747 -0.286755,0.332031 -0.166015,0.128285 -0.369762,0.188654 -0.1962,0.06037 -0.362216,0.06037 -0.513139,0 -0.830078,-0.279208 -0.316939,-0.286755 -0.316939,-0.731978 0,-0.196201 0.04528,-0.505593 0.04528,-0.309393 0.173562,-0.656517 0.128285,-0.347123 0.362216,-0.694247 0.233931,-0.347123 0.611239,-0.626331 0.377308,-0.279209 0.920632,-0.45277 0.543324,-0.173562 1.297941,-0.173562 z"
id="text45"
style="font-size:15.4545px;line-height:1;font-family:'Concert One';-inkscape-font-specification:'Concert One, Normal';letter-spacing:-1px;word-spacing:5px;fill:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
aria-label="2" /><path
d="M 70.623647,46.946934 V 46.841288 H 68.66165 q -0.256569,0 -0.535776,-0.03018 -0.271661,-0.03019 -0.498045,-0.135831 -0.226385,-0.105646 -0.377307,-0.316938 -0.143377,-0.211292 -0.143377,-0.573506 0,-0.354669 0.143377,-0.558415 0.150922,-0.211292 0.377307,-0.316938 0.226384,-0.105646 0.498045,-0.13583 0.279207,-0.03019 0.535776,-0.03019 h 3.36558 q 0.301846,0 0.535776,0.0981 0.241477,0.0981 0.399946,0.264115 0.166015,0.158469 0.249022,0.369761 0.08301,0.211292 0.08301,0.43013 0,0.128284 -0.02264,0.249023 -0.01509,0.120738 -0.08301,0.23393 l -1.358306,2.263843 q 0.347123,0.143377 0.679153,0.384853 0.33203,0.233931 0.588599,0.596145 0.264115,0.362215 0.415038,0.860261 0.158469,0.490499 0.158469,1.131921 0,0.72443 -0.158469,1.29039 -0.150923,0.558415 -0.415038,0.973453 -0.256569,0.415038 -0.611237,0.701791 -0.347123,0.279207 -0.747069,0.452769 -0.392399,0.173561 -0.814983,0.249022 -0.422584,0.07546 -0.830076,0.07546 -0.309392,0 -0.66406,-0.06037 -0.354669,-0.06037 -0.709338,-0.188654 -0.347122,-0.128284 -0.671606,-0.309392 -0.316938,-0.188653 -0.565961,-0.445222 -0.249023,-0.256569 -0.399946,-0.573507 -0.143376,-0.316938 -0.143376,-0.701791 0,-0.188654 0.05282,-0.354669 0.05282,-0.173561 0.158469,-0.301846 0.113192,-0.13583 0.279207,-0.211292 0.166015,-0.08301 0.399946,-0.08301 0.309391,0 0.573506,0.173561 0.271662,0.173561 0.452769,0.535776 0.04528,0.08301 0.135831,0.188654 0.0981,0.105646 0.249022,0.196199 0.158469,0.09055 0.377307,0.158469 0.218838,0.06037 0.505592,0.06037 0.437676,0 0.716883,-0.135831 0.286754,-0.143377 0.452769,-0.369761 0.166015,-0.226384 0.226384,-0.513138 0.06792,-0.286753 0.06792,-0.581053 0,-0.339576 -0.113193,-0.626329 -0.113192,-0.286754 -0.309391,-0.498046 -0.188654,-0.211292 -0.437677,-0.324484 -0.241476,-0.120738 -0.498045,-0.120738 -0.362215,0 -0.588599,-0.218838 -0.226384,-0.218839 -0.226384,-0.596146 0,-0.158469 0.06037,-0.347122 0.06037,-0.1962 0.196199,-0.437677 z"
id="text46"
style="font-size:15.4545px;line-height:1;font-family:'Concert One';-inkscape-font-specification:'Concert One, Normal';letter-spacing:-1px;word-spacing:5px;fill:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
aria-label="3" /><path
d="m 31.77858,68.317547 q 0.369761,0 0.581053,0.105646 0.211292,0.0981 0.316938,0.271661 0.113192,0.173561 0.143376,0.407492 0.03019,0.226384 0.03019,0.482953 v 4.467316 q 0,0.264115 -0.03019,0.4905 -0.03018,0.226384 -0.13583,0.399945 -0.105646,0.166015 -0.316938,0.264115 -0.211292,0.0981 -0.573507,0.0981 -0.354669,0 -0.565961,-0.09055 -0.211292,-0.0981 -0.324484,-0.256569 -0.113192,-0.158469 -0.150923,-0.377307 -0.03018,-0.218838 -0.03018,-0.467861 v -1.343213 h -2.580781 q -0.467861,0 -0.769707,-0.07546 -0.301845,-0.07546 -0.482953,-0.1962 -0.173561,-0.128285 -0.249023,-0.2943 -0.06791,-0.166015 -0.06791,-0.339576 0,-0.09055 0.01509,-0.181108 0.02264,-0.09055 0.04528,-0.181107 l 1.524321,-5.297392 q 0.07546,-0.279207 0.143377,-0.543322 0.07546,-0.264115 0.1962,-0.467861 0.128284,-0.203746 0.347122,-0.324484 0.218838,-0.128285 0.596145,-0.128285 0.271662,0 0.445223,0.07546 0.181107,0.07546 0.286753,0.203745 0.105646,0.120739 0.143377,0.286754 0.04528,0.158469 0.04528,0.339576 0,0.181108 -0.03019,0.369761 -0.03018,0.181108 -0.07546,0.339577 -0.03773,0.158469 -0.08301,0.286753 -0.03773,0.120738 -0.05282,0.181108 0,0 -0.04528,0.166015 -0.04528,0.158469 -0.120738,0.422584 -0.07546,0.256569 -0.166015,0.596145 -0.09055,0.33203 -0.188654,0.679153 -0.23393,0.814983 -0.520683,1.848805 l 0.06037,0.06037 h 1.58469 v -1.026275 q 0,-0.256569 0.03018,-0.482954 0.03019,-0.226384 0.135831,-0.392399 0.105646,-0.173561 0.316938,-0.271661 0.211292,-0.105646 0.573507,-0.105646 z"
id="text47"
style="font-size:15.4545px;line-height:1;font-family:'Concert One';-inkscape-font-specification:'Concert One, Normal';letter-spacing:-1px;word-spacing:5px;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
aria-label="4" /><path
d="m 48.981411,68.651633 q 0.309392,-0.113192 0.671606,-0.158469 0.369761,-0.05282 0.656515,-0.05282 0.271661,0 0.603691,0.06037 0.332031,0.06037 0.671607,0.211292 0.339576,0.143377 0.664061,0.384853 0.324484,0.241477 0.573506,0.603692 0.249023,0.354669 0.399946,0.845168 0.150923,0.490499 0.150923,1.124375 0,0.72443 -0.158469,1.29039 -0.150923,0.558415 -0.415038,0.973453 -0.256569,0.415038 -0.611238,0.701791 -0.347122,0.279207 -0.747068,0.452769 -0.392399,0.173561 -0.814983,0.249022 -0.422584,0.07546 -0.830076,0.07546 -0.445222,0 -0.943268,-0.113192 -0.490499,-0.120739 -0.935721,-0.354669 -0.437677,-0.241477 -0.777253,-0.596145 -0.33203,-0.362215 -0.452769,-0.852714 -0.04528,-0.181108 -0.04528,-0.362215 0,-0.188654 0.05282,-0.354669 0.05282,-0.173561 0.158469,-0.301846 0.113192,-0.13583 0.279207,-0.211292 0.166015,-0.08301 0.399945,-0.08301 0.309392,0 0.573507,0.173561 0.271661,0.173561 0.452769,0.535776 0.04528,0.08301 0.13583,0.188654 0.0981,0.105646 0.249023,0.196199 0.158469,0.09055 0.377307,0.158469 0.218838,0.06037 0.505592,0.06037 0.437676,0 0.716883,-0.135831 0.286754,-0.143377 0.452769,-0.369761 0.166015,-0.226384 0.226384,-0.513138 0.06791,-0.286753 0.06791,-0.581053 0,-0.369761 -0.128284,-0.633876 -0.128284,-0.271661 -0.347123,-0.445222 -0.211292,-0.181107 -0.482953,-0.264115 -0.271661,-0.09055 -0.550868,-0.09055 -0.241477,0 -0.482953,0.06792 -0.233931,0.06037 -0.475407,0.13583 -0.233931,0.07546 -0.475407,0.143377 -0.241477,0.06037 -0.498046,0.06037 -0.173561,0 -0.347122,-0.03773 -0.173561,-0.03773 -0.309392,-0.13583 -0.135831,-0.105646 -0.226384,-0.271661 -0.08301,-0.166016 -0.08301,-0.430131 l 0.437676,-4.150378 q 0.01509,-0.166015 0.06037,-0.347123 0.05282,-0.188653 0.150922,-0.347122 0.105646,-0.158469 0.271662,-0.256569 0.173561,-0.105646 0.437676,-0.105646 h 3.018457 q 0.279207,0 0.565961,0.03018 0.294299,0.03018 0.520683,0.13583 0.233931,0.105646 0.377308,0.316938 0.150922,0.203746 0.150922,0.558415 0,0.362215 -0.150922,0.573507 -0.143377,0.211292 -0.377308,0.316938 -0.226384,0.105646 -0.520683,0.13583 -0.286754,0.03019 -0.565961,0.03019 h -1.811074 q -0.06037,0 -0.120739,0.0075 -0.05282,0 -0.0981,0.03018 -0.03773,0.03019 -0.06791,0.0981 -0.02264,0.06037 -0.03018,0.181108 z"
id="text48"
style="font-size:15.4545px;line-height:1;font-family:'Concert One';-inkscape-font-specification:'Concert One, Normal';letter-spacing:-1px;word-spacing:5px;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
aria-label="5" /><path
d="m 67.13104,71.482665 q 0,-0.354669 0,-0.814983 0.0075,-0.467861 0.0075,-0.92063 0,-0.452768 0,-0.830075 0.0075,-0.377308 0.0075,-0.558415 0,-0.437676 0.09055,-0.882899 0.09055,-0.452768 0.256569,-0.867806 0.173562,-0.415038 0.415038,-0.777253 0.241477,-0.362215 0.535776,-0.633876 0.301846,-0.271661 0.641422,-0.422584 0.347123,-0.158469 0.731976,-0.158469 0.264115,0.01509 0.460315,0.105646 0.203746,0.08301 0.339576,0.218838 0.135831,0.128285 0.203746,0.2943 0.06792,0.166015 0.06792,0.347122 0,0.249023 -0.135831,0.498046 -0.135831,0.249023 -0.422584,0.467861 -0.226384,0.181107 -0.392399,0.324484 -0.166016,0.143377 -0.279208,0.286753 -0.105646,0.143377 -0.173561,0.309392 -0.06037,0.158469 -0.0981,0.369761 -0.03018,0.203746 -0.04528,0.482953 -0.0075,0.279208 -0.0075,0.656515 0.07546,-0.158469 0.218838,-0.264115 0.143377,-0.105646 0.309392,-0.166015 0.173561,-0.06037 0.362215,-0.08301 0.188654,-0.03019 0.362215,-0.03019 0.211292,0 0.498045,0.03773 0.2943,0.03773 0.603692,0.150923 0.309392,0.105646 0.611237,0.309392 0.301846,0.203746 0.535776,0.535776 0.233931,0.324484 0.377307,0.799891 0.150923,0.475407 0.150923,1.124375 v 0.694245 q 0,0.754615 -0.188653,1.282845 -0.181108,0.52823 -0.467861,0.882898 -0.286754,0.347123 -0.641422,0.543323 -0.354669,0.196199 -0.694245,0.301845 -0.339577,0.0981 -0.633876,0.120739 -0.286754,0.02264 -0.437677,0.02264 -0.203745,0 -0.520683,-0.02264 -0.309392,-0.02264 -0.664061,-0.120739 -0.354669,-0.105646 -0.709337,-0.301845 -0.347123,-0.1962 -0.633876,-0.543323 -0.286754,-0.354668 -0.467861,-0.882898 -0.173562,-0.52823 -0.173562,-1.282845 z m 2.203474,0.890445 q 0.01509,0.249023 0.113192,0.445222 0.08301,0.173562 0.264115,0.309392 0.181108,0.135831 0.513138,0.135831 0.33203,0 0.505592,-0.135831 0.173561,-0.13583 0.256568,-0.309392 0.09055,-0.196199 0.113193,-0.445222 v -1.101737 q -0.02264,-0.249023 -0.113193,-0.445222 -0.08301,-0.166015 -0.264115,-0.301846 -0.173561,-0.143377 -0.498045,-0.143377 -0.33203,0 -0.513138,0.143377 -0.181107,0.135831 -0.264115,0.301846 -0.0981,0.196199 -0.113192,0.445222 z"
id="text49"
style="font-size:15.4545px;line-height:1;font-family:'Concert One';-inkscape-font-specification:'Concert One, Normal';letter-spacing:-1px;word-spacing:5px;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
aria-label="6" /><path
d="m 90.621152,67.038889 v -0.09055 h -2.369488 q -0.264115,0 -0.550869,-0.03019 -0.279207,-0.03018 -0.520684,-0.13583 -0.23393,-0.105646 -0.392399,-0.316938 -0.150923,-0.211292 -0.150923,-0.573507 0,-0.354669 0.150923,-0.558415 0.158469,-0.211292 0.392399,-0.316938 0.241477,-0.105646 0.520684,-0.13583 0.286754,-0.03018 0.550869,-0.03018 h 4.044732 q 0.301846,0 0.535776,0.0981 0.241477,0.0981 0.399946,0.264115 0.166015,0.158469 0.249023,0.369761 0.08301,0.211292 0.08301,0.43013 0,0.128284 -0.02264,0.249023 -0.02264,0.120738 -0.08301,0.23393 l -2.173289,3.969271 q -0.188654,0.339577 -0.301846,0.588599 -0.105646,0.241477 -0.166015,0.460315 -0.05282,0.218838 -0.07546,0.452769 -0.01509,0.23393 -0.01509,0.550868 v 1.131921 q 0,0.332031 -0.02264,0.648969 -0.01509,0.316938 -0.128284,0.56596 -0.113193,0.249023 -0.362215,0.399946 -0.241477,0.150923 -0.694245,0.150923 -0.445223,0 -0.686699,-0.150923 -0.233931,-0.150923 -0.347123,-0.399946 -0.105646,-0.249022 -0.128284,-0.56596 -0.01509,-0.316938 -0.01509,-0.648969 v -1.131921 q 0,-0.535776 0.02264,-0.875353 0.03018,-0.339576 0.105646,-0.596145 0.07546,-0.264115 0.203746,-0.513138 0.128284,-0.249022 0.33203,-0.611237 z"
id="text50"
style="font-size:15.4545px;line-height:1;font-family:'Concert One';-inkscape-font-specification:'Concert One, Normal';letter-spacing:-1px;word-spacing:5px;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
aria-label="7" /><path
d="m 109.15229,72.552229 q 0.0151,0.23393 0.12073,0.415038 0.0906,0.158469 0.27921,0.286753 0.18865,0.128284 0.55087,0.128284 0.33958,0 0.52068,-0.128284 0.18866,-0.128284 0.27166,-0.286753 0.0981,-0.181108 0.1132,-0.415038 v -1.433767 q -0.0151,-0.256569 -0.1132,-0.452769 -0.083,-0.173561 -0.27166,-0.309392 -0.1811,-0.143377 -0.52068,-0.143377 -0.18111,0 -0.31694,0.03773 -0.12828,0.03773 -0.23393,0.105646 -0.0981,0.06037 -0.16601,0.143377 -0.0679,0.08301 -0.1132,0.166015 -0.10564,0.1962 -0.12073,0.452769 z m 0.95081,-8.029096 q 0.17356,0 0.46031,0.02264 0.2943,0.01509 0.62633,0.0981 0.33203,0.08301 0.66406,0.256569 0.33958,0.173561 0.61124,0.490499 0.27166,0.316938 0.43768,0.799891 0.17356,0.475407 0.17356,1.169653 v 0.196199 q 0,0.467861 -0.0604,0.799891 -0.0604,0.324485 -0.17356,0.550869 -0.11319,0.226384 -0.27166,0.377307 -0.15847,0.150923 -0.35467,0.264115 0.1962,0.113192 0.37731,0.264115 0.18865,0.150923 0.32448,0.377307 0.14338,0.226384 0.22638,0.558415 0.083,0.324484 0.083,0.792345 v 0.830075 q 0,0.694245 -0.18111,1.177199 -0.1811,0.475407 -0.4754,0.792345 -0.28676,0.316938 -0.64142,0.490499 -0.35467,0.173561 -0.7018,0.256569 -0.34712,0.08301 -0.64896,0.0981 -0.30185,0.02264 -0.47541,0.02264 -0.17356,0 -0.48295,-0.02264 -0.30185,-0.01509 -0.66406,-0.0981 -0.35467,-0.08301 -0.72443,-0.256569 -0.36222,-0.173561 -0.66406,-0.490499 -0.30185,-0.316938 -0.4905,-0.792345 -0.18866,-0.482954 -0.18866,-1.177199 v -0.830075 q 0,-0.467861 0.083,-0.792345 0.083,-0.332031 0.21884,-0.558415 0.14337,-0.226384 0.32448,-0.377307 0.18866,-0.150923 0.38486,-0.264115 -0.1962,-0.113192 -0.35467,-0.264115 -0.15847,-0.150923 -0.27166,-0.377307 -0.1132,-0.226384 -0.17357,-0.550869 -0.0604,-0.33203 -0.0604,-0.799891 v -0.196199 q 0,-0.694246 0.17356,-1.169653 0.1811,-0.482953 0.46031,-0.799891 0.28675,-0.316938 0.63388,-0.490499 0.35467,-0.173561 0.69424,-0.256569 0.33958,-0.08301 0.63388,-0.0981 0.2943,-0.02264 0.46786,-0.02264 z m -0.95081,3.441041 q 0.0151,0.23393 0.12073,0.415038 0.0906,0.158469 0.27921,0.286753 0.18865,0.128285 0.55087,0.128285 0.33958,0 0.52068,-0.128285 0.18866,-0.128284 0.27166,-0.286753 0.0981,-0.181108 0.1132,-0.415038 V 67.34539 q -0.0151,-0.256569 -0.1132,-0.452768 -0.083,-0.173562 -0.27166,-0.309392 -0.1811,-0.143377 -0.52068,-0.143377 -0.18111,0 -0.31694,0.03773 -0.12828,0.03773 -0.23393,0.105646 -0.0981,0.06037 -0.16601,0.143377 -0.0679,0.08301 -0.1132,0.166015 -0.10564,0.196199 -0.12073,0.452768 z"
id="text51"
style="font-size:15.4545px;line-height:1;font-family:'Concert One';-inkscape-font-specification:'Concert One, Normal';letter-spacing:-1px;word-spacing:5px;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
aria-label="8" /><path
d="m 133.23948,68.343537 q 0,0.354669 -0.008,0.814983 0,0.460315 0,0.905538 0,0.445222 -0.008,0.822529 0,0.369761 0,0.550869 0,0.66406 -0.20374,1.335667 -0.1962,0.66406 -0.55087,1.207383 -0.34713,0.535776 -0.83008,0.875352 -0.48295,0.339577 -1.05646,0.339577 -0.27166,-0.01509 -0.47541,-0.0981 -0.20374,-0.09055 -0.33957,-0.218838 -0.13583,-0.135831 -0.20375,-0.301846 -0.0679,-0.166015 -0.0679,-0.347123 0,-0.249022 0.13583,-0.498045 0.13583,-0.249023 0.42258,-0.467861 0.23393,-0.173561 0.39995,-0.316938 0.16601,-0.150923 0.2792,-0.2943 0.1132,-0.143376 0.17357,-0.301845 0.0679,-0.166015 0.0981,-0.377307 0.0377,-0.211292 0.0453,-0.4905 0.0151,-0.279207 0.0151,-0.66406 -0.083,0.158469 -0.22638,0.264115 -0.13583,0.105646 -0.30939,0.166015 -0.16602,0.06037 -0.36222,0.09055 -0.18865,0.02264 -0.36976,0.02264 -0.21129,0 -0.50559,-0.03773 -0.28675,-0.03773 -0.59614,-0.143376 -0.3094,-0.113192 -0.61124,-0.309392 -0.30185,-0.203746 -0.53578,-0.52823 -0.23393,-0.33203 -0.38485,-0.792345 -0.14338,-0.467861 -0.14338,-1.116829 v -0.694245 q 0,-0.754615 0.18111,-1.282845 0.18865,-0.535776 0.47541,-0.882898 0.28675,-0.354669 0.64142,-0.550869 0.35467,-0.203746 0.69424,-0.301845 0.33958,-0.105646 0.62633,-0.128285 0.28676,-0.02264 0.44523,-0.02264 0.20374,0 0.52068,0.02264 0.31694,0.02264 0.66406,0.128285 0.35467,0.0981 0.70179,0.301845 0.35467,0.1962 0.63388,0.550869 0.2792,0.347122 0.45277,0.882898 0.1811,0.52823 0.1811,1.282845 z m -2.17329,-0.890445 q -0.0151,-0.249023 -0.11319,-0.445222 -0.083,-0.166015 -0.26411,-0.301846 -0.18111,-0.143377 -0.51314,-0.143377 -0.33203,0 -0.51314,0.143377 -0.18111,0.135831 -0.26411,0.301846 -0.0981,0.196199 -0.1132,0.445222 v 1.101737 q 0.0151,0.249023 0.1132,0.445222 0.083,0.173562 0.26411,0.309392 0.18111,0.135831 0.51314,0.135831 0.33203,0 0.51314,-0.135831 0.1811,-0.13583 0.26411,-0.309392 0.0981,-0.196199 0.11319,-0.445222 z"
id="text52"
style="font-size:15.4545px;line-height:1;font-family:'Concert One';-inkscape-font-specification:'Concert One, Normal';letter-spacing:-1px;word-spacing:5px;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
aria-label="9" /><path
d="m 146.21384,68.364815 q -0.22559,0.205078 -0.49219,0.321289 -0.2666,0.116211 -0.62891,0.116211 -0.20508,0 -0.38281,-0.0752 -0.1709,-0.08203 -0.29394,-0.211914 -0.12305,-0.136718 -0.19825,-0.307617 -0.0684,-0.170898 -0.0684,-0.362304 0,-0.191407 0.0615,-0.328125 0.0615,-0.143555 0.1709,-0.246094 0.10937,-0.102539 0.25293,-0.184571 0.14355,-0.08203 0.30761,-0.170898 0.3418,-0.225586 0.6084,-0.458008 0.27344,-0.232422 0.48535,-0.430664 0.21192,-0.205078 0.36914,-0.348633 0.15723,-0.143554 0.27344,-0.198242 0.0684,-0.03418 0.12305,-0.05469 0.0615,-0.02734 0.12304,-0.03418 0.0615,-0.01367 0.13672,-0.01367 0.0752,-0.0068 0.19141,-0.0068 0.35547,0 0.56055,0.143555 0.21191,0.143555 0.31445,0.375976 0.10937,0.225586 0.13672,0.512696 0.0273,0.287109 0.0273,0.581054 v 6.34375 q 0,0.300782 -0.0273,0.594727 -0.0274,0.287109 -0.13672,0.512695 -0.10254,0.225586 -0.31445,0.369141 -0.20508,0.136719 -0.56055,0.136719 -0.36231,0 -0.56738,-0.136719 -0.20508,-0.143555 -0.31446,-0.369141 -0.10254,-0.225586 -0.12988,-0.512695 -0.0273,-0.293945 -0.0273,-0.594727 z m 3.00937,-0.259766 q 0,-0.663086 0.18457,-1.12793 0.19141,-0.471679 0.48535,-0.779296 0.29395,-0.307618 0.64942,-0.485352 0.35547,-0.177734 0.69043,-0.266602 0.34179,-0.08887 0.6084,-0.109375 0.27343,-0.02051 0.40332,-0.02051 h 0.23242 q 0.12988,0 0.40332,0.02051 0.27344,0.02051 0.60156,0.109375 0.33496,0.08887 0.68359,0.266602 0.35547,0.177734 0.64258,0.485352 0.29395,0.307617 0.47852,0.779296 0.18457,0.464844 0.18457,1.12793 v 4.033203 q 0,0.683594 -0.18457,1.16211 -0.18457,0.478515 -0.47852,0.799804 -0.28711,0.314453 -0.64258,0.492188 -0.34863,0.177734 -0.68359,0.273437 -0.32812,0.08887 -0.60156,0.109375 -0.27344,0.02051 -0.40332,0.02051 h -0.23242 q -0.12989,0 -0.40332,-0.02051 -0.26661,-0.02051 -0.6084,-0.109375 -0.33496,-0.0957 -0.69043,-0.273437 -0.35547,-0.177735 -0.64942,-0.492188 -0.29394,-0.321289 -0.48535,-0.799804 -0.18457,-0.478516 -0.18457,-1.16211 z m 2.24219,4.388672 q 0.0205,0.239258 0.12988,0.423828 0.0889,0.157227 0.2666,0.293945 0.18457,0.129883 0.50586,0.129883 0.31446,0 0.49219,-0.129883 0.17773,-0.136718 0.2666,-0.293945 0.10254,-0.18457 0.12988,-0.423828 v -4.621094 q -0.0273,-0.239258 -0.12988,-0.423828 -0.0889,-0.157226 -0.2666,-0.287109 -0.17773,-0.136719 -0.49219,-0.136719 -0.32129,0 -0.50586,0.136719 -0.17773,0.129883 -0.2666,0.287109 -0.10937,0.18457 -0.12988,0.423828 z"
id="text53"
style="font-size:14px;line-height:1;font-family:'Concert One';-inkscape-font-specification:'Concert One, Normal';letter-spacing:-0.6px;word-spacing:5px;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
aria-label="10" /><path
d="m 165.96384,68.364815 q -0.22559,0.205078 -0.49219,0.321289 -0.2666,0.116211 -0.62891,0.116211 -0.20508,0 -0.38281,-0.0752 -0.1709,-0.08203 -0.29394,-0.211914 -0.12305,-0.136718 -0.19825,-0.307617 -0.0684,-0.170898 -0.0684,-0.362304 0,-0.191407 0.0615,-0.328125 0.0615,-0.143555 0.1709,-0.246094 0.10937,-0.102539 0.25293,-0.184571 0.14355,-0.08203 0.30761,-0.170898 0.3418,-0.225586 0.6084,-0.458008 0.27344,-0.232422 0.48535,-0.430664 0.21192,-0.205078 0.36914,-0.348633 0.15723,-0.143554 0.27344,-0.198242 0.0684,-0.03418 0.12305,-0.05469 0.0615,-0.02734 0.12304,-0.03418 0.0615,-0.01367 0.13672,-0.01367 0.0752,-0.0068 0.19141,-0.0068 0.35547,0 0.56055,0.143555 0.21191,0.143555 0.31445,0.375976 0.10937,0.225586 0.13672,0.512696 0.0273,0.287109 0.0273,0.581054 v 6.34375 q 0,0.300782 -0.0273,0.594727 -0.0274,0.287109 -0.13672,0.512695 -0.10254,0.225586 -0.31445,0.369141 -0.20508,0.136719 -0.56055,0.136719 -0.36231,0 -0.56738,-0.136719 -0.20508,-0.143555 -0.31446,-0.369141 -0.10254,-0.225586 -0.12988,-0.512695 -0.0273,-0.293945 -0.0273,-0.594727 z m 6.08554,-3.048828 q 0.77246,0 1.35352,0.211914 0.58789,0.205078 0.97754,0.581054 0.38965,0.369141 0.58789,0.888672 0.19824,0.512696 0.19824,1.134766 0,0.410156 -0.11621,0.758789 -0.10937,0.348633 -0.29395,0.635742 -0.17773,0.28711 -0.41015,0.519531 -0.23242,0.232422 -0.47168,0.410157 -0.57422,0.423828 -1.29199,0.683593 -0.23926,0.0957 -0.47852,0.232422 -0.23242,0.129883 -0.41699,0.307617 -0.18457,0.170899 -0.30078,0.403321 -0.11621,0.225586 -0.11621,0.519531 v 0.423828 h 2.3789 q 0.28711,0 0.57422,0.02734 0.28711,0.02734 0.51953,0.123047 0.23242,0.0957 0.37598,0.287109 0.14355,0.18457 0.14355,0.50586 0,0.328125 -0.14355,0.519531 -0.14356,0.191406 -0.37598,0.293945 -0.23242,0.102539 -0.51953,0.129883 -0.28711,0.02734 -0.57422,0.02734 h -3.40429 q -0.3418,0 -0.56739,-0.109375 -0.21875,-0.116211 -0.34863,-0.28711 -0.12988,-0.177734 -0.18457,-0.389648 -0.0479,-0.21875 -0.0479,-0.430664 v -1.09375 q 0,-0.526367 0.1709,-0.984375 0.17773,-0.464844 0.49218,-0.840821 0.32129,-0.382812 0.75879,-0.683593 0.44434,-0.300782 0.98438,-0.512696 0.54004,-0.211914 0.86133,-0.40332 0.32129,-0.198242 0.49218,-0.382812 0.17774,-0.191407 0.23243,-0.382813 0.0547,-0.191406 0.0547,-0.389648 0,-0.355469 -0.12304,-0.560547 -0.11621,-0.205078 -0.29395,-0.300781 -0.1709,-0.10254 -0.3623,-0.123047 -0.18457,-0.02734 -0.31446,-0.02734 -0.34179,0 -0.54687,0.102539 -0.20508,0.0957 -0.31445,0.246094 -0.10254,0.15039 -0.13672,0.334961 -0.0274,0.177734 -0.0274,0.341797 0,0.266601 -0.10254,0.458007 -0.10254,0.184571 -0.25976,0.300782 -0.15039,0.116211 -0.33496,0.170898 -0.17774,0.05469 -0.32813,0.05469 -0.46484,0 -0.75195,-0.25293 -0.28711,-0.259766 -0.28711,-0.663086 0,-0.177734 0.041,-0.458008 0.041,-0.280273 0.15722,-0.594726 0.11621,-0.314454 0.32813,-0.628907 0.21191,-0.314453 0.55371,-0.567383 0.34179,-0.252929 0.83398,-0.410156 0.49219,-0.157226 1.17578,-0.157226 z"
id="text1"
style="font-size:14px;line-height:1;font-family:'Concert One';-inkscape-font-specification:'Concert One, Normal';letter-spacing:-0.6px;word-spacing:5px;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
aria-label="12" /></g><g
id="layer2"
style="display:inline"><g
id="g57"
transform="matrix(1.6041667,0,0,1.6041667,30,31.604167)"
style="stroke-width:0.623377"><path
style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.24675;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 0,-7 3.8,4 h -7.6 z"
id="path57" /><path
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.24675;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 0,-5 3,4 0,5 -3,4 Z"
id="path56" /></g><g
id="g59"
transform="matrix(1.6041667,0,0,-1.6041667,49.999833,28.395834)"
style="stroke-width:0.623377"><path
style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.24675;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 0,-7 3.8,4 h -7.6 z"
id="path58" /><path
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.24675;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 0,-5 3,4 0,5 -3,4 Z"
id="path59" /><path
style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.24675;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 0,-3.4155845 1.3234947,0.4155845 0,-0.44155834 -1.3234947,0.4155845 Z"
id="path60" /></g></g><g
id="layer1"><circle
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.99998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="path54"
cx="10"
cy="10"
r="10" /><path
style="opacity:1;fill:#fff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m8.92 1.99q3.29 0 4.68 1.13 1.39 1.1 1.39 2.74 0 2.06-1.78 3.36-.67.5-1.63.91l-.38.17q-.17.07-.34 1.01-.14.91-.14 1.32-.12.12-.7.29-.55.14-.94.14-.36 0-.55-.05-.26-.43-.26-1.87 0-1.44.12-1.85 3.79-.91 3.79-3.07 0-1.87-3.46-1.87-1.22 0-1.78.24-.14-.24-.14-1.01 0-.77.22-1.3.65-.29 1.9-.29zm-.7 15.5q-.46-.48-.46-1.15 0-.67.46-1.13.48-.48 1.15-.48.67 0 1.13.48.48.46.48 1.13 0 .67-.48 1.15-.46.46-1.13.46-.67 0-1.15-.46z"
id="path55" /><circle
style="opacity:1;fill:#c4693e;fill-opacity:1;stroke:none;stroke-width:1.99998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="circle1"
cx="10"
cy="90"
r="10" /><path
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 10,83.8 5.56,6.03 H 13.9 v 5.2 H 11.39 V 91.1 H 8.61 v 3.93 H 6.1 v -5.2 H 4.44 Z"
id="path1" /><circle
style="opacity:1;fill:#978e47;fill-opacity:1;stroke:none;stroke-width:1.99998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="circle2"
cx="30"
cy="90"
r="10" /><use
x="0"
y="0"
xlink:href="#path1"
id="use2"
transform="translate(20)" /><circle
style="opacity:1;fill:#b44;fill-opacity:1;stroke:none;stroke-width:1.99998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="circle3"
cx="50"
cy="90"
r="10" /><use
x="0"
y="0"
xlink:href="#path1"
id="use3"
transform="translate(40)" /><circle
style="opacity:1;fill:#3fa5ff;fill-opacity:1;stroke:none;stroke-width:1.99998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="circle4"
cx="10"
cy="110"
r="10" /><path
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 10,102 c -1.02,2.01 -7.08,14.38 0,14.38 7.08,0 1.02,-12.46 0,-14.38 z"
id="path4" /><circle
style="opacity:1;fill:#202020;fill-opacity:1;stroke:none;stroke-width:1.99998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="circle5"
cx="30"
cy="110"
r="10" /><path
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 30.75,102.98 -2.9,1.04 -2.21,3.37 0.1,0.15 1.34,-0.48 3.06,1.97 0.33,-0.43 3.2,-1.2 0.98,0.63 0.29,-0.82 -1.29,-2.24 z m -3.68,4.62 -2.89,1.04 -2.22,3.37 1.53,3.32 h 6.78 l 0.99,-3.5 -1.3,-2.25 z m 6.61,0.47 -2.9,1.04 -0.27,0.4 0.17,0.13 1.22,2.21 -0.91,3.48 h 5.88 l 0.99,-3.02 -1.3,-2.25 z"
id="path5" /><circle
style="opacity:1;fill:#c02c2c;fill-opacity:1;stroke:none;stroke-width:1.99998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="circle6"
cx="50"
cy="110"
r="10" /><path
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 51.389932,101.42993 c -0.2,-0.09 -0.42,0 -0.51,0.2 l -6.66,15.25 c -0.09,0.2 0,0.42 0.2,0.5 0.2,0.09 0.42,0 0.51,-0.2 l 6.66,-15.25 c 0.09,-0.2 0,-0.42 -0.2,-0.51 z m 0.53,0.93 -2.39,5.48 c 0.21,0 0.42,0 0.62,0.01 0.2,0.01 0.41,0.02 0.61,0.03 0.2,0.01 0.4,0.03 0.6,0.05 0.2,0.02 0.39,0.04 0.59,0.06 0.19,0.02 0.39,0.05 0.58,0.08 0.19,0.03 0.38,0.06 0.57,0.1 0.09,0.02 0.18,0.04 0.28,0.05 l 2.12,-2.56 c -0.01,-0.01 -0.02,-0.03 -0.04,-0.05 -0.15,-0.15 -0.28,-0.31 -0.41,-0.47 -0.14,-0.15 -0.27,-0.31 -0.41,-0.46 -0.14,-0.15 -0.28,-0.29 -0.42,-0.44 -0.14,-0.14 -0.29,-0.28 -0.43,-0.42 -0.15,-0.14 -0.3,-0.27 -0.45,-0.4 -0.07,-0.07 -0.15,-0.13 -0.22,-0.19 -0.08,-0.07 -0.16,-0.13 -0.24,-0.19 -0.07,-0.06 -0.15,-0.12 -0.23,-0.18 -0.08,-0.06 -0.16,-0.12 -0.24,-0.17 -0.08,-0.06 -0.16,-0.12 -0.25,-0.17 -0.08,-0.05 -0.16,-0.11 -0.24,-0.16 z m 3.37,4.06 -2.12,2.56 c 0.1,0.02 0.19,0.04 0.29,0.06 0.19,0.04 0.37,0.08 0.55,0.13 0.18,0.04 0.37,0.09 0.55,0.14 0.18,0.05 0.36,0.1 0.54,0.15 0.18,0.05 0.36,0.1 0.53,0.16 0.18,0.05 0.35,0.11 0.53,0.17 0.18,0.06 0.35,0.12 0.53,0.17 0.17,0.06 0.35,0.12 0.52,0.18 0.35,0.12 0.69,0.25 1.04,0.37 -0.25,-0.36 -0.49,-0.72 -0.74,-1.07 -0.24,-0.35 -0.49,-0.7 -0.73,-1.05 -0.13,-0.17 -0.25,-0.34 -0.38,-0.51 -0.12,-0.18 -0.25,-0.35 -0.38,-0.52 -0.12,-0.16 -0.25,-0.33 -0.38,-0.5 -0.11,-0.15 -0.23,-0.29 -0.35,-0.44 z"
id="path6" /><circle
style="opacity:1;fill:#4f54e9;fill-opacity:1;stroke:none;stroke-width:1.99998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="circle7"
cx="70"
cy="110"
r="10" /><path
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 69.25,117.9 v -1.55 c -0.96,-0.09 -1.5,-0.44 -2.13,-1.04 -0.63,-0.6 -0.98,-1.45 -1.06,-2.56 l 2.2,-0.49 c 0.03,0.65 0.21,1.14 0.54,1.49 0.33,0.35 0.75,0.52 1.24,0.52 0.47,0 0.81,-0.13 1.03,-0.4 0.23,-0.27 0.34,-0.55 0.34,-0.85 0,-0.39 -0.11,-0.71 -0.34,-0.94 -0.23,-0.24 -0.52,-0.46 -0.88,-0.65 l -1.53,-0.81 c -0.75,-0.39 -1.33,-0.87 -1.76,-1.42 -0.42,-0.55 -0.63,-1.28 -0.63,-2.18 0,-0.65 0.14,-1.22 0.41,-1.71 0.29,-0.51 0.67,-0.91 1.15,-1.21 0.49,-0.3 0.8,-0.49 1.42,-0.56 v -1.44 h 1.65 v 1.48 c 0.91,0.12 1.41,0.49 1.94,1.1 0.53,0.6 0.82,1.39 0.88,2.36 l -2.07,0.43 c -0.03,-0.6 -0.2,-1.06 -0.5,-1.39 -0.29,-0.33 -0.65,-0.5 -1.08,-0.5 -0.41,0 -0.74,0.12 -0.99,0.36 -0.24,0.23 -0.36,0.54 -0.36,0.94 0,0.36 0.11,0.65 0.32,0.86 0.21,0.21 0.51,0.42 0.88,0.63 l 1.63,0.86 c 0.83,0.45 1.43,0.95 1.82,1.51 0.39,0.55 0.58,1.23 0.58,2.02 0,0.6 -0.13,1.16 -0.4,1.67 -0.25,0.51 -0.63,0.95 -1.12,1.3 -0.48,0.33 -0.84,0.54 -1.53,0.63 v 1.4 z"
id="path7" /><circle
style="opacity:1;fill:#8b5dd7;fill-opacity:1;stroke:none;stroke-width:1.99998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="circle8"
cx="90"
cy="110"
r="10" /><path
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 90,102.88 -7.02,3.54 v 8.68 h 2.55 v -6.19 h 8.94 v 6.19 h 2.55 v -8.68 z"
id="path8" /><circle
style="opacity:1;fill:#239a96;fill-opacity:1;stroke:none;stroke-width:1.99998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="circle9"
cx="10"
cy="130"
r="10" /><path
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 8.92,121.99 q 3.29,0 4.68,1.13 1.39,1.1 1.39,2.74 0,2.06 -1.78,3.36 -0.67,0.5 -1.63,0.91 l -0.38,0.17 q -0.17,0.07 -0.34,1.01 -0.14,0.91 -0.14,1.32 -0.12,0.12 -0.7,0.29 -0.55,0.14 -0.94,0.14 -0.36,0 -0.55,-0.05 -0.26,-0.43 -0.26,-1.87 0,-1.44 0.12,-1.85 3.79,-0.91 3.79,-3.07 0,-1.87 -3.46,-1.87 -1.22,0 -1.78,0.24 -0.14,-0.24 -0.14,-1.01 0,-0.77 0.22,-1.3 0.65,-0.29 1.9,-0.29 z m -0.7,15.5 q -0.46,-0.48 -0.46,-1.15 0,-0.67 0.46,-1.13 0.48,-0.48 1.15,-0.48 0.67,0 1.13,0.48 0.48,0.46 0.48,1.13 0,0.67 -0.48,1.15 -0.46,0.46 -1.13,0.46 -0.67,0 -1.15,-0.46 z"
id="path9" /><circle
style="opacity:1;fill:#239a96;fill-opacity:1;stroke:none;stroke-width:1.99998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="circle10"
cx="30"
cy="130"
r="10" /><path
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 32.879599,122.82 c -0.1,0.01 -0.2,0.05 -0.28,0.13 l -2.19,2.19 c -0.15,0.16 -0.17,0.39 -0.03,0.53 l 1.12,1.12 c 0.29,-0.29 0.37,0.07 0.57,0.58 l -1.61,1.6 1.77,1.72 1.7,-1.7 c 0.44,0.03 0.82,-0.06 0.99,0.11 0.22,0.22 0.19,0.59 -0.06,0.85 l -0.02,0.02 c 1.48,1.26 3.85,-1.11 2.59,-2.59 l -0.02,0.02 c -0.25,0.25 -0.63,0.28 -0.85,0.06 -0.3,-0.3 0.23,-1.31 -0.61,-2.15 l -0.1,-0.1 c -0.73,-0.74 -2.07,-0.7 -1.6,-1.18 l -1.12,-1.12 c -0.06,-0.06 -0.16,-0.09 -0.25,-0.09 z m -7.25,0.86 c -0.28,0 -0.55,0.05 -0.83,0.14 l 2.02,2.02 -1.85,1.86 -2.02,-2.02 c -0.72,2.23 1.45,4.34 3.67,3.55 l 7.27,7.27 c 0.46,0.47 1.22,0.47 1.71,-0.02 0.49,-0.49 0.5,-1.25 0.03,-1.72 l -7.27,-7.27 c 0.69,-1.94 -0.84,-3.84 -2.73,-3.81 z m 2.41,7.72 -3.36,3.35 c -0.47,0.47 -0.47,1.23 0.02,1.72 0.49,0.49 1.25,0.49 1.72,0.02 l 3.38,-3.38 z"
id="path10" /><circle
style="opacity:1;fill:#239a96;fill-opacity:1;stroke:none;stroke-width:1.99998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="circle11"
cx="50"
cy="130"
r="10" /><path
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 52.669556,122.92968 c -0.5,-0.01 -1,0.09 -1.46,0.31 l -1.35,0.64 -0.1,-0.22 -2.12,1.01 -0.46,-0.65 c -0.09,-0.13 -0.26,-0.16 -0.39,-0.07 l -1.24,0.85 c -0.12,0.09 -0.15,0.27 -0.07,0.4 l 0.39,0.55 -0.36,0.47 -2.81,4.07 c -0.2,0.3 -0.18,0.7 0.06,0.97 -0.4,0.74 -0.49,1.54 -0.3,2.26 0.22,0.86 0.77,1.6 1.5,2.16 1.46,1.12 3.68,1.56 5.63,0.5 1.07,-0.59 2.19,-0.79 2.99,-0.64 0.8,0.14 1.26,0.52 1.39,1.32 0.05,0.33 0.36,0.55 0.69,0.5 0.33,-0.05 0.55,-0.36 0.5,-0.69 -0.2,-1.27 -1.2,-2.1 -2.36,-2.31 -1.16,-0.21 -2.51,0.08 -3.78,0.77 -1.49,0.82 -3.22,0.47 -4.33,-0.39 -0.55,-0.42 -0.93,-0.97 -1.07,-1.51 -0.1,-0.4 -0.09,-0.81 0.11,-1.24 l 1.39,0.96 c 0.81,0.56 1.93,0.36 2.49,-0.46 l 2.8,-4.03 -0.53,-2.86 0.65,-0.31 -0.11,-0.24 1.34,-0.63 c 0.44,-0.21 0.94,-0.25 1.41,-0.1 l 3.54,1.12 0.39,-1.24 -3.54,-1.12 c -0.29,-0.09 -0.59,-0.14 -0.89,-0.15 z m 4.24,3.31 c -0.39,0.63 -2.7,4.48 0,4.48 2.7,0 0.39,-3.88 0,-4.48 z m -10,1.23 1.31,0.45 -1.02,0.66 c -0.04,0.03 -0.08,0.06 -0.11,0.11 l -1.55,2.46 c -0.1,0.16 -0.05,0.38 0.11,0.48 0.16,0.1 0.38,0.05 0.48,-0.11 l 1.51,-2.4 1.3,-0.83 c 0.24,0.29 0.25,0.71 0.02,1.03 l -1.52,2.2 c -0.52,0.75 -1.51,0.96 -2.22,0.47 l -0.86,-0.59 z"
id="path11" /><circle
style="opacity:1;fill:#239a96;fill-opacity:1;stroke:none;stroke-width:1.99998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="circle12"
cx="70"
cy="130"
r="10" /><path
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 67.96,122.6 h 4.56 l -1.97,5.8 h 3.02 l -4.96,9.96 0.99,-8.11 h -2.34 z"
id="path12" /><circle
style="opacity:1;fill:#b13204;fill-opacity:1;stroke:none;stroke-width:1.99998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
id="circle13"
cx="110"
cy="110"
r="10" /><path
id="path13"
style="fill:#ffffff;fill-opacity:1;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
d="m 115.6875,105.5 -0.125,2.5625 -2.5625,0.0625 -0.875,1.1875 0.875,-0.0625 -2.07583,2.73928 0.45267,-2.13259 -1.08081,0.0183 0.37814,-1.37814 -3.61167,0.19064 L 107,106.5 l 0.4375,0.0625 0.125,-0.9375 -5.25,0.375 0.8125,6.5625 12.6875,-0.4375 -0.125,1.5625 h 2.125 L 117.25,111.5 l 0.4375,-3.5625 h -0.875 l 0.25,-2.25 z m -9.48828,1.14062 0.0879,2.1211 -2.29883,0.13281 -0.4414,-1.94531 z m 8.17578,5.98438 c -0.51777,0 -0.9375,0.4757 -0.9375,1.0625 0,0.5868 0.41973,1.0625 0.9375,1.0625 0.51777,0 0.9375,-0.4757 0.9375,-1.0625 0,-0.5868 -0.41973,-1.0625 -0.9375,-1.0625 z m -2.125,0.0625 c -0.5868,0 -1.0625,0.51767 -1.0625,1.15625 0,0.63858 0.4757,1.15625 1.0625,1.15625 0.5868,0 1.0625,-0.51767 1.0625,-1.15625 0,-0.63858 -0.4757,-1.15625 -1.0625,-1.15625 z m -2.65625,0.125 c -0.63858,0 -1.15625,0.51767 -1.15625,1.15625 0,0.63858 0.51767,1.15625 1.15625,1.15625 0.63858,0 1.15625,-0.51767 1.15625,-1.15625 0,-0.63858 -0.51767,-1.15625 -1.15625,-1.15625 z m -2.625,0.25 c -0.63858,0 -1.15625,0.48969 -1.15625,1.09375 0,0.60406 0.51767,1.09375 1.15625,1.09375 0.63858,0 1.15625,-0.48969 1.15625,-1.09375 0,-0.60406 -0.51767,-1.09375 -1.15625,-1.09375 z m 9.125,0.75 c -0.25888,0 -0.46875,0.22386 -0.46875,0.5 0,0.27614 0.20987,0.5 0.46875,0.5 0.25888,0 0.46875,-0.22386 0.46875,-0.5 0,-0.27614 -0.20987,-0.5 -0.46875,-0.5 z m -11.9375,0.5 c -0.25888,0 -0.46875,0.22386 -0.46875,0.5 0,0.27614 0.20987,0.5 0.46875,0.5 0.25888,0 0.46875,-0.22386 0.46875,-0.5 0,-0.27614 -0.20987,-0.5 -0.46875,-0.5 z" /></g></svg>

After

Width:  |  Height:  |  Size: 41 KiB

+106
View File
@@ -107,6 +107,7 @@
{
"name":"Olaf's Garage",
"type":"garage",
"description":"DM1U-150 Utility Rail Vehicle\nKey sold at Machine Factory's Shop",
"position":{"x":10448.56,"y":134.9,"z":1331.074}
}
]
@@ -135,6 +136,39 @@
},
{
"type":"shop",
"stock":[
"Boombox",
"Red Beacon",
"Plastic Crate",
"Cassette A7",
"Cassette P6",
"Cassette P9",
"Red Light Bar",
"File Box",
"Purple Flag Marker",
"Reginald's Garage Key",
"Cassette P10",
"Cassette P5",
"Cassette P7",
"Handheld Game Console",
"Wooden Crate",
"Cassette A1",
"Cassette P2",
"Yellow Flag Marker",
"Green Flag Marker",
"Orange Flag Marker",
"Cyan Flag Marker",
"Cassette P4",
"Green Light Bar",
"Cassette P1",
"Blue Flag Marker",
"Cassette P8",
"White Flag Marker",
"Red Flag Marker",
"Cassette A2",
"Blue Beacon",
"Cassette P3"
],
"position":{"x":1911.48,"y":122.26,"z":5788.65}
}
]
@@ -164,6 +198,13 @@
},
{
"type":"shop",
"stock":[
"Cassette A12",
"Cassette A15",
"Cassette A6",
"Cassette A3",
"Old Bob's Garage Key"
],
"position":{"x":9528.669,"y":119.3432,"z":13419.99}
}
]
@@ -248,6 +289,30 @@
},
{
"type":"shop",
"stock":[
"Shelf",
"Rotary Controller",
"Analog Controller",
"Cassette A16",
"Crimping Tool",
"Blue Light Bar",
"Brake Cylinder LED Bar",
"Electric Hand Drill",
"Digital Speedometer",
"Golden Shovel",
"Infrared Thermometer",
"Button Controller",
"Filler Gun",
"Sun Visor",
"Cassette A10",
"Expert Shovel",
"Digital Clock",
"Alternating Controller",
"White Light Bar",
"Soldering Gun",
"Clinometer",
"Cassette A8"
],
"position":{"x":13035.07,"y":140.1661,"z":11158.79}
}
]
@@ -286,6 +351,20 @@
},
{
"type":"shop",
"stock":[
"Plastic Crate",
"Steve's Garage Key",
"Wooden Crate",
"Red Beacon",
"Steam Locomotive Checklist",
"File Box",
"Cassette A11",
"Purple Light Bar",
"Cassette A4",
"Pocketwatch",
"Blue Beacon",
"Cyan Light Bar"
],
"position":{"x":13423.26,"y":113.1099,"z":3611.852}
}
]
@@ -396,6 +475,30 @@
},
{
"type":"shop",
"stock":[
"Amber Beacon",
"Headlight",
"Button Controller",
"Automatic Train Stop",
"Analog Controller",
"Cassette A13",
"Rotary Controller",
"Locomotive Remote",
"Alternating Controller",
"Olaf's Garage Key",
"Demonstrator Paint Can",
"Yellow Light Bar",
"Wireless MU Controller",
"Anti-Wheelslip Computer",
"Overheat Protection",
"Cassette A14",
"Orange Light Bar",
"Cassette A9",
"Distance Tracker",
"Amp Limiter",
"Switch Setter",
"Cassette A5"
],
"position":{"x":2236.745,"y":159.266,"z":10834.23}
}
]
@@ -525,16 +628,19 @@
{
"name":"Old Bob's Garage",
"type":"garage",
"description":"BE2 Microshunter\nKey sold at Food Factory's Shop",
"position":{"x":1590.981,"y":137.4275,"z":12227.22}
},
{
"name":"Reginald's Garage",
"type":"garage",
"description":"Caboose\nKey sold at City West's Shop",
"position":{"x":4387.325,"y":147.84,"z":2458.901}
},
{
"name":"Steve's Garage",
"type":"garage",
"description":"DE6 Slug\nKey sold at Harbor's Shop",
"position":{"x":14527.17,"y":163.7,"z":2365.416}
},
{
+40
View File
@@ -42,6 +42,11 @@ a:visited{
pointer-events: all;
}
.panel hr {
border: 0px;
border-bottom: 2px solid #b3b3b396;
}
#topleft{
position:fixed;
top: 0px;
@@ -183,3 +188,38 @@ button{
height: var(--legendButtonSize);
}
}
.signText {
font-family: Arial;
}
#changelog {
width: fit-content;
font-size: 11px;
}
#tooltip{
position: fixed;
left: 0px;
top: 0px;
pointer-events: none;
padding: 5px;
margin: 0;
font-size: 12px;
}
#tooltip h1{
font-variation-settings: "wght" 600;
font-style: italic;
font-size: 15px;
line-height: 1em;
padding-bottom: 4px;
padding: 0 4px 4px 4px;
}
#tooltip h2{
font-variation-settings: "wght" 600;
font-size: 10px;
line-height: 1em;
padding-bottom: 4px;
opacity: 0.7;
border-bottom: 2px solid #b3b3b396;
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB