2024-05-20 00:31:58 +02:00
|
|
|
<svelte:options runes={true} />
|
|
|
|
|
|
|
|
<script lang="ts">
|
|
|
|
import * as Card from "$lib/components/ui/card";
|
|
|
|
import {Button} from "$lib/components/ui/button";
|
|
|
|
import {
|
|
|
|
Grids,
|
|
|
|
inventorySizes,
|
|
|
|
InventoryType,
|
|
|
|
localization, metricModifiers, type Thruster,
|
|
|
|
ThrusterSize,
|
|
|
|
ThrusterType,
|
2024-05-20 18:42:28 +02:00
|
|
|
thrustValues, Density, densityValues
|
2024-05-20 00:31:58 +02:00
|
|
|
} from "$lib/constants";
|
|
|
|
import {Input} from "$lib/components/ui/input";
|
|
|
|
import {Label} from "$lib/components/ui/label";
|
|
|
|
import {Separator} from "$lib/components/ui/separator";
|
|
|
|
import {onMount} from "svelte";
|
|
|
|
|
2024-05-20 01:21:35 +02:00
|
|
|
let locale = $state("en-GB");
|
2024-05-20 00:31:58 +02:00
|
|
|
let mounted = $state(false);
|
|
|
|
|
|
|
|
let thrusters: Array<Thruster> = $state([])
|
|
|
|
$effect(() => {
|
|
|
|
if (mounted) {
|
|
|
|
localStorage.setItem("thrusters", JSON.stringify(thrusters));
|
|
|
|
}
|
|
|
|
})
|
2024-05-20 18:42:28 +02:00
|
|
|
|
2024-05-20 00:31:58 +02:00
|
|
|
let inventories: Array<InventoryType> = $state([])
|
|
|
|
$effect(() => {
|
|
|
|
if (mounted) {
|
|
|
|
localStorage.setItem("inventories", JSON.stringify(inventories));
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2024-05-20 18:42:28 +02:00
|
|
|
let inventoryMultiplier: number = $state(3);
|
2024-05-20 00:31:58 +02:00
|
|
|
$effect(() => {
|
|
|
|
if (mounted) {
|
|
|
|
localStorage.setItem("inventoryMultiplier", inventoryMultiplier);
|
|
|
|
}
|
|
|
|
})
|
2024-05-20 18:42:28 +02:00
|
|
|
let gridSize: Grids = $state(Grids.Small);
|
2024-05-20 00:31:58 +02:00
|
|
|
$effect(() => {
|
|
|
|
if (mounted) {
|
|
|
|
localStorage.setItem("gridSize", gridSize);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2024-05-20 18:42:28 +02:00
|
|
|
let gravity: number = $state(1);
|
2024-05-20 00:31:58 +02:00
|
|
|
$effect(() => {
|
|
|
|
if (mounted) {
|
|
|
|
localStorage.setItem("gravity", gravity);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2024-05-20 18:42:28 +02:00
|
|
|
let density: Density = $state(Density.Ore);
|
|
|
|
$effect(() => {
|
|
|
|
if (mounted) {
|
|
|
|
localStorage.setItem("density", density);
|
|
|
|
}
|
|
|
|
})
|
2024-05-20 00:31:58 +02:00
|
|
|
|
2024-05-20 18:42:28 +02:00
|
|
|
let newThruster: Thruster = $state({
|
2024-05-20 00:31:58 +02:00
|
|
|
type: ThrusterType.Atmospheric,
|
|
|
|
size: ThrusterSize.Small,
|
|
|
|
})
|
|
|
|
$effect(() => {
|
|
|
|
if (mounted) {
|
|
|
|
localStorage.setItem("newThruster", JSON.stringify(newThruster))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2024-05-20 18:42:28 +02:00
|
|
|
let newInventory: InventoryType = $state(InventoryType.CargoMedium)
|
2024-05-20 00:31:58 +02:00
|
|
|
$effect(() => {
|
|
|
|
if (mounted) {
|
|
|
|
localStorage.setItem("newInventory", JSON.stringify(newInventory))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2024-05-20 18:42:28 +02:00
|
|
|
let totalThrust: number = $derived.by(() => {
|
2024-05-20 00:31:58 +02:00
|
|
|
let thrust = 0;
|
|
|
|
thrusters.forEach((thruster) => {
|
|
|
|
thrust += getThrusterPower(thruster)
|
|
|
|
})
|
|
|
|
return thrust
|
|
|
|
});
|
2024-05-20 18:42:28 +02:00
|
|
|
let totalVolume: number = $derived.by(() => {
|
2024-05-20 00:31:58 +02:00
|
|
|
let volume = 0;
|
|
|
|
inventories.forEach((inventory) => {
|
|
|
|
volume += getInventoryVolume(inventory);
|
|
|
|
})
|
|
|
|
return volume;
|
|
|
|
})
|
|
|
|
|
2024-05-20 18:42:28 +02:00
|
|
|
let maxWeight: number = $derived(totalThrust / (gravity * 9.81))
|
2024-05-20 13:21:07 +02:00
|
|
|
|
2024-05-20 18:42:28 +02:00
|
|
|
let maxVehicleWeight: number = $derived(maxWeight - totalVolume * densityValues.get(density))
|
2024-05-20 00:31:58 +02:00
|
|
|
|
|
|
|
function weightConversion(weight: number): string {
|
2024-05-20 18:42:28 +02:00
|
|
|
if (weight > 1000 || weight < -1000) {
|
2024-05-20 00:31:58 +02:00
|
|
|
return applyUnits(weight/1000, "t")
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return `${weight.toFixed(2)}kg`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getInventoryVolume(inventory: InventoryType): number {
|
|
|
|
return inventorySizes.get(gridSize).get(inventory) * inventoryMultiplier
|
|
|
|
}
|
|
|
|
|
|
|
|
function getThrusterPower(thruster: Thruster): number {
|
|
|
|
return thrustValues.get(gridSize).get(thruster.type).get(thruster.size)
|
|
|
|
}
|
|
|
|
|
|
|
|
function addThruster() {
|
|
|
|
thrusters.push({ type: newThruster.type, size: newThruster.size});
|
|
|
|
}
|
|
|
|
|
|
|
|
function addInventory() {
|
|
|
|
inventories.push(newInventory);
|
|
|
|
}
|
|
|
|
|
|
|
|
function localized(key: string): string {
|
|
|
|
let localizationObject = localization.get(key)
|
|
|
|
|
|
|
|
if (typeof localizationObject === "undefined") {
|
|
|
|
return `missing text: ${key}`
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let localizedText = localizationObject.get(locale)
|
|
|
|
if (typeof localizedText === "undefined") {
|
|
|
|
return localizationObject.get("en-GB")
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return localizedText
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function applyUnits(value: number, unit: string) {
|
|
|
|
for (const [power, modifier] of [...metricModifiers.entries()]) {
|
|
|
|
if (value > power) {
|
|
|
|
if (value / power >= 10000) {
|
|
|
|
let base = value / power;
|
|
|
|
let rest = base % 1000;
|
|
|
|
base = (base - rest) / 1000;
|
|
|
|
rest = Math.round((rest + Number.EPSILON) * 100) / 100
|
|
|
|
return `${base}${localized("separator")}${rest}${modifier}${unit}`
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let rounded = Math.round(((value / power) + Number.EPSILON) * 100) / 100
|
|
|
|
return `${rounded}${modifier}${unit}`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-05-20 18:42:28 +02:00
|
|
|
if (value < 0) {
|
|
|
|
return `-${applyUnits(value * -1, unit)}`
|
|
|
|
}
|
2024-05-20 00:31:58 +02:00
|
|
|
let rounded = Math.round(((value + Number.EPSILON) * 100) / 100)
|
|
|
|
return `${rounded}${unit}`
|
|
|
|
}
|
|
|
|
|
|
|
|
onMount(() => {
|
2024-05-20 01:21:35 +02:00
|
|
|
if (navigator) {
|
|
|
|
locale = navigator.language;
|
|
|
|
}
|
2024-05-20 02:19:19 +02:00
|
|
|
|
|
|
|
if (localStorage.getItem("thrusters") !== null) {
|
|
|
|
thrusters = JSON.parse(localStorage.getItem("thrusters"))
|
2024-05-20 00:31:58 +02:00
|
|
|
}
|
|
|
|
|
2024-05-20 02:19:19 +02:00
|
|
|
if (localStorage.getItem("inventories") !== null) {
|
|
|
|
inventories = JSON.parse(localStorage.getItem("inventories"))
|
2024-05-20 00:31:58 +02:00
|
|
|
}
|
|
|
|
|
2024-05-20 02:19:19 +02:00
|
|
|
if (localStorage.getItem("gridSize") !== null) {
|
|
|
|
gridSize = localStorage.getItem("gridSize")
|
2024-05-20 00:31:58 +02:00
|
|
|
}
|
|
|
|
|
2024-05-20 02:19:19 +02:00
|
|
|
if (localStorage.getItem("gravity") !== null) {
|
|
|
|
gravity = localStorage.getItem("gravity")
|
2024-05-20 00:31:58 +02:00
|
|
|
}
|
|
|
|
|
2024-05-20 18:42:28 +02:00
|
|
|
if (localStorage.getItem("density") !== null) {
|
|
|
|
density = localStorage.getItem("density")
|
|
|
|
}
|
|
|
|
|
2024-05-20 02:19:19 +02:00
|
|
|
if (localStorage.getItem("inventoryMultiplier") !== null) {
|
|
|
|
inventoryMultiplier = localStorage.getItem("inventoryMultiplier")
|
2024-05-20 00:31:58 +02:00
|
|
|
}
|
|
|
|
|
2024-05-20 02:19:19 +02:00
|
|
|
if (localStorage.getItem("newThruster") !== null) {
|
|
|
|
newThruster = JSON.parse(localStorage.getItem("newThruster"))
|
2024-05-20 00:31:58 +02:00
|
|
|
}
|
|
|
|
|
2024-05-20 02:19:19 +02:00
|
|
|
if (localStorage.getItem("newInventory") !== null) {
|
|
|
|
newInventory = JSON.parse(localStorage.getItem("newInventory"))
|
2024-05-20 00:31:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
mounted = true;
|
|
|
|
})
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<svelte:head>
|
|
|
|
<title>{localized("title")}</title>
|
|
|
|
<meta name="robots" content="noindex nofollow" />
|
|
|
|
</svelte:head>
|
|
|
|
|
|
|
|
<div class="gap-4 grid grid-cols-3 h-full">
|
|
|
|
<Card.Root class="col-span-1">
|
|
|
|
<Card.Header>
|
|
|
|
<Card.Title>{localized("title")}</Card.Title>
|
|
|
|
</Card.Header>
|
|
|
|
<Card.Content class="flex flex-col gap-3">
|
|
|
|
<Separator />
|
|
|
|
<Label for="gravity">{localized("gravity")}</Label>
|
|
|
|
<Input type="number" step="0.01" id="gravity" bind:value={gravity}/>
|
|
|
|
<Separator />
|
|
|
|
<Label for="gridSize">{localized("gridSize")}</Label>
|
|
|
|
<div id="gridSize" class="flex">
|
|
|
|
{#each Object.values(Grids) as size}
|
|
|
|
{#if gridSize === size}
|
|
|
|
<Button variant="primary">{localized(size)}</Button>
|
|
|
|
{:else}
|
|
|
|
<Button variant="secondary" onclick={() => gridSize = size}>{localized(size)}</Button>
|
|
|
|
{/if}
|
|
|
|
{/each}
|
|
|
|
</div>
|
|
|
|
<Separator />
|
2024-05-20 18:42:28 +02:00
|
|
|
<Label for="density">{localized("density")}</Label>
|
|
|
|
<div id="density" class="flex">
|
|
|
|
{#each Object.values(Density) as value}
|
|
|
|
{#if density === value}
|
|
|
|
<Button variant="primary">{localized(value)}</Button>
|
|
|
|
{:else}
|
|
|
|
<Button variant="secondary" onclick={() => density = value}>{localized(value)}</Button>
|
|
|
|
{/if}
|
|
|
|
{/each}
|
|
|
|
</div>
|
|
|
|
<Separator />
|
2024-05-20 13:30:08 +02:00
|
|
|
<p>{localized("liftableVehcileWeight")}: {weightConversion(maxVehicleWeight)}</p>
|
2024-05-20 00:31:58 +02:00
|
|
|
<p>{localized("liftableWeight")}: {weightConversion(maxWeight)}</p>
|
|
|
|
</Card.Content>
|
|
|
|
</Card.Root>
|
|
|
|
<Card.Root class="col-span-1 flex flex-col flex-grow">
|
|
|
|
<Card.Header>
|
|
|
|
<Card.Title>{localized("thrusterSettings")}</Card.Title>
|
|
|
|
</Card.Header>
|
|
|
|
<Card.Content class="flex flex-col flex-grow gap-3">
|
|
|
|
<Separator />
|
|
|
|
<Card.Root>
|
|
|
|
<Card.Content class="flex flex-col gap-3 pt-6">
|
|
|
|
<div class="flex gap-2">
|
|
|
|
{#each Object.values(ThrusterType) as thruster}
|
|
|
|
{#if newThruster.type === thruster}
|
|
|
|
<Button variant="primary">{localized(thruster)}</Button>
|
|
|
|
{:else}
|
|
|
|
<Button variant="secondary" onclick={() => newThruster.type = thruster}>{localized(thruster)}</Button>
|
|
|
|
{/if}
|
|
|
|
{/each}
|
|
|
|
</div>
|
|
|
|
<div class="flex gap-2">
|
|
|
|
{#each Object.values(ThrusterSize) as size}
|
|
|
|
{#if newThruster.size === size}
|
|
|
|
<Button variant="primary">{localized(size)}</Button>
|
|
|
|
{:else}
|
|
|
|
<Button variant="secondary" onclick={() => newThruster.size = size}>{localized(size)}</Button>
|
|
|
|
{/if}
|
|
|
|
{/each}
|
|
|
|
</div>
|
|
|
|
<Button variant="secondary" onclick={() => addThruster()}>{localized("addThruster")}</Button>
|
|
|
|
</Card.Content>
|
|
|
|
</Card.Root>
|
|
|
|
<Card.Root class="flex flex-col h-0 flex-grow">
|
|
|
|
<Card.Header>
|
|
|
|
<Card.Title>{localized("thrusters")} ({applyUnits(totalThrust, "N")})</Card.Title>
|
|
|
|
</Card.Header>
|
|
|
|
<Card.Content class="overflow-hidden">
|
|
|
|
<Separator />
|
|
|
|
<div class="max-h-full overflow-y-auto">
|
|
|
|
{#each thrusters as thruster, index}
|
|
|
|
<p class="p-1">
|
|
|
|
- {localized(thruster.size)} {localized(thruster.type)} {localized("thruster")}: {applyUnits(getThrusterPower(thruster), "N")}
|
|
|
|
<Button variant="destructive" onclick={() => thrusters.splice(index, 1)}>X</Button>
|
|
|
|
</p>
|
|
|
|
{/each}
|
|
|
|
</div>
|
|
|
|
</Card.Content>
|
|
|
|
</Card.Root>
|
|
|
|
</Card.Content>
|
|
|
|
</Card.Root>
|
|
|
|
|
|
|
|
<Card.Root class="col-span-1 flex flex-col flex-grow">
|
|
|
|
<Card.Header>
|
|
|
|
<Card.Title>{localized("inventorySettings")}</Card.Title>
|
|
|
|
</Card.Header>
|
|
|
|
<Card.Content class="flex flex-col flex-grow gap-3">
|
|
|
|
<Separator />
|
|
|
|
<Label for="inventorySize">{localized("inventory")} {localized("multiplier")}</Label>
|
|
|
|
<Input id="inventorySize" type="number" bind:value={inventoryMultiplier}/>
|
|
|
|
<Card.Root>
|
|
|
|
<Card.Content class="pt-6 flex flex-col gap-3">
|
|
|
|
<div class="flex gap-2 flex-wrap">
|
|
|
|
{#each Object.values(InventoryType) as inventory}
|
|
|
|
{#if newInventory === inventory}
|
|
|
|
<Button variant="primary">{localized(inventory)}</Button>
|
|
|
|
{:else}
|
|
|
|
<Button variant="secondary" onclick={() => newInventory = inventory}>{localized(inventory)}</Button>
|
|
|
|
{/if}
|
|
|
|
{/each}
|
|
|
|
</div>
|
|
|
|
<Button variant="secondary" onclick={() => addInventory()}>{localized("addInventory")}</Button>
|
|
|
|
</Card.Content>
|
|
|
|
</Card.Root>
|
|
|
|
<Card.Root class="flex flex-col h-0 flex-grow">
|
|
|
|
<Card.Header>
|
|
|
|
<Card.Title>{localized("inventories")} ({applyUnits(totalVolume, "l")})</Card.Title>
|
|
|
|
</Card.Header>
|
|
|
|
<Card.Content class="overflow-hidden">
|
|
|
|
<Separator />
|
|
|
|
<div class="max-h-full overflow-y-auto">
|
|
|
|
{#each inventories as inventory, index}
|
|
|
|
<p class="p-1">
|
|
|
|
- {localized(inventory)} {localized("volume")}: {applyUnits(getInventoryVolume(inventory), "l")}
|
|
|
|
<Button variant="destructive" onclick={() => inventories.splice(index, 1)}>X</Button>
|
|
|
|
</p>
|
|
|
|
{/each}
|
|
|
|
</div>
|
|
|
|
</Card.Content>
|
|
|
|
</Card.Root>
|
|
|
|
</Card.Content>
|
|
|
|
</Card.Root>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|