Merge branch 'main' into pages

This commit is contained in:
Yanick Champoux 2022-04-05 19:59:26 -04:00
commit 409762c8c9
28 changed files with 32 additions and 599 deletions

View File

@ -1,30 +0,0 @@
import tap from "tap";
import { calc_ship_req } from "./utils";
const sample = {
general: {
mass: 10,
used_mass: 0,
cost: 10,
},
ftl: {
cost: 4,
mass: 2,
},
structure: {
hull: {
cost: 2,
mass: 1,
},
},
};
tap.same(
calc_ship_req(sample),
{
cost: 16,
mass: 3,
},
"sample ship"
);

View File

@ -1,22 +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: {
space: 0,
cost: 0,
mass: 0,
},
});
const set_cargo = action("set_cargo", payload());
dux.addMutation(set_cargo, (space) => () => ({
space,
cost: 0,
mass: space,
}));
export default dux.asDux;

View File

@ -1,58 +0,0 @@
import Updux from "updux";
import { action, payload } from "ts-action";
import u from "@yanick/updeep";
import _ from 'lodash';
import { createSelector } from "reselect";
import squadron_types from './squadron_types';
const uu = transform => state => transform(state)(state);
const dux = new Updux({
initial: {
bays: 0,
cost: 0,
mass: 0,
squadrons: [],
},
});
const set_squadron = action('set_squadron',payload());
dux.addMutation(set_squadron, ({id,type}) => u.update({ squadrons: u.map(
u.if(_.matches({id}), u({type, cost: 6 * _.find(squadron_types,{type}).cost, mass: 6 }))
)}));
const set_carrier_bays = action('set_carrier_bays', payload() );
dux.addMutation( set_carrier_bays, bays => state => {
state = u.update({
bays,
mass: 1.5*6*bays,
cost: 3 * 1.5 * 6 * bays,
})(state);
if( state.squadrons.length > bays ) {
state = u.update({
squadrons: squadrons => squadrons.slice(0,bays)
}, state)
}
if( state.squadrons.length < bays ) {
state = u.update({
squadrons: squadrons => [ ...squadrons, ..._.times(
bays - state.squadrons.length, i => ({
id: 1 + i + state.squadrons.length,
cost: 6 * squadron_types[0].cost,
mass: 6,
type: squadron_types[0].type,
})
)] }, state)
}
return state;
});
export default dux.asDux;

View File

@ -1,9 +0,0 @@
export default [
{ 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 },
];

View File

@ -1,27 +0,0 @@
import Updux from 'updux';
import { action, payload } from 'ts-action';
import u from "@yanick/updeep";
import { createSelector } from 'reselect';
const set_engine = action('set_engine',payload());
const set_drive_reqs = action('set_drive_reqs',payload());
const dux = new Updux({
initial: {
mass: 1,
cost: 2,
rating: 1,
}
});
dux.addMutation(set_engine, engine => u.update(engine));
dux.addMutation(set_drive_reqs, rate => u.update(rate));
export function calc_drive_reqs(ship_mass,rating,advanced=false) {
const mass = Math.ceil(rating * 0.05 * ship_mass);
const cost = mass * ( advanced ? 3 : 2 );
return { mass, cost };
}
export default dux.asDux;

View File

@ -1,25 +0,0 @@
import Updux from "updux";
import { action, payload } from 'ts-action';
import u from "@yanick/updeep";
import { createSelector } from 'reselect';
import { calc_ftl_reqs } from './rules';
// 'none' | 'standard' | 'advanced'
const set_ftl = action('set_ftl',payload());
const set_ftl_reqs = action('set_ftl_reqs', payload() );
console.log(Updux);
const dux = new Updux({
initial: {
mass: 0,
cost: 0,
type: 'none'
}
});
dux.addMutation(set_ftl, type => u.update({type}));
dux.addMutation(set_ftl_reqs, reqs => u.update(reqs) );
export default dux.asDux;

View File

@ -1,10 +0,0 @@
export function calc_ftl_reqs(type,ship_mass) {
if(type==="none") return { cost: 0, mass: 0 };
const mass = Math.ceil(ship_mass / 10);
return {
mass,
cost: mass * ( type === 'advanced' ? 3 : 2 ),
}
}

View File

