Merge branch 'version-2'

main
Yanick Champoux 2022-04-02 19:34:41 -04:00
commit abb0ee3d9b
438 changed files with 3636 additions and 1767 deletions

6
.gitignore vendored
View File

@ -7,3 +7,9 @@ node_modules
.vercel_build_output/
package-lock.json
.svench
.DS_STORE
.cache
.temp
node_modules/
dist/yarn-error.log

View File

@ -1,12 +1,47 @@
const path = require('path');
const path = require("path");
const preprocess = require("svelte-preprocess");
module.exports = {
addons: ['@storybook/addon-actions/register'],
stories: [ '../src/**/*stories.js' ],
webpackFinal: (config) => {
config.resolve.alias['~'] = path.resolve(__dirname, '../src/');
config.resolve.alias['~C'] = path.resolve(__dirname, '../src/components/');
core: { builder: "storybook-builder-vite" },
staticDirs: ["../static", "../pictures"],
stories: [
"../src/**/*.stories.mdx",
"../src/**/*.stories.svelte",
"../src/**/stories.svelte",
],
addons: [
"@storybook/addon-essentials",
"@storybook/addon-svelte-csf",
],
framework: "@storybook/svelte",
svelteOptions: {
preprocess: preprocess(),
},
async viteFinal(config, { configType }) {
return config;
}
if(!config.resolve.alias) config.resolve.alias = {};
// customize the Vite config here
config.resolve.alias.$lib = path.resolve(__dirname, "../src/lib/");
config.resolve.alias.$app = path.resolve(__dirname, "../fake/app/");
config.resolve.dedupe = ["@storybook/client-api"];
// return the customized config
return config;
},
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/"),
"\\$app/env": path.resolve(__dirname, "../fake/app/env.js"),
},
},
};
},
};

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$/,
},
},
};

6
.vitebook/App.svelte Normal file
View File

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

31
.vitebook/config.js Normal file
View File

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

18
.vitebook/index.html Normal file
View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="{{ lang }}">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="generator" content="vitebook@{{ version }}" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/manifest.json" />
<!--@vitebook/head-->
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="app"><!--@vitebook/app--></div>
<!--@vitebook/body-->
</body>
</html>

5
.vitebook/public/404.svg Normal file
View File

