Merge branch 'running-sveltekit' into version-2

This commit is contained in:
Yanick Champoux 2022-03-01 12:44:01 -05:00
commit 67c6abbe96
363 changed files with 1404 additions and 1353 deletions

34
.storybook/main.js Normal file
View File

@ -0,0 +1,34 @@
const path = require("path");
const preprocess = require("svelte-preprocess");
module.exports = {
staticDirs: ["../static", "../pictures"],
stories: [
"../src/**/*.stories.mdx",
"../src/**/*.stories.svelte",
"../src/**/stories.svelte",
],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-svelte-csf",
],
framework: "@storybook/svelte",
svelteOptions: {
preprocess: preprocess(),
},
webpackFinal: async (config) => {
return {
...config,
resolve: {
...config.resolve,
alias: {
...config.resolve?.alias,
$app: path.resolve("./fake/app"),
"\\$lib": path.resolve(__dirname, "../src/lib/"),
$lib: path.resolve(__dirname, "../src/lib/"),
},
},
};
},
};

3
.storybook/package.json Normal file
View File

@ -0,0 +1,3 @@
{
"type": "commonjs"
}

11
.storybook/preview.js Normal file
View File

@ -0,0 +1,11 @@
import "$lib/style/index.js";
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
};

View File

@ -1,5 +1,6 @@
<svelte:component this={component} />
<script> <script>
import "$lib/style/index.js";
export let component; export let component;
</script> </script>
<svelte:component this={component} />

View File