@ -1,7 +0,0 @@
import tap from 'tap';
import { calc_ftl_rate } from './rules';
tap.same( calc_ftl_rate(111,'standard'), { mass: 12, cost: 24 },
'mass rounded up' );

View File

@ -1,135 +0,0 @@
import Updux from "updux";
import { action, payload } from "ts-action";
import u from "@yanick/updeep";
import { createSelector } from "reselect";
import ftl from "./ftl";
import engine, { calc_drive_reqs } from "./engine";
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";
const set_ship_mass = action("set_ship_mass", payload());
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 reset = action("reset");
const initial = {
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,
});
dux.addMutation(reset, () => () => initial);
dux.addMutation(set_hull, ({ rating }) => (state) => {
return u.updateIn("structure.hull", {
cost: 2 * rating,
rating,
mass: 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(set_ship_reqs, ({ cost, mass: used_mass }) =>
u.updateIn("general", { cost, used_mass })
);
// set ship's req
dux.addSubscription((store) =>
createSelector(calc_ship_req, (reqs) => store.dispatch(set_ship_reqs(reqs)))
);
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.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)))
)
);
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,
})
);
}
)
);
dux.addSubscription((store) =>
createSelector(
[
(ship) => ship.general.mass,
(ship) => ship.engine.rating,
(ship) => ship.engine.advanced,
],
(ship_mass, rating, advanced) =>
store.dispatch(
dux.actions.set_drive_reqs(calc_drive_reqs(ship_mass, rating, advanced))
)
)
);
export default dux.asDux;
export const actions = dux.actions;

View File

@ -1,22 +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: {
type: 'none',
cost: 0,
mass: 0,
},
});
const set_streamlining = action('set_streamlining',payload());
dux.addMutation(set_streamlining, type => u.update({type}) );
const set_streamlining_cost_mass = action('set_streamlining_cost_mass',payload());
dux.addMutation( set_streamlining_cost_mass, reqs => u.update(reqs) );
export default dux.asDux;

View File

@ -1,36 +0,0 @@
import Updux from "updux";
import { action, payload } from "ts-action";
import u from "@yanick/updeep";
import { createSelector } from "reselect";
import _ from 'lodash';
const dux = new Updux({
initial: [],
});
const set_armour_nbr_layers = action('set_armour_nbr_layers',payload());
dux.addMutation( set_armour_nbr_layers, nbr_layers => state => {
if( state.length > nbr_layers ) state = state.slice(0,nbr_layers);
if( state.length < nbr_layers ) {
state = [ state, _.times( nbr_layers - state.length, () => [] ) ].flat();
}
state = u.map( (el,i) => ({layer: i +1, rating: 0, cost: 0, mass: 0, ...el }), state);
return state;
});
dux.addMutation(
action('set_armour_layer', payload()),
({layer, rating}) => u.map(
u.if(_.matches({layer}), u({
rating,
cost: 2 * layer * rating,
mass: 2 * rating,
}))
)
);
export default dux.asDux;

View File

@ -1,26 +0,0 @@
import Updux from "updux";
import { action, payload } from "ts-action";
import u from "@yanick/updeep";
import { createSelector } from "reselect";
import screens from './screens';
import armour from './armour';
const dux = new Updux({
initial: {
mass: 0,
cost: 0,
hull: {
rating: 1,
advanced: false,
cost: 2,
mass: 1,
}
},
subduxes: {
screens,
armour,
}
})
export default dux.asDux;

View File

@ -1,17 +0,0 @@
import tap from 'tap';
import ship, { actions } from '.';
ship.dispatch( actions.set_ship_mass(100));
ship.dispatch( actions.set_ftl('standard'));
tap.match(
ship.getState(),
{
ship_type: { mass: 100 },
ftl: { type: 'standard', cost: 20, mass: 10 },
},
'standard ftl drive'
);

View File