@ -0,0 +1,5 @@
<svg width="400" height="163" viewBox="0 0 400 163" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M192.333 4.56055C159.748 4.56055 136.352 25.4677 136.352 74.2511C136.352 129.539 159.748 144.174 192.333 144.174C224.918 144.174 249.708 128.145 249.708 74.2511C249.708 16.6402 224.918 4.56055 192.333 4.56055ZM192.547 125.822C169.978 125.822 158.187 112.58 158.187 74.2868C158.187 40.4983 170.46 22.6801 193.03 22.6801C215.599 22.6801 227.873 34.3842 227.873 74.2868C227.873 111.615 215.117 125.822 192.547 125.822Z" fill="currentColor"/>
<path d="M116.842 105.612H103.369V65.936C103.369 63.8286 102.532 61.8076 101.042 60.3175C99.5523 58.8273 97.5314 57.9902 95.4241 57.9902H92.2672C91.2238 57.9902 90.1906 58.1957 89.2266 58.595C88.2627 58.9943 87.3868 59.5796 86.649 60.3175C85.9112 61.0553 85.3259 61.9312 84.9266 62.8953C84.5274 63.8593 84.3218 64.8925 84.3218 65.936V105.612H46.3199C45.5381 105.612 44.7696 105.41 44.0889 105.025C43.4083 104.641 42.8385 104.087 42.435 103.417C42.0315 102.748 41.8079 101.985 41.7859 101.204C41.7639 100.422 41.9442 99.6481 42.3094 98.9568L82.5303 22.8271C83.0317 21.878 83.3356 20.837 83.4237 19.7672C83.5118 18.6973 83.3822 17.6207 83.0427 16.6023C82.7032 15.5839 82.161 14.6449 81.4486 13.8419C80.7362 13.0389 79.8685 12.3886 78.8979 11.9302L76.5235 10.8089C74.6799 9.93827 72.5722 9.80971 70.6364 10.4498C68.7006 11.09 67.085 12.4498 66.1238 14.248L17.0692 106.019C16.2339 107.582 15.7969 109.327 15.7969 111.099V111.099C15.7969 112.514 16.0755 113.915 16.617 115.222C17.1584 116.529 17.952 117.717 18.9524 118.717C19.9529 119.718 21.1406 120.512 22.4477 121.053C23.7548 121.594 25.1558 121.873 26.5706 121.873H84.3218V146.962C84.3218 148.212 84.5682 149.451 85.0468 150.607C85.5254 151.762 86.2269 152.812 87.1113 153.696C87.9957 154.581 89.0456 155.282 90.201 155.761C91.3565 156.24 92.595 156.486 93.8456 156.486H93.8457C95.0963 156.486 96.3348 156.24 97.4903 155.761C98.6457 155.282 99.6956 154.581 100.58 153.696C101.464 152.812 102.166 151.762 102.644 150.607C103.123 149.451 103.369 148.212 103.369 146.962V121.873H116.842C118.998 121.873 121.066 121.017 122.591 119.492C124.116 117.967 124.972 115.899 124.972 113.743V113.743C124.972 111.586 124.116 109.518 122.591 107.993C121.066 106.469 118.998 105.612 116.842 105.612V105.612Z" fill="currentColor"/>
<path d="M366.319 105.612H352.846V65.936C352.846 63.8286 352.009 61.8076 350.519 60.3175C349.029 58.8273 347.008 57.9902 344.901 57.9902H341.744C340.7 57.9902 339.667 58.1957 338.703 58.595C337.739 58.9943 336.863 59.5796 336.126 60.3175C335.388 61.0553 334.802 61.9312 334.403 62.8953C334.004 63.8593 333.798 64.8925 333.798 65.936V105.612H295.796C295.015 105.612 294.246 105.41 293.565 105.025C292.885 104.641 292.315 104.087 291.912 103.417C291.508 102.748 291.284 101.985 291.262 101.204C291.24 100.422 291.421 99.6481 291.786 98.9568L332.007 22.8271C332.508 21.878 332.812 20.8371 332.9 19.7672C332.988 18.6973 332.859 17.6207 332.519 16.6023C332.18 15.5839 331.638 14.6449 330.925 13.8419C330.213 13.0389 329.345 12.3886 328.374 11.9302L326 10.8089C324.156 9.93827 322.049 9.80971 320.113 10.4498C318.177 11.09 316.562 12.4498 315.6 14.248L266.546 106.019C265.71 107.582 265.273 109.327 265.273 111.099V111.099C265.273 113.956 266.409 116.697 268.429 118.717C270.449 120.738 273.19 121.873 276.047 121.873H333.798V146.962C333.798 149.488 334.802 151.91 336.588 153.696C338.374 155.483 340.796 156.486 343.322 156.486V156.486C345.848 156.486 348.27 155.483 350.057 153.696C351.843 151.91 352.846 149.488 352.846 146.962V121.873H366.319C368.475 121.873 370.543 121.017 372.068 119.492C373.592 117.967 374.449 115.899 374.449 113.743V113.743C374.449 111.586 373.592 109.518 372.068 107.993C370.543 106.469 368.475 105.612 366.319 105.612Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M39.7265 4.50427L39.7265 33.3318H12.3391L28.8049 4.50427H39.7265Z" fill="white" stroke="black" stroke-width="3" stroke-linejoin="bevel"/>
<path d="M5.6571 22.6991L17.0572 4.50427H32.2324L25.249 15.1287L38.8616 15.1287L5.6571 44.8828L15.3886 22.6987L5.6571 22.6991Z" fill="currentColor" stroke="black" stroke-width="3" stroke-linejoin="bevel"/>
</svg>

After

Width:  |  Height:  |  Size: 456 B

View File

