Merge branch 'cumulative-cost' into version-2

This commit is contained in:
Yanick Champoux 2022-03-14 12:37:25 -04:00
commit e2560d6e16
42 changed files with 1025 additions and 197 deletions

View File

@ -2,6 +2,7 @@ const path = require("path");
const preprocess = require("svelte-preprocess"); const preprocess = require("svelte-preprocess");
module.exports = { module.exports = {
// core: { builder: "storybook-builder-vite" },
staticDirs: ["../static", "../pictures"], staticDirs: ["../static", "../pictures"],
stories: [ stories: [
"../src/**/*.stories.mdx", "../src/**/*.stories.mdx",
@ -9,13 +10,20 @@ module.exports = {
"../src/**/stories.svelte", "../src/**/stories.svelte",
], ],
addons: [ addons: [
"@storybook/addon-links",
"@storybook/addon-essentials", "@storybook/addon-essentials",
"@storybook/addon-svelte-csf", "@storybook/addon-svelte-csf",
], ],
framework: "@storybook/svelte", framework: "@storybook/svelte",
svelteOptions: { svelteOptions: {
preprocess: preprocess(), preprocess: preprocess(),
},
async viteFinal(config, { configType }) {
// customize the Vite config here
config.resolve.alias.$lib = path.resolve(__dirname, "../src/lib/");
config.resolve.alias.$app = path.resolve(__dirname, "../fake/app/");
// return the customized config
return config;
}, },
webpackFinal: async (config) => { webpackFinal: async (config) => {
return { return {

View File

@ -24,14 +24,13 @@
"eslint-plugin-svelte3": "^3.4.1", "eslint-plugin-svelte3": "^3.4.1",
"prettier": "~2.5.1", "prettier": "~2.5.1",
"prettier-plugin-svelte": "^2.6.0", "prettier-plugin-svelte": "^2.6.0",
"storybook-builder-vite": "0.1.17",
"svelte": "^3.46.4", "svelte": "^3.46.4",
"vite": "^2.7.0" "vite": "^2.7.0"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@storybook/addon-actions": "^6.4.19",
"@storybook/addon-essentials": "^6.4.19", "@storybook/addon-essentials": "^6.4.19",
"@storybook/addon-links": "6.4.19",
"@storybook/addon-svelte-csf": "^1.1.0", "@storybook/addon-svelte-csf": "^1.1.0",
"@storybook/svelte": "^6.4.19", "@storybook/svelte": "^6.4.19",
"@sveltejs/adapter-node": "^1.0.0-next.0", "@sveltejs/adapter-node": "^1.0.0-next.0",

View File

@ -66,22 +66,6 @@ dux.addSubscription((store) =>
createSelector(calc_ship_req, (reqs) => store.dispatch(set_ship_reqs(reqs))) 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.dispatch(dux.actions.set_streamlining_cost_mass({ cost, mass }));
}
)
);
dux.addSubscription((store) => dux.addSubscription((store) =>
createSelector( createSelector(
@ -145,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; 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
}
}

View File

@ -0,0 +1,33 @@
<Meta title="ShipEdit/Carrier" component={Carrier} argTypes={{}} />
<Story name="Primary" args={{}} />
<Template let:args>
<div style="width: 50em">
<Carrier {...args} {...$state}/>
</div>
</Template>
<script>
import { Meta, Template, Story } from "@storybook/addon-svelte-csf";
import { action } from "@storybook/addon-actions";
import { setContext } from "svelte";
import { readable, get, derived } from "svelte/store";
import Carrier from "./index.svelte";
import carrierDux from '$lib/shipDux/carrier.js'
const carrierStore = carrierDux.createStore();
const state = readable(carrierStore.getState(), (set) => {
carrierStore.subscribe(() => {
set(carrierStore.getState());
});
});
setContext("ship", {
dispatch: carrierStore.dispatch,
shipMass: readable(50),
state,
});
</script>

View File

@ -1,4 +1,4 @@
<ShipItem {cost} {mass}> <ShipItem {...reqs}>
<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)}
@ -14,24 +14,20 @@
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 { squadronTypes } from "$lib/shipDux/carrier.js";
import squadron_types from "$lib/dux/carrier/squadron_types";
const types = squadron_types.map(({ type }) => type); const types = squadronTypes.map(({ type }) => type);
export let id = 1; export let id = 1;
export let type = "standard"; export let type = types[0].type;
export let ftl = false; export let reqs= {};
export let cost = 0;
export let mass = 0;
export let ship = getContext("ship"); export let { dispatch } = getContext("ship");
$: console.log(type)
$: dispatch.setSquadronType({type, id});
$: ship?.dispatch_action("set_squadron", { id, type });
</script> </script>
<style> <style>
div {
background-color: red;
}
</style> </style>

View File

@ -0,0 +1,34 @@
<Section label="carrier">
<ShipItem {...reqs}>
<Field label="bays">
<input type="number" min="0" bind:value={bays} />
</Field>
</ShipItem>
{#each squadrons as squadron,id (id)}
<Squadron {...squadron} id={id+1} />
{/each}
</Section>
<script>
import { getContext } from 'svelte';
import Field from '$lib/components/Field/index.svelte';
import ShipItem from '$lib/components/ShipItem/index.svelte';
import Section from '$lib/components/Section/index.svelte';
import Squadron from './Squadron.svelte';
export let bays = 0;
export let reqs = {};
export let squadrons = [];
const { dispatch } = getContext('ship');
$: dispatch.setCarrierBays(bays);
</script>
<style>
input {
width: 5em;
}
</style>

View File

@ -1,4 +1,4 @@
<Meta title="Identification" component={Identification} argTypes={{}} /> <Meta title="ShipEdit/Identification" component={Identification} argTypes={{}} />
<Story name="Primary" args={{}} /> <Story name="Primary" args={{}} />

View File

@ -1,5 +1,5 @@
const ship_types = [ const ship_types = [
{ name: "Scout", mass: [4, 10], abbrev: "SC" }, { name: "Scout", mass: [0, 10], abbrev: "SC" },
{ name: "Courier", mass: [4, 10], abbrev: "SC" }, { name: "Courier", mass: [4, 10], abbrev: "SC" },
{ name: "Corvette", mass: [8, 16], abbrev: "CT" }, { name: "Corvette", mass: [8, 16], abbrev: "CT" },
{ name: "Frigate", mass: [14, 28], abbrev: "FF" }, { name: "Frigate", mass: [14, 28], abbrev: "FF" },
@ -14,7 +14,7 @@ const ship_types = [
{ name: "Heavy Battleship", mass: [120, 160], abbrev: "BDN" }, { name: "Heavy Battleship", mass: [120, 160], abbrev: "BDN" },
{ name: "Dreadnought", mass: [140, 180], abbrev: "DN" }, { name: "Dreadnought", mass: [140, 180], abbrev: "DN" },
{ name: "Superdreadnought", mass: [160, 300], abbrev: "SDN" }, { name: "Superdreadnought", mass: [160, 300], abbrev: "SDN" },
{ name: "Escort Carrier", mass: [60, 140], abbrev: "CVE", carrier: true }, { name: "Escort Carrier", mass: [0, 140], abbrev: "CVE", carrier: true },
{ name: "Light Carrier", mass: [120, 180], abbrev: "CVL", carrier: true }, { name: "Light Carrier", mass: [120, 180], abbrev: "CVL", carrier: true },
{ name: "Heavy Carrier", mass: [160, 300], abbrev: "CVH", carrier: true }, { name: "Heavy Carrier", mass: [160, 300], abbrev: "CVH", carrier: true },
{ name: "Attack Carrier", mass: [150, 300], abbrev: "CVA", carrier: true }, { name: "Attack Carrier", mass: [150, 300], abbrev: "CVA", carrier: true },

View File

@ -19,7 +19,6 @@
const ship = getContext("ship"); const ship = getContext("ship");
console.log( ship.dispatch )
$: ship.dispatch.setDrive({ rating, advanced }); $: ship.dispatch.setDrive({ rating, advanced });
</script> </script>

View File

@ -0,0 +1,41 @@
<ShipItem {...reqs}>
<div>
<div class="nbr_layers">
<Field label="armour layers">
<input type="number" min="0" bind:value={nbr_layers} />
</Field>
</div>
<div class="layers">
{#each layers as rating,i (i)}
<Layer {rating} layer={i+1} />
{/each}
</div>
</div>
</ShipItem>
<script>
import { getContext } from 'svelte';
import ShipItem from "$lib/components/ShipItem/index.svelte";
import Field from "$lib/components/Field/index.svelte";
import Layer from "./Armor/Layer.svelte";
export let layers = [];
export let reqs = {};
let nbr_layers = layers.length;
const { dispatch } = getContext('ship');
$: dispatch.setArmorLayers(nbr_layers);
</script>
<style>
.layers {
display: flex;
}
input {
width: 5em;
}
</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.setArmorRating({ layer, rating });
</script>
<style>
input {
width: 5em;
}
</style>

View File

@ -0,0 +1,25 @@
<ShipItem {...reqs}>
<Field label="cargo">
<input type="number" min="0" bind:value={space} />
</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 ship = getContext("ship");
export let space = 0;
export let reqs = {};
$: ship.dispatch.setCargo(space);
</script>
<style>
input {
width: 5em;
}
</style>

View File

@ -0,0 +1,41 @@
<ShipItem {...reqs}>
<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 reqs = {};
export let {dispatch, shipMass} = getContext("ship");
$: dispatch.setStreamlining({type, shipMass: $shipMass});
</script>
<style>
div {
display: flex;
}
label {
margin-left: 1em;
}
</style>

View File

@ -1,15 +1,24 @@
<Section label="structure"> <Section label="structure">
<Hull {...hull}/> <Hull {...hull}/>
<Screens {...screens} /> <Screens {...screens} />
<Armor {...armor} />
<Cargo {...cargo} />
<Streamlining {...streamlining} />
</Section> </Section>
<script> <script>
import Section from "$lib/components/Section/index.svelte"; import Section from "$lib/components/Section/index.svelte";
import Hull from './Hull.svelte'; import Hull from './Hull.svelte';
import Screens from './Screens.svelte'; import Screens from './Screens.svelte';
import Cargo from './Cargo.svelte';
import Armor from './Armor.svelte';
import Streamlining from './Streamlining.svelte';
export let hull = {}; export let hull = {};
export let screens = {}; export let screens = {};
export let cargo = {};
export let streamlining = {};
export let armor = {};
</script> </script>

View File

@ -0,0 +1,18 @@
<ShipItem {...reqs}>
<Field label="ADFC">
<input type="number" class="short" bind:value={rating} />
</Field>
</ShipItem>
<script>
import { getContext } from 'svelte';
import ShipItem from "$lib/components/ShipItem/index.svelte";
import Field from "$lib/components/Field/index.svelte";
export let rating = 0;
export let reqs = {};
const { dispatch } = getContext('ship');
$: dispatch.setADFC(rating);
</script>

View File

@ -0,0 +1,22 @@
<Field label="weapon type">
<select bind:value={type}>
{#each weaponTypes as weapon (weapon.type)}
<option value={weapon.type}>{weapon.name}</option>
{/each}
</select>
<button class="button small blue" on:click={addWeapon} >add weapon</button>
</Field>
<script>
import { getContext } from "svelte";
import Field from "../../Field/index.svelte";
import { weaponTypes } from '$lib/shipDux/weaponry/weapons.js';
export let ship = getContext("ship");
let type = weaponTypes[0].value;
const addWeapon = () => ship.dispatch.addWeapon(type);
</script>

View File

@ -0,0 +1,21 @@
<ShipItem {...reqs}>
<Field label="firecons">
<input type="number" class="short" bind:value={stations} />
</Field>
</ShipItem>
<script>
import { getContext } from 'svelte';
import ShipItem from "$lib/components/ShipItem/index.svelte";
import Field from "$lib/components/Field/index.svelte";
export let stations = 0;
export let reqs = {};
const { dispatch } = getContext('ship');
$: dispatch.setFirecons(stations);
</script>
<style>
</style>

View File

@ -12,7 +12,7 @@
</svg> </svg>
<script> <script>
import Arc from "../../Weapons/Arc.svelte"; import Arc from "./Arc.svelte";
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
const all_arcs = ["FS", "F", "FP", "AP", "A", "AS"]; const all_arcs = ["FS", "F", "FP", "AP", "A", "AS"];

Before

Width:  |  Height:  |  Size: 651 B

After

Width:  |  Height:  |  Size: 639 B

View File

@ -0,0 +1,83 @@
<label>beam</label>
<Field label="beam class">
<select bind:value={weaponClass}>
<option value={1}>1</option>
<option value={2}>2</option>
<option value={3}>3</option>
<option value={4}>4</option>
</select>
</Field>
<Field label="arcs">
<select bind:value={nbrArcs}>
{#each arc_options[weaponClass] || [] as nbrArcs (nbrArcs)}
<option>{nbrArcs}</option>
{/each}
</select>
</Field>
<Arcs selected={arcs} on:click_arc={({ detail }) => setArcs(detail)} />
<script>
import { getContext } from "svelte";
import Arcs from "../Arcs.svelte";
import ShipItem from "$lib/components/ShipItem/index.svelte";
import Field from "$lib/components/Field/index.svelte";
import { createEventDispatcher } from "svelte";
const all_arcs = ["FS", "F", "FP", "AP", "A", "AS"];
export let weaponClass = 1;
export let arcs = ["F"];
let arc_options = {
1: [6],
2: [3, 6],
3: [1, 2, 3, 4, 5, 6, "broadside"],
4: [1, 2, 3, 4, 5, 6, "broadside"],
};
$: arcsCaches = arcs.join(',');
let nbrArcs = arcs.length;
$: if (!arc_options[weaponClass].includes(nbrArcs)) {
nbrArcs = arc_options[weaponClass][0];
console.log({nbrArcs, label:"in if"})
}
const broadside = ["FS", "FP", "AP", "AS"];
function setArcs(firstArc) {
console.log(firstArc);
if (nbrArcs === "broadside") {
arcs = broadside;
return;
}
let first_index = all_arcs.findIndex((arc) => arc === firstArc);
if (first_index === -1) first_index = 0;
console.log({arcs, label:"before"});
arcs = _.range(nbrArcs).map(
(i) => all_arcs[(first_index + i) % all_arcs.length]
);
arcsCaches = arcs.join(',');
console.log({arcs, label:"after"});
}
$: if (arcs.length !== nbrArcs) setArcs(arcs[0]);
$: console.log("it changed!", arcs)
$: console.log("it changed!", arcsCaches)
const dispatch = createEventDispatcher();
$: dispatch("change", {
weaponClass,
arcs: arcsCaches.split(','),
});
</script>

View File

@ -0,0 +1,27 @@
<label>needle weapon</label>
<Arcs selected={[arc]} on:click_arc={({ detail }) => click_arc(detail)} />
<script>
import { getContext } from "svelte";
import Arcs from "./Arcs.svelte";
import { createEventDispatcher } from "svelte";
const all_arcs = ["FS", "F", "FP", "AP", "A", "AS"];
export let arc = "F";
const dispatch = createEventDispatcher();
const click_arc = (arc) => {
dispatch("change",{arc});
};
</script>
<style>
.arc {
display: flex;
flex-direction: column;
margin-right: 1em;
}
</style>

View File

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

View File

@ -0,0 +1,19 @@
<label>submunition pack</label>
<Arcs selected={[arc]} on:click_arc={({ detail }) => click_arc(detail)} />
<script>
import { getContext } from "svelte";
import Arcs from "./Arcs.svelte";
import { createEventDispatcher } from "svelte";
const all_arcs = ["FS", "F", "FP", "AP", "A", "AS"];
export let arc = "F";
const dispatch = createEventDispatcher();
const click_arc = (arc) => {
dispatch("change",{arc});
};
</script>

View File

@ -0,0 +1,121 @@
<ShipItem {...reqs}>
<div class="weapon_row">
<button
class="button small red remove"
on:click={remove}>remove
</button>
<svelte:component
this={component[type]}
{...weapon}
on:change={update}
/>
</div>
</ShipItem>
<script>
import { getContext } from "svelte";
import Arc from "./Arc.svelte";
import ShipItem from "$lib/components/ShipItem/index.svelte";
import Field from "$lib/components/Field/index.svelte";
import Beam from "./Beam/index.svelte";
import Submunition from "./Submunition.svelte";
import PointDefenceSystem from "./PDS.svelte";
import Scattergun from "./Scattergun.svelte";
import Needle from "./Needle.svelte";
const component = {
beam: Beam,
submunition: Submunition,
pds: PointDefenceSystem,
scattergun: Scattergun,
needle: Needle,
};
export let weapon = {};
$: reqs = weapon.reqs;
export let id;
const ship = getContext("ship");
$: type = weapon.type;
const remove = () => ship.dispatch.removeWeapon(id);
const update = ({ detail }) => {
console.log({id,type})
ship.dispatch.setWeapon({
id,
type,
...detail,
});
};
</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;
}
.add-weapon {
display: block;
}
.weapon_row {
display: flex;
align-items: center;
}
.weapon_row > :global(*) {
margin-right: 2em;
}
</style>

View File

@ -0,0 +1,34 @@
<Meta title="ShipEdit/Weaponry" component={Weaponry} argTypes={{}} />
<Story name="Primary" args={{}} />
<Template let:args>
<div style="width: 50em">
<Weaponry {...args} {...$state}/>
</div>
</Template>
<script>
import { Meta, Template, Story } from "@storybook/addon-svelte-csf";
import { action } from "@storybook/addon-actions";
import { setContext } from "svelte";
import { readable, get, derived } from "svelte/store";
import Weaponry from "./index.svelte";
import weaponryDux from '$lib/shipDux/weaponry/index.js';
const store = weaponryDux.createStore();
const state = readable(store.getState(), (set) => {
store.subscribe(() => {
console.log(store.getState());
set(store.getState());
});
});
setContext("ship", {
dispatch: store.dispatch,
shipMass: readable(50),
state,
});
</script>

View File

@ -0,0 +1,34 @@
<Section label="weaponry">
<Firecons {...firecons} />
<ADFC {...adfc} />
<AddWeapon />
{#each weapons as weapon (weapon.id)}
<Weapon {weapon} id={weapon.id} />
{/each}
</Section>
<script>
import { getContext } from 'svelte';
import Section from "$lib/components/Section/index.svelte";
import ShipItem from '$lib/components/ShipItem/index.svelte';
import Field from '$lib/components/Field/index.svelte';
import Firecons from './Firecons.svelte';
import ADFC from './ADFC.svelte';
import AddWeapon from './AddWeapon.svelte';
import Weapon from './Weapon/index.svelte';
export let firecons = {};
export let adfc = {};
export let weapons = [];
</script>
<style>
</style>

View File

@ -8,6 +8,10 @@
<Structure {...$shipState.structure} /> <Structure {...$shipState.structure} />
<Weaponry {...$shipState.weaponry}/>
<Carrier {...$shipState.carrier} />
<script> <script>
import { getContext } from "svelte"; import { getContext } from "svelte";
@ -15,6 +19,8 @@
import ShipCost from "./ShipCost.svelte"; import ShipCost from "./ShipCost.svelte";
import Propulsion from "./Propulsion/index.svelte"; import Propulsion from "./Propulsion/index.svelte";
import Structure from "./Structure/index.svelte"; import Structure from "./Structure/index.svelte";
import Carrier from "./Carrier/index.svelte";
import Weaponry from "./Weaponry/index.svelte";
const { state: shipState } = getContext("ship"); const { state: shipState } = getContext("ship");
</script> </script>

View File

@ -1,35 +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

@ -0,0 +1,76 @@
import { Updux } from "updux";
import u from "updeep";
import _ from 'lodash';
import reqs from "./reqs.js";
const dux = new Updux({
subduxes: { reqs },
initial: {
bays: 0,
squadrons: [],
},
actions: {
setCarrierBays: null,
setSquadronType: null,
}
});
dux.setMutation( 'setCarrierBays', bays => u({bays, reqs:
calcBaysReqs(bays),
squadrons: adjustSquadrons(bays),
}) );
dux.setMutation('setSquadronType', ({type, id}) => state => {
return u.updateIn(['squadrons', id-1], {
type,
reqs: squadronReqs(type)
}, state )
} );
export const squadronTypes= [
{ type: "standard", cost: 3 },
{ type: "fast", cost: 4 },
{ type: "heavy", cost: 5 },
{ type: "interceptor", cost: 3 },
{ type: "attack", cost: 4 },
{ type: "long range", cost: 4 },
{ type: "torpedo", cost: 6 },
];
function squadronReqs(type) {
return { mass: 6, cost: 6 * squadronTypes.find( s => s.type === type )?.cost }
}
const adjustSquadrons = bays => squadrons => {
if( squadrons.length > bays ) {
squadrons = squadrons.slice(0,bays);
}
if( squadrons.length < bays ) {
squadrons = [ ...squadrons, ..._.times(
bays - squadrons.length, () => ({
type: squadronTypes[0].type,
reqs: {
cost: 6 * squadronTypes[0].cost,
mass: 6,
},
})
)];
}
return squadrons;
}
function calcBaysReqs(bays) {
return {
mass: 9 * bays,
cost: 18 * bays,
}
}
export default dux;

View File

@ -1,10 +1,13 @@
import { Updux } from "updux"; import { Updux } from "updux";
import u from "updeep"; import u from "updeep";
import carrier from './carrier.js';
const dux = new Updux({ const dux = new Updux({
actions: { actions: {
setShipType: null, setShipType: null,
setShipClass: null, setShipClass: null,
setCarrierBays: carrier.actions.setCarrierBays,
}, },
initial: { initial: {
shipType: "", shipType: "",
@ -16,5 +19,8 @@ const dux = new Updux({
dux.setMutation("setShipType", (shipType) => u({ shipType })); dux.setMutation("setShipType", (shipType) => u({ shipType }));
dux.setMutation("setShipClass", (shipClass) => u({ shipClass })); dux.setMutation("setShipClass", (shipClass) => u({ shipClass }));
dux.setMutation('setCarrierBays', (bays) => u({
isCarrier: bays > 0,
}))
export default dux; export default dux;

View File

@ -1,30 +1,59 @@
import { Updux } from "updux"; import { Updux } from "updux";
import u from 'updeep'; import u from "updeep";
import propulsion from "./propulsion/index.js"; import propulsion from "./propulsion/index.js";
import identification from "./identification.js"; import identification from "./identification.js";
import { calculateDriveReqs } from './propulsion/drive.js'; import { calculateDriveReqs } from "./propulsion/drive.js";
import { ftlReqsReaction } from './propulsion/ftl.js'; import { ftlReqsReaction } from "./propulsion/ftl.js";
import structure from './structure/index.js'; import structure from "./structure/index.js";
import { screenReqsReaction, screensReqsReaction } from './structure/screens.js' import carrier from "./carrier.js";
import weaponry from "./weaponry/index.js";
import { screensReqsReaction } from "./structure/screens.js";
const dux = new Updux({ const dux = new Updux({
subduxes: { subduxes: {
identification, identification,
propulsion, propulsion,
structure structure,
}, carrier,
initial: { weaponry,
reqs: { cost: 0, mass: 10, usedMass: 0 }, },
}, initial: {
reqs: { cost: 0, mass: 10, usedMass: 0 },
},
actions: { actions: {
} setShipReqs: null,
},
}); });
dux.setMutation( 'setShipMass', mass => u({reqs: {mass}}) ); dux.setMutation("setShipMass", (mass) => u({ reqs: { mass } }));
dux.setMutation('setShipReqs', reqs => u({reqs}));
dux.addReaction(calculateDriveReqs);
dux.addReaction(ftlReqsReaction);
dux.addReaction(screensReqsReaction);
dux.addReaction( (store) => (state) => {
let cost = 0;
let mass = 0;
let subsystems = Object.values(state);
while(subsystems.length>0) {
const subsystem = subsystems.shift();
if( typeof subsystem !== 'object' ) continue;
if( subsystem.reqs ) {
cost += subsystem.reqs.cost;
mass += subsystem.reqs.mass;
}
subsystems.push( ...Object.values(subsystem));
}
store.dispatch.setShipReqs({cost,usedMass: mass});
});
dux.addReaction( calculateDriveReqs );
dux.addReaction( ftlReqsReaction );
dux.addReaction( screenReqsReaction );
export default dux; export default dux;

View File

@ -0,0 +1,50 @@
import { Updux } from "updux";
import u from "updeep";
import reqs from "../reqs.js";
const dux = new Updux({
subduxes: {
reqs,
},
initial: {
layers: [],
},
actions: {
setArmorLayers: null,
setArmorRating: null,
},
});
export default dux;
dux.setMutation('setArmorRating', ({layer, rating}) => state => {
let layers = [ ...state.layers ].map( (v,k) => k === layer-1 ? rating : v );
return { layers, reqs: calcArmorReqs(layers) }
} );
dux.setMutation( 'setArmorLayers', nbrLayers => state => {
let layers = [...state.layers];
if( nbrLayers < state.layers.length )
layers = [ ...state.layers ].slice(0,nbrLayers);
while( layers.length < nbrLayers ) {
layers.push(0);
}
return {
layers,
reqs: calcArmorReqs(layers),
}
});
function calcArmorReqs(layers) {
const mass = 2* layers.reduce( (a,b) => a+ b,0 );
const cost = 2* layers.map( (v,k) => v * (k+1) ).reduce( (a,b) => a+ b,0 );
return {
mass, cost
}
}

View File

@ -0,0 +1,24 @@
import { Updux } from "updux";
import u from 'updeep';
import { createSelector } from 'reselect';
import reqs from '../reqs.js';
const dux = new Updux({
subduxes: {
reqs
},
initial: {
space: 0,
},
actions: {
setCargo: null,
}
});
export default dux;
dux.setMutation('setCargo', space => u({
space,
reqs: { mass: space }
}));

View File

@ -2,8 +2,12 @@ import { Updux } from 'updux';
import hull from './hull.js'; import hull from './hull.js';
import screens from './screens.js'; import screens from './screens.js';
import cargo from './cargo.js';
import armor from './armor.js';
import streamlining from './streamlining.js';
const dux = new Updux({ const dux = new Updux({
subduxes: { hull, screens } subduxes: { hull, screens, cargo, streamlining, armor }
}); });
export default dux; export default dux;

View File

@ -21,7 +21,7 @@ export default dux;
dux.setMutation('setScreens', payload => u(payload)); dux.setMutation('setScreens', payload => u(payload));
dux.setMutation('setScreensReqs', reqs => u({reqs})); dux.setMutation('setScreensReqs', reqs => u({reqs}));
export const screenReqsReaction = store => createSelector( export const screensReqsReaction = store => createSelector(
(ship) => ship.reqs.mass, (ship) => ship.reqs.mass,
(ship) => ship.structure.screens.standard, (ship) => ship.structure.screens.standard,
(ship) => ship.structure.screens.advanced, (ship) => ship.structure.screens.advanced,

View File

@ -0,0 +1,32 @@
import { Updux } from "updux";
import u from "updeep";
import reqs from "../reqs.js";
const dux = new Updux({
subduxes: {
reqs,
},
initial: {
type: "none",
},
actions: {
setStreamlining: null,
},
});
export default dux;
dux.setMutation("setStreamlining", ({ shipMass, type }) =>
u({
type,
reqs: calcStreamliningReqs({ shipMass, type }),
})
);
function calcStreamliningReqs({ shipMass, type }) {
const mass = Math.ceil(
(shipMass * (type === "none" ? 0 : type === "partial" ? 5 : 10)) / 100
);
return { mass, cost: 2 * mass };
}

View File

@ -0,0 +1,46 @@
import { Updux } from "updux";
import u from "updeep";
import weapons from './weapons.js';
const reqs = { cost: 0, mass: 0 };
const dux = new Updux({
subduxes: { weapons },
initial: {
firecons: {
stations: 0,
reqs,
},
adfc: { rating: 0, reqs },
},
actions: {
setADFC: null,
setFirecons: null,
},
});
dux.setMutation("setFirecons", (stations) =>
u({
firecons: {
stations,
reqs: {
cost: 4 * stations,
mass: stations,
},
},
})
);
dux.setMutation("setADFC", (rating) =>
u({
adfc: {
rating,
reqs: {
cost: 8 * rating,
mass: 2 * rating,
},
},
})
);
export default dux;

View File

@ -0,0 +1,122 @@
import { Updux } from "updux";
import u from "updeep";
const reqs = { cost: 0, mass: 0 };
export const weaponTypes = [
{ name: 'beam', type: 'beam', reqs: beam_cost_mass, initial: {
weaponClass: 1
}},
{ name: 'submunition pack', type: 'submunition', reqs: { mass:1, cost:3 },
initial: { arc: 'F' }
},
{ name: 'point defence system', type: 'pds', reqs: {mass:1,cost:3}, initial: {}},
{ name: 'scattergun', type: 'scattergun', reqs: { mass:1,cost:4 }, initial: {}},
{ name: 'needle weapon', type: 'needle', reqs: { mass: 2, cost: 6 },
initial: { arc: 'F' }},
];
const dux = new Updux({
initial: [],
actions: {
addWeapon: null,
removeWeapon: null,
setWeapon: null,
},
});
dux.setMutation('setWeapon', ({id,...rest}) => state => {
console.log(id,rest,state);
state = u.map( u.if( (w) => w.id === id,
weapon => {
return {
id,
...rest,
reqs: weaponReqs(rest),
}
} ), state );
console.log(state);
return state;
}
);
dux.setMutation('removeWeapon', id => state => [
...state.filter( (w) => w.id !== id )
]);
dux.setMutation('addWeapon', type => state => {
const initial = weaponTypes.find(w => w.type === type ).initial;
return [
...state,
{
id: state.length === 0 ? 1 : state[state.length -1]+1,
type,
reqs: weaponReqs({type,...initial}),
...initial,
}
]
});
function weaponReqs(weapon) {
const {reqs} = weaponTypes.find( wt => wt.type === weapon.type ) ||{};
if(!reqs) return {};
if( typeof reqs === 'function' ) return reqs(weapon);
return reqs;
}
const isBroadside = arcs => {
if( arcs.length !== 4 ) return false;
// that'd be A or F
return !arcs.some( a => a.length === 1 );
}
function beam_cost_mass({weaponClass, arcs}) {
console.log({weaponClass,arcs})
let mass;
if( weaponClass === 1 ) {
mass = 1;
}
if( weaponClass === 2 ) {
mass = 2 + (arcs.length > 3 ? 1 : 0);
}
if( weaponClass == 3 ) {
mass = 4;
if( isBroadside(arcs) ) {
mass += 2;
}
else {
mass += arcs.length - 1;
}
}
if( weaponClass == 4 ) {
mass = 8;
if( isBroadside(arcs) ) {
mass += 4;
}
else {
mass += 2*(arcs.length - 1);
}
}
return {
mass, cost: 3 * mass
}
}
export default dux;

View File

@ -1,5 +1,5 @@
import { browser } from "$app/env"; import { browser } from "$app/env";
import { readable, get } from "svelte/store"; import { readable, get, derived } from "svelte/store";
import { compose, applyMiddleware } from "redux"; import { compose, applyMiddleware } from "redux";
import shipDux from "../shipDux/index.js"; import shipDux from "../shipDux/index.js";
@ -20,7 +20,6 @@ export default () => {
duxStore.subscribe(() => { duxStore.subscribe(() => {
if (previous === duxStore.getState()) return; if (previous === duxStore.getState()) return;
previous = duxStore.getState(); previous = duxStore.getState();
console.log("Setting!", previous);
set(previous); set(previous);
}); });
}); });
@ -28,5 +27,6 @@ export default () => {
return { return {
dispatch: duxStore.dispatch, dispatch: duxStore.dispatch,
state, state,
shipMass: derived( state, state => state.reqs.mass )
}; };
}; };