@ -1,38 +0,0 @@
import flow from 'lodash/fp/flow.js';
import sumBy from 'lodash/fp/sumBy.js';
import pick from 'lodash/fp/pick.js';
import filter from 'lodash/fp/filter.js';
import flattenDeep from 'lodash/fp/flattenDeep.js';
import has from 'lodash/fp/has.js';
const expand_cost_mass = (obj={}) => ([
pick(['cost','mass'],obj),
...Object.values(obj || {}).filter( val => typeof val === 'object' ).map(
val => expand_cost_mass(val)
)
]);
export function calc_ship_req(ship) {
console.log(ship);
let { general, ...rest } = ship;
//if(!general) general = {};
const items = flow(
expand_cost_mass,
flattenDeep,
filter(has('cost'))
)({
...rest,
cost: general.mass, mass: 0
})
return {
mass: sumBy('mass',items),
cost: sumBy('cost',items),
}
}
// to get around 3.00001 ceiling up to 4
export const ceil = number => Math.ceil( Math.round(10*number)/10 );

View File

@ -1,21 +0,0 @@
import Updux from "updux";
import { action, payload } from "ts-action";
import u from "@yanick/updeep";
import { createSelector } from "reselect";
import weapons from './weapons';
import adfc from './adfc';
const dux = new Updux({
initial: {
firecons: {
nbr: 0, cost: 0, mass: 0
}
},
subduxes: {
weapons,
adfc,
}
})
export default dux.asDux;

View File

@ -1,51 +0,0 @@
import matches from 'lodash/matches.js';
import Updux from "updux";
import { action, payload } from "ts-action";
import u from "@yanick/updeep";
import { createSelector } from "reselect";
import { weapon_cost_mass } from "../../weapons/rules";
const add_weapon = action("add_weapon", payload());
const remove_weapon = action("remove_weapon", payload());
const uu = (transform) => (state) => transform(state)(state);
const with_reqs = uu((weapon) => u(weapon_cost_mass(weapon)));
const dux = new Updux({
initial: [],
});
const weapon_initial = {
beam: {
weapon_type: "beam",
weapon_class: 1,
arcs: ["F", "A", "FS", "FP", "AS", "AP"],
},
submunition: {
weapon_type: "submunition",
arcs: ["F"],
},
pds: { weapon_type: "pds" },
scattergun: { weapon_type: "scattergun" },
needle: { weapon_type: "needle", arcs: ["F"] },
};
dux.addMutation(add_weapon, (type) => (state) => {
const id = 1 + Math.max(0, ...state.map(({ id }) => id));
return [...state, { ...with_reqs(weapon_initial[type]), id }];
});
dux.addMutation(remove_weapon, (id) => (state) =>
state.filter((w) => w.id !== id)
);
const set_weapon = action("set_weapon", payload());
dux.addMutation(set_weapon, (payload) =>
u.map(
u.if(matches({ id: payload.id }), (state) => with_reqs(u(payload, state)))
)
);
export default dux.asDux;

View File

@ -1,5 +0,0 @@
import dux from ".";
import tap from "tap";
const state = dux.upreducer(dux.actions.add_weapon("beam"))([]);
tap.equal(state[0].arcs.length, 6, "default beam is all arcs");

View File

@ -1,29 +0,0 @@
import tap from 'tap';
import { weapon_cost_mass } from './rules';
const cases = [
[{
weapon_type: "beam",
weapon_class: 2,
arcs: [ 'AP', 'A', 'AF' ]
},{
cost: 6,
mass: 2,
}
],
[{
weapon_type: "beam",
weapon_class: 1,
arcs: [ 'AP', 'A', 'AF' ]
},{
mass: 1,
cost: 3,
}
],
[{ weapon_type: "beam", weapon_class: 3, arcs: [ 'AP', 'A', 'AF' ] },{ mass: 6, cost: 18, } ],
[{ weapon_type: "beam", weapon_class: 4, arcs: [ 'AP', 'A', 'AF' ] },{ mass: 12, cost: 36, } ],
]
cases.forEach( ([weapon,expected]) =>
tap.match(
weapon_cost_mass(weapon), expected, JSON.stringify(weapon) ) );

View File