@ -1,30 +1,30 @@
import { svelte } from '@sveltejs/vite-plugin-svelte'; import { svelte } from "@sveltejs/vite-plugin-svelte";
import { clientPlugin, defineConfig } from '@vitebook/client/node'; import { clientPlugin, defineConfig } from "@vitebook/client/node";
import { defaultThemePlugin } from '@vitebook/theme-default/node'; import { defaultThemePlugin } from "@vitebook/theme-default/node";
import preprocess from 'svelte-preprocess'; import preprocess from "svelte-preprocess";
export default defineConfig({ export default defineConfig({
include: ['src/**/*.story.svelte'], include: ["src/**/*.story.svelte"],
alias: { alias: {
$app: '/node_modules/@sveltejs/kit/assets/app', $app: "/node_modules/@sveltejs/kit/assets/app",
$lib: '/src/lib', $lib: "/src/lib",
}, },
plugins: [ plugins: [
clientPlugin({ appFile: 'App.svelte' }), clientPlugin({ appFile: "App.svelte" }),
defaultThemePlugin(), defaultThemePlugin(),
svelte({ svelte({
compilerOptions: { compilerOptions: {
hydratable: true hydratable: true,
}, },
extensions: ['.svelte'], extensions: [".svelte"],
// Consult https://github.com/sveltejs/svelte-preprocess for more information // Consult https://github.com/sveltejs/svelte-preprocess for more information
// about preprocessors. // about preprocessors.
preprocess: preprocess(), preprocess: preprocess(),
}), }),
], ],
site: { site: {
title: '', title: "",
description: '', description: "",
/** @type {(import('@vitebook/theme-default/node').DefaultThemeConfig} */ /** @type {(import('@vitebook/theme-default/node').DefaultThemeConfig} */
theme: {}, theme: {},
}, },

View File

@ -1,3 +1,3 @@
import Theme from '@vitebook/theme-default'; import Theme from "@vitebook/theme-default";
export default Theme; export default Theme;

View File

@ -7,6 +7,7 @@
"preview": "svelte-kit preview", "preview": "svelte-kit preview",
"lint": "prettier --check . && eslint --ignore-path .gitignore .", "lint": "prettier --check . && eslint --ignore-path .gitignore .",
"format": "prettier --write .", "format": "prettier --write .",
"storybook": "storybook-server",
"vitebook:dev": "vitebook dev", "vitebook:dev": "vitebook dev",
"vitebook:build": "vitebook build", "vitebook:build": "vitebook build",
"vitebook:preview": "vitebook preview" "vitebook:preview": "vitebook preview"
@ -28,14 +29,21 @@
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@storybook/addon-actions": "^6.4.19",
"@storybook/addon-essentials": "^6.4.19",
"@storybook/addon-links": "6.4.19",
"@storybook/addon-svelte-csf": "^1.1.0",
"@storybook/svelte": "^6.4.19",
"@sveltejs/adapter-node": "^1.0.0-next.0", "@sveltejs/adapter-node": "^1.0.0-next.0",
"@yanick/updeep": "link:/home/yanick/work/javascript/updeep", "@yanick/updeep": "link:/home/yanick/work/javascript/updeep",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"redux": "^4.1.2", "redux": "^4.1.2",
"reselect": "^4.1.5", "reselect": "^4.1.5",
"rollup-plugin-analyzer": "^4.0.0", "rollup-plugin-analyzer": "^4.0.0",
"svelte-knobby": "^0.3.4",
"ts-action": "^11.0.0", "ts-action": "^11.0.0",
"updux": "link:/home/yanick/work/javascript/updux/" "updux": "link:/home/yanick/work/javascript/updux/",
"webpack": "5"
}, },
"prettier": { "prettier": {
"svelteSortOrder": "options-markup-scripts-styles", "svelteSortOrder": "options-markup-scripts-styles",

View File

@ -1,20 +0,0 @@
import { action } from '@storybook/addon-actions';
import Carrier from './index.svelte';
export default {
title: 'Carrier',
};
export const basic = () => ({
Component: Carrier,
props: {
bays: 3,
squadrons: [
{ id: 1, type: "standard", ftl: false, nbr_fighters: 6 },
{ id: 2, type: "fast", ftl: false, nbr_fighters: 6 },
{ id: 3, type: "none", ftl: false, nbr_fighters: 6 },
],
ship_change: action('ship_change'),
},
})

View File

@ -1,5 +0,0 @@
<Cost cost={12}/>
<script>
import Cost from './Cost.svelte';
</script>

View File

@ -1,21 +0,0 @@
<div class="mass">{ mass }</div>
<div class="cost">{ cost }</div>
<script>
export let mass;
export let cost;
</script>
<style>
.cost { grid-column: 3; }
.mass { grid-column: 2; }
img {
width: 0.75em;
}
.cost:after { content: '\00A4'; margin-left: 0.5em; }
.mass:after { content: url("/mass.svg"); width: 0.75em; display:
inline-block; margin-left: 0.5em; }
</style>

View File

@ -1,40 +0,0 @@
<ShipItem {cost} {mass}>
<div>
<Field label="thrust rating">
<input type="number" bind:value={ rating }
min="0" max="20" step="1" />
</Field>
<label><input type="checkbox" bind:checked={advanced} /> advanced</label>
</div>
</ShipItem>
<script>
import { createEventDispatcher } from 'svelte';
import Field from '$lib/components/Field/index.svelte';
import ShipItem from '$lib/components/ShipItem/index.svelte';
export let cost;
export let mass;
export let advanced = false;
export let rating = 0;
const dispatch = createEventDispatcher();
console.log(advanced);
$: dispatch( 'change_engine', { rating, advanced } );
</script>
<style>
div {
display: flex;
align-items: end;
}
label { margin-left: 2em; }
input[type="number"] { width: 5em; }
</style>

View File

@ -1,29 +0,0 @@
<div>
{#if label}
<label>{label}</label>
{/if}
<slot>
<input type="text" {placeholder} {value} on:change />
</slot>
</div>
<script>
export let label = "";
export let value = "";
export let placeholder;
</script>
<style>
div > :global(*) {
margin-left: 1em;
}
div > label {
margin-left: 0em;
}
label {
font-size: var(--font-scale-8);
font-weight: lighter;
font-family: Dosis;
color: var(--indigo-dye);
}
</style>

View File

@ -1,12 +0,0 @@
import Component from './index.svelte';
import BasicStory from './BasicStory.svelte';
import '../../../public/global.css';
export default {
title: 'Field'
};
export const basic = () => ({
Component: BasicStory,
});

View File

@ -1,20 +0,0 @@
<ShipItem {cost} {mass}>
<Field label="firecons">
<input type="number" class="short" bind:value={nbr} />
</Field>
</ShipItem>
<script>
import { createEventDispatcher } from 'svelte';
import ShipItem from './ShipItem/index.svelte';
import Field from './Field/index.svelte';
export let nbr, cost, mass = (0,0,0);
const dispatch = createEventDispatcher();
$: dispatch( 'change_firecons', nbr);
</script>
<style>
</style>

View File

@ -1,35 +0,0 @@
<script>
import { createEventDispatcher } from 'svelte';
import ShipItem from '../ShipItem/index.svelte';
import Field from '../Field/index.svelte';
export let type = 'none';
export let cost = 0;
export let mass = 0;
const dispatch = createEventDispatcher();
const change = () => dispatch( 'change_ftl', type );
const types = [ 'none', 'standard', 'advanced' ];
</script>
<ShipItem {mass} {cost}>
<Field label="FTL drive">
{#each types as t (t)}
<label><input type="radio" bind:group={type} value={t}
on:change={change} /> {t} </label>
{/each}
</Field>
</ShipItem>
<style>
label {
display: inline;
margin-right: 1em;
}
</style>

View File

@ -1,13 +0,0 @@
export default {
title: 'FTL Drive'
};
import Component from '.';
import shipStore from '../../stores/ship.js';
export const basic = () => ({
Component,
props: {
ship: shipStore()
}
});

View File

@ -1,28 +0,0 @@
<header>
<h1>The Docks</h1>
<h2>a <a
href="https://shop.groundzerogames.co.uk/rules.html">Full Thrust</a> ship builder</h2>
</header>
<style>
header {
display: flex;
align-items: baseline;
width: var(--main-width);
margin-left: auto;
margin-right: auto;
}
h1, h2 {
font-family: Faktos;
padding: 0px;
margin: 0px;
}
h2 {
text-align: right;
flex: 1;
padding-left: 2em;
font-size: var(--font-scale-12);
}
</style>

View File

@ -1,24 +0,0 @@
<Field label={ `layer ${layer}` }>
<input type="number" min="0" bind:value={rating} />
</Field>
<script>
import { getContext} from 'svelte';
import Field from '$lib/components/Field/index.svelte';
export let layer = 1;
export let rating = 0;
const ship = getContext('ship');
$: ship?.dispatch_action( 'set_armour_layer', {layer,rating} );
</script>
<style>
input {
width: 5em;
}
</style>

View File

@ -1,56 +0,0 @@
<ShipItem {cost} {mass} >
<div>
<div class="nbr_layers">
<Field label="armour layers">
<input type="number" min="0" bind:value={nbr_layers} />
</Field>
</div>
<div class="layers">
{#each armour as layer ( layer.layer )}
<Layer {...layer} on:ship_change/>
{/each}
</div>
</div>
</ShipItem>
<script>
import { createEventDispatcher} from 'svelte';
import ShipItem from '$lib/components/ShipItem/index.svelte';
import Field from '$lib/components/Field/index.svelte';
import Layer from './Layer/index.svelte';
import dux from '$lib/dux/structure/armour';
import _ from 'lodash';
export let armour = [];
export let cost = 0;
export let mass = 0;
$: cost = _.sum( _.map( armour, 'cost' ) );
$: mass = _.sum( _.map( armour, 'mass' ) );
let nbr_layers = armour.length;
const dispatch = createEventDispatcher();
$: dispatch( 'ship_change', dux.actions.set_armour_nbr_layers(nbr_layers) );
</script>
<style>
.layers {
display: flex;
}
input {
width: 5em;
}
</style>

View File

@ -1,18 +0,0 @@
import Armour from './index.svelte';
export default {
title: "Armour",
};
export const basic = () => ({
Component: Armour,
props: {
armour: [
{ layer: 1, rating: 12, cost: 1, mass: 2 },
{ layer: 2, rating: 12, cost: 1, mass: 2 },
]
},
on: {
ship_change: ({detail}) => console.log(detail)
}
});

View File

@ -1,42 +0,0 @@
<ShipItem { cost } { mass }>
<div>
<Field label="screens">
<input type="number" bind:value={standard} min="0" />
</Field>
<Field label="advanced screens">
<input type="number" bind:value={advanced} min="0" />
</Field>
</div>
</ShipItem>
<script>
import { createEventDispatcher } from 'svelte';
import Section from '$lib/components/Section/index.svelte';
import Field from '$lib/components/Field/index.svelte';
import ShipItem from '../../ShipItem/index.svelte';
export let cost = 0;
export let mass = 0;
export let standard = 0;
export let advanced = 0;
let nbr_regular, nbr_advanced;
const dispatch = createEventDispatcher();
$: dispatch( 'set_screens', { standard, advanced } );
</script>
<style>
input {
width: 3em;
}
div {
display: flex;
gap: 2em;
}
</style>

View File

@ -1,38 +0,0 @@
<ShipItem {cost} {mass}>
<Field label="streamlining">
<div>
<label>
<input type="radio" bind:group={type} value="none" />
none</label>
<label>
<input type="radio" bind:group={type} value="partial" />
partial</label>
<label>
<input type="radio" bind:group={type} value="full" />
full</label>
</div>
</Field>
</ShipItem>
<script>
import ShipItem from '$lib/components/ShipItem/index.svelte';
import Field from '$lib/components/Field/index.svelte';
import {getContext } from 'svelte';
export let type = 'none';
export let cost = 0;
export let mass = 0;
export let ship = getContext('ship');
$: ship?.dispatch_action( 'set_streamlining', type);
</script>
<style>
div { display: flex }
label {
margin-left: 1em;
}
</style>

View File

@ -1,51 +0,0 @@
<Section label="hull">
<ShipItem {cost} {mass} >
<Field label="integrity">
<input
bind:value={rating}
type="number" {min} {max} />
</Field>
</ShipItem>
<Screens {...screens} on:set_screens />
<Armour {armour} on:ship_change />
<Cargo {...cargo} on:set_cargo />
<Streamlining {...streamlining} />
</Section>
<script>
import { createEventDispatcher } from 'svelte';
import Section from '../Section/index.svelte';
import Field from '../Field/index.svelte';
import ShipItem from '../ShipItem/index.svelte';
import Screens from './Screens/index.svelte';
import Armour from './Armour/index.svelte';
import Cargo from './Cargo/index.svelte';
import Streamlining from './Streamlining/index.svelte';
export let cost, mass, ship_mass, rating, screens, armour = (
0, 0, 10, 1, [], []
);
export let cargo = {};
export let streamlining = {};
let min, max;
$: min = Math.ceil(ship_mass / 10);
$: max = ship_mass;
const dispatch = createEventDispatcher();
$: dispatch( 'change_hull', { rating } );
</script>
<style>
input { width: 5em; }
</style>

View File

@ -1,23 +0,0 @@
import Component from '.';
export default {
title: "printouts/hull/integrity"
};
export const basic = () => ({
Component,
props: {
ship_mass: 50,
rating: 14,
advanced: false,
}
});
export const advanced = () => ({
Component,
props: {
ship_mass: 50,
rating: 14,
advanced: true,
}
});

View File

@ -1,13 +0,0 @@
import Component from '.';
export default {
title: "printouts/main_systems"
};
export const basic = () => ({
Component,
props: {
ftl: 'standard',
engine: 4,
}
})

View File

@ -1,33 +0,0 @@
import Component from '.';
export default {
title: "printouts/weapons"
};
export const basic = () => ({
Component,
props: {
"weapons": [
{
"weapon_type": "submunition",
"arcs": [
"F"
],
"mass": 1,
"cost": 3,
"id": 1
},
{
"weapon_type": "beam",
"weapon_class": "2",
"arcs": [
"A",
"AS",
"FS"
],
"mass": 2,
"cost": 6,
"id": 2
}, ]
}
})

View File

@ -1,53 +0,0 @@
<aside class="ship-sheet" transition:fade>
<Identification {...ship.general} />
<Weapons weapons={ship.weaponry.weapons} />
<div class="section-2">
<Hull structure={ship.structure}
ship_mass={ship.general.mass} />
<Systems
firecons={ship.weaponry.firecons.nbr}
screens={ship.structure.screens}
/>
</div>
<MainSystems
ftl={ship.ftl.type}
engine={ship.engine.rating}
/>
</aside>
<script>
import _ from 'lodash';
import Identification from './Identification/index.svelte';
import MainSystems from './MainSystems/index.svelte';
import Hull from './Hull/index.svelte';
import Weapons from './Weapons/index.svelte';
import Systems from './Systems/index.svelte';
export let ship;
import { fly, fade } from 'svelte/transition';
</script>
<style>
.ship-sheet {
width: 4.25in;
height: 5.5in;
border: 1px solid black;
padding: 1em;
}
.section-2 {
display: flex;
align-items: start;
}
</style>

View File

@ -1,114 +0,0 @@
export default {
"ftl": {
"mass": 0,
"cost": 0,
"type": "standard"
},
"engine": {
"mass": 40,
"cost": 80,
"rating": 6,
"advanced": false
},
"general": {
"ship_class": "Deviant",
"name": "",
"ship_type": "Battleship",
"mass": 132,
"used_mass": 131,
"cost": 415
},
"weaponry": {
"firecons": {
"nbr": 5,
"mass": 5,
"cost": 20
},
"weapons": [
{
"weapon_type": "submunition",
"arcs": [
"F"
],
"mass": 1,
"cost": 3,
"id": 1
},
{
"weapon_type": "beam",
"weapon_class": "2",
"arcs": [
"A",
"AS",
"FS"
],
"mass": 2,
"cost": 6,
"id": 2
},
{
"weapon_type": "pds",
"mass": 1,
"cost": 3,
"id": 3
}
],
"adfc": {
"rating": 0,
"cost": 0,
"mass": 0
}
},
"structure": {
"mass": 0,
"cost": 0,
"hull": {
"rating": 22,
"advanced": false,
"cost": 44,
"mass": 22
},
"screens": {
"standard": 2,
"advanced": 1,
"cost": 105,
"mass": 35
},
"armour": [
{
"layer": 1,
"rating": 7,
"cost": 14,
"mass": 14
},
{
"layer": 2,
"rating": 2,
"cost": 8,
"mass": 4
},
{
"layer": 3,
"rating": 0,
"cost": 0,
"mass": 0
}
]
},
"cargo": {
"space": 7,
"cost": 0,
"mass": 7
},
"streamlining": {
"type": "none",
"cost": 0,
"mass": 0
},
"carrier": {
"bays": 0,
"cost": 0,
"mass": 0,
"squadrons": []
}
}

View File

@ -1,13 +0,0 @@
import Print from './index.svelte';
import sample from './sample';
export default {
title: "printouts"
};
export const basic = () => ({
Component: Print,
props: {
ship: sample
}
});

View File

@ -1,18 +0,0 @@
<Section label="propulsion">
<Engine {...engine} on:change_engine />
<Ftl {...ftl} on:change_ftl />
</Section>
<script>
import Ftl from '../Ftl/index.svelte';
import Engine from '../Engine/index.svelte';
import Section from '../Section/index.svelte';
export let ftl = {};
export let engine = {};
</script>

View File

@ -1,47 +0,0 @@
<div>
<Field label="ship class" value={general.ship_class}
on:change={change_class} />
<Field label="ship type">
<select value={ship_type} on:change={change_ship_type}>
{#each ship_types as type (type)}
<option>{type}</option>
{/each}
</select>
</Field>
</div>
<script>
import { getContext } from 'svelte';
import Field from '$lib/components/Field/index.svelte';
import { candidate_ship_types } from '../../dux/ship_types';
export let ship = getContext('ship');
let general;
$: general = $ship.general;
const change_class = (event) => ship.dispatch(
ship.actions.set_ship_class(event.target.value)
);
let ship_type;
$: ship_type = $ship.general.ship_type;
const change_ship_type = ({ target: {value}}) =>
ship.dispatch.set_ship_type(value);
let ship_types;
$: ship_types = candidate_ship_types($ship.general.mass,$ship.carrier.bays>0).map(
({name}) => name
);
</script>
<style>
div {
display: flex;
align-items: end;
gap: 2em;
}
</style>

View File

@ -1,32 +0,0 @@
<svg width="{size}px" height="{size}px">
{#each all_arcs as arc (arc)}
<Arc {arc} radius={size/2}
active={selected.includes(arc)}
on:click={()=>click_arc(arc)}
/>
{/each}
<circle cx="50%" cy="50%" r={size /3} />
<slot />
</svg>
<script>
import Arc from '../../Weapons/Arc.svelte';
import { createEventDispatcher } from 'svelte';
const all_arcs = [ 'FS', 'F', 'FP', 'AP', 'A', 'AS' ];
export let selected = [];
export let size = 60;
const dispatch = createEventDispatcher();
const click_arc = arc => dispatch('click_arc',arc);
</script>
<style>
circle {
fill: white;
}
</style>

Before

Width:  |  Height:  |  Size: 637 B

View File

@ -1,37 +0,0 @@
<label> needle weapon</label>
<Arcs selected={arcs} on:click_arc={({detail}) => click_arc(detail)} />
<script>
import {getContext } from 'svelte';
import Arcs from '../Arcs/index.svelte';
import dux from '$lib/dux';
import { createEventDispatcher } from 'svelte';
const all_arcs = [ 'FS', 'F', 'FP', 'AP', 'A', 'AS' ];
export let arcs = ['F'];
export let ship_change = getContext('ship_change') || ( () => {} );
const click_arc = (arc) => {
if( arcs[0] === arc ) return;
arcs = [ arc ];
}
const dispatch = createEventDispatcher();
let cache;
$: cache = arcs.join(":");
$: dispatch( 'change', {
arcs: cache.split(":"),
})
</script>
<style>
.arc {
display: flex;
flex-direction: column;
margin-right: 1em;
}
</style>

View File

@ -1,37 +0,0 @@
<label> submunition pack</label>
<Arcs selected={arcs} on:click_arc={({detail}) => click_arc(detail)} />
<script>
import {getContext } from 'svelte';
import Arcs from '../Arcs/index.svelte';
import dux from '$lib/dux';
import { createEventDispatcher } from 'svelte';
const all_arcs = [ 'FS', 'F', 'FP', 'AP', 'A', 'AS' ];
export let arcs = ['F'];
export let ship_change = getContext('ship_change') || ( () => {} );
const click_arc = (arc) => {
if( arcs[0] === arc ) return;
arcs = [ arc ];
}
const dispatch = createEventDispatcher();
let cache;
$: cache = arcs.join(":");
$: dispatch( 'change', {
arcs: cache.split(":"),
})
</script>
<style>
.arc {
display: flex;
flex-direction: column;
margin-right: 1em;
}
</style>

View File

@ -1,202 +0,0 @@
<input class="add-weapon button small blue" type="button" value="add"
on:click={add} />
<div class="weapon">
<select bind:value={weapon_type}>
<option>beam</option>
</select>
<select bind:value={weapon_class}>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
</select>
<select bind:value={nbr_arcs}>
{#each arc_options[weapon_class] as opt (opt)}
<option>{opt}</option>
{/each}
</select>
<svg width="60px" height="60px">
{#each arcs as arc (arc)}
<Arc {arc} radius={30}
active={selected_arc[arc]}
on:click={()=>click_arc(arc)}
/>
{/each}
<circle cx="30" cy="30" r="15" />
</svg>
<div>{weapon.cost}</div>
<div>{weapon.mass}</div>
</div>
<script>
let weapon_type = 'beam';
let weapon_class = 1;
let nbr_arcs = 6;
$: nbr_arcs = arc_options[weapon_class][0];
import Arc from './Arc.svelte';
import { weapon_cost_mass } from '../../dux/weapons/rules.js';
const arcs = [
'FS', 'F', 'FP', 'AP', 'A', 'AS'
];
import _ from 'lodash';
import { createEventDispatcher } from 'svelte';
let arc_options = {
1: [ 6],
2: [ 3, 6 ],
3: [ 1, 2, 3, 4, 5, 6, 'broadside' ],
4: [ 1, 2, 3, 4, 5, 6, 'broadside' ]
};
let selected_arc = Object.fromEntries(
arcs.map( arc => [ arc, false ] )
);
const nbr_selected_arcs = () => Object.values(selected_arc).filter(
x => x ).length;
$: if ( nbr_selected_arcs() !== nbr_arcs ) {
if( nbr_arcs === 'broadside' ) {
const new_arcs = {};
arcs.forEach( arc => new_arcs[arc] = true );
new_arcs.A = false;
new_arcs.F = false;
selected_arc = new_arcs;
}
else{
let first_index = arcs.findIndex( arc => selected_arc[arc] );
if( first_index === -1 ) first_index = 0;
const new_arcs = {};
arcs.forEach( arc => new_arcs[arc] = false );
_.range(nbr_arcs).forEach( i => {
new_arcs[ arcs[first_index] ] = true;
first_index = ( first_index + 1 ) % arcs.length;
});
selected_arc = new_arcs;
}
}
const click_arc = (first_arc) => {
if( nbr_arcs === 'broadside' ) return;
let first_index = arcs.findIndex( arc => arc === first_arc );
const new_arcs = {};
arcs.forEach( arc => new_arcs[arc] = false );
_.range(nbr_arcs).forEach( i => {
console.log(first_index);
console.log(selected_arc);
new_arcs[ arcs[first_index] ] = true;
first_index = ( first_index + 1 ) % arcs.length;
});
selected_arc = new_arcs;
}
let weapon = {};
$: weapon= {
weapon_type,
weapon_class,
arcs,
...weapon_cost_mass({ weapon_type, weapon_class, arcs: arcs.filter(
arc => selected_arc[arc]
) })
};
const dispatch = createEventDispatcher();
const add = () => {
dispatch('add_weapon', weapon);
}
</script>
<style>
.weapon {
display: flex;
align-items: center;
}
.weapon > * {
margin-right: 2em;
}
.arcs {
display: grid;
grid-template-rows: 1fr 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr 1fr;
align-items: center;
justify-items: center;
width: 6em;
}
.arc input {
margin: 0px;
}
.arc.F {
grid-column: 2 / span 2;
grid-row: 1;
}
.arc.FS {
grid-column: 1;
grid-row: 1 / span 2;
}
.arc.FP {
grid-column: 4;
grid-row: 1 / span 2;
}
.arc.AS {
grid-column: 1;
grid-row: 3 / span 2;
}
.arc.AP {
grid-column: 4;
grid-row: 3 / span 2;
}
.arc.A {
grid-column: 2 / span 2;
grid-row: 4;
}
.arc {
display: flex;
flex-direction: column;
margin-right: 1em;
}
.add-weapon {
display: block;
}
circle {
fill: white;
}
</style>

View File

@ -1,50 +0,0 @@
<script>
export let arc;
export let radius;
export let active = false;
const rotation = {
F: 0,
FS: 300,
AS: 240,
A: 180,
AP: 120,
FP: 60,
};
let y, x_delta;
$: y = Math.round( radius * ( 1 - Math.sin(60/180*Math.PI) ) );
$: x_delta = Math.round( radius*Math.cos(60/180*Math.PI) );
let path;
$: path = `M ${radius},${radius} L ${radius-x_delta},${y} A ${radius},${radius} 0 0 1 ${radius+x_delta},${y} Z`;
let transform;
$: transform = `rotate(${rotation[arc]},${radius},${radius})`
</script>
<g { transform }>
<path d={path} class:active on:click />
</g>
<style>
path {
fill: lightgrey;
stroke: white;
stroke-width: 2px;
}
path:hover {
fill: pink;
}
path.active:hover {
fill: pink;
}
path.active {
fill: #313131;
}
</style>

View File

@ -1,10 +0,0 @@
import AddWeapon from './Add.svelte';
export default {
title: "add weapon",
};
export const beam = () => ({
Component: AddWeapon,
});

3
src/global.d.ts vendored
View File

@ -1,3 +0,0 @@
/// <reference types="@sveltejs/kit" />
/// <reference types="svelte" />
/// <reference types="vite/client" />

View File

@ -1,10 +1,5 @@
<Ribbon />
<Header />
<nav> <nav>
<button class="button is-danger" type="button" on:click={reset} <button class="button is-danger" type="button" on:click={reset}>reset</button>
>reset</button
>
<div class="spacer" /> <div class="spacer" />
@ -18,9 +13,7 @@
<button class="button" on:click={() => set_output("json")}>json</button> <button class="button" on:click={() => set_output("json")}>json</button>
</p> </p>
<p class="control"> <p class="control">
<button class="button" on:click={() => set_output("print")} <button class="button" on:click={() => set_output("print")}>print</button>
>print</button
>
</p> </p>
</div> </div>
</nav> </nav>
@ -68,12 +61,7 @@
<AddWeapon /> <AddWeapon />
{#each weapons as weapon (weapon.id)} {#each weapons as weapon (weapon.id)}
<Weapon <Weapon {weapon} id={weapon.id} cost={weapon.cost} mass={weapon.mass} />
{weapon}
id={weapon.id}
cost={weapon.cost}
mass={weapon.mass}
/>
{/each} {/each}
</Section> </Section>
@ -89,7 +77,6 @@
<script> <script>
import { setContext } from "svelte"; import { setContext } from "svelte";
import Header from "./Header.svelte";
import Ribbon from "./Ribbon.svelte"; import Ribbon from "./Ribbon.svelte";
import shipStore from "../stores/ship"; import shipStore from "../stores/ship";
import OutputJson from "./Output/Json.svelte"; import OutputJson from "./Output/Json.svelte";

View File

@ -0,0 +1,10 @@
<Ribbon />
<Header />
<script>
import Ribbon from "./Ribbon.svelte";
import Header from "./Header.svelte";
</script>
<style>
</style>

View File

@ -0,0 +1,14 @@
<Carrier />
<script>
import Carrier from "./index.svelte";
const props = {
bays: 3,
squadrons: [
{ id: 1, type: "standard", ftl: false, nbr_fighters: 6 },
{ id: 2, type: "fast", ftl: false, nbr_fighters: 6 },
{ id: 3, type: "none", ftl: false, nbr_fighters: 6 },
],
};
</script>

View File

@ -1,4 +1,4 @@
<ShipItem {cost} {mass} > <ShipItem {cost} {mass}>
<Field label={`squadron ${id}`}> <Field label={`squadron ${id}`}>
<select bind:value={type}> <select bind:value={type}>
{#each types as type (type)} {#each types as type (type)}
@ -9,30 +9,29 @@
</ShipItem> </ShipItem>
<script> <script>
import {getContext } from 'svelte'; import { getContext } from "svelte";
import Section from "$lib/components/Section/index.svelte"; import Section from "$lib/components/Section/index.svelte";
import Field from "$lib/components/Field/index.svelte"; import Field from "$lib/components/Field/index.svelte";
import ShipItem from "$lib/components/ShipItem/index.svelte"; import ShipItem from "$lib/components/ShipItem/index.svelte";
import dux from '$lib/dux/carrier'; import dux from "$lib/dux/carrier";
import squadron_types from '$lib/dux/carrier/squadron_types'; import squadron_types from "$lib/dux/carrier/squadron_types";
const types = squadron_types.map( ({type}) => type ); const types = squadron_types.map(({ type }) => type);
export let id = 1; export let id = 1;
export let type = "standard"; export let type = "standard";
export let ftl = false; export let ftl = false;
export let cost =0; export let cost = 0;
export let mass = 0; export let mass = 0;
export let ship = getContext('ship'); export let ship = getContext("ship");
$: ship?.dispatch_action('set_squadron',{ id, type, });
$: ship?.dispatch_action("set_squadron", { id, type });
</script> </script>
<style> <style>
div { div {
background-color: red; background-color: red;
} }
</style> </style>

View File

@ -1,3 +1,4 @@
<!--
<Section label="carrier"> <Section label="carrier">
<ShipItem {cost} {mass}> <ShipItem {cost} {mass}>
<Field label="bays"> <Field label="bays">
@ -9,15 +10,18 @@
<Squadron {...squad} /> <Squadron {...squad} />
{/each} {/each}
</Section> </Section>
-->
Hey!
<script> <script>
import { getContext } from "svelte"; import { getContext } from "svelte";
import Section from "../Section/index.svelte"; /* import Section from "../Section/index.svelte"; */
import Field from "../Field/index.svelte"; /* import Field from "../Field/index.svelte"; */
import ShipItem from "../ShipItem/index.svelte"; /* import ShipItem from "../ShipItem/index.svelte"; */
import Squadron from "./Squadron/index.svelte"; // import Squadron from "./Squadron/index.svelte";
import dux from "../../dux/carrier"; // import dux from "../../dux/carrier";
export let bays = 0; export let bays = 0;
export let squadrons = []; export let squadrons = [];
@ -26,6 +30,5 @@
export let ship = getContext("ship"); export let ship = getContext("ship");
$: ship?.dispatch_action('set_carrier_bays',bays); $: ship?.dispatch_action("set_carrier_bays", bays);
</script> </script>

View File

@ -0,0 +1,20 @@
import { action } from "@storybook/addon-actions";
import Carrier from "./index.svelte";
export default {
title: "Carrier",
};
export const basic = () => ({
Component: Carrier,
props: {
bays: 3,
squadrons: [
{ id: 1, type: "standard", ftl: false, nbr_fighters: 6 },
{ id: 2, type: "fast", ftl: false, nbr_fighters: 6 },
{ id: 3, type: "none", ftl: false, nbr_fighters: 6 },
],
ship_change: action("ship_change"),
},
});

View File

@ -0,0 +1,5 @@
<Cost cost={12} />
<script>
import Cost from "./Cost.svelte";
</script>

View File

@ -5,5 +5,8 @@
</script> </script>
<style> <style>
div:after { content: '\00A4'; margin-left: 0.5em; } div:after {
content: "\00A4";
margin-left: 0.5em;
}
</style> </style>

View File

@ -0,0 +1,29 @@
<div class="mass">{mass}</div>
<div class="cost">{cost}</div>
<script>
export let mass;
export let cost;
</script>
<style>
.cost {
grid-column: 3;
}
.mass {
grid-column: 2;
}
img {
width: 0.75em;
}
.cost:after {
content: "\00A4";
margin-left: 0.5em;
}
.mass:after {
content: url("/mass.svg");
width: 0.75em;
display: inline-block;
margin-left: 0.5em;
}
</style>

View File

@ -0,0 +1,33 @@
<Meta
title="Engine"
component={Engine}
argTypes={{
rating: { type: "number", defaultValue: 6 },
advanced: { type: "boolean", defaultValue: false },
reqs: { type: "object", defaultValue: { cost: 6, mass: 3 } },
}}
/>
<Story name="Primary" args={{}} />
<Template let:args>
<div style="width: 50em">
<Engine {...args} />
</div>
</Template>
<script>
import { Meta, Template, Story } from "@storybook/addon-svelte-csf";
import { action } from "@storybook/addon-actions";
import { setContext } from "svelte";
import Engine from "./index.svelte";
setContext("ship", {
dispatch: (type, detail) => action(type)(detail),
});
let advanced = false;
let rating = 0;
</script>

View File

@ -0,0 +1,39 @@
<ShipItem {...reqs}>
<div>
<Field label="thrust rating">
<input type="number" bind:value={rating} min="0" max="20" step="1" />
</Field>
<label><input type="checkbox" bind:checked={advanced} /> advanced</label>
</div>
</ShipItem>
<script>
import { getContext } from "svelte";
import Field from "$lib/components/Field/index.svelte";
import ShipItem from "$lib/components/ShipItem/index.svelte";
export let reqs = {};
export let advanced = false;
export let rating = 0;
const ship = getContext("ship") || {
dispatch: (...args) => console.log(args),
};
$: ship.dispatch("setEngine", { rating, advanced });
</script>
<style>
div {
display: flex;
align-items: end;
}
label {
font-family: var(--main-font-family);
margin-left: 2em;
}
input[type="number"] {
width: 5em;
}
</style>

View File

@ -1,5 +1,5 @@
<Field label="the label" /> <Field label="the label" />
<script> <script>
import Field from './index.svelte'; import Field from "./index.svelte";
</script> </script>

View File

@ -0,0 +1,32 @@
<div>
{#if label}
<label>{label}</label>
{/if}
<slot>
<input type="text" {placeholder} {value} on:change />
</slot>
</div>
<script>
export let label = "";
export let value = "";
export let placeholder;
</script>
<style>
div {
font-family: var(--main-font-family);
}
div > :global(*) {
margin-left: 1em;
}
div > label {
margin-left: 0em;
}
label {
font-size: var(--font-scale-8);
font-weight: lighter;
font-family: Dosis;
color: var(--indigo-dye);
}
</style>

View File

@ -0,0 +1,12 @@
import Component from "./index.svelte";
import BasicStory from "./BasicStory.svelte";
import "../../../public/global.css";
export default {
title: "Field",
};
export const basic = () => ({
Component: BasicStory,
});

View File

@ -0,0 +1,21 @@
<ShipItem {cost} {mass}>
<Field label="firecons">
<input type="number" class="short" bind:value={nbr} />
</Field>
</ShipItem>
<script>
import { createEventDispatcher } from "svelte";
import ShipItem from "./ShipItem/index.svelte";
import Field from "./Field/index.svelte";
export let nbr,
cost,
mass = (0, 0, 0);
const dispatch = createEventDispatcher();
$: dispatch("change_firecons", nbr);
</script>
<style>
</style>

View File

@ -0,0 +1,34 @@
<ShipItem {mass} {cost}>
<Field label="FTL drive">
{#each types as t (t)}
<label
><input type="radio" bind:group={type} value={t} on:change={change} />
{t}
</label>
{/each}
</Field>
</ShipItem>
<script>
import { createEventDispatcher } from "svelte";
import ShipItem from "../ShipItem/index.svelte";
import Field from "../Field/index.svelte";
export let type = "none";
export let cost = 0;
export let mass = 0;
const dispatch = createEventDispatcher();
const change = () => dispatch("change_ftl", type);
const types = ["none", "standard", "advanced"];
</script>
<style>
label {
display: inline;
margin-right: 1em;
}
</style>

View File

@ -0,0 +1,13 @@
export default {
title: "FTL Drive",
};
import Component from ".";
import shipStore from "../../stores/ship.js";
export const basic = () => ({
Component,
props: {
ship: shipStore(),
},
});

View File

@ -0,0 +1,30 @@
<header>
<h1>The Docks</h1>
<h2>
a <a href="https://shop.groundzerogames.co.uk/rules.html">Full Thrust</a> ship
builder
</h2>
</header>
<style>
header {
display: flex;
align-items: baseline;
width: var(--main-width);
margin-left: auto;
margin-right: auto;
}
h1,
h2 {
font-family: Faktos;
padding: 0px;
margin: 0px;
}
h2 {
text-align: right;
flex: 1;
padding-left: 2em;
font-size: var(--font-scale-12);
}
</style>

View File

@ -0,0 +1,22 @@
<Field label={`layer ${layer}`}>
<input type="number" min="0" bind:value={rating} />
</Field>
<script>
import { getContext } from "svelte";
import Field from "$lib/components/Field/index.svelte";
export let layer = 1;
export let rating = 0;
const ship = getContext("ship");
$: ship?.dispatch_action("set_armour_layer", { layer, rating });
</script>
<style>
input {
width: 5em;
}
</style>

View File

@ -0,0 +1,48 @@
<ShipItem {cost} {mass}>
<div>
<div class="nbr_layers">
<Field label="armour layers">
<input type="number" min="0" bind:value={nbr_layers} />
</Field>
</div>
<div class="layers">
{#each armour as layer (layer.layer)}
<Layer {...layer} on:ship_change />
{/each}
</div>
</div>
</ShipItem>
<script>
import { createEventDispatcher } from "svelte";
import ShipItem from "$lib/components/ShipItem/index.svelte";
import Field from "$lib/components/Field/index.svelte";
import Layer from "./Layer/index.svelte";
import dux from "$lib/dux/structure/armour";
import _ from "lodash";
export let armour = [];
export let cost = 0;
export let mass = 0;
$: cost = _.sum(_.map(armour, "cost"));
$: mass = _.sum(_.map(armour, "mass"));
let nbr_layers = armour.length;
const dispatch = createEventDispatcher();
$: dispatch("ship_change", dux.actions.set_armour_nbr_layers(nbr_layers));
</script>
<style>
.layers {
display: flex;
}
input {
width: 5em;
}
</style>

View File

@ -0,0 +1,18 @@
import Armour from "./index.svelte";
export default {
title: "Armour",
};
export const basic = () => ({
Component: Armour,
props: {
armour: [
{ layer: 1, rating: 12, cost: 1, mass: 2 },
{ layer: 2, rating: 12, cost: 1, mass: 2 },
],
},
on: {
ship_change: ({ detail }) => console.log(detail),
},
});

View File

@ -17,12 +17,10 @@
export let mass = 0; export let mass = 0;
$: ship?.dispatch_action("set_cargo", space); $: ship?.dispatch_action("set_cargo", space);
</script> </script>
<style> <style>
input { input {
width: 5em; width: 5em;
} }
</style> </style>

View File

@ -0,0 +1,40 @@
<ShipItem {cost} {mass}>
<div>
<Field label="screens">
<input type="number" bind:value={standard} min="0" />
</Field>
<Field label="advanced screens">
<input type="number" bind:value={advanced} min="0" />
</Field>
</div>
</ShipItem>
<script>
import { createEventDispatcher } from "svelte";
import Section from "$lib/components/Section/index.svelte";
import Field from "$lib/components/Field/index.svelte";
import ShipItem from "../../ShipItem/index.svelte";
export let cost = 0;
export let mass = 0;
export let standard = 0;
export let advanced = 0;
let nbr_regular, nbr_advanced;
const dispatch = createEventDispatcher();
$: dispatch("set_screens", { standard, advanced });
</script>
<style>
input {
width: 3em;
}
div {
display: flex;
gap: 2em;
}
</style>

View File

@ -0,0 +1,41 @@
<ShipItem {cost} {mass}>
<Field label="streamlining">
<div>
<label>
<input type="radio" bind:group={type} value="none" />
none</label
>
<label>
<input type="radio" bind:group={type} value="partial" />
partial</label
>
<label>
<input type="radio" bind:group={type} value="full" />
full</label
>
</div>
</Field>
</ShipItem>
<script>
import ShipItem from "$lib/components/ShipItem/index.svelte";
import Field from "$lib/components/Field/index.svelte";
import { getContext } from "svelte";
export let type = "none";
export let cost = 0;
export let mass = 0;
export let ship = getContext("ship");
$: ship?.dispatch_action("set_streamlining", type);
</script>
<style>
div {
display: flex;
}
label {
margin-left: 1em;
}
</style>

View File

@ -0,0 +1,51 @@
<Section label="hull">
<ShipItem {cost} {mass}>
<Field label="integrity">
<input bind:value={rating} type="number" {min} {max} />
</Field>
</ShipItem>
<Screens {...screens} on:set_screens />
<Armour {armour} on:ship_change />
<Cargo {...cargo} on:set_cargo />
<Streamlining {...streamlining} />
</Section>
<script>
import { createEventDispatcher } from "svelte";
import Section from "../Section/index.svelte";
import Field from "../Field/index.svelte";
import ShipItem from "../ShipItem/index.svelte";
import Screens from "./Screens/index.svelte";
import Armour from "./Armour/index.svelte";
import Cargo from "./Cargo/index.svelte";
import Streamlining from "./Streamlining/index.svelte";
export let cost,
mass,
ship_mass,
rating,
screens,
armour = (0, 0, 10, 1, [], []);
export let cargo = {};
export let streamlining = {};
let min, max;
$: min = Math.ceil(ship_mass / 10);
$: max = ship_mass;
const dispatch = createEventDispatcher();
$: dispatch("change_hull", { rating });
</script>
<style>
input {
width: 5em;
}
</style>

View File

@ -24,9 +24,9 @@
</p> </p>
<p> <p>
The app is mostly developed for Firefox. I also check as much as I can The app is mostly developed for Firefox. I also check as much as I can that
that I don't mess things too badly on Chrome. For the other browsers... I don't mess things too badly on Chrome. For the other browsers... caveat
caveat emptor. emptor.
</p> </p>
<p> <p>

View File

@ -5,13 +5,13 @@
<script> <script>
export let ship = {}; export let ship = {};
let json; let json;
$: json = JSON.stringify(ship,null,2); $: json = JSON.stringify(ship, null, 2);
import { fly, fade } from 'svelte/transition'; import { fly, fade } from "svelte/transition";
import {createEventDispatcher} from 'svelte'; import { createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
const close = () => dispatch('close'); const close = () => dispatch("close");
</script> </script>
<style> <style>
@ -20,13 +20,11 @@
font-size: var(--font-scale-9); font-size: var(--font-scale-9);
overflow: scroll; overflow: scroll;
height: 90%; height: 90%;
} }
div { div {
text-align: right; text-align: right;
} }
aside { aside {
padding: 1em; padding: 1em;
border: 3px solid var(--indigo-dye); border: 3px solid var(--indigo-dye);
border-radius: 1em; border-radius: 1em;

View File

@ -1,17 +1,15 @@
<div class="layers"> <div class="layers">
{#each armour as layer (layer)} {#each armour as layer (layer)}
<div class="layer"> <div class="layer">
{#each _.range(layer.rating) as i (i) } {#each _.range(layer.rating) as i (i)}
<div class="cell"> <div class="cell" />
</div>
{/each} {/each}
</div> </div>
{/each} {/each}
</div> </div>
<script> <script>
import _ from 'lodash'; import _ from "lodash";
export let armour = []; export let armour = [];
@ -19,14 +17,14 @@
</script> </script>
<style> <style>
.cell { .cell {
display: inline-block; display: inline-block;
margin-right: 0.5em; margin-right: 0.5em;
width: 1em; width: 1em;
height: 1em; height: 1em;
border: 1px solid black; border: 1px solid black;
border-radius: 0.5em; border-radius: 0.5em;
} }
.layers { .layers {
display: flex; display: flex;
flex-direction: column-reverse; flex-direction: column-reverse;

View File

@ -1,7 +1,7 @@
<div> <div>
{#each rows as row,i (i)} {#each rows as row, i (i)}
<div class="row"> <div class="row">
{#each row as item,j (j)} {#each row as item, j (j)}
<div class="cell"> <div class="cell">
{#if item} {#if item}
<img src="icons/crew-star.svg" alt="crew loss threshold" /> <img src="icons/crew-star.svg" alt="crew loss threshold" />
@ -10,11 +10,10 @@
{/each} {/each}
</div> </div>
{/each} {/each}
</div> </div>
<script> <script>
import { ceil } from '$lib/dux/utils'; import { ceil } from "$lib/dux/utils";
export let ship_mass = 0; export let ship_mass = 0;
export let rating = 0; export let rating = 0;
@ -27,45 +26,40 @@
$: cells = Array(rating).fill(false); $: cells = Array(rating).fill(false);
let dcp; let dcp;
$: dcp= ceil(ship_mass/20); $: dcp = ceil(ship_mass / 20);
$: cells = divide(cells, dcp).map( $: cells = divide(cells, dcp)
g => { .map((g) => {
g[g.length-1] = true; g[g.length - 1] = true;
return g; return g;
} })
).flat(); .flat();
function divide(list, divider) { function divide(list, divider) {
if( divider <= 1 ) return [ list ]; if (divider <= 1) return [list];
let div = list.length / divider; let div = list.length / divider;
const mod = list.length % divider; const mod = list.length % divider;
if(mod) div++; if (mod) div++;
return [ return [list.slice(0, div), ...divide(list.slice(div), divider - 1)];
list.slice(0,div),
...divide( list.slice(div), divider-1 )
]
} }
let rows = []; let rows = [];
$: rows = divide( cells, nbr_rows ); $: rows = divide(cells, nbr_rows);
</script> </script>
<style> <style>
.row { .row {
} }
.cell { .cell {
display: inline-block; display: inline-block;
margin-right: 0.5em; margin-right: 0.5em;
width: 1em; width: 1em;
height: 1em; height: 1em;
border: 1px solid black; border: 1px solid black;
} }
img { img {
width: 1em; width: 1em;
} }

View File

@ -0,0 +1,23 @@
import Component from ".";
export default {
title: "printouts/hull/integrity",
};
export const basic = () => ({
Component,
props: {
ship_mass: 50,
rating: 14,
advanced: false,
},
});
export const advanced = () => ({
Component,
props: {
ship_mass: 50,
rating: 14,
advanced: true,
},
});

View File

@ -8,8 +8,8 @@
</div> </div>
<script> <script>
import Integrity from './Integrity/index.svelte'; import Integrity from "./Integrity/index.svelte";
import Armour from './Armour/index.svelte'; import Armour from "./Armour/index.svelte";
export let structure = {}; export let structure = {};
export let ship_mass = 0; export let ship_mass = 0;

View File

@ -1,4 +1,6 @@
<h1>ship name: <div class="fill"></div> </h1> <h1>
ship name: <div class="fill" />
</h1>
<div class="details"> <div class="details">
<h2>{ship_class}-class, {ship_type}</h2> <h2>{ship_class}-class, {ship_type}</h2>
@ -10,12 +12,13 @@
</div> </div>
<script> <script>
import Cost from '../../../Cost.svelte'; import Cost from "../../../Cost.svelte";
import Mass from '$lib/components/Mass.svelte'; import Mass from "$lib/components/Mass.svelte";
export let ship_class; export let ship_class;
export let ship_type; export let ship_type;
export let cost, mass = ( 0, 0 ); export let cost,
mass = (0, 0);
</script> </script>
<style> <style>

View File

@ -1,6 +1,5 @@
<div class="main_systems"> <div class="main_systems">
{#if ftl !== "none"}
{#if ftl !== "none" }
<img class="ftl" src="icons/ftl-drive.svg" alt="ftl drive" /> <img class="ftl" src="icons/ftl-drive.svg" alt="ftl drive" />
{/if} {/if}
@ -8,13 +7,16 @@
<div class="thrust">{engine}</div> <div class="thrust">{engine}</div>
{/if} {/if}
<img class="internal" src="icons/internal-systems.svg" alt="internal systems"/> <img
</div> class="internal"
src="icons/internal-systems.svg"
alt="internal systems"
/>
</div>
<script> <script>
export let ftl = 'none'; export let ftl = "none";
export let engine = 0; export let engine = 0;
</script> </script>
<style> <style>
@ -27,14 +29,16 @@
line-height: 2em; line-height: 2em;
text-align: center; text-align: center;
} }
.main_systems { .main_systems {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 1em; gap: 1em;
justify-content: space-evenly; justify-content: space-evenly;
margin-top: 1em; margin-top: 1em;
} }
img.ftl { height: 2em; } img.ftl {
height: 2em;
}
img.internal { img.internal {
height: 2em; height: 2em;
} }

View File

@ -0,0 +1,13 @@
import Component from ".";
export default {
title: "printouts/main_systems",
};
export const basic = () => ({
Component,
props: {
ftl: "standard",
engine: 4,
},
});

View File

@ -1,15 +1,14 @@
<div> <div>
{#each _.range(standard) as i } {#each _.range(standard) as i}
<img src="icons/screen.svg" alt="screen" /> <img src="icons/screen.svg" alt="screen" />
{/each} {/each}
{#each _.range(advanced) as i } {#each _.range(advanced) as i}
<img src="icons/screen-advanced.svg" alt="advanced screen" /> <img src="icons/screen-advanced.svg" alt="advanced screen" />
{/each} {/each}
</div> </div>
<script> <script>
import _ from 'lodash'; import _ from "lodash";
export let standard = 0; export let standard = 0;
export let advanced = 0; export let advanced = 0;

View File

@ -1,16 +1,15 @@
<div> <div>
<Firecons {firecons}/> <Firecons {firecons} />
<Screens {...screens} /> <Screens {...screens} />
</div> </div>
<script> <script>
import Firecons from './Firecons/index.svelte'; import Firecons from "./Firecons/index.svelte";
import Screens from './Screens/index.svelte'; import Screens from "./Screens/index.svelte";
export let firecons = 0; export let firecons = 0;
export let screens = {}; export let screens = {};
</script> </script>
<style> <style>
@ -23,6 +22,5 @@
} }
div > :global(*) { div > :global(*) {
margin-bottom: 1em; margin-bottom: 1em;
} }
</style> </style>

View File

@ -1,13 +1,13 @@
<div> <div>
<Arcs selected={arcs} size="40"> <Arcs selected={arcs} size="40">
<text x="50%" y="50%" > <text x="50%" y="50%">
{weapon_class} {weapon_class}
</text> </text>
</Arcs> </Arcs>
</div> </div>
<script> <script>
import Arcs from '$lib/components/Weapon/Arcs/index.svelte'; import Arcs from "$lib/components/Weapon/Arcs/index.svelte";
export let weapon_class = 1; export let weapon_class = 1;
export let arcs = []; export let arcs = [];
</script> </script>

View File

@ -1,21 +1,17 @@
<div class="weapons"> <div class="weapons">
<div class="beams"> <div class="beams">
{#each beams as beam} {#each beams as beam}
<Beam {...beam} /> <Beam {...beam} />
{/each} {/each}
</div> </div>
</div> </div>
<script> <script>
import Beam from './Beam/index.svelte'; import Beam from "./Beam/index.svelte";
export let weapons = []; export let weapons = [];
let beams = []; let beams = [];
$: beams = weapons.filter( ({ weapon_type }) => weapon_type === 'beam' ); $: beams = weapons.filter(({ weapon_type }) => weapon_type === "beam");
</script> </script>
<style> <style>

View File

@ -0,0 +1,28 @@
import Component from ".";
export default {
title: "printouts/weapons",
};
export const basic = () => ({
Component,
props: {
weapons: [
{
weapon_type: "submunition",
arcs: ["F"],
mass: 1,
cost: 3,
id: 1,
},
{
weapon_type: "beam",
weapon_class: "2",
arcs: ["A", "AS", "FS"],
mass: 2,
cost: 6,
id: 2,
},
],
},
});

View File

@ -0,0 +1,44 @@
<aside class="ship-sheet" transition:fade>
<Identification {...ship.general} />
<Weapons weapons={ship.weaponry.weapons} />
<div class="section-2">
<Hull structure={ship.structure} ship_mass={ship.general.mass} />
<Systems
firecons={ship.weaponry.firecons.nbr}
screens={ship.structure.screens}
/>
</div>
<MainSystems ftl={ship.ftl.type} engine={ship.engine.rating} />
</aside>
<script>
import _ from "lodash";
import Identification from "./Identification/index.svelte";
import MainSystems from "./MainSystems/index.svelte";
import Hull from "./Hull/index.svelte";
import Weapons from "./Weapons/index.svelte";
import Systems from "./Systems/index.svelte";
export let ship;
import { fly, fade } from "svelte/transition";
</script>
<style>
.ship-sheet {
width: 4.25in;
height: 5.5in;
border: 1px solid black;
padding: 1em;
}
.section-2 {
display: flex;
align-items: start;
}
</style>

View File

@ -0,0 +1,108 @@
export default {
ftl: {
mass: 0,
cost: 0,
type: "standard",
},
engine: {
mass: 40,
cost: 80,
rating: 6,
advanced: false,
},
general: {
ship_class: "Deviant",
name: "",
ship_type: "Battleship",
mass: 132,
used_mass: 131,
cost: 415,
},
weaponry: {
firecons: {
nbr: 5,
mass: 5,
cost: 20,
},
weapons: [
{
weapon_type: "submunition",
arcs: ["F"],
mass: 1,
cost: 3,
id: 1,
},
{
weapon_type: "beam",
weapon_class: "2",
arcs: ["A", "AS", "FS"],
mass: 2,
cost: 6,
id: 2,
},
{
weapon_type: "pds",
mass: 1,
cost: 3,
id: 3,
},
],
adfc: {
rating: 0,
cost: 0,
mass: 0,
},
},
structure: {
mass: 0,
cost: 0,
hull: {
rating: 22,
advanced: false,
cost: 44,
mass: 22,
},
screens: {
standard: 2,
advanced: 1,
cost: 105,
mass: 35,
},
armour: [
{
layer: 1,
rating: 7,
cost: 14,
mass: 14,
},
{
layer: 2,
rating: 2,
cost: 8,
mass: 4,
},
{
layer: 3,
rating: 0,
cost: 0,
mass: 0,
},
],
},
cargo: {
space: 7,
cost: 0,
mass: 7,
},
streamlining: {
type: "none",
cost: 0,
mass: 0,
},
carrier: {
bays: 0,
cost: 0,
mass: 0,
squadrons: [],
},
};

View File

@ -0,0 +1,13 @@
import Print from "./index.svelte";
import sample from "./sample";
export default {
title: "printouts",
};
export const basic = () => ({
Component: Print,
props: {
ship: sample,
},
});

View File

@ -0,0 +1,14 @@
<Section label="propulsion">
<Engine {...engine} on:change_engine />
<Ftl {...ftl} on:change_ftl />
</Section>
<script>
import Ftl from "../Ftl/index.svelte";
import Engine from "../Engine/index.svelte";
import Section from "../Section/index.svelte";
export let ftl = {};
export let engine = {};
</script>

View File

@ -7,7 +7,6 @@
<script> <script>
export let label; export let label;
</script> </script>
<style> <style>
@ -24,5 +23,4 @@
flex: 1; flex: 1;
background: #333; background: #333;
} }
</style> </style>

View File

@ -1,7 +1,11 @@
<div><slot /></div> <div class="ship-item">
<div><slot /></div>
<div class="mass" bind:this={mass_el}>{mass}</div> <div class="reqs">
<div class="cost" bind:this={cost_el}>{cost}</div> <div class="mass" bind:this={mass_el}>{mass}</div>
<div class="cost" bind:this={cost_el}>{cost}</div>
</div>
</div>
<script> <script>
import { tick } from "svelte"; import { tick } from "svelte";
@ -21,24 +25,19 @@
$: update_el(mass_el, mass); $: update_el(mass_el, mass);
$: update_el(cost_el, cost); $: update_el(cost_el, cost);
</script> </script>
<style> <style>
div { div {
margin-bottom: 1em; margin-bottom: 1em;
} }
.cost, .cost,
.mass { .mass {
padding: 0px 2em; width: 4em;
padding: 0px 0.5em;
text-align: right; text-align: right;
} }
.cost {
grid-column: 3;
}
.mass {
grid-column: 2;
}
.ship-item { .ship-item {
display: flex; display: flex;
@ -87,4 +86,12 @@
} }
} }
.ship-item {
display: flex;
}
.reqs {
flex: 0;
width: 10em;
display: flex;
}
</style> </style>

View File

@ -0,0 +1,49 @@
<div>
<Field
label="ship class"
value={general.ship_class}
on:change={change_class}
/>
<Field label="ship type">
<select value={ship_type} on:change={change_ship_type}>
{#each ship_types as type (type)}
<option>{type}</option>
{/each}
</select>
</Field>
</div>
<script>
import { getContext } from "svelte";
import Field from "$lib/components/Field/index.svelte";
import { candidate_ship_types } from "../../dux/ship_types";
export let ship = getContext("ship");
let general;
$: general = $ship.general;
const change_class = (event) =>
ship.dispatch(ship.actions.set_ship_class(event.target.value));
let ship_type;
$: ship_type = $ship.general.ship_type;
const change_ship_type = ({ target: { value } }) =>
ship.dispatch.set_ship_type(value);
let ship_types;
$: ship_types = candidate_ship_types(
$ship.general.mass,
$ship.carrier.bays > 0
).map(({ name }) => name);
</script>
<style>
div {
display: flex;
align-items: end;
gap: 2em;
}
</style>

View File

@ -5,8 +5,9 @@
on:change={change_tonnage} on:change={change_tonnage}
type="number" type="number"
min="10" min="10"
max="300" /> max="300"
<span class="mass_symbol"></span> />
<span class="mass_symbol" />
<div class="note" class:warning={!within_budget}> <div class="note" class:warning={!within_budget}>
{#if within_budget} {#if within_budget}
@ -18,7 +19,8 @@
<div class="cost"> <div class="cost">
<Field label="cost"> <Field label="cost">
<span class="cost">{$ship.general.cost}</span></Field> <span class="cost">{$ship.general.cost}</span></Field
>
</div> </div>
<script> <script>
@ -60,8 +62,10 @@
font-size: smaller; font-size: smaller;
} }
.mass,div.cost { .mass,
padding: 0px 2em; justify-self: right; div.cost {
padding: 0px 2em;
justify-self: right;
} }
.mass { .mass {
@ -71,7 +75,7 @@
grid-column: 3; grid-column: 3;
} }
span.cost:after { span.cost:after {
content: '\00A4'; content: "\00A4";
margin-left: 0.5em; margin-left: 0.5em;
} }
</style> </style>

View File

@ -1,16 +1,14 @@
<Identification /> <Identification />
<ShipCost />
<ShipCost />
<script> <script>
import Identification from "./Identification.svelte"; import Identification from "./Identification.svelte";
import ShipCost from "./ShipCost.svelte"; import ShipCost from "./ShipCost.svelte";
</script> </script>
<style> <style>
div { div {
background-color: red; background-color: red;
} }
</style> </style>

View File

@ -0,0 +1,32 @@
<svg width="{size}px" height="{size}px">
{#each all_arcs as arc (arc)}
<Arc
{arc}
radius={size / 2}
active={selected.includes(arc)}
on:click={() => click_arc(arc)}
/>
{/each}
<circle cx="50%" cy="50%" r={size / 3} />
<slot />
</svg>
<script>
import Arc from "../../Weapons/Arc.svelte";
import { createEventDispatcher } from "svelte";
const all_arcs = ["FS", "F", "FP", "AP", "A", "AS"];
export let selected = [];
export let size = 60;
const dispatch = createEventDispatcher();
const click_arc = (arc) => dispatch("click_arc", arc);
</script>
<style>
circle {
fill: white;
}
</style>

After

Width:  |  Height:  |  Size: 651 B

View File

@ -93,7 +93,6 @@
weapon_class, weapon_class,
arcs: cache.split(":"), arcs: cache.split(":"),
}); });
</script> </script>
<style> <style>
@ -102,5 +101,4 @@
flex-direction: column; flex-direction: column;
margin-right: 1em; margin-right: 1em;
} }
</style> </style>

View File

@ -0,0 +1,35 @@
<label> needle weapon</label>
<Arcs selected={arcs} on:click_arc={({ detail }) => click_arc(detail)} />
<script>
import { getContext } from "svelte";
import Arcs from "../Arcs/index.svelte";
import dux from "$lib/dux";
import { createEventDispatcher } from "svelte";
const all_arcs = ["FS", "F", "FP", "AP", "A", "AS"];
export let arcs = ["F"];
export let ship_change = getContext("ship_change") || (() => {});
const click_arc = (arc) => {
if (arcs[0] === arc) return;
arcs = [arc];
};
const dispatch = createEventDispatcher();
let cache;
$: cache = arcs.join(":");
$: dispatch("change", {
arcs: cache.split(":"),
});
</script>
<style>
.arc {
display: flex;
flex-direction: column;
margin-right: 1em;
}
</style>

View File

@ -1,2 +1 @@
<label>point defence system</label> <label>point defence system</label>

View File

@ -1,2 +1 @@
<label>scattergun</label> <label>scattergun</label>

View File

@ -0,0 +1,35 @@
<label> submunition pack</label>
<Arcs selected={arcs} on:click_arc={({ detail }) => click_arc(detail)} />
<script>
import { getContext } from "svelte";
import Arcs from "../Arcs/index.svelte";
import dux from "$lib/dux";
import { createEventDispatcher } from "svelte";
const all_arcs = ["FS", "F", "FP", "AP", "A", "AS"];
export let arcs = ["F"];
export let ship_change = getContext("ship_change") || (() => {});
const click_arc = (arc) => {
if (arcs[0] === arc) return;
arcs = [arc];
};
const dispatch = createEventDispatcher();
let cache;
$: cache = arcs.join(":");
$: dispatch("change", {
arcs: cache.split(":"),
});
</script>
<style>
.arc {
display: flex;
flex-direction: column;
margin-right: 1em;
}
</style>

View File

@ -56,7 +56,6 @@
...detail, ...detail,
}); });
}; };
</script> </script>
<style> <style>
@ -124,5 +123,4 @@
.weapon_row > :global(*) { .weapon_row > :global(*) {
margin-right: 2em; margin-right: 2em;
} }
</style> </style>

View File

@ -4,7 +4,6 @@
</Field> </Field>
</ShipItem> </ShipItem>
<script> <script>
import ShipItem from "../../ShipItem/index.svelte"; import ShipItem from "../../ShipItem/index.svelte";
import Field from "../../Field/index.svelte"; import Field from "../../Field/index.svelte";
@ -16,5 +15,5 @@
export let mass = 0; export let mass = 0;
export let ship = getContext("ship"); export let ship = getContext("ship");
$: ship?.dispatch_action('set_adfc',rating); $: ship?.dispatch_action("set_adfc", rating);
</script> </script>

View File

@ -23,5 +23,4 @@
export let ship = getContext("ship"); export let ship = getContext("ship");
const add_weapon = () => ship?.dispatch_action("add_weapon", weapon_type); const add_weapon = () => ship?.dispatch_action("add_weapon", weapon_type);
</script> </script>

View File

@ -0,0 +1,194 @@
<input
class="add-weapon button small blue"
type="button"
value="add"
on:click={add}
/>
<div class="weapon">
<select bind:value={weapon_type}>
<option>beam</option>
</select>
<select bind:value={weapon_class}>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
</select>
<select bind:value={nbr_arcs}>
{#each arc_options[weapon_class] as opt (opt)}
<option>{opt}</option>
{/each}
</select>
<svg width="60px" height="60px">
{#each arcs as arc (arc)}
<Arc
{arc}
radius={30}
active={selected_arc[arc]}
on:click={() => click_arc(arc)}
/>
{/each}
<circle cx="30" cy="30" r="15" />
</svg>
<div>{weapon.cost}</div>
<div>{weapon.mass}</div>
</div>
<script>
let weapon_type = "beam";
let weapon_class = 1;
let nbr_arcs = 6;
$: nbr_arcs = arc_options[weapon_class][0];
import Arc from "./Arc.svelte";
import { weapon_cost_mass } from "../../dux/weapons/rules.js";
const arcs = ["FS", "F", "FP", "AP", "A", "AS"];
import _ from "lodash";
import { createEventDispatcher } from "svelte";
let arc_options = {
1: [6],
2: [3, 6],
3: [1, 2, 3, 4, 5, 6, "broadside"],
4: [1, 2, 3, 4, 5, 6, "broadside"],
};
let selected_arc = Object.fromEntries(arcs.map((arc) => [arc, false]));
const nbr_selected_arcs = () =>
Object.values(selected_arc).filter((x) => x).length;
$: if (nbr_selected_arcs() !== nbr_arcs) {
if (nbr_arcs === "broadside") {
const new_arcs = {};
arcs.forEach((arc) => (new_arcs[arc] = true));
new_arcs.A = false;
new_arcs.F = false;
selected_arc = new_arcs;
} else {
let first_index = arcs.findIndex((arc) => selected_arc[arc]);
if (first_index === -1) first_index = 0;
const new_arcs = {};
arcs.forEach((arc) => (new_arcs[arc] = false));
_.range(nbr_arcs).forEach((i) => {
new_arcs[arcs[first_index]] = true;
first_index = (first_index + 1) % arcs.length;
});
selected_arc = new_arcs;
}
}
const click_arc = (first_arc) => {
if (nbr_arcs === "broadside") return;
let first_index = arcs.findIndex((arc) => arc === first_arc);
const new_arcs = {};
arcs.forEach((arc) => (new_arcs[arc] = false));
_.range(nbr_arcs).forEach((i) => {
console.log(first_index);
console.log(selected_arc);
new_arcs[arcs[first_index]] = true;
first_index = (first_index + 1) % arcs.length;
});
selected_arc = new_arcs;
};
let weapon = {};
$: weapon = {
weapon_type,
weapon_class,
arcs,
...weapon_cost_mass({
weapon_type,
weapon_class,
arcs: arcs.filter((arc) => selected_arc[arc]),
}),
};
const dispatch = createEventDispatcher();
const add = () => {
dispatch("add_weapon", weapon);
};
</script>
<style>
.weapon {
display: flex;
align-items: center;
}
.weapon > * {
margin-right: 2em;
}
.arcs {
display: grid;
grid-template-rows: 1fr 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr 1fr;
align-items: center;
justify-items: center;
width: 6em;
}
.arc input {
margin: 0px;
}
.arc.F {
grid-column: 2 / span 2;
grid-row: 1;
}
.arc.FS {
grid-column: 1;
grid-row: 1 / span 2;
}
.arc.FP {
grid-column: 4;
grid-row: 1 / span 2;
}
.arc.AS {
grid-column: 1;
grid-row: 3 / span 2;
}
.arc.AP {
grid-column: 4;
grid-row: 3 / span 2;
}
.arc.A {
grid-column: 2 / span 2;
grid-row: 4;
}
.arc {
display: flex;
flex-direction: column;
margin-right: 1em;
}
.add-weapon {
display: block;
}
circle {
fill: white;
}
</style>

Some files were not shown because too many files have changed in this diff Show More