Major Rewrite of underlying code
This commit is contained in:
parent
d031e2b7a1
commit
c7fa40b454
15 changed files with 923 additions and 437 deletions
97
src/app.css
97
src/app.css
|
@ -4,67 +4,48 @@
|
|||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--destructive: 0 72.2% 50.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--ring: 222.2 84% 4.9%;
|
||||
|
||||
--background: 239 19% 95%;
|
||||
--foreground: 239 5% 0%;
|
||||
--card: 239 19% 90%;
|
||||
--card-foreground: 239 5% 10%;
|
||||
--popover: 239 19% 95%;
|
||||
--popover-foreground: 239 95% 0%;
|
||||
--primary: 239 58% 35%;
|
||||
--primary-foreground: 0 0% 100%;
|
||||
--secondary: 239 19% 70%;
|
||||
--secondary-foreground: 0 0% 0%;
|
||||
--muted: 201 19% 85%;
|
||||
--muted-foreground: 239 5% 35%;
|
||||
--accent: 201 19% 80%;
|
||||
--accent-foreground: 239 5% 10%;
|
||||
--destructive: 0 50% 30%;
|
||||
--destructive-foreground: 239 5% 90%;
|
||||
--border: 239 20% 50%;
|
||||
--input: 239 20% 18%;
|
||||
--ring: 239 58% 35%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--ring: hsl(212.7,26.8%,83.9);
|
||||
--background: 239 19% 5%;
|
||||
--foreground: 239 5% 90%;
|
||||
--card: 239 19% 0%;
|
||||
--card-foreground: 239 5% 90%;
|
||||
--popover: 239 19% 5%;
|
||||
--popover-foreground: 239 5% 90%;
|
||||
--primary: 239 58% 35%;
|
||||
--primary-foreground: 0 0% 100%;
|
||||
--secondary: 239 19% 10%;
|
||||
--secondary-foreground: 0 0% 100%;
|
||||
--muted: 201 19% 15%;
|
||||
--muted-foreground: 239 5% 60%;
|
||||
--accent: 201 19% 15%;
|
||||
--accent-foreground: 239 5% 90%;
|
||||
--destructive: 0 50% 30%;
|
||||
--destructive-foreground: 239 5% 90%;
|
||||
--border: 239 20% 18%;
|
||||
--input: 239 20% 18%;
|
||||
--ring: 239 58% 35%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
34
src/lib/components/InventoryList.svelte
Normal file
34
src/lib/components/InventoryList.svelte
Normal file
|
@ -0,0 +1,34 @@
|
|||
<svelte:options runes />
|
||||
|
||||
<script lang="ts">
|
||||
import {applyUnits} from "$lib/constants.js";
|
||||
import {Button} from "$lib/components/ui/button/index.js";
|
||||
import {Grid} from "$lib/grid";
|
||||
|
||||
let { i18n = new Localization("en-GB"), inventories = $bindable(), grid, multiplier } = $props();
|
||||
let valid = $derived.by(() => {
|
||||
let valids = [];
|
||||
valids[0] = typeof inventories !== "undefined";
|
||||
valids[1] = typeof grid !== "undefined";
|
||||
valids[2] = typeof multiplier !== "undefined";
|
||||
return !valids.includes(false);
|
||||
});
|
||||
|
||||
function removeInventory(index: number) {
|
||||
thrusters.splice(index, 1);
|
||||
}
|
||||
$inspect(inventories)
|
||||
</script>
|
||||
|
||||
<div class="max-h-full flex flex-col gap-2 overflow-y-auto">
|
||||
{#if valid}
|
||||
{#each inventories as inventory, index}
|
||||
<p class="flex gap-2 justify-between">
|
||||
- {i18n.localize(inventory.details.name)} {i18n.localize("volume")}: {applyUnits(i18n, inventory.getVolume(grid, multiplier), "l")}
|
||||
<Button variant="destructive" onclick={() => removeInventory(index)}>X</Button>
|
||||
</p>
|
||||
{/each}
|
||||
{:else}
|
||||
<p>Invalid Props</p>
|
||||
{/if}
|
||||
</div>
|
66
src/lib/components/NewInventory.svelte
Normal file
66
src/lib/components/NewInventory.svelte
Normal file
|
@ -0,0 +1,66 @@
|
|||
<svelte:options runes />
|
||||
|
||||
<script lang="ts">
|
||||
import {getFromLocalStorage, Localization} from "$lib/constants.js";
|
||||
import {Button} from "$lib/components/ui/button/index.js";
|
||||
import {Grid} from "$lib/grid";
|
||||
import * as DropdownMenu from "$lib/components/ui/dropdown-menu";
|
||||
import {Inventory, INVENTORIES} from "$lib/containers.svelte";
|
||||
import {THRUSTER_TYPE_LIST, ThrusterType} from "$lib/thruster.svelte";
|
||||
import {onMount} from "svelte";
|
||||
|
||||
let { i18n = new Localization("en-GB"), onAddInventory, grid } = $props();
|
||||
let mounted = $state(false);
|
||||
|
||||
let valid = $derived.by(() => {
|
||||
let valids = [];
|
||||
valids[0] = typeof onAddInventory !== "undefined";
|
||||
valids[1] = typeof grid !== "undefined";
|
||||
return !valids.includes(false);
|
||||
});
|
||||
|
||||
let key: string = $state(INVENTORIES.CargoSmall.key)
|
||||
let name = $derived(INVENTORIES[key].name);
|
||||
$effect(() => {
|
||||
if (mounted) {
|
||||
localStorage.setItem("newInventory", key)
|
||||
}
|
||||
})
|
||||
|
||||
function updateDetails(newKey: string) {
|
||||
key = newKey;
|
||||
}
|
||||
|
||||
function construct() {
|
||||
return new Inventory(INVENTORIES[key]);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
let ret = getFromLocalStorage("newInventory");
|
||||
if (ret.result) {
|
||||
key = ret.value;
|
||||
}
|
||||
|
||||
mounted = true;
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if valid}
|
||||
<div class="flex flex-col flex-wrap gap-3 max-w-fit place-self-center items-center">
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button variant="secondary">{i18n.localize(name)}</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content>
|
||||
<DropdownMenu.RadioGroup value={key} onValueChange={(change) => updateDetails(change)}>
|
||||
{#each Object.values(INVENTORIES) as inventory}
|
||||
<DropdownMenu.RadioItem value={inventory.key}>{i18n.localize(inventory.name)}</DropdownMenu.RadioItem>
|
||||
{/each}
|
||||
</DropdownMenu.RadioGroup>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
<Button class="bg-primary max-w-fit" onclick={() => onAddInventory(construct())}>{i18n.localize("addInventory")}</Button>
|
||||
</div>
|
||||
{:else}
|
||||
<p>Invalid Props</p>
|
||||
{/if}
|
115
src/lib/components/NewThruster.svelte
Normal file
115
src/lib/components/NewThruster.svelte
Normal file
|
@ -0,0 +1,115 @@
|
|||
<svelte:options runes />
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
Thruster, THRUSTER_LIST,
|
||||
THRUSTER_TYPE_LIST,
|
||||
ThrusterSize,
|
||||
ThrusterType
|
||||
} from "$lib/thruster.svelte";
|
||||
import {onMount} from "svelte";
|
||||
import * as DropdownMenu from "$lib/components/ui/dropdown-menu";
|
||||
import {getFromLocalStorage, Localization} from "$lib/constants";
|
||||
import {Button} from "$lib/components/ui/button";
|
||||
import {Grid} from "$lib/grid";
|
||||
|
||||
let { i18n = new Localization("en-GB"), onAddThruster, grid = Grid.Small } = $props();
|
||||
let mounted = $state(false);
|
||||
|
||||
let valid = $derived.by(() => {
|
||||
let valids = [];
|
||||
valids[0] = typeof onAddThruster !== "undefined";
|
||||
valids[1] = typeof grid !== "undefined";
|
||||
return !valids.includes(false);
|
||||
});
|
||||
|
||||
let type: ThrusterType = $state(new ThrusterType(THRUSTER_TYPE_LIST.Atmospheric));
|
||||
$effect(() => {
|
||||
if (mounted) {
|
||||
localStorage.setItem("newThrusterType", type.details.key);
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
let size = $state(ThrusterSize.Small);
|
||||
$effect(() => {
|
||||
if (mounted) {
|
||||
localStorage.setItem("newThrusterSize", size)
|
||||
}
|
||||
})
|
||||
|
||||
let sizes: Array<ThrusterSize> = $derived(Thruster.getByType(type, size).map((thruster) => {return thruster.size}));
|
||||
$effect(() => {
|
||||
if (!sizes.includes(size)) {
|
||||
size = sizes[0]
|
||||
}
|
||||
})
|
||||
|
||||
function construct() {
|
||||
let newThruster;
|
||||
Object.values(THRUSTER_LIST).forEach((thruster) => {
|
||||
if (thruster.type.equals(type) && thruster.size === size) {
|
||||
newThruster = new Thruster(thruster);
|
||||
}
|
||||
})
|
||||
return newThruster;
|
||||
}
|
||||
|
||||
function updateDetails(newKey: string) {
|
||||
type.details = THRUSTER_TYPE_LIST[newKey];
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
let ret = getFromLocalStorage("newThrusterType");
|
||||
if (ret.result) {
|
||||
type = new ThrusterType(THRUSTER_TYPE_LIST[ret.value]);
|
||||
}
|
||||
|
||||
ret = getFromLocalStorage("newThrusterSize");
|
||||
if (ret.result) {
|
||||
size = ret.value;
|
||||
}
|
||||
|
||||
mounted = true;
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-3 place-self-center items-center">
|
||||
{#if valid}
|
||||
<div class="flex flex-row gap-3 flex-wrap justify-center">
|
||||
<div>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button variant="secondary">
|
||||
{i18n.localize(size)}
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content>
|
||||
<DropdownMenu.RadioGroup bind:value={size}>
|
||||
{#each sizes as thrusterSize}
|
||||
<DropdownMenu.RadioItem value={thrusterSize}>{i18n.localize(thrusterSize)}</DropdownMenu.RadioItem>
|
||||
{/each}
|
||||
</DropdownMenu.RadioGroup>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</div>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button variant="secondary">
|
||||
{i18n.localize(type.details.name)}
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content>
|
||||
<DropdownMenu.RadioGroup value={type.details.key} onValueChange={(change) => {updateDetails(change)}}>
|
||||
{#each Object.values(THRUSTER_TYPE_LIST) as thrusterType}
|
||||
<DropdownMenu.RadioItem value={thrusterType.key}>{i18n.localize(thrusterType.name)}</DropdownMenu.RadioItem>
|
||||
{/each}
|
||||
</DropdownMenu.RadioGroup>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</div>
|
||||
<Button vclass="bg-primary max-w-fit" onclick={() => onAddThruster(construct())}>{i18n.localize("addThruster")}</Button>
|
||||
{:else}
|
||||
<p>Invalid Props</p>
|
||||
{/if}
|
||||
</div>
|
34
src/lib/components/ThrusterList.svelte
Normal file
34
src/lib/components/ThrusterList.svelte
Normal file
|
@ -0,0 +1,34 @@
|
|||
<svelte:options runes />
|
||||
|
||||
<script lang="ts">
|
||||
import {Button} from "$lib/components/ui/button/index";
|
||||
import {applyUnits, Localization} from "$lib/constants";
|
||||
import {Grid} from "$lib/grid";
|
||||
|
||||
let { i18n = new Localization("en-GB"), thrusters = $bindable(), grid, atmosphere } = $props();
|
||||
let valid = $derived.by(() => {
|
||||
let valids = [];
|
||||
valids[0] = typeof thrusters !== "undefined";
|
||||
valids[1] = typeof grid !== "undefined";
|
||||
valids[2] = typeof atmosphere !== "undefined";
|
||||
return !valids.includes(false);
|
||||
});
|
||||
|
||||
function removeThruster(index: number) {
|
||||
thrusters.splice(index, 1);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<div class="max-h-full flex flex-col gap-2 overflow-y-auto">
|
||||
{#if valid}
|
||||
{#each thrusters as thruster, index}
|
||||
<p class="gap-2 flex justify-between">
|
||||
- {i18n.localize(thruster.details.size)} {i18n.localize(thruster.details.type.details.name)}: {applyUnits(i18n, thruster.getThrust(grid, atmosphere), "N")}
|
||||
<Button variant="destructive" onclick={() => removeThruster(index)}>X</Button>
|
||||
</p>
|
||||
{/each}
|
||||
{:else}
|
||||
<p>Invalid Props</p>
|
||||
{/if}
|
||||
</div>
|
1
src/lib/components/object-values.ts
Normal file
1
src/lib/components/object-values.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export type ObjectValues<T> = T[keyof T];
|
|
@ -1,114 +1,3 @@
|
|||
export enum Grids {
|
||||
Small = "smallGrid",
|
||||
Large = "largeGrid"
|
||||
}
|
||||
|
||||
export enum ThrusterFuel {
|
||||
Electric = "electric",
|
||||
Hydrogen = "hydrogen",
|
||||
Deuterium = "deuterium"
|
||||
}
|
||||
|
||||
export class ThrusterTypeDetails {
|
||||
atmosphericFactor: number;
|
||||
vacuumFactor: number;
|
||||
fuel: ThrusterFuel;
|
||||
sizes: Map<ThrusterSize, ThrusterSizeDetails>;
|
||||
|
||||
constructor(atmos: number, vacuum: number, fuel: ThrusterFuel, sizes: Map<ThrusterSize, ThrusterSizeDetails>) {
|
||||
this.atmosphericFactor = atmos;
|
||||
this.vacuumFactor = vacuum;
|
||||
this.fuel = fuel;
|
||||
this.sizes = sizes;
|
||||
}
|
||||
}
|
||||
|
||||
export enum ThrusterSize {
|
||||
Small = "smallThruster",
|
||||
Large = "largeThruster",
|
||||
Huge = "hugeThruster",
|
||||
}
|
||||
|
||||
export enum ThrusterType {
|
||||
Ion = "ion",
|
||||
Atmospheric = "atmos",
|
||||
Hydrogen = "hydrogen",
|
||||
Fusion = "fusion"
|
||||
}
|
||||
|
||||
export class ThrusterSizeDetails {
|
||||
thrust: number;
|
||||
maxFuelConsumption: number;
|
||||
|
||||
constructor(thrust: number, maxFuelConsumption = 0) {
|
||||
this.thrust = thrust;
|
||||
this.maxFuelConsumption = maxFuelConsumption;
|
||||
}
|
||||
}
|
||||
|
||||
export const thrusterDetails: Map<Grids, Map<ThrusterType, ThrusterTypeDetails>> = new Map([
|
||||
[Grids.Large, new Map([
|
||||
[ThrusterType.Atmospheric, new ThrusterTypeDetails(1, 0, ThrusterFuel.Electric, new Map([
|
||||
[ThrusterSize.Large, new ThrusterSizeDetails(4500000)],
|
||||
[ThrusterSize.Small, new ThrusterSizeDetails(350000)]
|
||||
]))],
|
||||
[ThrusterType.Ion, new ThrusterTypeDetails(0.2, 1, ThrusterFuel.Electric, new Map([
|
||||
[ThrusterSize.Large, new ThrusterSizeDetails(4320000)],
|
||||
[ThrusterSize.Small, new ThrusterSizeDetails(345600)]
|
||||
]))],
|
||||
[ThrusterType.Hydrogen, new ThrusterTypeDetails(1, 1, ThrusterFuel.Hydrogen, new Map([
|
||||
[ThrusterSize.Large, new ThrusterSizeDetails(7200000)],
|
||||
[ThrusterSize.Small, new ThrusterSizeDetails(1080000)]
|
||||
]))],
|
||||
[ThrusterType.Fusion, new ThrusterTypeDetails(0.5, 1, ThrusterFuel.Deuterium, new Map([
|
||||
[ThrusterSize.Large, new ThrusterSizeDetails(33780000)],
|
||||
[ThrusterSize.Small, new ThrusterSizeDetails(9040000)]
|
||||
]))]
|
||||
|
||||
])],
|
||||
[Grids.Small, new Map([
|
||||
[ThrusterType.Atmospheric, new ThrusterTypeDetails(1, 0, ThrusterFuel.Electric, new Map([
|
||||
[ThrusterSize.Large, new ThrusterSizeDetails(340000)],
|
||||
[ThrusterSize.Small, new ThrusterSizeDetails(65000)]
|
||||
]))],
|
||||
[ThrusterType.Ion, new ThrusterTypeDetails(0.2, 1, ThrusterFuel.Electric, new Map([
|
||||
[ThrusterSize.Large, new ThrusterSizeDetails(172800)],
|
||||
[ThrusterSize.Small, new ThrusterSizeDetails(14400)]
|
||||
]))],
|
||||
[ThrusterType.Hydrogen, new ThrusterTypeDetails(1, 1, ThrusterFuel.Hydrogen, new Map([
|
||||
[ThrusterSize.Large, new ThrusterSizeDetails(480000)],
|
||||
[ThrusterSize.Small, new ThrusterSizeDetails(98400)]
|
||||
]))],
|
||||
[ThrusterType.Fusion, new ThrusterTypeDetails(0.5, 1, ThrusterFuel.Deuterium, new Map([
|
||||
[ThrusterSize.Huge, new ThrusterSizeDetails(9040000)],
|
||||
[ThrusterSize.Large, new ThrusterSizeDetails(1030000)],
|
||||
[ThrusterSize.Small, new ThrusterSizeDetails(210000)]
|
||||
]))]
|
||||
])]
|
||||
])
|
||||
|
||||
export enum InventoryType {
|
||||
Connector = "connector",
|
||||
CargoSmall = "smallCargo",
|
||||
CargoMedium = "mediumCargo",
|
||||
CargoLarge = "largeCargo",
|
||||
}
|
||||
|
||||
export const inventorySizes = new Map([
|
||||
[Grids.Large, new Map([
|
||||
[InventoryType.CargoLarge, 421000],
|
||||
[InventoryType.CargoMedium, 0],
|
||||
[InventoryType.CargoSmall, 15625],
|
||||
[InventoryType.Connector, 8000]
|
||||
])],
|
||||
[Grids.Small, new Map([
|
||||
[InventoryType.CargoLarge, 15625],
|
||||
[InventoryType.CargoMedium, 3375],
|
||||
[InventoryType.CargoSmall, 125],
|
||||
[InventoryType.Connector, 1152]
|
||||
])],
|
||||
]);
|
||||
|
||||
export const metricModifiers: Map<number, string> = new Map([
|
||||
[1000000000, "G"],
|
||||
[1000000, "M"],
|
||||
|
@ -116,17 +5,7 @@ export const metricModifiers: Map<number, string> = new Map([
|
|||
[1, ""]
|
||||
]);
|
||||
|
||||
export enum Density {
|
||||
Ore = "ore",
|
||||
Component = "component",
|
||||
}
|
||||
|
||||
export const densityValues: Map<Density, number> = new Map([
|
||||
[Density.Ore, 1/0.37],
|
||||
[Density.Component, 1/0.047],
|
||||
])
|
||||
|
||||
export const localization = new Map([
|
||||
const localization = new Map([
|
||||
["space engineers", new Map([
|
||||
["en-GB", "Space Engineers"]
|
||||
])],
|
||||
|
@ -210,21 +89,21 @@ export const localization = new Map([
|
|||
["en-GB", "Large Cargo Container"],
|
||||
["de-DE", "Großer Frachtcontainer"]
|
||||
])],
|
||||
["fusion", new Map([
|
||||
["en-GB", "Fusion"],
|
||||
["de-DE", "Fusion"]
|
||||
["fusionThruster", new Map([
|
||||
["en-GB", "Fusion Thruster"],
|
||||
["de-DE", "Fusionstriebwerk"]
|
||||
])],
|
||||
["hydrogen", new Map([
|
||||
["en-GB", "Hydrogen"],
|
||||
["de-DE", "Wasserstoff"]
|
||||
["hydrogenThruster", new Map([
|
||||
["en-GB", "Hydrogen Thruster"],
|
||||
["de-DE", "Wasserstofftriebwerk"]
|
||||
])],
|
||||
["ion", new Map([
|
||||
["en-GB", "Ion"],
|
||||
["de-DE", "Ionen"]
|
||||
["ionThruster", new Map([
|
||||
["en-GB", "Ion Thruster"],
|
||||
["de-DE", "Ionentriebwerk"]
|
||||
])],
|
||||
["atmos", new Map([
|
||||
["en-GB", "Atmospheric"],
|
||||
["de-DE", "Atmosphären"]
|
||||
["atmosphericThruster", new Map([
|
||||
["en-GB", "Atmospheric Thruster"],
|
||||
["de-DE", "Atmosphärentriebwerk"]
|
||||
])],
|
||||
["addInventory", new Map([
|
||||
["en-GB", "Add Inventory"],
|
||||
|
@ -278,9 +157,9 @@ export const localization = new Map([
|
|||
["en-GB", "Ore"],
|
||||
["de-DE", "Erz"]
|
||||
])],
|
||||
["component", new Map([
|
||||
["en-GB", "Component"],
|
||||
["de-DE", "Komponente"]
|
||||
["platinumIngot", new Map([
|
||||
["en-GB", "Platinum Ingot"],
|
||||
["de-DE", "Platinbarren"]
|
||||
])],
|
||||
["density", new Map([
|
||||
["en-GB", "Density"],
|
||||
|
@ -292,7 +171,72 @@ export const localization = new Map([
|
|||
])],
|
||||
])
|
||||
|
||||
export type Thruster = {
|
||||
type: ThrusterType,
|
||||
size: ThrusterSize
|
||||
|
||||
export class Localization {
|
||||
dictionary: Map<string, Map<string, string>>;
|
||||
language: string;
|
||||
|
||||
constructor(language: string) {
|
||||
this.language = language;
|
||||
this.dictionary = localization;
|
||||
};
|
||||
|
||||
localize(key: string): string {
|
||||
let localizationObject = this.dictionary.get(key)
|
||||
|
||||
if (typeof localizationObject === "undefined") {
|
||||
return `missing text: ${key}`
|
||||
}
|
||||
else {
|
||||
let localizedText = localizationObject.get(this.language)
|
||||
if (typeof localizedText === "undefined") {
|
||||
return localizationObject.get("en-GB") || `mising text: ${key}`;
|
||||
}
|
||||
else {
|
||||
return localizedText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getFromLocalStorage(key: string) {
|
||||
let val = localStorage.getItem(key);
|
||||
if (val !== null && typeof val !== "undefined" && val !== '{}') {
|
||||
return { result: true, value: val };
|
||||
}
|
||||
else {
|
||||
return { result: false, value: undefined };
|
||||
}
|
||||
}
|
||||
|
||||
export function applyUnits(i18n: Localization, value: number, unit: string): 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}${i18n.localize("separator")}${rest}${modifier}${unit}`
|
||||
}
|
||||
else {
|
||||
let rounded = Math.round(((value / power) + Number.EPSILON) * 100) / 100
|
||||
return `${rounded}${modifier}${unit}`
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value < 0) {
|
||||
return `-${applyUnits(i18n,value * -1, unit)}`
|
||||
}
|
||||
let rounded = Math.round(((value + Number.EPSILON) * 100) / 100)
|
||||
return `${rounded}${unit}`
|
||||
}
|
||||
|
||||
export function weightConversion(i18n: Localization, weight: number): string {
|
||||
if (weight > 1000 || weight < -1000) {
|
||||
return applyUnits(i18n, weight/1000, "t")
|
||||
}
|
||||
else {
|
||||
return `${weight.toFixed(2)}kg`
|
||||
}
|
||||
}
|
62
src/lib/containers.svelte.ts
Normal file
62
src/lib/containers.svelte.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
import {Grid} from "$lib/grid";
|
||||
import type {ObjectValues} from "$lib/components/object-values";
|
||||
import {THRUSTER_LIST, THRUSTER_TYPE_LIST} from "$lib/thruster.svelte";
|
||||
import {applyUnits} from "$lib/constants";
|
||||
|
||||
type InventoryDetails = {
|
||||
key: string,
|
||||
name: string,
|
||||
capacity: Map<Grid, number>
|
||||
}
|
||||
|
||||
export const INVENTORIES: { [key: string]: InventoryDetails } = {
|
||||
CargoLarge: {
|
||||
key: "CargoLarge",
|
||||
name: "largeCargo",
|
||||
capacity: new Map([
|
||||
[Grid.Small, 15625],
|
||||
[Grid.Large, 421000],
|
||||
])
|
||||
},
|
||||
CargoMedium: {
|
||||
key: "CargoMedium",
|
||||
name: "mediumCargo",
|
||||
capacity: new Map([
|
||||
[Grid.Small, 3375],
|
||||
])
|
||||
},
|
||||
CargoSmall: {
|
||||
key: "CargoSmall",
|
||||
name: "smallCargo",
|
||||
capacity: new Map([
|
||||
[Grid.Small, 125],
|
||||
[Grid.Large, 15625],
|
||||
])
|
||||
},
|
||||
Connector: {
|
||||
key: "Connector",
|
||||
name: "connector",
|
||||
capacity: new Map([
|
||||
[Grid.Small, 1152],
|
||||
[Grid.Large, 8000],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
type InventoryList = ObjectValues<typeof INVENTORIES>;
|
||||
|
||||
export class Inventory {
|
||||
details: InventoryDetails;
|
||||
|
||||
constructor(details: InventoryDetails) {
|
||||
if (typeof details === "undefined") {
|
||||
throw("Inventory undefined, cannot create Object");
|
||||
}
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
getVolume(grid: Grid, multiplier: number): number {
|
||||
console.log(this.details.capacity.get(grid), multiplier);
|
||||
return (this.details.capacity.get(grid) || 0) * multiplier;
|
||||
}
|
||||
}
|
13
src/lib/fuel.ts
Normal file
13
src/lib/fuel.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
export class Fuel {
|
||||
static readonly Electricity: Fuel = new Fuel('electric', 'W');
|
||||
static readonly Hydrogen: Fuel = new Fuel('hydrogen', 'l');
|
||||
static readonly Deuterium: Fuel = new Fuel('deuterium', 'l');
|
||||
|
||||
readonly unit: string;
|
||||
readonly name: string;
|
||||
|
||||
private constructor(name: string, unit: string) {
|
||||
this.name = name;
|
||||
this.unit = unit;
|
||||
}
|
||||
}
|
4
src/lib/grid.ts
Normal file
4
src/lib/grid.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export enum Grid {
|
||||
Small = "small",
|
||||
Large = "large",
|
||||
}
|
26
src/lib/materials.ts
Normal file
26
src/lib/materials.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
export class CargoMaterial {
|
||||
static readonly Ore: CargoMaterial = new CargoMaterial('ore', 1/0.37);
|
||||
static readonly PlatinumIngot: CargoMaterial = new CargoMaterial('platinumIngot', 1/0.047);
|
||||
|
||||
readonly density: number;
|
||||
readonly name: string;
|
||||
|
||||
private constructor(name: string, density: number) {
|
||||
this.name = name;
|
||||
this.density = density;
|
||||
}
|
||||
|
||||
static load(name: string) {
|
||||
let ret = CargoMaterial.Ore;
|
||||
Object.values(CargoMaterial).forEach((material) => {
|
||||
if (material.name === name) {
|
||||
ret = material;
|
||||
}
|
||||
})
|
||||
return ret;
|
||||
}
|
||||
|
||||
save() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
90
src/lib/ship.svelte.ts
Normal file
90
src/lib/ship.svelte.ts
Normal file
|
@ -0,0 +1,90 @@
|
|||
import {Thruster, THRUSTER_LIST} from "$lib/thruster.svelte";
|
||||
import {Grid} from "$lib/grid";
|
||||
import {INVENTORIES, Inventory} from "$lib/containers.svelte";
|
||||
|
||||
export class Ship {
|
||||
thrusters: Array<Thruster> = $state([]);
|
||||
inventories: Array<Inventory> = $state([]);
|
||||
grid: Grid = $state(Grid.Small);
|
||||
weight: number = $state(0);
|
||||
|
||||
constructor(grid: Grid = Grid.Small) {
|
||||
this.grid = grid;
|
||||
}
|
||||
|
||||
save() {
|
||||
return {
|
||||
thrusters: this.thrusters.map((thruster) => {
|
||||
return thruster.details.key;
|
||||
}),
|
||||
inventories: this.inventories.map((inventory) => {
|
||||
return inventory.details.key;
|
||||
}),
|
||||
grid: this.grid,
|
||||
weight: this.weight,
|
||||
}
|
||||
}
|
||||
|
||||
load(ship: {
|
||||
grid: Grid,
|
||||
thrusters: Array<string>,
|
||||
inventories: Array<string>,
|
||||
weight: number,
|
||||
}) {
|
||||
if (ship.grid !== undefined) {
|
||||
this.grid = ship.grid;
|
||||
}
|
||||
|
||||
if (ship.thrusters !== undefined) {
|
||||
this.thrusters = ship.thrusters.map((thruster) => {
|
||||
return new Thruster(THRUSTER_LIST[thruster]);
|
||||
});
|
||||
}
|
||||
|
||||
if (ship.inventories !== undefined) {
|
||||
this.inventories = ship.inventories.map((inventory) => {
|
||||
return new Inventory(INVENTORIES[inventory]);
|
||||
});
|
||||
}
|
||||
|
||||
if (ship.weight !== undefined) {
|
||||
this.weight = ship.weight;
|
||||
}
|
||||
}
|
||||
|
||||
addThruster(thruster: Thruster): void {
|
||||
this.thrusters.push(thruster);
|
||||
}
|
||||
|
||||
addInventory(inventory: Inventory): void {
|
||||
this.inventories.push(inventory);
|
||||
}
|
||||
|
||||
getTotalVolume(inventoryMultiplier: number) {
|
||||
let volume = 0;
|
||||
this.inventories.forEach((inventory) => {
|
||||
volume += inventory.getVolume(this.grid, inventoryMultiplier)
|
||||
});
|
||||
return volume;
|
||||
}
|
||||
|
||||
spliceInventories(index: number, length: number): void {
|
||||
this.inventories.splice(index, length);
|
||||
}
|
||||
|
||||
getTotalThrust(atmosphere: number): number {
|
||||
let thrust: number = 0;
|
||||
this.thrusters.forEach((thruster) => {
|
||||
thrust += thruster.getThrust(this.grid, atmosphere);
|
||||
});
|
||||
return thrust;
|
||||
}
|
||||
|
||||
getTotalMaxThrust(): number {
|
||||
let thrust = 0;
|
||||
this.thrusters.forEach((thruster) => {
|
||||
thrust += thruster.getMaxThrust(this.grid);
|
||||
});
|
||||
return thrust;
|
||||
}
|
||||
}
|
267
src/lib/thruster.svelte.ts
Normal file
267
src/lib/thruster.svelte.ts
Normal file
|
@ -0,0 +1,267 @@
|
|||
import {Fuel} from "$lib/fuel";
|
||||
import {Grid} from "$lib/grid";
|
||||
import type {ObjectValues} from "$lib/components/object-values";
|
||||
|
||||
export enum ThrusterSize {
|
||||
Small = "smallThruster",
|
||||
Large = "largeThruster",
|
||||
Huge = "hugeThruster",
|
||||
}
|
||||
|
||||
type ThrusterTypeDetails = {
|
||||
key: string,
|
||||
name: string,
|
||||
vacuum: number,
|
||||
atmosphere: number,
|
||||
fuel: Fuel,
|
||||
};
|
||||
|
||||
export const THRUSTER_TYPE_LIST: { [key: string]: ThrusterTypeDetails } = {
|
||||
Atmospheric: {
|
||||
key: "Atmospheric",
|
||||
name: "atmosphericThruster",
|
||||
vacuum: 0,
|
||||
atmosphere: 1,
|
||||
fuel: Fuel.Electricity,
|
||||
},
|
||||
Ion: {
|
||||
key: "Ion",
|
||||
name: "ionThruster",
|
||||
vacuum: 1,
|
||||
atmosphere: 0.2,
|
||||
fuel: Fuel.Electricity,
|
||||
},
|
||||
Hydrogen: {
|
||||
key: "Hydrogen",
|
||||
name: "hydrogenThruster",
|
||||
vacuum: 1,
|
||||
atmosphere: 1,
|
||||
fuel: Fuel.Hydrogen,
|
||||
},
|
||||
Fusion: {
|
||||
key: "Fusion",
|
||||
name: "fusionThruster",
|
||||
vacuum: 1,
|
||||
atmosphere: 0.5,
|
||||
fuel: Fuel.Deuterium,
|
||||
},
|
||||
}
|
||||
|
||||
export class ThrusterType {
|
||||
details: ThrusterTypeDetails = $state(THRUSTER_TYPE_LIST.Atmospheric);
|
||||
|
||||
constructor(type: ThrusterTypeDetails) {
|
||||
if (typeof type === "undefined") {
|
||||
throw("ThrusterType undefined, cannot create Object");
|
||||
}
|
||||
this.details = type;
|
||||
}
|
||||
|
||||
equals(obj: ThrusterType) {
|
||||
let equals: Array<boolean> = [];
|
||||
equals[0] = this.details.key === obj.details.key;
|
||||
equals[1] = this.details.name === obj.details.name;
|
||||
equals[2] = this.details.vacuum === obj.details.vacuum;
|
||||
equals[3] = this.details.atmosphere === obj.details.atmosphere;
|
||||
equals[4] = this.details.fuel === obj.details.fuel;
|
||||
return !equals.includes(false);
|
||||
}
|
||||
}
|
||||
|
||||
type ThrusterDetails = {
|
||||
key: string,
|
||||
name: string,
|
||||
type: ThrusterType,
|
||||
size: ThrusterSize,
|
||||
fuelConsumption: Map<Grid, number>;
|
||||
thrust: Map<Grid, number>;
|
||||
}
|
||||
|
||||
export const THRUSTER_LIST: {[key: string]: ThrusterDetails } = {
|
||||
SmallAtmos: {
|
||||
key: "SmallAtmos",
|
||||
name: "smallAtmos",
|
||||
type: new ThrusterType(THRUSTER_TYPE_LIST.Atmospheric),
|
||||
size: ThrusterSize.Small,
|
||||
fuelConsumption: new Map([
|
||||
[Grid.Small, 0],
|
||||
[Grid.Large, 0],
|
||||
]),
|
||||
thrust: new Map([
|
||||
[Grid.Small, 65000],
|
||||
[Grid.Large, 350000],
|
||||
])
|
||||
},
|
||||
LargeAtmos: {
|
||||
key: "LargeAtmos",
|
||||
name: "largeAtmos",
|
||||
type: new ThrusterType(THRUSTER_TYPE_LIST.Atmospheric),
|
||||
size: ThrusterSize.Large,
|
||||
fuelConsumption: new Map([
|
||||
[Grid.Small, 0],
|
||||
[Grid.Large, 0],
|
||||
]),
|
||||
thrust: new Map([
|
||||
[Grid.Small, 340000],
|
||||
[Grid.Large, 4500000],
|
||||
])
|
||||
},
|
||||
|
||||
SmallIon: {
|
||||
key: "SmallIon",
|
||||
name: "smallIon",
|
||||
type: new ThrusterType(THRUSTER_TYPE_LIST.Ion),
|
||||
size: ThrusterSize.Small,
|
||||
fuelConsumption: new Map([
|
||||
[Grid.Small, 0],
|
||||
[Grid.Large, 0],
|
||||
]),
|
||||
thrust: new Map([
|
||||
[Grid.Small, 14400],
|
||||
[Grid.Large, 345600],
|
||||
])
|
||||
},
|
||||
LargeIon: {
|
||||
key: "LargeIon",
|
||||
name: "largeIon",
|
||||
type: new ThrusterType(THRUSTER_TYPE_LIST.Ion),
|
||||
size: ThrusterSize.Large,
|
||||
fuelConsumption: new Map([
|
||||
[Grid.Small, 0],
|
||||
[Grid.Large, 0],
|
||||
]),
|
||||
thrust: new Map([
|
||||
[Grid.Small, 172800],
|
||||
[Grid.Large, 4320000],
|
||||
])
|
||||
},
|
||||
|
||||
SmallHydrogen: {
|
||||
key: "SmallHydrogen",
|
||||
name: "smallHydrogen",
|
||||
type: new ThrusterType(THRUSTER_TYPE_LIST.Hydrogen),
|
||||
size: ThrusterSize.Small,
|
||||
fuelConsumption: new Map([
|
||||
[Grid.Small, 0],
|
||||
[Grid.Large, 0],
|
||||
]),
|
||||
thrust: new Map([
|
||||
[Grid.Small, 98400],
|
||||
[Grid.Large, 1080000],
|
||||
])
|
||||
},
|
||||
LargeHydrogen: {
|
||||
key: "LargeHydrogen",
|
||||
name: "largeHydrogen",
|
||||
type: new ThrusterType(THRUSTER_TYPE_LIST.Hydrogen),
|
||||
size: ThrusterSize.Large,
|
||||
fuelConsumption: new Map([
|
||||
[Grid.Small, 0],
|
||||
[Grid.Large, 0],
|
||||
]),
|
||||
thrust: new Map([
|
||||
[Grid.Small, 480000],
|
||||
[Grid.Large, 7200000],
|
||||
])
|
||||
},
|
||||
|
||||
SmallFusion: {
|
||||
key: "SmallFusion",
|
||||
name: "smallFusion",
|
||||
type: new ThrusterType(THRUSTER_TYPE_LIST.Fusion),
|
||||
size: ThrusterSize.Small,
|
||||
fuelConsumption: new Map([
|
||||
[Grid.Small, 0],
|
||||
[Grid.Large, 0],
|
||||
]),
|
||||
thrust: new Map([
|
||||
[Grid.Small, 210000],
|
||||
[Grid.Large, 9040000],
|
||||
])
|
||||
},
|
||||
LargeFusion: {
|
||||
key: "LargeFusion",
|
||||
name: "largeFusion",
|
||||
type: new ThrusterType(THRUSTER_TYPE_LIST.Fusion),
|
||||
size: ThrusterSize.Large,
|
||||
fuelConsumption: new Map([
|
||||
[Grid.Small, 0],
|
||||
[Grid.Large, 0],
|
||||
]),
|
||||
thrust: new Map([
|
||||
[Grid.Small, 1030000],
|
||||
[Grid.Large, 33780000],
|
||||
])
|
||||
},
|
||||
HugeFusion: {
|
||||
key: "HugeFusion",
|
||||
name: "hugeFusion",
|
||||
type: new ThrusterType(THRUSTER_TYPE_LIST.Fusion),
|
||||
size: ThrusterSize.Huge,
|
||||
fuelConsumption: new Map([
|
||||
[Grid.Small, 0],
|
||||
]),
|
||||
thrust: new Map([
|
||||
[Grid.Small, 9040000],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export class Thruster {
|
||||
details: ThrusterDetails;
|
||||
|
||||
constructor(thruster: ThrusterDetails) {
|
||||
if (typeof thruster === "undefined") {
|
||||
throw("Thruster undefined, cannot create Object");
|
||||
}
|
||||
this.details = thruster;
|
||||
}
|
||||
|
||||
equals(obj: Thruster): boolean {
|
||||
let equals: Array<boolean> = [];
|
||||
equals[0] = this.details.key === obj.details.key;
|
||||
equals[1] = this.details.name === obj.details.name;
|
||||
equals[2] = this.details.type === obj.details.type;
|
||||
equals[3] = this.details.size === obj.details.size;
|
||||
equals[4] = this.details.fuelConsumption === obj.details.fuelConsumption;
|
||||
equals[5] = this.details.thrust === obj.details.thrust;
|
||||
return !equals.includes(false);
|
||||
}
|
||||
|
||||
getThrust(grid: Grid, atmosphereDensity: number): number {
|
||||
let efficiencyCoefficient: number = (this.details.type.details.vacuum * (1 - atmosphereDensity)) + (this.details.type.details.atmosphere * atmosphereDensity);
|
||||
let thruster: ThrusterDetails | undefined = THRUSTER_LIST[this.details.key];
|
||||
if (thruster === undefined) {
|
||||
throw(`Thruster ${this.details.key} undefined, cannot retrieve thrust values`);
|
||||
}
|
||||
let thrust: number | undefined = thruster.thrust.get(grid);
|
||||
if (thrust === undefined) {
|
||||
console.warn(`Thruster ${this.details.key} has no thrust values. Defaulting to 0`);
|
||||
thrust = 0;
|
||||
}
|
||||
return thrust * efficiencyCoefficient;
|
||||
}
|
||||
|
||||
getMaxThrust(grid: Grid): number {
|
||||
let thruster: ThrusterDetails | undefined = THRUSTER_LIST[this.details.key];
|
||||
if (thruster === undefined) {
|
||||
throw("Thruster undefined, cannot retrieve thrust values");
|
||||
}
|
||||
let thrust: number | undefined = thruster.thrust.get(grid);
|
||||
if (thrust === undefined) {
|
||||
console.warn(`Thruster ${this.details.key} has no thrust values. Defaulting to 0`);
|
||||
thrust = 0;
|
||||
}
|
||||
return thrust;
|
||||
}
|
||||
|
||||
static getByType(type: ThrusterType, grid: Array<Grid> = Object.values(Grid)): Array<ThrusterDetails> {
|
||||
let thrusters: Array<ThrusterDetails> = [];
|
||||
Object.values(THRUSTER_LIST).forEach((thruster) => {
|
||||
if (thruster.type.details.key === type.details.key) {
|
||||
thrusters.push(thruster);
|
||||
}
|
||||
})
|
||||
return thrusters;
|
||||
}
|
||||
}
|
|
@ -1,3 +1,7 @@
|
|||
<script>
|
||||
import "../app.css";
|
||||
</script>
|
||||
|
||||
<slot></slot>
|
||||
|
||||
<slot></slot><script>import "../app.css";</script><style></style>
|
||||
<style></style>
|
|
@ -4,34 +4,32 @@
|
|||
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,
|
||||
thrusterDetails, Density, densityValues, ThrusterTypeDetails, ThrusterSizeDetails
|
||||
applyUnits,
|
||||
getFromLocalStorage,
|
||||
Localization, weightConversion
|
||||
} 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 * as DropdownMenu from "$lib/components/ui/dropdown-menu";
|
||||
import {onMount} from "svelte";
|
||||
import {CargoMaterial} from "$lib/materials";
|
||||
import ThrusterList from "$lib/components/ThrusterList.svelte";
|
||||
import {Ship} from "$lib/ship.svelte";
|
||||
import {Grid} from "$lib/grid";
|
||||
import NewThruster from "$lib/components/NewThruster.svelte";
|
||||
import NewInventory from "$lib/components/NewInventory.svelte";
|
||||
import {Inventory} from "$lib/containers.svelte";
|
||||
import InventoryList from "$lib/components/InventoryList.svelte";
|
||||
|
||||
let locale = $state("en-GB");
|
||||
let i18n = $state(new Localization("en-GB"));
|
||||
let mounted = $state(false);
|
||||
|
||||
let thrusters: Array<Thruster> = $state([])
|
||||
let ship = $state(new Ship(Grid.Small));
|
||||
$effect(() => {
|
||||
ship.thrusters;
|
||||
if (mounted) {
|
||||
localStorage.setItem("thrusters", JSON.stringify(thrusters));
|
||||
}
|
||||
})
|
||||
|
||||
let inventories: Array<InventoryType> = $state([])
|
||||
$effect(() => {
|
||||
if (mounted) {
|
||||
localStorage.setItem("inventories", JSON.stringify(inventories));
|
||||
localStorage.setItem("ship", JSON.stringify(ship.save()));
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -41,12 +39,6 @@
|
|||
localStorage.setItem("inventoryMultiplier", inventoryMultiplier);
|
||||
}
|
||||
})
|
||||
let gridSize: Grids = $state(Grids.Small);
|
||||
$effect(() => {
|
||||
if (mounted) {
|
||||
localStorage.setItem("gridSize", gridSize);
|
||||
}
|
||||
})
|
||||
|
||||
let gravity: number = $state(1);
|
||||
$effect(() => {
|
||||
|
@ -55,10 +47,10 @@
|
|||
}
|
||||
})
|
||||
|
||||
let density: Density = $state(Density.Ore);
|
||||
let material: CargoMaterial = $state(CargoMaterial.Ore);
|
||||
$effect(() => {
|
||||
if (mounted) {
|
||||
localStorage.setItem("density", density);
|
||||
localStorage.setItem("density", JSON.stringify(material.save()));
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -69,157 +61,53 @@
|
|||
}
|
||||
})
|
||||
|
||||
let newThruster: Thruster = $state({
|
||||
type: ThrusterType.Atmospheric,
|
||||
size: ThrusterSize.Small,
|
||||
})
|
||||
$effect(() => {
|
||||
if (mounted) {
|
||||
localStorage.setItem("newThruster", JSON.stringify(newThruster))
|
||||
}
|
||||
})
|
||||
let maxWeight: number = $derived(ship.getTotalMaxThrust() / (gravity * 9.81))
|
||||
|
||||
let newInventory: InventoryType = $state(InventoryType.CargoMedium)
|
||||
$effect(() => {
|
||||
if (mounted) {
|
||||
localStorage.setItem("newInventory", JSON.stringify(newInventory))
|
||||
}
|
||||
})
|
||||
let maxVehicleWeight: number = $derived(maxWeight - ship.getTotalVolume(inventoryMultiplier) * material.density)
|
||||
$inspect(maxVehicleWeight);
|
||||
$inspect(`${maxWeight} - ${ship.getTotalVolume(inventoryMultiplier)} * ${material.density} = ${maxVehicleWeight}`)
|
||||
|
||||
let totalThrust: number = $derived.by(() => {
|
||||
let thrust = 0;
|
||||
thrusters.forEach((thruster) => {
|
||||
thrust += getThrust(thruster)
|
||||
})
|
||||
return thrust
|
||||
});
|
||||
let totalVolume: number = $derived.by(() => {
|
||||
let volume = 0;
|
||||
inventories.forEach((inventory) => {
|
||||
volume += getInventoryVolume(inventory);
|
||||
})
|
||||
return volume;
|
||||
})
|
||||
|
||||
let maxWeight: number = $derived(totalThrust / (gravity * 9.81))
|
||||
|
||||
let maxVehicleWeight: number = $derived(maxWeight - totalVolume * densityValues.get(density))
|
||||
|
||||
function weightConversion(weight: number): string {
|
||||
if (weight > 1000 || weight < -1000) {
|
||||
return applyUnits(weight/1000, "t")
|
||||
}
|
||||
else {
|
||||
return `${weight.toFixed(2)}kg`
|
||||
}
|
||||
function addInventory(newInventory: Inventory) {
|
||||
ship.addInventory(newInventory);
|
||||
}
|
||||
|
||||
function getInventoryVolume(inventory: InventoryType): number {
|
||||
return inventorySizes.get(gridSize).get(inventory) * inventoryMultiplier
|
||||
}
|
||||
|
||||
function getThrusterDetails(thruster: Thruster): ThrusterSizeDetails {
|
||||
return thrusterDetails.get(gridSize).get(thruster.type).sizes.get(thruster.size)
|
||||
}
|
||||
|
||||
function getThrust(thruster: Thruster): number {
|
||||
let typeDetails = getThrusterTypeDetails(thruster);
|
||||
let sizeDetails = getThrusterDetails(thruster);
|
||||
let thrustEfficiency = (typeDetails.vacuumFactor * (1 - atmosphericDensity)) + (typeDetails.atmosphericFactor * atmosphericDensity);
|
||||
return sizeDetails.thrust * thrustEfficiency;
|
||||
}
|
||||
|
||||
function getThrusterTypeDetails(thruster: Thruster): ThrusterTypeDetails {
|
||||
return thrusterDetails.get(gridSize).get(thruster.type)
|
||||
}
|
||||
|
||||
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}`
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value < 0) {
|
||||
return `-${applyUnits(value * -1, unit)}`
|
||||
}
|
||||
let rounded = Math.round(((value + Number.EPSILON) * 100) / 100)
|
||||
return `${rounded}${unit}`
|
||||
function addThruster(newThruster: Thruster) {
|
||||
ship.addThruster(newThruster);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if (navigator) {
|
||||
locale = navigator.language;
|
||||
i18n.language = navigator.language;
|
||||
}
|
||||
|
||||
if (localStorage.getItem("thrusters") !== null) {
|
||||
thrusters = JSON.parse(localStorage.getItem("thrusters"))
|
||||
let ret = getFromLocalStorage("ship");
|
||||
if (ret.result) {
|
||||
ship.load(JSON.parse(ret.value));
|
||||
}
|
||||
|
||||
if (localStorage.getItem("inventories") !== null) {
|
||||
inventories = JSON.parse(localStorage.getItem("inventories"))
|
||||
ret = getFromLocalStorage("gravity");
|
||||
if (ret.result) {
|
||||
gravity = ret.value;
|
||||
}
|
||||
|
||||
if (localStorage.getItem("gridSize") !== null) {
|
||||
gridSize = localStorage.getItem("gridSize")
|
||||
ret = getFromLocalStorage("density");
|
||||
if (ret.result) {
|
||||
let parsed = JSON.parse(ret.value);
|
||||
Object.values(CargoMaterial).forEach((cargoMaterial) => {
|
||||
if (cargoMaterial.name === parsed) {
|
||||
material = cargoMaterial
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (localStorage.getItem("gravity") !== null) {
|
||||
gravity = localStorage.getItem("gravity")
|
||||
ret = getFromLocalStorage("atmosphericDensity");
|
||||
if (ret.result) {
|
||||
atmosphericDensity = ret.value;
|
||||
}
|
||||
|
||||
if (localStorage.getItem("density") !== null) {
|
||||
density = localStorage.getItem("density")
|
||||
}
|
||||
|
||||
if (localStorage.getItem("atmosphericDensity") !== null) {
|
||||
atmosphericDensity = localStorage.getItem("atmosphericDensity")
|
||||
}
|
||||
|
||||
if (localStorage.getItem("inventoryMultiplier") !== null) {
|
||||
inventoryMultiplier = localStorage.getItem("inventoryMultiplier")
|
||||
}
|
||||
|
||||
if (localStorage.getItem("newThruster") !== null) {
|
||||
newThruster = JSON.parse(localStorage.getItem("newThruster"))
|
||||
}
|
||||
|
||||
if (localStorage.getItem("newInventory") !== null) {
|
||||
newInventory = JSON.parse(localStorage.getItem("newInventory"))
|
||||
ret = getFromLocalStorage("inventoryMultiplier");
|
||||
if (ret.result) {
|
||||
inventoryMultiplier = ret.value;
|
||||
}
|
||||
|
||||
mounted = true;
|
||||
|
@ -227,134 +115,91 @@
|
|||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{localized("title")}</title>
|
||||
<title>{i18n.localize("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">
|
||||
<div class="gap-4 flex flex-row flex-wrap h-full">
|
||||
<Card.Root class="flex-1">
|
||||
<Card.Header>
|
||||
<Card.Title>{localized("title")}</Card.Title>
|
||||
<Card.Title>{i18n.localize("title")}</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content class="flex flex-col gap-3">
|
||||
<Separator />
|
||||
<Label for="gravity">{localized("gravity")}</Label>
|
||||
<Label for="gravity">{i18n.localize("gravity")}</Label>
|
||||
<Input type="number" step="0.01" id="gravity" bind:value={gravity}/>
|
||||
<Label for="atmosDensity">{localized("atmosDensity")}</Label>
|
||||
<Label for="atmosDensity">{i18n.localize("atmosDensity")}</Label>
|
||||
<Input type="number" step="0.01" id="atmosDensity" bind:value={atmosphericDensity}/>
|
||||
<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>
|
||||
<Label for="gridSize">{i18n.localize("gridSize")}</Label>
|
||||
<div id="gridSize" class="flex gap-2">
|
||||
{#each Object.values(Grid) as size}
|
||||
{#if ship.grid === size}
|
||||
<Button class="bg-primary">{i18n.localize(size)}</Button>
|
||||
{:else}
|
||||
<Button variant="secondary" onclick={() => gridSize = size}>{localized(size)}</Button>
|
||||
<Button variant="secondary" onclick={() => ship.grid = size}>{i18n.localize(size)}</Button>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<Separator />
|
||||
<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>
|
||||
<Label for="density">{i18n.localize("density")}</Label>
|
||||
<div id="density" class="flex gap-2">
|
||||
{#each Object.values(CargoMaterial) as value}
|
||||
{#if material === value}
|
||||
<Button class="bg-primary">{i18n.localize(value.name)}</Button>
|
||||
{:else}
|
||||
<Button variant="secondary" onclick={() => density = value}>{localized(value)}</Button>
|
||||
<Button variant="secondary" onclick={() => material = value}>{i18n.localize(value.name)}</Button>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<Separator />
|
||||
<p>{localized("liftableVehcileWeight")}: {weightConversion(maxVehicleWeight)}</p>
|
||||
<p>{localized("liftableWeight")}: {weightConversion(maxWeight)}</p>
|
||||
<p>{i18n.localize("liftableVehcileWeight")}: {weightConversion(i18n, maxVehicleWeight)}</p>
|
||||
<p>{i18n.localize("liftableWeight")}: {weightConversion(i18n, maxWeight)}</p>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
<Card.Root class="col-span-1 flex flex-col flex-grow">
|
||||
<Card.Root class="flex-1 flex flex-col flex-grow">
|
||||
<Card.Header>
|
||||
<Card.Title>{localized("thrusterSettings")}</Card.Title>
|
||||
<Card.Title>{i18n.localize("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">
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>{localized(newThruster.type)}</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content>
|
||||
<DropdownMenu.RadioGroup bind:value={newThruster.type}>
|
||||
{#each Object.values(ThrusterType) as thrusterType}
|
||||
<DropdownMenu.RadioItem value={thrusterType}>{localized(thrusterType)}</DropdownMenu.RadioItem>
|
||||
{/each}
|
||||
</DropdownMenu.RadioGroup>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>{localized(newThruster.size)}</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content>
|
||||
<DropdownMenu.RadioGroup bind:value={newThruster.size}>
|
||||
{#each getThrusterTypeDetails(newThruster).sizes.keys() as thrusterDetails}
|
||||
<DropdownMenu.RadioItem value={thrusterDetails}>{localized(thrusterDetails)}</DropdownMenu.RadioItem>
|
||||
{/each}
|
||||
</DropdownMenu.RadioGroup>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
<Button variant="secondary" onclick={() => addThruster()}>{localized("addThruster")}</Button>
|
||||
<NewThruster i18n={i18n} onAddThruster={addThruster} />
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
<Card.Root class="flex flex-col h-0 flex-grow">
|
||||
<Card.Root class="flex flex-col h-60 flex-grow">
|
||||
<Card.Header>
|
||||
<Card.Title>{localized("thrusters")} ({applyUnits(totalThrust, "N")})</Card.Title>
|
||||
<Card.Title>{i18n.localize("thrusters")} ({applyUnits(i18n, ship.getTotalThrust(atmosphericDensity), "N")})</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content class="overflow-hidden">
|
||||
<Card.Content class="flex flex-col gap-3 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(getThrust(thruster), "N")}
|
||||
<Button variant="destructive" onclick={() => thrusters.splice(index, 1)}>X</Button>
|
||||
</p>
|
||||
{/each}
|
||||
</div>
|
||||
<ThrusterList i18n={i18n} bind:thrusters={ship.thrusters} grid={ship.grid} atmosphere={atmosphericDensity}/>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
<Card.Root class="col-span-1 flex flex-col flex-grow">
|
||||
<Card.Root class="flex-1 flex flex-col flex-grow">
|
||||
<Card.Header>
|
||||
<Card.Title>{localized("inventorySettings")}</Card.Title>
|
||||
<Card.Title>{i18n.localize("inventorySettings")}</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content class="flex flex-col flex-grow gap-3">
|
||||
<Separator />
|
||||
<Label for="inventorySize">{localized("inventory")} {localized("multiplier")}</Label>
|
||||
<Label for="inventorySize">{i18n.localize("inventory")} {i18n.localize("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>
|
||||
<NewInventory i18n={i18n} grid={ship.grid} onAddInventory={addInventory} />
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
<Card.Root class="flex flex-col h-0 flex-grow">
|
||||
<Card.Root class="flex flex-col h-60 flex-grow">
|
||||
<Card.Header>
|
||||
<Card.Title>{localized("inventories")} ({applyUnits(totalVolume, "l")})</Card.Title>
|
||||
<Card.Title>{i18n.localize("inventories")} ({applyUnits(i18n, ship.getTotalVolume(inventoryMultiplier), "l")})</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content class="overflow-hidden">
|
||||
<Card.Content class="flex flex-col gap-3 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>
|
||||
<InventoryList i18n={i18n} bind:inventories={ship.inventories} grid={ship.grid} multiplier={inventoryMultiplier} />
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</Card.Content>
|
||||
|
|
Loading…
Reference in a new issue