diff --git a/CHANGELOG.yml b/CHANGELOG.yml index 754345c..4c5a36f 100644 --- a/CHANGELOG.yml +++ b/CHANGELOG.yml @@ -8,6 +8,8 @@ releases: changes: - desc: add SMRs type: feat + - desc: add SMLs + type: feat - version: 3.1.0 changes: - desc: add version and changelog to the about section diff --git a/src/lib/components/App-original.svelte b/src/lib/components/App-original.svelte deleted file mode 100644 index 0493fba..0000000 --- a/src/lib/components/App-original.svelte +++ /dev/null @@ -1,175 +0,0 @@ - - -{#if show_notes} - -{/if} - -{#if output === "json"} - set_output(null)} /> -{:else if output === "print"} - -{:else} -
- - - - - - -
- - - - - - - {#each weapons as weapon (weapon.id)} - - {/each} -
- - -
- -{/if} - - - - diff --git a/src/lib/components/ShipEdit/Carrier/Squadron.svelte b/src/lib/components/ShipEdit/Carrier/Squadron.svelte index a136a98..4059cd6 100644 --- a/src/lib/components/ShipEdit/Carrier/Squadron.svelte +++ b/src/lib/components/ShipEdit/Carrier/Squadron.svelte @@ -26,7 +26,6 @@ export let { dispatch } = getContext("api"); - $: console.log(type); $: dispatch.setSquadronType({ type, id }); diff --git a/src/lib/components/ShipEdit/MissileMagazines.svelte b/src/lib/components/ShipEdit/MissileMagazines.svelte new file mode 100644 index 0000000..0a78e7b --- /dev/null +++ b/src/lib/components/ShipEdit/MissileMagazines.svelte @@ -0,0 +1,40 @@ +{#if magazines.length > 0} + +
missile magazines
+
+ {#each magazines as magazine (magazine.id)} + + {/each} +
+
+{/if} + + + + diff --git a/src/lib/components/ShipEdit/MissileMagazines/Magazine.svelte b/src/lib/components/ShipEdit/MissileMagazines/Magazine.svelte new file mode 100644 index 0000000..33cab17 --- /dev/null +++ b/src/lib/components/ShipEdit/MissileMagazines/Magazine.svelte @@ -0,0 +1,42 @@ +
+
+ + +
+ +
+ + + + diff --git a/src/lib/components/ShipEdit/Weaponry.svelte b/src/lib/components/ShipEdit/Weaponry.svelte index 7e3dec6..5d7431e 100644 --- a/src/lib/components/ShipEdit/Weaponry.svelte +++ b/src/lib/components/ShipEdit/Weaponry.svelte @@ -3,15 +3,18 @@ + + {#each weapons as weapon (weapon.id)} - + {/each} diff --git a/src/lib/components/ShipEdit/Weaponry/Weapon/Beam/index.svelte b/src/lib/components/ShipEdit/Weaponry/Weapon/Beam/index.svelte index b7dc8cc..e481655 100644 --- a/src/lib/components/ShipEdit/Weaponry/Weapon/Beam/index.svelte +++ b/src/lib/components/ShipEdit/Weaponry/Weapon/Beam/index.svelte @@ -54,7 +54,6 @@ $: if (!arc_options[weaponClass].includes(nbrArcs)) { nbrArcs = arc_options[weaponClass][0]; - console.log({ nbrArcs, label: "in if" }); } const broadside = ["FS", "FP", "AP", "AS"]; @@ -72,7 +71,6 @@ ); } - $: console.log({ newArcs, arcs }); if ( arcs.length !== newArcs.length || arcs.length !== R.intersection(arcs, newArcs).length @@ -83,8 +81,6 @@ $: if (arcs.length !== nbrArcs) setArcs(arcs[0]); - $: console.log("it changed!", arcs); - const dispatch = createEventDispatcher(); let i = 5; diff --git a/src/lib/components/ShipEdit/Weaponry/Weapon/SML/index.stories.js b/src/lib/components/ShipEdit/Weaponry/Weapon/SML/index.stories.js new file mode 100644 index 0000000..784570d --- /dev/null +++ b/src/lib/components/ShipEdit/Weaponry/Weapon/SML/index.stories.js @@ -0,0 +1,18 @@ +import SML from "./index.svelte"; + +export default { + title: "Salvo Missile Launcher", + component: SML, +}; + +export const Primary = { + render: (args) => ({ + Component: SML, + props: args, + }), + args: { + availableMissileMagazineIds: [1, 2, 3], + missileMagazineId: 1, + arcs: ["F", "FS", "FP"], + }, +}; diff --git a/src/lib/components/ShipEdit/Weaponry/Weapon/SML/index.story.svelte b/src/lib/components/ShipEdit/Weaponry/Weapon/SML/index.story.svelte new file mode 100644 index 0000000..b5e96b5 --- /dev/null +++ b/src/lib/components/ShipEdit/Weaponry/Weapon/SML/index.story.svelte @@ -0,0 +1,8 @@ + + + + + diff --git a/src/lib/components/ShipEdit/Weaponry/Weapon/SML/index.svelte b/src/lib/components/ShipEdit/Weaponry/Weapon/SML/index.svelte new file mode 100644 index 0000000..91c2047 --- /dev/null +++ b/src/lib/components/ShipEdit/Weaponry/Weapon/SML/index.svelte @@ -0,0 +1,74 @@ +salvo missile launcher + +
+ setFirstArc(detail)} + /> +
+ +
+ + +
+ + + + diff --git a/src/lib/components/ShipEdit/Weaponry/Weapon/SML/index.svelte.orig b/src/lib/components/ShipEdit/Weaponry/Weapon/SML/index.svelte.orig new file mode 100644 index 0000000..afe76ff --- /dev/null +++ b/src/lib/components/ShipEdit/Weaponry/Weapon/SML/index.svelte.orig @@ -0,0 +1,79 @@ +salvo missile launcher + +
+ setFirstArc(detail)} + /> +
+ +
+ + +
+ + + + diff --git a/src/lib/components/ShipEdit/Weaponry/Weapon/index.svelte b/src/lib/components/ShipEdit/Weaponry/Weapon/index.svelte index c1b2815..ec974f6 100644 --- a/src/lib/components/ShipEdit/Weaponry/Weapon/index.svelte +++ b/src/lib/components/ShipEdit/Weaponry/Weapon/index.svelte @@ -2,7 +2,12 @@
Delete - +
@@ -21,6 +26,7 @@ import Torpedo from "./Torpedo/index.svelte"; import Missile from "./HeavyMissile/index.svelte"; import SalvoMissileRack from "./SalvoMissileRack.svelte"; + import SalvoMissileLauncher from "./SML/index.svelte"; const component = { beam: Beam, @@ -32,11 +38,13 @@ torpedo: Torpedo, heavyMissile: Missile, smr: SalvoMissileRack, + sml: SalvoMissileLauncher, }; export let reqs = {}; export let specs = {}; export let id; + export let nbrMissileMagazines = 0; const api = getContext("api"); @@ -45,7 +53,6 @@ const remove = () => api?.dispatch?.removeWeapon?.(id); const update = ({ detail }) => { - console.log({ id, type }); api?.dispatch?.setWeapon?.(id, { type, ...detail, diff --git a/src/lib/components/ShipEdit/Weaponry/Weaponry.stories.svelte.todo b/src/lib/components/ShipEdit/Weaponry/Weaponry.stories.svelte.todo index 4072200..35be883 100644 --- a/src/lib/components/ShipEdit/Weaponry/Weaponry.stories.svelte.todo +++ b/src/lib/components/ShipEdit/Weaponry/Weaponry.stories.svelte.todo @@ -21,7 +21,6 @@ import { readable, get, derived } from "svelte/store"; const store = weaponryDux.createStore(); const state = readable(store.getState(), (set) => { store.subscribe(() => { - console.log(store.getState()); set(store.getState()); }); }); diff --git a/src/lib/components/Weapon/Beam/index.svelte b/src/lib/components/Weapon/Beam/index.svelte index b0fc2c4..23ca009 100644 --- a/src/lib/components/Weapon/Beam/index.svelte +++ b/src/lib/components/Weapon/Beam/index.svelte @@ -46,8 +46,6 @@ let nbr_arcs = 6; $: nbr_arcs = arc_options[weapon_class][0]; - $: console.log({ arcs, nbr_arcs }); - $: if (arcs.length !== nbr_arcs) { if (nbr_arcs === "broadside") { arcs = all_arcs.filter((arc) => arc.length === 1); diff --git a/src/lib/components/Weapons/Add.svelte b/src/lib/components/Weapons/Add.svelte index 8cf4bf1..42b1b8d 100644 --- a/src/lib/components/Weapons/Add.svelte +++ b/src/lib/components/Weapons/Add.svelte @@ -97,8 +97,6 @@ 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; }); diff --git a/src/lib/store/__snapshots__/ship.test.ts.snap b/src/lib/store/__snapshots__/ship.test.ts.snap index 4f447f0..4d7d3d6 100644 --- a/src/lib/store/__snapshots__/ship.test.ts.snap +++ b/src/lib/store/__snapshots__/ship.test.ts.snap @@ -101,6 +101,7 @@ exports[`state has the expected shape 1`] = ` }, "stations": 0, }, + "missileMagazines": [], "weapons": [], }, } diff --git a/src/lib/store/ship.ts b/src/lib/store/ship.ts index b7ba86c..70d9980 100644 --- a/src/lib/store/ship.ts +++ b/src/lib/store/ship.ts @@ -17,6 +17,7 @@ import { armorDux } from "./ship/structure/armor"; import { fireconsDux } from "./ship/weaponry/firecons"; import { adfcDux } from "./ship/weaponry/adfc"; import { weaponsDux } from "./ship/weaponry/weapons"; +import { weaponryDux } from "./ship/weaponry"; if (typeof process !== "undefined") { process.env.UPDEEP_MODE = "dangerously_never_freeze"; @@ -42,15 +43,6 @@ const propulsion = new Updux({ }, }); -const weaponry = new Updux({ - initialState: {}, - subduxes: { - adfc: adfcDux, - firecons: fireconsDux, - weapons: weaponsDux, - }, -}); - const restore = createPayloadAction("restore"); const importShip = createPayloadAction("importShip"); @@ -68,7 +60,7 @@ const shipDux = new Updux({ structure, propulsion, carrier: carrierDux, - weaponry, + weaponry: weaponryDux, }, }); diff --git a/src/lib/store/ship/weaponry/index.test.ts b/src/lib/store/ship/weaponry/index.test.ts new file mode 100644 index 0000000..2e84027 --- /dev/null +++ b/src/lib/store/ship/weaponry/index.test.ts @@ -0,0 +1,50 @@ +import { weaponryDux } from "./index.ts"; + +test("sml and magazine", () => { + const store = weaponryDux.createStore(); + + expect(store.getState().missileMagazines).toHaveLength(0); + + store.dispatch.addWeapon("sml"); + store.dispatch.addWeapon("sml"); + expect(store.getState().missileMagazines).toHaveLength(2); + + expect(store.getState().missileMagazines[0]).toMatchObject({ + extended: false, + maxAmmo: 1, + }); + + store.dispatch.setMissileMagazine(1, 3, true); + + expect(store.getState().missileMagazines[0]).toMatchObject({ + extended: true, + maxAmmo: 3, + }); + + store.dispatch.addWeapon("sml"); + expect(store.getState().missileMagazines).toHaveLength(3); + + expect(store.getState().missileMagazines[2]).toHaveProperty("id", 3); + // they all get assigned '1' at birth + expect(store.getState().weapons[2].specs).toHaveProperty( + "missileMagazineId", + 1 + ); + + store.dispatch.setWeapon(3, { + missileMagazineId: 3, + arcs: ["F", "FS", "FP"], + }); + + expect(store.getState().weapons[2].specs).toHaveProperty( + "missileMagazineId", + 3 + ); + + store.dispatch.removeWeapon(store.getState().weapons[1].id); + + expect(store.getState().missileMagazines).toHaveLength(2); + + expect(store.getState().weapons[1].specs.missileMagazineId).toEqual(2); + expect(store.getState().missileMagazines[1].id).toEqual(2); +}); diff --git a/src/lib/store/ship/weaponry/index.ts b/src/lib/store/ship/weaponry/index.ts new file mode 100644 index 0000000..378f4da --- /dev/null +++ b/src/lib/store/ship/weaponry/index.ts @@ -0,0 +1,116 @@ +import Updux, { createPayloadAction } from "updux"; +import { adfcDux } from "./adfc"; +import { fireconsDux } from "./firecons"; +import { weaponsDux } from "./weapons"; +import type { Reqs } from "$lib/shipDux/reqs"; +import u from "@yanick/updeep-remeda"; +import * as R from "remeda"; + +if (typeof process !== "undefined") { + process.env.UPDEEP_MODE = "dangerously_never_freeze"; +} + +type MissileMagazine = { + id: number; + maxAmmo: number; + extended: boolean; +}; + +const setMissileMagazine = createPayloadAction( + "setMissileMagazine", + (id, maxAmmo, extended = undefined) => ({ id, maxAmmo, extended }) +); + +const moveMissileMagazine = createPayloadAction( + "moveMissileMagazine", + (from, to) => ({ + from, + to, + }) +); + +const removeMissileMagazine = createPayloadAction( + "removeMissileMagazine" +); + +const magazinesDux = new Updux({ + actions: { setMissileMagazine, removeMissileMagazine, moveMissileMagazine }, + initialState: [] as MissileMagazine[], +}); + +magazinesDux.addMutation(weaponsDux.actions.addWeapon, (payload) => (state) => { + if (payload !== "sml") return state; + + state.push({ + id: state.length + 1, + extended: false, + maxAmmo: 1, + reqs: magazineReqs({ extended: false, maxAmmo: 1 }), + }); +}); + +magazinesDux.addMutation(setMissileMagazine, (payload) => + u.map( + u.if(u.matches({ id: payload.id }), (state) => { + state = u(state, payload); + return u(state, { reqs: magazineReqs(state) }); + }) + ) +); +magazinesDux.addMutation(moveMissileMagazine, ({ from, to }) => + u.map(u.if(u.matches({ id: from }), { id: to })) +); + +magazinesDux.addMutation(removeMissileMagazine, (id) => + u.reject(u.matches({ id })) +); + +magazinesDux.addReaction((api) => (store) => { + store + .map(R.prop("id")) + .filter(({ id }, i) => id !== i + 1) + .forEach((id, i) => api.dispatch.moveMissileMagazine(id, i + 1)); +}); + +function magazineReqs(magazine: MissileMagazine): Reqs { + let mass = magazine.maxAmmo * (magazine.extended ? 3 : 2); + return { mass, cost: 3 * mass }; +} + +export const weaponryDux = new Updux({ + initialState: {}, + subduxes: { + adfc: adfcDux, + firecons: fireconsDux, + weapons: weaponsDux, + missileMagazines: magazinesDux, + }, +}); + +weaponryDux.addMutation(moveMissileMagazine, ({ from, to }) => + u({ + weapons: u.map( + u.if(u.matches({ specs: { missileMagazineId: from } }), { + specs: { missileMagazineId: to }, + }) + ), + }) +); + +weaponryDux.addReaction((api) => (state) => { + const smls = state.weapons.filter(u.matches({ specs: { type: "sml" } })); + + const usedMagazines = smls.map( + ({ specs: { missileMagazineId } }) => missileMagazineId + ); + + const unusedMags = state.missileMagazines + .map(R.prop("id")) + .filter((id) => !usedMagazines.includes(id)); + + unusedMags.forEach((id) => api.dispatch.setMissileMagazine(id, 0)); + + if (smls.length >= state.missileMagazines.length) return; + + api.dispatch.removeMissileMagazine(unusedMags[0]); +}); diff --git a/src/lib/store/ship/weaponry/rules.ts b/src/lib/store/ship/weaponry/rules.ts index e4184c4..707c448 100644 --- a/src/lib/store/ship/weaponry/rules.ts +++ b/src/lib/store/ship/weaponry/rules.ts @@ -40,6 +40,18 @@ type SalvoMissileRack = { extended: boolean; }; +type MissileMagazine = { + id: number; + maxAmmo: number; + extended: boolean; +}; + +type SalvoMissileLauncher = { + type: "salvoMissileLauncher"; + arcs: Arc[]; + missileMagazineId: number; +}; + type Graser = { type: "graser"; weaponClass: 1 | 2 | 3; @@ -60,7 +72,8 @@ export type Weapon = | Needle | Graser | Torpedo - | HeavyMissile; + | HeavyMissile + | SalvoMissileLauncher; export const weaponTypes = [ { @@ -155,6 +168,16 @@ export const weaponTypes = [ type: "smr", }, }, + { + name: "salvo missile launcher", + type: "sml", + reqs: { cost: 9, mass: 3 }, + initial: { + arcs: ["FP", "F", "FS"], + type: "sml", + missileMagazineId: 1, + }, + }, ]; export function weaponReqs(weapon): Reqs { diff --git a/src/routes/(editor)/export/print/PrintShip/Weapons/SML/index.stories.js b/src/routes/(editor)/export/print/PrintShip/Weapons/SML/index.stories.js new file mode 100644 index 0000000..6be7cb1 --- /dev/null +++ b/src/routes/(editor)/export/print/PrintShip/Weapons/SML/index.stories.js @@ -0,0 +1,16 @@ +import SML from "./index.svelte"; + +export default { + title: "PrintShip/Weapons/SML", + component: SML, + tags: ["autodocs"], +}; + +export const Primary = { + args: { + magazine: { + maxAmmo: 3, + }, + launchers: [{ arcs: ["F"] }, { arcs: ["FS"] }], + }, +}; diff --git a/src/routes/(editor)/export/print/PrintShip/Weapons/SML/index.svelte b/src/routes/(editor)/export/print/PrintShip/Weapons/SML/index.svelte new file mode 100644 index 0000000..869ecac --- /dev/null +++ b/src/routes/(editor)/export/print/PrintShip/Weapons/SML/index.svelte @@ -0,0 +1,59 @@ +
+
+ {#each launchers as launcher, i (i)} +
+ +
 
+
+ {/each} +
+
+ {#each Array.from({ length: magazine.maxAmmo }).fill(1) as a, i (i)} + + {/each} +
+
+ {#if magazine.extended} +
extended range
+ {/if} +
+
+ + + + diff --git a/src/routes/(editor)/export/print/PrintShip/Weapons/printComps.js b/src/routes/(editor)/export/print/PrintShip/Weapons/printComps.js index 516a706..7b0c2a3 100644 --- a/src/routes/(editor)/export/print/PrintShip/Weapons/printComps.js +++ b/src/routes/(editor)/export/print/PrintShip/Weapons/printComps.js @@ -7,6 +7,7 @@ import Graser from "./Graser/index.svelte"; import Torpedo from "./Torpedo/index.svelte"; import HeavyMissile from "./HeavyMissile/index.svelte"; import SalvoMissileRack from "./SMR/index.svelte"; +import SalvoMissileLauncher from "./SML/index.svelte"; export default { torpedo: Torpedo, @@ -18,4 +19,5 @@ export default { needle: Needlebeam, heavyMissile: HeavyMissile, smr: SalvoMissileRack, + sml: SalvoMissileLauncher, }; diff --git a/src/routes/(editor)/export/print/PrintShip/index.svelte b/src/routes/(editor)/export/print/PrintShip/index.svelte index 2562d1a..c4de762 100644 --- a/src/routes/(editor)/export/print/PrintShip/index.svelte +++ b/src/routes/(editor)/export/print/PrintShip/index.svelte @@ -15,6 +15,15 @@ +
+ {#each Object.keys(smls) as magId (magId)} + id == magId)} + /> + {/each} +
+ @@ -61,6 +70,8 @@ import Beams from "./Weapons/Beams.svelte"; import HeavyMissiles from "./Weapons/HeavyMissiles.svelte"; import SalvoMissileRack from "./Weapons/SMR/index.svelte"; + import SML from "./Weapons/SML/index.svelte"; + import * as R from "remeda"; export let identification = {}; export let propulsion = {}; @@ -76,11 +87,13 @@ weapons, u.matches({ specs: { - type: (t) => ["smr", "pds", "beam", "heavyMissile"].includes(t), + type: (t) => ["sml", "smr", "pds", "beam", "heavyMissile"].includes(t), }, }) ); + $: magazines = weaponry.missileMagazines; + $: pds = (weaponry?.weapons ?? []).filter( u.matches({ specs: { type: "pds" } }) ); @@ -93,6 +106,10 @@ $: smrs = (weaponry?.weapons ?? []).filter( u.matches({ specs: { type: "smr" } }) ); + $: smls = R.groupBy( + (weaponry?.weapons ?? []).filter(u.matches({ specs: { type: "sml" } })), + ({ specs: { missileMagazineId } }) => missileMagazineId + );