<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, thrustValues, weightPerVolume } 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"; let locale = $state("en-GB"); let mounted = $state(false); let thrusters: Array<Thruster> = $state([]) $effect(() => { if (mounted) { localStorage.setItem("thrusters", JSON.stringify(thrusters)); } }) let inventories: Array<InventoryType> = $state([]) $effect(() => { if (mounted) { localStorage.setItem("inventories", JSON.stringify(inventories)); } }) let inventoryMultiplier = $state(1); $effect(() => { if (mounted) { localStorage.setItem("inventoryMultiplier", inventoryMultiplier); } }) let gridSize = $state(Grids.Small); $effect(() => { if (mounted) { localStorage.setItem("gridSize", gridSize); } }) let gravity = $state(1); $effect(() => { if (mounted) { localStorage.setItem("gravity", gravity); } }) let newThruster = $state({ type: ThrusterType.Atmospheric, size: ThrusterSize.Small, }) $effect(() => { if (mounted) { localStorage.setItem("newThruster", JSON.stringify(newThruster)) } }) let newInventory = $state(InventoryType.CargoSmall) $effect(() => { if (mounted) { localStorage.setItem("newInventory", JSON.stringify(newInventory)) } }) let totalThrust = $derived.by(() => { let thrust = 0; thrusters.forEach((thruster) => { thrust += getThrusterPower(thruster) }) return thrust }); let totalVolume = $derived.by(() => { let volume = 0; inventories.forEach((inventory) => { volume += getInventoryVolume(inventory); }) return volume; }) let maxWeight = $derived.by(() => { let weight = totalThrust / (gravity * 9.81); weight -= totalVolume * weightPerVolume; return weight; }) function weightConversion(weight: number): string { if (weight > 1000) { 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}` } } } let rounded = Math.round(((value + Number.EPSILON) * 100) / 100) return `${rounded}${unit}` } onMount(() => { if (navigator) { locale = navigator.language; } let localThrusters = localStorage.getItem("thrusters"); if (typeof localThrusters !== "undefined" && localThrusters !== "" && localGravity != null) { thrusters = JSON.parse(localThrusters); } let localInventories = localStorage.getItem("inventories"); if (typeof localInventories !== "undefined" && localInventories !== "" && localGravity != null) { inventories = JSON.parse(localInventories); } let localGridSize = localStorage.getItem("gridSize"); if (typeof localGridSize !== "undefined" && localGridSize !== "" && localGravity != null) { gridSize = localGridSize; } console.log(gravity); let localGravity = localStorage.getItem("gravity"); if (typeof localGravity !== "undefined" && localGravity !== "" && localGravity != null) { console.log(localGravity); gravity = localGravity; } let localInventoryMultiplicator = localStorage.getItem("inventoryMultiplier"); if (typeof localInventoryMultiplicator !== "undefined" && localInventoryMultiplicator !== "" && localGravity != null) { inventoryMultiplier = localInventoryMultiplicator; } let localNewThruster = localStorage.getItem("newThruster"); if (typeof localNewThruster !== "undefined" && localNewThruster !== "" && localGravity != null) { newThruster = JSON.parse(localNewThruster); } let localNewInventory = localStorage.getItem("newInventory"); if (typeof localNewInventory !== "undefined" && localNewInventory !== "" && localGravity != null) { newInventory = JSON.parse(localNewInventory); } 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 /> <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>