@ -1,7 +1,8 @@
<div class="mass">{mass}</div>
<div class="mass">{mass}<img src="{base}/mass.svg" alt="mass"/></div>
<div class="cost">{cost}</div>
<script>
import { base } from '$app/paths';
export let mass;
export let cost;
</script>
@ -20,8 +21,7 @@
content: "\00A4";
margin-left: 0.5em;
}
.mass:after {
content: url("/mass.svg");
.mass img {
width: 0.75em;
display: inline-block;
margin-left: 0.5em;

View File

@ -1,12 +1,12 @@
<div>{mass}</div>
<div>{mass}<img src={base+'/mass.svg'} alt="mass"/></div>
<script>
import { base } from '$app/paths';
export let mass = 0;
</script>
<style>
div:after {
content: url("/mass.svg");
img {
width: 0.75em;
display: inline-block;
margin-left: 0.5em;

View File

@ -4,7 +4,7 @@
{#each row as threshold, j (j)}
<div class="cell">
{#if threshold}
<img src="icons/crew-star.svg" alt="crew loss threshold" />
<img src="{base}/icons/crew-star.svg" alt="crew loss threshold" />
{/if}
</div>
{/each}
@ -13,6 +13,7 @@
</div>
<script>
import { base } from '$app/paths';
export let shipMass = 0;
export let rating = 0;
export let advanced = false;

View File

@ -1,27 +1,30 @@
<div class="main_systems">
{#if ftl !== "none"}
<img class="ftl" src="icons/ftl-drive.svg" alt="ftl drive" />
<img class="ftl" src="{base}/icons/ftl-drive.svg" alt="ftl drive" />
{/if}
{#if engine > 0}
<div class="thrust">{engine}</div>
<div class="thrust"
style="background-image: url({base}/icons/standard-drive.svg);"
>{engine}</div>
{/if}
<img
class="internal"
src="icons/internal-systems.svg"
src="{base}/icons/internal-systems.svg"
alt="internal systems"
/>
</div>
<script>
import { base } from '$app/paths';
export let ftl = "none";
export let engine = 0;
</script>
<style>
.thrust {
background-image: url(icons/standard-drive.svg);
width: 2em;
background-size: 2em;
background-repeat: no-repeat;

View File

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

View File

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

View File

@ -1,7 +1,7 @@
<div class="mass">
<Field label="ship tonnage">
<input class="short" bind:value={mass} type="number" min="10" max="300" />
<span class="mass_symbol" />
<img class="mass_symbol" src="{base}/mass.svg" alt="mass"/>
<div class="note" class:warning={!withinBudget}>
{#if withinBudget}
@ -20,6 +20,7 @@
</div>
<script>
import { base } from '$app/paths';
import { getContext } from "svelte";
import Field from "$lib/components/Field/index.svelte";
@ -55,8 +56,7 @@
width: 5em;
display: inline !important;
}
.mass_symbol:after {
content: url("/mass.svg");
.mass_symbol {
width: 0.75em;
display: inline-block;
margin-left: 0.5em;

View File

@ -2,12 +2,14 @@
<div><slot /></div>
<div class="reqs">
<div class="mass" bind:this={mass_el}>{mass}</div>
<div class="mass" bind:this={mass_el}>{mass} <img src="{base}/mass.svg"
alt="mass"/></div>
<div class="cost" bind:this={cost_el}>{cost}</div>
</div>
</div>
<script>
import { base } from '$app/paths';
import { tick } from "svelte";
export let mass;
@ -54,8 +56,7 @@
content: "\00A4";
margin-left: 0.5em;
}
.mass:after {
content: url("/mass.svg");
.mass img {
width: 0.75em;
display: inline-block;
margin-left: 0.5em;

View File

@ -7,7 +7,7 @@
min="10"
max="300"
/>
<span class="mass_symbol" />
<img class="mass_symbol" src="{base}/mass.svg" alt="mass"/>
<div class="note" class:warning={!within_budget}>
{#if within_budget}
@ -24,6 +24,7 @@
</div>
<script>
import { base } from '$app/paths';
import { getContext } from "svelte";
import Field from "$lib/components/Field/index.svelte";
@ -49,8 +50,7 @@
input {
width: 5em;
}
.mass_symbol:after {
content: url("/mass.svg");
.mass_symbol {
width: 0.75em;
display: inline-block;
margin-left: 0.5em;

View File

@ -4,14 +4,8 @@ import analyze from "rollup-plugin-analyzer";
/** @type {import('@sveltejs/kit').Config} */
export default {
kit: {
// outDir: './docs',
// hydrate the <div id="svelte"> element in src/app.html
paths: {
base: "/aotds-docks",
},
adapter: adapter({
fallback: "index.html",
}),
adapter: adapter(),
paths: { base: "/aotds-docks" },
vite: {
build: {
rollupOptions: {