From 11b89b308a61dd14ebd21c3ffd4f7e266b7dcef9 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Fri, 3 Mar 2023 13:23:13 -0500 Subject: [PATCH] port them all --- src/lib/shipDux/carrier.js | 76 ----------- src/lib/shipDux/carrier.ts | 79 +++++++++++ src/lib/shipDux/combineSlices.ts | 26 ++++ src/lib/shipDux/engine.js | 28 ---- src/lib/shipDux/engine.ts | 40 ++++++ .../{identification.js => identification.ts} | 10 +- src/lib/shipDux/index.js | 109 --------------- src/lib/shipDux/index.ts | 106 ++++++++++++++ src/lib/shipDux/propulsion/drive.js | 41 ------ src/lib/shipDux/propulsion/drive.ts | 50 +++++++ src/lib/shipDux/propulsion/ftl.js | 41 ------ src/lib/shipDux/propulsion/ftl.ts | 47 +++++++ src/lib/shipDux/propulsion/index.js | 13 -- src/lib/shipDux/propulsion/index.ts | 8 ++ src/lib/shipDux/reqs.js | 10 -- src/lib/shipDux/reqs.ts | 6 + src/lib/shipDux/structure/armor.js | 64 --------- src/lib/shipDux/structure/armor.ts | 35 +++++ src/lib/shipDux/structure/cargo.js | 24 ---- src/lib/shipDux/structure/cargo.ts | 17 +++ src/lib/shipDux/structure/hull.js | 31 ----- src/lib/shipDux/structure/hull.ts | 34 +++++ src/lib/shipDux/structure/index.js | 15 -- src/lib/shipDux/structure/index.ts | 21 +++ src/lib/shipDux/structure/screen.ts | 42 ++++++ src/lib/shipDux/structure/screens.js | 40 ------ src/lib/shipDux/structure/streamlining.js | 32 ----- src/lib/shipDux/structure/streamlining.ts | 28 ++++ src/lib/shipDux/weaponry/index.js | 46 ------- src/lib/shipDux/weaponry/index.ts | 45 ++++++ src/lib/shipDux/weaponry/weapons.js | 122 ----------------- src/lib/shipDux/weaponry/weapons.ts | 129 ++++++++++++++++++ 32 files changed, 719 insertions(+), 696 deletions(-) delete mode 100644 src/lib/shipDux/carrier.js create mode 100644 src/lib/shipDux/carrier.ts create mode 100644 src/lib/shipDux/combineSlices.ts delete mode 100644 src/lib/shipDux/engine.js create mode 100644 src/lib/shipDux/engine.ts rename src/lib/shipDux/{identification.js => identification.ts} (74%) delete mode 100644 src/lib/shipDux/index.js create mode 100644 src/lib/shipDux/index.ts delete mode 100644 src/lib/shipDux/propulsion/drive.js create mode 100644 src/lib/shipDux/propulsion/drive.ts delete mode 100644 src/lib/shipDux/propulsion/ftl.js create mode 100644 src/lib/shipDux/propulsion/ftl.ts delete mode 100644 src/lib/shipDux/propulsion/index.js create mode 100644 src/lib/shipDux/propulsion/index.ts delete mode 100644 src/lib/shipDux/reqs.js create mode 100644 src/lib/shipDux/reqs.ts delete mode 100644 src/lib/shipDux/structure/armor.js create mode 100644 src/lib/shipDux/structure/armor.ts delete mode 100644 src/lib/shipDux/structure/cargo.js create mode 100644 src/lib/shipDux/structure/cargo.ts delete mode 100644 src/lib/shipDux/structure/hull.js create mode 100644 src/lib/shipDux/structure/hull.ts delete mode 100644 src/lib/shipDux/structure/index.js create mode 100644 src/lib/shipDux/structure/index.ts create mode 100644 src/lib/shipDux/structure/screen.ts delete mode 100644 src/lib/shipDux/structure/screens.js delete mode 100644 src/lib/shipDux/structure/streamlining.js create mode 100644 src/lib/shipDux/structure/streamlining.ts delete mode 100644 src/lib/shipDux/weaponry/index.js create mode 100644 src/lib/shipDux/weaponry/index.ts delete mode 100644 src/lib/shipDux/weaponry/weapons.js create mode 100644 src/lib/shipDux/weaponry/weapons.ts diff --git a/src/lib/shipDux/carrier.js b/src/lib/shipDux/carrier.js deleted file mode 100644 index 6d8d145..0000000 --- a/src/lib/shipDux/carrier.js +++ /dev/null @@ -1,76 +0,0 @@ -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; diff --git a/src/lib/shipDux/carrier.ts b/src/lib/shipDux/carrier.ts new file mode 100644 index 0000000..d4592f3 --- /dev/null +++ b/src/lib/shipDux/carrier.ts @@ -0,0 +1,79 @@ +import { PayloadAction, createSlice } from "@reduxjs/toolkit"; +import { reqs, Reqs } from "./reqs"; + +type Squadron = { + type: string; + reqs: Reqs; +}; + +const initialState = { + bays: 0, + squadrons: [] as Squadron[], + reqs, +}; + +export const carrierSlice = createSlice({ + name: "carrier", + initialState, + reducers: { + setCarrierBays: (state, action: PayloadAction) => { + state.bays = action.payload; + state.reqs = calcBaysReqs(action.payload); + state.squadrons = adjustSquadrons(action.payload)(state.squadrons); + }, + setSquadronType: ( + state, + action: PayloadAction<{ type: string; id: number }> + ) => { + state.squadrons[action.payload.id - 1] = { + type: action.payload.type, + reqs: squadronReqs(action.payload.type), + }; + }, + }, +}); + +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: string) { + 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, + }; +} diff --git a/src/lib/shipDux/combineSlices.ts b/src/lib/shipDux/combineSlices.ts new file mode 100644 index 0000000..85f3de9 --- /dev/null +++ b/src/lib/shipDux/combineSlices.ts @@ -0,0 +1,26 @@ +import { combineReducers } from "redux"; +import { createAction, Slice } from "@reduxjs/toolkit"; +import * as R from "remeda"; + +type MergedActions = S["actions"]; + +export default function combineSlices< + S extends Record = Record +>(slices: S) { + const reducer = combineReducers(R.mapValues(slices, R.prop("reducer"))); + const actions: MergedActions = Object.values(slices) + .map(R.prop("actions")) + .map(Object.values) + .flat() as any; + + return { reducer, actions }; +} + +const x = combineSlices({ + a: { + reducer: (state) => state, + actions: { + a: createAction("a"), + }, + }, +}); diff --git a/src/lib/shipDux/engine.js b/src/lib/shipDux/engine.js deleted file mode 100644 index 1e33ed8..0000000 --- a/src/lib/shipDux/engine.js +++ /dev/null @@ -1,28 +0,0 @@ -import { Updux } from "updux"; -import u from "updeep"; - -import reqs from "./reqs.js"; - -const dux = new Updux({ - subduxes: { reqs }, - initial: { - rating: 1, - advanced: false, - }, - actions: { - setDrive: null, - setDriveReqs: null, - }, -}); - -dux.setMutation("setDrive", (changes) => u(changes)); -dux.setMutation("setDriveReqs", (reqs) => u({ reqs })); - -export function calcDriveReqs(shipMass, rating, advanced = false) { - const mass = Math.ceil(rating * 0.05 * shipMass); - const cost = mass * (advanced ? 3 : 2); - - return { mass, cost }; -} - -export default dux; diff --git a/src/lib/shipDux/engine.ts b/src/lib/shipDux/engine.ts new file mode 100644 index 0000000..b98a6c0 --- /dev/null +++ b/src/lib/shipDux/engine.ts @@ -0,0 +1,40 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { Updux } from "updux"; +import u from "updeep"; + +import { Reqs, reqs } from "./reqs.js"; + +const initialState = { + rating: 1, + advanced: false, + reqs, +}; + +const engine = createSlice({ + name: "engine", + initialState, + reducers: { + setDriveRating(state, action: PayloadAction) { + state.rating = action.payload; + }, + setDriveAdvanced(state, action: PayloadAction) { + state.advanced = action.payload; + }, + setDriverReqs(state, action: PayloadAction) { + state.reqs = action.payload; + }, + }, +}); + +export function calcDriveReqs( + shipMass: number, + rating: number, + advanced = false +) { + const mass = Math.ceil(rating * 0.05 * shipMass); + const cost = mass * (advanced ? 3 : 2); + + return { mass, cost }; +} + +export default engine; diff --git a/src/lib/shipDux/identification.js b/src/lib/shipDux/identification.ts similarity index 74% rename from src/lib/shipDux/identification.js rename to src/lib/shipDux/identification.ts index 71e4085..bdb647d 100644 --- a/src/lib/shipDux/identification.js +++ b/src/lib/shipDux/identification.ts @@ -1,13 +1,13 @@ import { Updux } from "updux"; import u from "updeep"; -import carrier from './carrier.js'; +import carrier from "./carrier.js"; const dux = new Updux({ actions: { setShipType: null, setShipClass: null, - setCarrierBays: carrier.actions.setCarrierBays, + setCarrierBays: carrier.actions.setCarrierBays, }, initial: { shipType: "", @@ -19,8 +19,10 @@ const dux = new Updux({ dux.setMutation("setShipType", (shipType) => u({ shipType })); dux.setMutation("setShipClass", (shipClass) => u({ shipClass })); -dux.setMutation('setCarrierBays', (bays) => u({ +dux.setMutation("setCarrierBays", (bays) => + u({ isCarrier: bays > 0, -})) + }) +); export default dux; diff --git a/src/lib/shipDux/index.js b/src/lib/shipDux/index.js deleted file mode 100644 index 16f884c..0000000 --- a/src/lib/shipDux/index.js +++ /dev/null @@ -1,109 +0,0 @@ -import { Updux } from "updux"; -import u from "updeep"; - -import propulsion from "./propulsion/index.js"; -import identification from "./identification.js"; -import { calculateDriveReqs } from "./propulsion/drive.js"; -import { ftlReqsReaction } from "./propulsion/ftl.js"; -import structure from "./structure/index.js"; -import carrier from "./carrier.js"; -import weaponry from "./weaponry/index.js"; -import { screensReqsReaction } from "./structure/screens.js"; - -const dux = new Updux({ - subduxes: { - identification, - propulsion, - structure, - carrier, - weaponry, - }, - initial: { - reqs: { cost: 0, mass: 10, usedMass: 0 }, - }, - actions: { - setShipReqs: null, - setUITransform: null, - resetLayout: null, - resetShip: null, - }, -}); - -function resetUITransform(thing) { - if (typeof thing !== "object") return thing; - - return u.map( - (v, k) => (k === "uiTransform" ? "" : resetUITransform(v)), - thing - ); -} - -dux.setMutation("resetShip", () => () => dux.initial); - -dux.setMutation("resetLayout", () => resetUITransform); - -dux.setMutation("setShipMass", (mass) => u({ reqs: { mass } })); -dux.setMutation("setShipReqs", (reqs) => u({ reqs })); - -dux.setMutation("setUITransform", ({ system, systemId, translate }) => { - const transform = translate - ? `translate(${translate[0]}px,${translate[1]}px)` - : ""; - - switch (system) { - case "firecons": - return u.updateIn("weaponry.firecons.uiTransform", transform); - - case "weapon": - return u.updateIn( - "weaponry.weapons", - u.map(u.if(({ id }) => id === systemId, u({ uiTransform: transform }))) - ); - - case "screens": - return u.updateIn("structure.screens.uiTransform", transform); - - case "hull": - return u.updateIn("structure.hull.uiTransform", transform); - - case "internalSystems": - const path = "structure.uiTransform"; - return u.updateIn(path, transform); - - case "ftl": - return u.updateIn("propulsion.ftl.uiTransform", transform); - - case "drive": - return u.updateIn("propulsion.drive.uiTransform", transform); - - default: - return (state) => state; - } -}); - -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 }); -}); - -export default dux; diff --git a/src/lib/shipDux/index.ts b/src/lib/shipDux/index.ts new file mode 100644 index 0000000..dfd4852 --- /dev/null +++ b/src/lib/shipDux/index.ts @@ -0,0 +1,106 @@ +import * as propulsion from "./propulsion/index.js"; +import * as identification from "./identification.js"; +import { calculateDriveReqs } from "./propulsion/drive.js"; +import { ftlReqsReaction } from "./propulsion/ftl.js"; +import * as structure from "./structure/index.js"; +import * as carrier from "./carrier.js"; +import * as weaponry from "./weaponry/index.js"; +import { screensReqsReaction } from "./structure/screens.js"; + +const dux = new Updux({ + subduxes: { + identification, + propulsion, + structure, + carrier, + weaponry, + }, + initial: { + reqs: { cost: 0, mass: 10, usedMass: 0 }, + }, + actions: { + setShipReqs: null, + setUITransform: null, + resetLayout: null, + resetShip: null, + }, +}); + +function resetUITransform(thing) { + if (typeof thing !== "object") return thing; + + return u.map( + (v, k) => (k === "uiTransform" ? "" : resetUITransform(v)), + thing + ); +} + +dux.setMutation("resetShip", () => () => dux.initial); + +dux.setMutation("resetLayout", () => resetUITransform); + +dux.setMutation("setShipMass", (mass) => u({ reqs: { mass } })); +dux.setMutation("setShipReqs", (reqs) => u({ reqs })); + +dux.setMutation("setUITransform", ({ system, systemId, translate }) => { + const transform = translate + ? `translate(${translate[0]}px,${translate[1]}px)` + : ""; + + switch (system) { + case "firecons": + return u.updateIn("weaponry.firecons.uiTransform", transform); + + case "weapon": + return u.updateIn( + "weaponry.weapons", + u.map(u.if(({ id }) => id === systemId, u({ uiTransform: transform }))) + ); + + case "screens": + return u.updateIn("structure.screens.uiTransform", transform); + + case "hull": + return u.updateIn("structure.hull.uiTransform", transform); + + case "internalSystems": + const path = "structure.uiTransform"; + return u.updateIn(path, transform); + + case "ftl": + return u.updateIn("propulsion.ftl.uiTransform", transform); + + case "drive": + return u.updateIn("propulsion.drive.uiTransform", transform); + + default: + return (state) => state; + } +}); + +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 }); +}); + +export default dux; diff --git a/src/lib/shipDux/propulsion/drive.js b/src/lib/shipDux/propulsion/drive.js deleted file mode 100644 index c6fbb48..0000000 --- a/src/lib/shipDux/propulsion/drive.js +++ /dev/null @@ -1,41 +0,0 @@ -import { Updux } from "updux"; -import u from "updeep"; -import { createSelector } from "reselect"; - -import reqs from "../reqs.js"; - -const dux = new Updux({ - subduxes: { reqs }, - initial: { - rating: 0, - advanced: false, - }, - actions: { - setDrive: null, - setDriveReqs: null, - }, -}); - -dux.setMutation("setDrive", (changes) => u(changes)); -dux.setMutation("setDriveReqs", (reqs) => u({ reqs })); - -// needs to be at the top level -export const calculateDriveReqs = (store) => - createSelector( - [ - (ship) => ship.reqs.mass, - (ship) => ship.propulsion.drive.rating, - (ship) => ship.propulsion.drive.advanced, - ], - (ship_mass, rating, advanced) => - store.dispatch.setDriveReqs(calcDriveReqs(ship_mass, rating, advanced)) - ); - -export function calcDriveReqs(shipMass, rating, advanced = false) { - const mass = Math.ceil(rating * 0.05 * shipMass); - const cost = mass * (advanced ? 3 : 2); - - return { mass, cost }; -} - -export default dux; diff --git a/src/lib/shipDux/propulsion/drive.ts b/src/lib/shipDux/propulsion/drive.ts new file mode 100644 index 0000000..ffbd3f0 --- /dev/null +++ b/src/lib/shipDux/propulsion/drive.ts @@ -0,0 +1,50 @@ +import { PayloadAction, createSlice } from "@reduxjs/toolkit"; +import { reqs, Reqs } from "../reqs"; + +type DriveProps = { + rating: number; + advanced: boolean; +}; + +const initialState: DriveProps & { reqs: Reqs } = { + rating: 0, + advanced: false, + reqs, +}; + +const driveSlice = createSlice({ + initialState, + name: "drive", + reducers: { + setDrive: (state, action: PayloadAction) => { + state.rating = action.payload.rating; + state.advanced = action.payload.advanced; + }, + setDriveReqs: (state, action: PayloadAction) => { + state.reqs = action.payload; + }, + }, +}); + +export const { actions, reducer } = driveSlice; + +/* +// needs to be at the top level +export const calculateDriveReqs = (store) => + createSelector( + [ + (ship) => ship.reqs.mass, + (ship) => ship.propulsion.drive.rating, + (ship) => ship.propulsion.drive.advanced, + ], + (ship_mass, rating, advanced) => + store.dispatch.setDriveReqs(calcDriveReqs(ship_mass, rating, advanced)) + ); + +export function calcDriveReqs(shipMass, rating, advanced = false) { + const mass = Math.ceil(rating * 0.05 * shipMass); + const cost = mass * (advanced ? 3 : 2); + + return { mass, cost }; +} +*/ diff --git a/src/lib/shipDux/propulsion/ftl.js b/src/lib/shipDux/propulsion/ftl.js deleted file mode 100644 index 1a38061..0000000 --- a/src/lib/shipDux/propulsion/ftl.js +++ /dev/null @@ -1,41 +0,0 @@ -import { Updux } from "updux"; -import u from "updeep"; -import { createSelector } from "reselect"; - -import reqs from "../reqs.js"; - -export const ftlTypes = ["none", "standard", "advanced"]; - -const dux = new Updux({ - subduxes: { reqs }, - initial: { - type: "none", - uiTransform: "", - }, - actions: { - setFtl: null, - setFtlReqs: null, - }, -}); -export default dux; - -dux.setMutation("setFtl", (type) => u({ type })); -dux.setMutation("setFtlReqs", (reqs) => u({ reqs })); - -export function calcFtlReqs(type, shipMass) { - if (type === "none") return { cost: 0, mass: 0 }; - - const mass = Math.ceil(shipMass / 10); - - return { - mass, - cost: mass * (type === "advanced" ? 3 : 2), - }; -} - -// needs to be at the top level -export const ftlReqsReaction = (store) => - createSelector( - [(ship) => ship.propulsion.ftl.type, (ship) => ship.reqs.mass], - (type, shipMass) => store.dispatch.setFtlReqs(calcFtlReqs(type, shipMass)) - ); diff --git a/src/lib/shipDux/propulsion/ftl.ts b/src/lib/shipDux/propulsion/ftl.ts new file mode 100644 index 0000000..8d08f77 --- /dev/null +++ b/src/lib/shipDux/propulsion/ftl.ts @@ -0,0 +1,47 @@ +import { Updux } from "updux"; +import u from "updeep"; +import { createSelector } from "reselect"; +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; + +import { reqs, Reqs } from "../reqs.js"; + +export const ftlTypes = ["none", "standard", "advanced"] as const; +type FtlType = typeof ftlTypes[number]; + +const initialState = { + reqs, + type: "none" as FtlType, +}; + +const ftl = createSlice({ + name: "ftl", + initialState, + reducers: { + setFtl: (state, { payload }: PayloadAction) => { + state.type = payload; + }, + setFtlReqs: (state, action: PayloadAction) => { + state.reqs = action.payload; + }, + }, +}); + +export function calcFtlReqs(type: FtlType, shipMass: number): Reqs { + if (type === "none") return { cost: 0, mass: 0 }; + + const mass = Math.ceil(shipMass / 10); + + return { + mass, + cost: mass * (type === "advanced" ? 3 : 2), + }; +} + +export const { actions, reducer } = ftl; + +// needs to be at the top level +export const ftlReqsReaction = (store) => + createSelector( + [(ship) => ship.propulsion.ftl.type, (ship) => ship.reqs.mass], + (type, shipMass) => store.dispatch.setFtlReqs(calcFtlReqs(type, shipMass)) + ); diff --git a/src/lib/shipDux/propulsion/index.js b/src/lib/shipDux/propulsion/index.js deleted file mode 100644 index 47d28fc..0000000 --- a/src/lib/shipDux/propulsion/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import { Updux } from "updux"; -import u from 'updeep'; - -import drive from './drive.js'; -import ftl from './ftl.js'; - -const dux = new Updux({ - subduxes: { - drive, ftl - }, -}); - -export default dux; diff --git a/src/lib/shipDux/propulsion/index.ts b/src/lib/shipDux/propulsion/index.ts new file mode 100644 index 0000000..78142d0 --- /dev/null +++ b/src/lib/shipDux/propulsion/index.ts @@ -0,0 +1,8 @@ +import { combineReducers } from "@reduxjs/toolkit"; +import * as drive from "./drive.js"; +import * as ftl from "./ftl.js"; + +export const reducer = combineReducers({ + drive: drive.reducer, + ftl: ftl.reducer, +}); diff --git a/src/lib/shipDux/reqs.js b/src/lib/shipDux/reqs.js deleted file mode 100644 index 62ea2dd..0000000 --- a/src/lib/shipDux/reqs.js +++ /dev/null @@ -1,10 +0,0 @@ -import { Updux } from "updux"; - -const dux = new Updux({ - initial: { - cost: 0, - mass: 0, - }, -}); - -export default dux; diff --git a/src/lib/shipDux/reqs.ts b/src/lib/shipDux/reqs.ts new file mode 100644 index 0000000..16b931d --- /dev/null +++ b/src/lib/shipDux/reqs.ts @@ -0,0 +1,6 @@ +export type Reqs = { + mass: number; + cost: number; +}; + +export const reqs = { cost: 0, mass: 0 }; diff --git a/src/lib/shipDux/structure/armor.js b/src/lib/shipDux/structure/armor.js deleted file mode 100644 index 0724399..0000000 --- a/src/lib/shipDux/structure/armor.js +++ /dev/null @@ -1,64 +0,0 @@ -import { Updux } from "updux"; -import u from "updeep"; - -import reqs from "../reqs.js"; - -const schema = { - type: 'object', - properties: { - reqs: { type: 'object', properties: { - cost: { type: 'number' }, - mass: { type: 'number' }, - } }, - layers: { type: 'array', items: 'number' } - }, - examples: [ - { reqs: { cost: 5, mass: 5 }, layers: [2,4 ] } - ] -} - -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 - } -} diff --git a/src/lib/shipDux/structure/armor.ts b/src/lib/shipDux/structure/armor.ts new file mode 100644 index 0000000..bc95567 --- /dev/null +++ b/src/lib/shipDux/structure/armor.ts @@ -0,0 +1,35 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { Reqs, reqs } from "../reqs"; + +type Layer = number; + +const initialState = { + reqs, + layers: [] as Layer[], +}; + +const armor = createSlice({ + name: "armor", + initialState, + reducers: { + setArmorRating: (state, action) => { + state.layers[action.payload.layer - 1] = action.payload.rating; + state.reqs = calcArmorReqs(state.layers); + }, + setArmorLayers: (state, action: PayloadAction) => { + while (state.layers.length > action.payload) state.layers.pop(); + while (state.layers.length < action.payload) 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/shipDux/structure/cargo.js b/src/lib/shipDux/structure/cargo.js deleted file mode 100644 index 204d94b..0000000 --- a/src/lib/shipDux/structure/cargo.js +++ /dev/null @@ -1,24 +0,0 @@ -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 } -})); - diff --git a/src/lib/shipDux/structure/cargo.ts b/src/lib/shipDux/structure/cargo.ts new file mode 100644 index 0000000..d98ffa0 --- /dev/null +++ b/src/lib/shipDux/structure/cargo.ts @@ -0,0 +1,17 @@ +import { createSlice } from "@reduxjs/toolkit"; +import { reqs } from "../reqs"; + +const initialState = { space: 0, reqs }; + +const cargo = createSlice({ + name: "cargo", + initialState, + reducers: { + setCargo: (state, action) => { + state.space = action.payload; + state.reqs = { cost: 0, mass: action.payload }; + }, + }, +}); + +export const { actions, reducer } = cargo; diff --git a/src/lib/shipDux/structure/hull.js b/src/lib/shipDux/structure/hull.js deleted file mode 100644 index a91160c..0000000 --- a/src/lib/shipDux/structure/hull.js +++ /dev/null @@ -1,31 +0,0 @@ -import { Updux } from "updux"; -import u from 'updeep'; - -import reqs from '../reqs.js'; - -const dux = new Updux({ - subduxes: { - reqs - }, - initial: { - rating: 0, min: 0, max: 0, - }, - actions: { - setShipMass: null, - setHull: null, - } -}); -export default dux; - -dux.setMutation( 'setHull', rating => u({rating, - reqs: { -mass: rating, cost: 2 * rating - }}) ); - -dux.setMutation( 'setShipMass', mass => state => { - let {rating} = state; - if(rating > mass) rating = mass; - const min = Math.ceil(mass/10); - if(rating < min) rating = min; - return u({max: mass, min, rating}, state); -}) diff --git a/src/lib/shipDux/structure/hull.ts b/src/lib/shipDux/structure/hull.ts new file mode 100644 index 0000000..0da4623 --- /dev/null +++ b/src/lib/shipDux/structure/hull.ts @@ -0,0 +1,34 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { reqs } from "../reqs"; + +const initialState = { + rating: 0, + min: 0, + max: 0, + reqs, +}; + +const hull = createSlice({ + name: "hull", + initialState, + reducers: { + setHull: (state, action: PayloadAction) => { + state.rating = action.payload; + state.reqs = { + mass: action.payload, + cost: 2 * action.payload, + } + }, + setShipMass: (state, action: PayloadAction) => { + const mass = action.payload; + let { rating } = state; + if (rating > mass) state.rating = mass; + state.min = Math.ceil(mass / 10); + if (state.rating < state.min) state.rating = state.min; + state.max = mass; + }; + }, +}, +}); + +export const { actions, reducer } = hull; diff --git a/src/lib/shipDux/structure/index.js b/src/lib/shipDux/structure/index.js deleted file mode 100644 index d0a7684..0000000 --- a/src/lib/shipDux/structure/index.js +++ /dev/null @@ -1,15 +0,0 @@ -import { Updux } from "updux"; - -import hull from "./hull.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({ - subduxes: { hull, screens, cargo, streamlining, armor }, - initial: { - uiTransform: "", - }, -}); -export default dux; diff --git a/src/lib/shipDux/structure/index.ts b/src/lib/shipDux/structure/index.ts new file mode 100644 index 0000000..c775f89 --- /dev/null +++ b/src/lib/shipDux/structure/index.ts @@ -0,0 +1,21 @@ +import { combineReducers } from "redux"; +import * as R from "remeda"; + +import * as hull from "./hull.js"; +import * as screens from "./screens.js"; +import * as cargo from "./cargo.js"; +import * as armor from "./armor.js"; +import * as streamlining from "./streamlining.js"; + +export const reducer = combineReducers( + R.mapValues( + { + hull, + screens, + cargo, + armor, + streamlining, + }, + R.prop("reducer") + ) +); diff --git a/src/lib/shipDux/structure/screen.ts b/src/lib/shipDux/structure/screen.ts new file mode 100644 index 0000000..2309ef0 --- /dev/null +++ b/src/lib/shipDux/structure/screen.ts @@ -0,0 +1,42 @@ +import { createSelector, createSlice } from "@reduxjs/toolkit"; +import { reqs } from "../reqs"; + +const initialState = { + standard: 0, + advanced: 0, + reqs, +}; + +const screen = createSlice({ + name: "screens", + initialState, + reducers: { + setScreens(state, action) { + state.standard = action.payload.standard; + state.advanced = action.payload.advanced; + }, + setScreensReqs(state, action) { + state.reqs = action.payload; + }, + }, +}); + +export const { actions, reducer } = screen; + +export const screensReqsReaction = (store) => + createSelector( + (ship) => ship.reqs.mass, + (ship) => ship.structure.screens.standard, + (ship) => ship.structure.screens.advanced, + (...args) => store.dispatch.setScreensReqs(calcScreensReqs(...args)) + ); + +function calcScreensReqs(mass, standard, advanced) { + const standard_mass = standard * Math.max(3, Math.ceil(0.05 * mass)); + const advanced_mass = advanced * Math.max(4, Math.ceil(0.075 * mass)); + + return { + mass: standard_mass + advanced_mass, + cost: 3 * standard_mass + 4 * advanced_mass, + }; +} diff --git a/src/lib/shipDux/structure/screens.js b/src/lib/shipDux/structure/screens.js deleted file mode 100644 index 068e653..0000000 --- a/src/lib/shipDux/structure/screens.js +++ /dev/null @@ -1,40 +0,0 @@ -import { Updux } from "updux"; -import u from 'updeep'; -import { createSelector } from 'reselect'; - -import reqs from '../reqs.js'; - -const dux = new Updux({ - subduxes: { - reqs - }, - initial: { - standard: 0, advanced: 0, - }, - actions: { - setScreens: null, - setScreensReqs: null, - } -}); -export default dux; - -dux.setMutation('setScreens', payload => u(payload)); -dux.setMutation('setScreensReqs', reqs => u({reqs})); - -export const screensReqsReaction = store => createSelector( - (ship) => ship.reqs.mass, - (ship) => ship.structure.screens.standard, - (ship) => ship.structure.screens.advanced, - (...args) => store.dispatch.setScreensReqs(calcScreensReqs(...args)), -); - -function calcScreensReqs(mass,standard,advanced) { - - const standard_mass = standard * Math.max(3, Math.ceil(0.05 * mass)); - const advanced_mass = advanced * Math.max(4, Math.ceil(0.075 * mass)); - - return { - mass: standard_mass + advanced_mass, - cost: 3 * standard_mass + 4 * advanced_mass, - } -} diff --git a/src/lib/shipDux/structure/streamlining.js b/src/lib/shipDux/structure/streamlining.js deleted file mode 100644 index aca39e6..0000000 --- a/src/lib/shipDux/structure/streamlining.js +++ /dev/null @@ -1,32 +0,0 @@ -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 }; -} diff --git a/src/lib/shipDux/structure/streamlining.ts b/src/lib/shipDux/structure/streamlining.ts new file mode 100644 index 0000000..d8dc14a --- /dev/null +++ b/src/lib/shipDux/structure/streamlining.ts @@ -0,0 +1,28 @@ +import { createSlice } from "@reduxjs/toolkit"; +import { reqs } from "../reqs"; + +const initialState = { + type: "none", + reqs, +}; + +const streamlining = createSlice({ + name: "streamlining", + initialState, + reducers: { + setStreamlining(state, action) { + state.type = action.payload.type; + state.reqs = calcStreamliningReqs(action.payload); + }, + }, +}); + +export const { actions, reducer } = streamlining; + +function calcStreamliningReqs({ shipMass, type }) { + const mass = Math.ceil( + (shipMass * (type === "none" ? 0 : type === "partial" ? 5 : 10)) / 100 + ); + + return { mass, cost: 2 * mass }; +} diff --git a/src/lib/shipDux/weaponry/index.js b/src/lib/shipDux/weaponry/index.js deleted file mode 100644 index 7cfbed7..0000000 --- a/src/lib/shipDux/weaponry/index.js +++ /dev/null @@ -1,46 +0,0 @@ -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; diff --git a/src/lib/shipDux/weaponry/index.ts b/src/lib/shipDux/weaponry/index.ts new file mode 100644 index 0000000..000453e --- /dev/null +++ b/src/lib/shipDux/weaponry/index.ts @@ -0,0 +1,45 @@ +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, + }; + }, + }, +}); + +export const reducer = combineReducers({ + adfc: adfc.reducer, + firecons: firecons.reducer, + weapons: weapons.reducer, +}); diff --git a/src/lib/shipDux/weaponry/weapons.js b/src/lib/shipDux/weaponry/weapons.js deleted file mode 100644 index 6aa6167..0000000 --- a/src/lib/shipDux/weaponry/weapons.js +++ /dev/null @@ -1,122 +0,0 @@ -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; diff --git a/src/lib/shipDux/weaponry/weapons.ts b/src/lib/shipDux/weaponry/weapons.ts new file mode 100644 index 0000000..3a0f98d --- /dev/null +++ b/src/lib/shipDux/weaponry/weapons.ts @@ -0,0 +1,129 @@ +import { Updux } from "updux"; +import u from "updeep"; +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; + +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" }, + }, +]; + +const weaponsSlice = createSlice({ + name: "weapons", + initialState: [] as { + id: number; + weaponClass: string; + arcs?: unknown[]; + type: string; + }[], + reducers: { + removeWeapon: (state, action: PayloadAction) => { + return state.filter(({ id }) => id !== action.payload); + }, + setWeapon: (state, action: PayloadAction<{ id: number }>) => { + return state.map((weapon) => { + if (weapon.id !== action.payload.id) return weapon; + return { + ...action.payload, + reqs: weaponReqs(action.payload), + }; + }) as any; + }, + addWeapon: (state, action: PayloadAction<{ type: string }>) => { + const initial = weaponTypes.find( + (w) => w.type === action.payload.type + ).initial; + state.push({ + id: state.length + 1, + type: action.payload.type, + reqs: weaponReqs({ type: action.payload.type, ...initial }), + ...initial, + } as any); + }, + }, +}); + +export const { actions, reducer } = weaponsSlice; + +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 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, + }; +}