From 8bb1955dff3b36e70676bc2461f09ce78e1eea19 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Mon, 27 Mar 2023 12:59:32 -0400 Subject: [PATCH] got all subduxes in --- src/lib/shipDux/weaponry/index.ts | 52 +++++--------- src/lib/store/ship.test.ts | 11 +++ src/lib/store/ship.ts | 53 ++++++++++----- src/lib/store/ship/structure/armor.ts | 52 ++++++++++++++ src/lib/store/ship/weaponry/adfc.ts | 20 ++++++ src/lib/store/ship/weaponry/firecons.ts | 20 ++++++ src/lib/store/ship/weaponry/rules.ts | 90 +++++++++++++++++++++++++ src/lib/store/ship/weaponry/weapons.ts | 46 +++++++++++++ 8 files changed, 293 insertions(+), 51 deletions(-) create mode 100644 src/lib/store/ship/structure/armor.ts create mode 100644 src/lib/store/ship/weaponry/adfc.ts create mode 100644 src/lib/store/ship/weaponry/firecons.ts create mode 100644 src/lib/store/ship/weaponry/rules.ts create mode 100644 src/lib/store/ship/weaponry/weapons.ts diff --git a/src/lib/shipDux/weaponry/index.ts b/src/lib/shipDux/weaponry/index.ts index 000453e..5ee92b3 100644 --- a/src/lib/shipDux/weaponry/index.ts +++ b/src/lib/shipDux/weaponry/index.ts @@ -2,44 +2,26 @@ import { combineReducers, createSlice } from "@reduxjs/toolkit"; import { reqs } from "../reqs.js"; import * as weapons from "./weapons.js"; -const firecons = createSlice({ - name: "firecons", - initialState: { - stations: 0, - reqs, - }, - reducers: { - setFirecons(state, action) { - const stations = action.payload; - state.stations = action.payload; - state.reqs = { - cost: 4 * stations, - mass: stations, - }; - }, - }, -}); - const adfc = createSlice({ - name: "adfc", - initialState: { - rating: 0, - reqs, - }, - reducers: { - setAdfc(state, action) { - const rating = action.payload; - state.rating = action.payload; - state.reqs = { - cost: 8 * rating, - mass: 2 * rating, - }; - }, + name: "adfc", + initialState: { + rating: 0, + reqs, + }, + reducers: { + setAdfc(state, action) { + const rating = action.payload; + state.rating = action.payload; + state.reqs = { + cost: 8 * rating, + mass: 2 * rating, + }; }, + }, }); export const reducer = combineReducers({ - adfc: adfc.reducer, - firecons: firecons.reducer, - weapons: weapons.reducer, + adfc: adfc.reducer, + firecons: firecons.reducer, + weapons: weapons.reducer, }); diff --git a/src/lib/store/ship.test.ts b/src/lib/store/ship.test.ts index dfedc16..3e52631 100644 --- a/src/lib/store/ship.test.ts +++ b/src/lib/store/ship.test.ts @@ -36,4 +36,15 @@ test("kicking the tires", () => { cost: 0, }, }); + + store.dispatch.setNbrArmorLayers(1); + store.dispatch.setArmorRating(1, 3); + + expect(store.getState().structure.armor).toEqual({ + layers: [3], + reqs: { + cost: 6, + mass: 6, + }, + }); }); diff --git a/src/lib/store/ship.ts b/src/lib/store/ship.ts index d9e1d88..10073aa 100644 --- a/src/lib/store/ship.ts +++ b/src/lib/store/ship.ts @@ -13,27 +13,48 @@ import { calcStreamliningReqs } from "./ship/structure/rules"; import { cargoDux } from "./ship/structure/cargo"; import { hullDux } from "./ship/structure/hull"; import { screensDux, screensReqsReaction } from "./ship/structure/screens"; +import { armorDux } from "./ship/structure/armor"; +import { fireconsDux } from "./ship/weaponry/firecons"; +import { adfcDux } from "./ship/weaponry/adfc"; +import { weaponsDux } from "./ship/weaponry/weapons"; + +process.env.UPDEEP_MODE = "dangerously_never_freeze"; + +const structure = new Updux({ + initialState: {}, + subduxes: { + streamlining, + cargo: cargoDux, + hull: hullDux, + screens: screensDux, + armor: armorDux, + }, +}); + +const propulsion = new Updux({ + initialState: {}, + subduxes: { + ftl, + drive, + }, +}); + +const weaponry = new Updux({ + initialState: {}, + subduxes: { + adfc: adfcDux, + firecons: fireconsDux, + weapons: weaponsDux, + }, +}); const shipDux = new Updux({ subduxes: { identification, - structure: new Updux({ - initialState: {}, - subduxes: { - streamlining, - cargo: cargoDux, - hull: hullDux, - screens: screensDux, - }, - }), - propulsion: new Updux({ - initialState: {}, - subduxes: { - ftl, - drive, - }, - }), + structure, + propulsion, carrier: carrierDux, + weaponry, }, }); diff --git a/src/lib/store/ship/structure/armor.ts b/src/lib/store/ship/structure/armor.ts new file mode 100644 index 0000000..18e7cee --- /dev/null +++ b/src/lib/store/ship/structure/armor.ts @@ -0,0 +1,52 @@ +import { reqs, type Reqs } from "$lib/shipDux/reqs"; +import Updux, { createPayloadAction } from "updux"; +import u from "@yanick/updeep-remeda"; +import { createSelector } from "reselect"; +import prepare from "immer"; + +type Layer = number; + +const initialState = { + reqs, + layers: [] as Layer[], +}; + +const setNbrArmorLayers = createPayloadAction("setNbrArmorLayers"); +const setArmorRating = createPayloadAction( + "setArmorRating", + (id: number, rating: number) => ({ index: id - 1, rating }) +); + +export const armorDux = new Updux({ + initialState, + actions: { setNbrArmorLayers, setArmorRating }, +}); + +armorDux.addMutation(setArmorRating, ({ index, rating }) => + prepare((state) => { + state.layers[index] = rating; + state.reqs = calcArmorReqs(state.layers); + }) +); + +armorDux.addMutation(setNbrArmorLayers, (nbrLayers) => + prepare((state) => { + while (state.layers.length > nbrLayers) { + state.layers.pop(); + } + while (state.layers.length < nbrLayers) { + state.layers.push(0); + } + state.reqs = calcArmorReqs(state.layers); + }) +); + +function calcArmorReqs(layers: Layer[]): Reqs { + 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, + }; +} diff --git a/src/lib/store/ship/weaponry/adfc.ts b/src/lib/store/ship/weaponry/adfc.ts new file mode 100644 index 0000000..6038db6 --- /dev/null +++ b/src/lib/store/ship/weaponry/adfc.ts @@ -0,0 +1,20 @@ +import { reqs } from "$lib/shipDux/reqs"; +import Updux, { createPayloadAction } from "updux"; +import u from "@yanick/updeep-remeda"; + +export const adfcDux = new Updux({ + initialState: { + rating: 0, + reqs, + }, + actions: { + setADFC: (rating: number) => rating, + }, +}); + +adfcDux.addMutation(adfcDux.actions.setADFC, (rating) => + u({ + rating, + reqs: { cost: 8 * rating, mass: 2 * rating }, + }) +); diff --git a/src/lib/store/ship/weaponry/firecons.ts b/src/lib/store/ship/weaponry/firecons.ts new file mode 100644 index 0000000..24f2076 --- /dev/null +++ b/src/lib/store/ship/weaponry/firecons.ts @@ -0,0 +1,20 @@ +import { reqs } from "$lib/shipDux/reqs"; +import Updux, { createPayloadAction } from "updux"; +import u from "@yanick/updeep-remeda"; + +export const fireconsDux = new Updux({ + initialState: { + stations: 0, + reqs, + }, + actions: { + setFirecons: (stations: number) => stations, + }, +}); + +fireconsDux.addMutation(fireconsDux.actions.setFirecons, (stations) => + u({ + stations, + reqs: { cost: 4 * stations, mass: stations }, + }) +); diff --git a/src/lib/store/ship/weaponry/rules.ts b/src/lib/store/ship/weaponry/rules.ts new file mode 100644 index 0000000..1230483 --- /dev/null +++ b/src/lib/store/ship/weaponry/rules.ts @@ -0,0 +1,90 @@ +import type { Reqs } from "$lib/shipDux/reqs"; + +export const weaponTypes = [ + { + name: "beam", + type: "beam", + reqs: beamReqs, + 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" }, + }, +]; + +export function weaponReqs(weapon): Reqs { + 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 beamReqs({ 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, + }; +} diff --git a/src/lib/store/ship/weaponry/weapons.ts b/src/lib/store/ship/weaponry/weapons.ts new file mode 100644 index 0000000..e37fb0e --- /dev/null +++ b/src/lib/store/ship/weaponry/weapons.ts @@ -0,0 +1,46 @@ +import type { Reqs } from "$lib/shipDux/reqs"; +import Updux from "updux"; +import u from "@yanick/updeep-remeda"; +import * as R from "remeda"; +import { weaponReqs } from "./rules"; +import { nanoid } from "@reduxjs/toolkit"; + +type Weapon = { + weaponClass: string; + arcs?: unknown[]; + type: string; +}; + +type IndexedWeapon = { id: string; reqs: Reqs; specs: Weapon }; + +export const weaponsDux = new Updux({ + initialState: [] as IndexedWeapon[], + actions: { + removeWeapon: (id: string) => id, + setWeapon: (id: string, specs: Weapon) => ({ id, specs }), + addWeapon: (specs: Weapon) => specs, + }, +}); + +weaponsDux.addMutation(weaponsDux.actions.removeWeapon, (id) => + R.reject(u.matches({ id })) +); + +weaponsDux.addMutation( + weaponsDux.actions.setWeapon, + ({ id, specs }) => + (state) => { + const weapon = state.find(u.matches({ id })); + if (!weapon) return; + weapon.specs = specs; + weapon.reqs = weaponReqs(specs); + } +); + +weaponsDux.addMutation(weaponsDux.actions.addWeapon, (specs) => (state) => { + state.push({ + id: nanoid(), + specs, + reqs: weaponReqs(specs), + }); +});