space-engineers-thrust-calc.../src/routes/+page.svelte

339 lines
12 KiB
Svelte
Raw Normal View History

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>