@ -0,0 +1,20 @@
{
"short_name": "",
"name": " Site",
"icons": [
{
"src": "logo-192x192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo-512x512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#ffc107",
"background_color": "#212121"
}

View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

3
.vitebook/theme/index.js Normal file
View File

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

View File

@ -0,0 +1,5 @@
---
message: |
hygen {bold generator new} --name [NAME] --action [ACTION]
hygen {bold generator with-prompt} --name [NAME] --action [ACTION]
---

View File

@ -0,0 +1,18 @@
---
to: _templates/<%= name %>/<%= action || 'new' %>/hello.ejs.t
---
---
to: app/hello.js
---
const hello = ```
Hello!
This is your first hygen template.
Learn what it can do here:
https://github.com/jondot/hygen
```
console.log(hello)

View File

@ -0,0 +1,18 @@
---
to: _templates/<%= name %>/<%= action || 'new' %>/hello.ejs.t
---
---
to: app/hello.js
---
const hello = ```
Hello!
This is your first prompt based hygen template.
Learn what it can do here:
https://github.com/jondot/hygen
```
console.log(hello)

View File

@ -0,0 +1,14 @@
---
to: _templates/<%= name %>/<%= action || 'new' %>/prompt.js
---
// see types of prompts:
// https://github.com/enquirer/enquirer/tree/master/examples
//
module.exports = [
{
type: 'input',
name: 'message',
message: "What's your message?"
}
]

3
_templates/package.json Normal file
View File

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

View File

@ -0,0 +1,13 @@
const path = require('path');
module.exports = {
params: ({ args }) => {
const storypath = args.path.replace( '.svelte', '.stories.svelte' );
const component = path.basename(args.path.replace('/index.svelte','')).replace('.svelte','');
const to = path.join(path.dirname(args.path), component + '.stories.svelte' );
return { storypath, component, to}
}
}

View File

@ -0,0 +1,21 @@
---
to: <%= to %>
---
<Meta title="<%= component %>" component={<%= component %>} argTypes={{}} />
<Story name="Primary" args={{}} />
<Template let:args>
<div style="width: 50em">
<<%= component %> />
</div>
</Template>
<script>
import { Meta, Template, Story } from "@storybook/addon-svelte-csf";
import { action } from "@storybook/addon-actions";
import <%= component %> from './<%=
h.path.basename(path)
%>';
</script>

1
fake/app/env.js Normal file
View File

@ -0,0 +1 @@
export const browser = true;

View File

@ -6,32 +6,44 @@
"build": "svelte-kit build",
"preview": "svelte-kit preview",
"lint": "prettier --check . && eslint --ignore-path .gitignore .",
"format": "prettier --write ."
"format": "prettier --write .",
"storybook": "storybook-server",
"vitebook:dev": "vitebook dev",
"vitebook:build": "vitebook build",
"vitebook:preview": "vitebook preview"
},
"devDependencies": {
"@sveltejs/kit": "^1.0.0-next.115",
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.10",
"eslint": "^7.22.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-svelte3": "^3.2.0",
"prettier": "~2.2.1",
"prettier-plugin-svelte": "^2.2.0",
"svelte": "^3.34.0",
"svench": "^0.2.0-14",
"vite": "^2.3.2"
"@sveltejs/adapter-static": "^1.0.0-next.28",
"@sveltejs/kit": "^1.0.0-next.288",
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.38",
"@vitebook/client": "^0.23.2",
"@vitebook/core": "^0.23.2",
"@vitebook/theme-default": "^0.23.2",
"eslint": "^8.10.0",
"eslint-config-prettier": "^8.4.0",
"eslint-plugin-svelte3": "^3.4.1",
"prettier": "~2.5.1",
"prettier-plugin-svelte": "^2.6.0",
"storybook-builder-vite": "0.1.21",
"svelte": "^3.46.4",
"vite": "^2.7.0"
},
"type": "module",
"dependencies": {
"@sveltejs/adapter-node": "^1.0.0-next.18",
"@sveltejs/adapter-static": "^1.0.0-next.13",
"@yanick/updeep": "link:/home/yanick/work/javascript/updeep",
"bulma": "^0.9.2",
"@storybook/addon-essentials": "^6.4.19",
"@storybook/addon-svelte-csf": "^1.1.0",
"@storybook/svelte": "^6.4.19",
"@sveltejs/adapter-node": "^1.0.0-next.0",
"chota": "^0.8.0",
"lodash": "^4.17.21",
"redux": "^4.1.0",
"reselect": "^4.0.0",
"redux": "^4.1.2",
"reselect": "^4.1.5",
"rollup-plugin-analyzer": "^4.0.0",
"svelte-chota": "^1.8.6",
"svelte-knobby": "^0.3.4",
"ts-action": "^11.0.0",
"updux": "link:/home/yanick/work/javascript/updux/"
"updux": "link:/home/yanick/work/javascript/updux-js/",
"webpack": "5"
},
"prettier": {
"svelteSortOrder": "options-markup-scripts-styles",

View File

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

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

View File

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

View File

@ -1,72 +0,0 @@
<div>
{#each rows as row,i (i)}
<div class="row">
{#each row as item,j (j)}
<div class="cell">
{#if item}
<img src="icons/crew-star.svg" alt="crew loss threshold" />
{/if}
</div>
{/each}
</div>
{/each}
</div>
<script>
import { ceil } from '$lib/dux/utils';
export let ship_mass = 0;
export let rating = 0;
export let advanced = false;
let nbr_rows;
$: nbr_rows = advanced ? 3 : 4;
let cells;
$: cells = Array(rating).fill(false);
let dcp;
$: dcp= ceil(ship_mass/20);
$: cells = divide(cells, dcp).map(
g => {
g[g.length-1] = true;
return g;
}
).flat();
function divide(list, divider) {
if( divider <= 1 ) return [ list ];
let div = list.length / divider;
const mod = list.length % divider;
if(mod) div++;
return [
list.slice(0,div),
...divide( list.slice(div), divider-1 )
]
}
let rows = [];
$: rows = divide( cells, nbr_rows );
</script>
<style>
.row {
}
.cell {
display: inline-block;
margin-right: 0.5em;
width: 1em;
height: 1em;
border: 1px solid black;
}
img {
width: 1em;
}
</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,44 +0,0 @@
<h1>ship name: <div class="fill"></div> </h1>
<div class="details">
<h2>{ship_class}-class, {ship_type}</h2>
<div class="reqs">
<Cost {cost} />
&nbsp;
<Mass {mass} />
</div>
</div>
<script>
import Cost from '../../../Cost.svelte';
import Mass from '$lib/components/Mass.svelte';
export let ship_class;
export let ship_type;
export let cost, mass = ( 0, 0 );
</script>
<style>
h1 {
width: 100%;
display: flex;
font-size: var(--font-scale-8);
}
h2 {
font-size: var(--font-scale-7);
flex: 1;
}
.fill {
margin-left: 0.5em;
display: inline-block;
flex: 1;
border-bottom: 1px solid black;
}
.details {
display: flex;
align-items: baseline;
}
.reqs {
display: flex;
}
</style>

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,
});

View File

@ -5,15 +5,15 @@ import { createSelector } from "reselect";
import ftl from "./ftl";
import engine, { calc_drive_reqs } from "./engine";
import weaponry from './weaponry';
import weaponry from "./weaponry";
import { calc_ftl_reqs } from "./ftl/rules";
import { calc_ship_req } from "./utils";
import { candidate_ship_types } from './ship_types';
import structure from './structure';
import cargo from './cargo';
import streamlining from './streamlining';
import carrier from './carrier';
import { ceil } from './utils';
import { candidate_ship_types } from "./ship_types";
import structure from "./structure";
import cargo from "./cargo";
import streamlining from "./streamlining";
import carrier from "./carrier";
import { ceil } from "./utils";
const set_ship_mass = action("set_ship_mass", payload());
const set_name = action("set_name", payload());
@ -21,31 +21,29 @@ const set_name = action("set_name", payload());
const set_ship_reqs = action("set_ship_reqs", payload());
const set_hull = action("set_hull", payload());
const set_ship_type = action('set_ship_type',payload());
const set_ship_type = action("set_ship_type", payload());
const reset = action('reset' );
const reset = action("reset");
const initial = {
general: {
ship_class: "",
name: "",
ship_type: "",
mass: 10,
used_mass: 0,
cost: 10,
},
};
console.log(Updux);
general: {
ship_class: "",
name: "",
ship_type: "",
mass: 10,
used_mass: 0,
cost: 10,
},
};
const dux = new Updux({
subduxes: { ftl, engine, weaponry, structure, cargo, streamlining, carrier },
initial
initial,
});
dux.addMutation( reset, () => () => initial );
dux.addMutation(reset, () => () => initial);
dux.addMutation(set_hull, ({rating}) => (state) => {
dux.addMutation(set_hull, ({ rating }) => (state) => {
return u.updateIn("structure.hull", {
cost: 2 * rating,
rating,
@ -55,8 +53,8 @@ dux.addMutation(set_hull, ({rating}) => (state) => {
dux.addMutation(set_ship_mass, (mass) => u.updateIn("general", { mass }));
dux.addMutation(set_name, (name) => u.updateIn("general", { name }));
dux.addMutation( action('set_ship_class',payload() ),
ship_class => u.updateIn('general',{ship_class})
dux.addMutation(action("set_ship_class", payload()), (ship_class) =>
u.updateIn("general", { ship_class })
);
dux.addMutation(set_ship_reqs, ({ cost, mass: used_mass }) =>
@ -68,73 +66,54 @@ dux.addSubscription((store) =>
createSelector(calc_ship_req, (reqs) => store.dispatch(set_ship_reqs(reqs)))
);
dux.addSubscription((store) =>
createSelector(
store => store.general.mass,
store => store.streamlining.type,
(ship_mass, streamlining ) => {
const mass = ceil( ship_mass * (
streamlining === 'none' ? 0
: streamlining === 'partial' ? 5 : 10
) / 100 );
const cost = 2 * mass;
(store) => store.general.mass,
(store) => store.general.ship_type,
(store) => store.carrier.bays,
(mass, type, bays) => {
console.log({ bays });
const candidates = candidate_ship_types(mass, bays > 0);
store.dispatch( dux.actions.set_streamlining_cost_mass({cost,mass}) );
}
console.log({ candidates });
if (candidates.length === 0) return;
if (candidates.find(({ name }) => name === type)) return;
store.dispatch(store.actions.set_ship_type(candidates[0].name));
}
)
);
dux.addSubscription((store) =>
createSelector(
store => store.general.mass,
store => store.general.ship_type,
store => store.carrier.bays,
(mass,type,bays) => {
console.log({bays});
const candidates = candidate_ship_types(mass,bays > 0);
console.log({candidates});
if( candidates.length === 0 ) return;
if( candidates.find( ({name}) => name === type ) ) return;
store.dispatch(
store.actions.set_ship_type(
candidates[0].name
)
)
}
)
);
dux.addMutation(set_ship_type, type => u.updateIn('general.ship_type',type) );
dux.addMutation(set_ship_type, (type) => u.updateIn("general.ship_type", type));
dux.addSubscription((store) =>
createSelector(
[(ship) => ship.general.mass, (ship) => ship.ftl.type],
(ship_mass, type) =>
store.dispatch(ftl.actions.set_ftl_reqs(calc_ftl_reqs(type,ship_mass)))
store.dispatch(ftl.actions.set_ftl_reqs(calc_ftl_reqs(type, ship_mass)))
)
);
dux.addSubscription( store => createSelector(
ship => ship.general.mass,
ship => ship.structure.screens.standard,
ship => ship.structure.screens.advanced,
(mass,standard,advanced) => {
console.log({
mass, standard, advanced
})
const standard_mass = standard * Math.max(3,ceil( 0.05 * mass ));
const advanced_mass = advanced * Math.max(4,ceil( 0.075 * mass ));
dux.addSubscription((store) =>
createSelector(
(ship) => ship.general.mass,
(ship) => ship.structure.screens.standard,
(ship) => ship.structure.screens.advanced,
(mass, standard, advanced) => {
const standard_mass = standard * Math.max(3, ceil(0.05 * mass));
const advanced_mass = advanced * Math.max(4, ceil(0.075 * mass));
store.dispatch( dux.actions.set_screens_reqs({
mass: standard_mass + advanced_mass,
cost: 3 * standard_mass + 4 * advanced_mass
}));
store.dispatch(
dux.actions.set_screens_reqs({
mass: standard_mass + advanced_mass,
cost: 3 * standard_mass + 4 * advanced_mass,
})
);
}
));
)
);
dux.addSubscription((store) =>
createSelector(
@ -150,18 +129,6 @@ dux.addSubscription((store) =>
)
);
const calc_firecons_reqs = (nbr) => ({
cost: 4 * nbr,
mass: nbr,
});
const set_firecons = action("set_firecons", payload());
dux.addMutation(set_firecons, (nbr) =>
u.updateIn("weaponry.firecons", {
nbr,
...calc_firecons_reqs(nbr),
})
);
export default dux.asDux;

View File

@ -1,18 +0,0 @@
import Updux from "updux";
import { action, payload } from "ts-action";
import u from "@yanick/updeep";
import { createSelector } from "reselect";
const dux = new Updux({
initial: {
standard: 0, advanced: 0, cost: 0, mass: 0,
}
});
const set_screens = action('set_screens', payload() );
dux.addMutation(set_screens, payload => u.update(payload) );
const set_screens_reqs = action('set_screens_reqs', payload() );
dux.addMutation(set_screens_reqs, payload => u.update(payload) );
export default dux.asDux;

View File

@ -1,17 +0,0 @@
import Updux from "updux";
import { action, payload } from "ts-action";
import u from "@yanick/updeep";
const dux = new Updux({
initial: {
rating: 0,
cost: 0,
mass: 0,
},
})
dux.addMutation( action( 'set_adfc', payload() ), rating =>
u.update({ rating, mass: 2 * rating, cost: 8 * rating })
);
export default dux.asDux;

View File

@ -1,74 +0,0 @@
export function weapon_cost_mass(weapon){
let cost = 0;
let mass = 0;
if( weapon.weapon_type === 'beam' ) {
return beam_cost_mass(weapon);
}
if( weapon.weapon_type == 'submunition' ) {
return { mass: 1, cost: 3 };
}
if( weapon.weapon_type === 'pds' ) {
return { mass: 1, cost: 3 };
}
if( weapon.weapon_type === 'scattergun' ) {
return { mass: 1, cost: 4 };
}
if( weapon.weapon_type === 'needle' ) {
return { mass: 2, cost: 6 };
}
return { cost, mass };
}
const is_broadside = arcs => {
if( arcs.length !== 4 ) return false;
// that'd be A or F
return !arcs.some( a => a.length === 1 );
}
function beam_cost_mass({weapon_class, arcs}) {
let mass;
if( weapon_class === 1 ) {
mass = 1;
}
if( weapon_class == 2 ) {
mass = 2 + (arcs.length > 3 ? 1 : 0);
}
if( weapon_class == 3 ) {
mass = 4;
if( is_broadside(arcs) ) {
mass += 2;
}
else {
mass += arcs.length - 1;
}
}
if( weapon_class == 4 ) {
mass = 8;
if( is_broadside(arcs) ) {
mass += 4;
}
else {
mass += 2*(arcs.length - 1);
}
}
return {
mass, cost: 3 * mass
}
}

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,13 +1,5 @@
<aside transition:fly={{ x: -800, opacity: 1 }}>
<div>
<input
type="button"
class="button small red"
value="close"
on:click={close}
/>
</div>
<h3>welcome to the docks</h3>
<Card>
<h2 slot="header">Welcome to the docks</h2>
<p>
This app is a ship builder for the game
@ -16,7 +8,7 @@
</p>
<p>
The app, beside any exception mentioned here, is trying to follow the
The contruction rules are following the
<a href="http://members.ozemail.com.au/~laranzu/fullthrust/rules/">
Cross Dimensions rules
</a>
@ -24,9 +16,9 @@
</p>
<p>
The app is mostly developed for Firefox. I also check as much as I can
that I don't mess things too badly on Chrome. For the other browsers...
caveat emptor.
The app is mostly developed for Firefox. I also check as much as I can that
I don't mess things too badly on Chrome. For the other browsers... caveat
emptor.
</p>
<p>
@ -35,17 +27,22 @@
. Code available on
<a href="https://github.com/aotds/aotds-shipyard">Github</a>
</p>
</aside>
<div slot="footer" />
</Card>
<script>
import { fly } from "svelte/transition";
import { createEventDispatcher } from "svelte";
import {
Modal, Card
} from 'svelte-chota';
const dispatch = createEventDispatcher();
const close = () => dispatch("close");
</script>
<style>
h2 {
margin-bottom: 1em;
font-size: var(--font-scale-14);
font-family: Faktos;
}
aside {
background-color: rgb(254, 218, 184);
position: absolute;
@ -68,7 +65,7 @@
text-align: center;
margin: 0px;
}
div {
text-align: right;
}
p {
margin-left: 2em;
}
</style>

View File

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

View File

@ -0,0 +1,53 @@
<svelte:head>
<meta name="viewport" content="width=960" />
</svelte:head>
<main>
<Ribbon />
<Header on:changeTab={({ detail }) => activeTab = detail}/>
<div class:hide={activeTab !== 'editor'}>
<ShipEdit />
</div>
<div class:hide={activeTab !== 'json'}>
<JsonOutput />
</div>
<div class:hide={activeTab !== 'print'}>
<PrintOutput ship={$state}/>
</div>
</main>
<script>
import { getContext } from 'svelte';
import { Modal, Card, Nav } from "svelte-chota";
import Ribbon from "./Ribbon.svelte";
import Header from "./Header.svelte";
import ShipEdit from "./ShipEdit/index.svelte";
import About from "./About.svelte";
import JsonOutput from './Output/Json.svelte';
import PrintOutput from './Output/Print/index.svelte';
let activeTab = 'editor';
$: console.log(activeTab);
const {state} = getContext('ship');
</script>
<style>
.nav {
width: var(--main-width);
margin-left: auto;
margin-right: auto;
}
.hide {
display: none;
}
main {
width: var(--main-width);
margin-right: auto;
margin-left: auto;
}
</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,3 +1,4 @@
<!--
<Section label="carrier">
<ShipItem {cost} {mass}>
<Field label="bays">
@ -9,15 +10,18 @@
<Squadron {...squad} />
{/each}
</Section>
-->
Hey!
<script>
import { getContext } from "svelte";
import Section from "../Section/index.svelte";
import Field from "../Field/index.svelte";
import ShipItem from "../ShipItem/index.svelte";
import Squadron from "./Squadron/index.svelte";
import dux from "../../dux/carrier";
/* import Section from "../Section/index.svelte"; */
/* import Field from "../Field/index.svelte"; */
/* import ShipItem from "../ShipItem/index.svelte"; */
// import Squadron from "./Squadron/index.svelte";
// import dux from "../../dux/carrier";
export let bays = 0;
export let squadrons = [];
@ -26,6 +30,5 @@
export let ship = getContext("ship");
$: ship?.dispatch_action('set_carrier_bays',bays);
$: ship?.dispatch_action("set_carrier_bays", bays);
</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>
<style>
div:after { content: '\00A4'; margin-left: 0.5em; }
div:after {
content: "\00A4";
margin-left: 0.5em;
}
</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

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

View File

@ -0,0 +1,32 @@
<div>
{#if label}
<label>{label}</label>
{/if}
<slot>
<input type="text" {placeholder} bind: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} />
{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,76 @@
<header>
<h1>The Docks</h1>
<h2>
a <a href="https://shop.groundzerogames.co.uk/rules.html"
>Full Thrust</a
> ship builder
</h2>
<a on:click|preventDefault={() => (showAbout = true)}>about the app</a>
</header>
<div>
<Tabs bind:active={activeTab}>
<Tab tabid="editor">editor</Tab>
<Tab tabid="json">json view</Tab>
<Tab tabid="print">print view</Tab>
</Tabs>
</div>
<Modal bind:open={showAbout}>
<About />
</Modal>
<script>
import { createEventDispatcher } from 'svelte';
import { Modal, Card, Nav, Tab, Tabs } from "svelte-chota";
import About from "./About.svelte";
let showAbout = false;
let activeTab = 'editor';
const dispatch = createEventDispatcher();
$: dispatch( 'changeTab', activeTab );
</script>
<style>
header {
display: flex;
align-items: baseline;
width: var(--main-width);
margin-left: auto;
margin-right: auto;
margin-bottom: 1em;
}
h1,
h2 {
text-align: left;
font-family: Faktos;
padding: 0px;
margin: 0px;
}
h1 {
font-size: var(--font-scale-15);
}
h2 {
flex: 1;
padding-left: 1em;
font-size: var(--font-scale-13);
}
header > a {
margin: 0px 2em;
font-size: var(--font-scale-10);
}
div :global(nav) {
margin-left: 2em;
margin-bottom: 2em;
}
div :global(nav span) {
font-weight: bold;
font-family: var(--main-font-family);
font-size: var(--font-scale-12);
margin-right: 1em;
padding-bottom: 0.125em !important;
}
</style>

View File

@ -0,0 +1,49 @@
<div>
<button class="button primary" on:click={handleSave}>save</button>
<pre><code>{json}</code></pre>
</div>
<a style:display="none" {href} {download} bind:this={fileDownload} />
<script>
export let ship = {};
let json;
$: json = JSON.stringify(ship, null, 2);
import { fly, fade } from "svelte/transition";
import { createEventDispatcher, getContext } from "svelte";
const { state } = getContext("ship");
$: json = JSON.stringify($state, null, 2);
const dispatch = createEventDispatcher();
const close = () => dispatch("close");
$: href = "data:text/plain;charset=utf-8," + encodeURIComponent(json);
$: download = ($state?.identification?.shipClass ?? "ship") + ".json";
let fileDownload;
function handleSave() {
fileDownload?.click();
}
</script>
<style>
div {
position: relative;
}
button {
position: absolute;
right: 2em;
top: 1em;
}
pre {
font-family: monospace;
font-size: var(--font-scale-10);
overflow: scroll;
height: 90%;
}
</style>

View File

@ -0,0 +1,32 @@
<div class="layers">
{#each armor as layer,i (i)}
<div class="layer">
{#each Array.from({length: layer}) as j}
<div class="cell" />
{/each}
</div>
{/each}
</div>
<script>
import _ from "lodash";
export let armor = [];
console.log(armor)
</script>
<style>
.cell {
display: inline-block;
margin-right: 0.5em;
width: 1em;
height: 1em;
border: 1px solid black;
border-radius: 0.5em;
}
.layers {
display: flex;
flex-direction: column-reverse;
}
</style>

View File

@ -0,0 +1,22 @@
<Meta title="Output/Print/Structure/Armor" component={Armor} argTypes={{
armor: {
type: 'object',
defaultValue: [2,4],
}
}} />
<Story name="Primary" args={{}} />
<Template let:args>
<div style="width: 50em">
<Armor {...args} />
</div>
</Template>
<script>
import { Meta, Template, Story } from "@storybook/addon-svelte-csf";
import Armor from './Armor.svelte';
</script>

View File

@ -0,0 +1,20 @@
<Meta title="Output/Print/Structure/Integrity" component={Integrity} argTypes={{
shipMass: {defaultValue:50},
rating: {defaultValue:14},
advanced: {defaultValue:false},
}} />
<Story name="Primary" args={{}} />
<Story name="Advanced" args={{ advanced: true }} />
<Template let:args>
<div style="width: 50em">
<Integrity {...args} />
</div>
</Template>
<script>
import { Meta, Template, Story } from "@storybook/addon-svelte-csf";
import Integrity from './index.svelte';
</script>

View File

@ -0,0 +1,66 @@
<div>
{#each rows as row, i (i)}
<div class="row">
{#each row as threshold, j (j)}
<div class="cell">
{#if threshold}
<img src="icons/crew-star.svg" alt="crew loss threshold" />
{/if}
</div>
{/each}
</div>
{/each}
</div>
<script>
export let shipMass = 0;
export let rating = 0;
export let advanced = false;
let nbr_rows;
$: nbr_rows = advanced ? 3 : 4;
let cells;
$: cells = Array(rating).fill(false);
let dcp;
$: dcp = Math.ceil(shipMass / 20);
$: cells = divide(cells, dcp)
.map((g) => {
g[g.length - 1] = true;
return g;
})
.flat();
function divide(list, divider) {
if (divider <= 1) return [list];
let div = list.length / divider;
const mod = list.length % divider;
if (mod) div++;
return [list.slice(0, div), ...divide(list.slice(div), divider - 1)];
}
let rows = [];
$: rows = divide(cells, nbr_rows);
</script>
<style>
.row {
margin-bottom: 0.5em;
}
.cell {
display: inline-block;
margin-right: 0.5em;
width: 1.5em;
height: 1.5em;
border: 1px solid black;
}
img {
width: 1em;
margin-left: 0.2em;
}
</style>

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>
<script>
import Integrity from './Integrity/index.svelte';
import Armour from './Armour/index.svelte';
import Integrity from "./Integrity/index.svelte";
import Armour from "./Armor.svelte";
export let structure = {};
export let ship_mass = 0;

View File

@ -0,0 +1,51 @@
<h1>
ship name: <div class="fill" />
</h1>
<div class="details">
<h2>
{#if shipClass}
{shipClass}-class,
{/if}
{shipType}</h2>
<div class="reqs">
<Cost cost={cost} />
&nbsp;
<Mass mass={mass} />
</div>
</div>
<script>
import Cost from "../../../Cost.svelte";
import Mass from "$lib/components/Mass.svelte";
export let shipClass;
export let shipType;
export let cost = 0;
export let mass = 0;
</script>
<style>
h1 {
width: 100%;
display: flex;
font-size: var(--font-scale-8);
}
h2 {
font-size: var(--font-scale-7);
flex: 1;
}
.fill {
margin-left: 0.5em;
display: inline-block;
flex: 1;
border-bottom: 1px solid black;
}
.details {
display: flex;
align-items: baseline;
}
.reqs {
display: flex;
}
</style>

View File

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

@ -0,0 +1,23 @@
<Meta title="Output/Print" component={Print} argTypes={{
ship: {
type: 'object',
defaultValue: sample
}
}} />
<Story name="Primary" args={{}} />
<Template let:args>
<div style="width: 50em">
<Print ship={sample}/>
</div>
</Template>
<script>
import { Meta, Template, Story } from "@storybook/addon-svelte-csf";
import { action } from "@storybook/addon-actions";
import sample from './sample.js';
import Print from './index.svelte';
</script>

View File

@ -0,0 +1,17 @@
<Meta title="Output/Print/Systems/Firecons" component={Firecons} argTypes={{
firecons: { defaultValue: 2, type: 'number' }
}} />
<Story name="Primary" args={{}} />
<Template let:args>
<div style="width: 50em">
<Firecons {...args}/>
</div>
</Template>
<script>
import { Meta, Template, Story } from "@storybook/addon-svelte-csf";
import Firecons from './index.svelte';
</script>

View File

@ -1,11 +1,11 @@
<div>
{#each _.range(firecons) as firecon}
{#each range(1,firecons) as firecon}
<img class="firecon" src="icons/firecon.svg" alt="firecon" />
{/each}
</div>
<script>
import _ from "lodash";
import {range} from "$lib/utils.js";
export let firecons = 0;
</script>

View File

@ -0,0 +1,18 @@
<Meta title="Output/Print/Systems/Screens" component={Screens} argTypes={{
standard: { defaultValue: 3 },
advanced: { defaultValue: 3 },
}} />
<Story name="Primary" args={{}} />
<Template let:args>
<div style="width: 50em">
<Screens {...args}/>
</div>
</Template>
<script>
import { Meta, Template, Story } from "@storybook/addon-svelte-csf";
import Screens from './index.svelte';
</script>

View File

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

View File

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

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