port them all

docks66-json-schema
Yanick Champoux 2023-03-03 13:23:13 -05:00
parent 82331d1f26
commit 11b89b308a
32 changed files with 719 additions and 696 deletions

View File

@ -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;

View File

@ -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<number>) => {
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,
};
}

View File

@ -0,0 +1,26 @@
import { combineReducers } from "redux";
import { createAction, Slice } from "@reduxjs/toolkit";
import * as R from "remeda";
type MergedActions<S extends { actions: any }> = S["actions"];
export default function combineSlices<
S extends Record<string, Slice> = Record<string, Slice>
>(slices: S) {
const reducer = combineReducers(R.mapValues(slices, R.prop("reducer")));
const actions: MergedActions<S[keyof S]> = 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"),
},
},
});

View File

@ -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;

40
src/lib/shipDux/engine.ts Normal file
View File

@ -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<number>) {
state.rating = action.payload;
},
setDriveAdvanced(state, action: PayloadAction<boolean>) {
state.advanced = action.payload;
},
setDriverReqs(state, action: PayloadAction<Reqs>) {
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;

View File

@ -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;

View File

@ -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;

106
src/lib/shipDux/index.ts Normal file
View File

@ -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;

View File

@ -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;

View File

@ -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<DriveProps>) => {
state.rating = action.payload.rating;
state.advanced = action.payload.advanced;
},
setDriveReqs: (state, action: PayloadAction<Reqs>) => {
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 };
}
*/

View File

@ -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))
);

View File

@ -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<FtlType>) => {
state.type = payload;
},
setFtlReqs: (state, action: PayloadAction<Reqs>) => {
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))
);

View File

@ -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;

View File

@ -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,
});

View File

@ -1,10 +0,0 @@
import { Updux } from "updux";
const dux = new Updux({
initial: {
cost: 0,
mass: 0,
},
});
export default dux;

6
src/lib/shipDux/reqs.ts Normal file
View File

@ -0,0 +1,6 @@
export type Reqs = {
mass: number;
cost: number;
};
export const reqs = { cost: 0, mass: 0 };

View File

@ -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
}
}

View File

@ -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<number>) => {
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,
};
}

View File

@ -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 }
}));

View File

@ -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;

View File

@ -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);
})

View File

@ -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<number>) => {
state.rating = action.payload;
state.reqs = {
mass: action.payload,
cost: 2 * action.payload,
}
},
setShipMass: (state, action: PayloadAction<number>) => {
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;

View File

@ -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;

View File

@ -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")
)
);

View File

@ -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,
};
}

View File

@ -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,
}
}

View File

@ -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 };
}

View File

@ -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 };
}

View File

@ -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;

View File

@ -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,
});

View File

@ -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;

View File

@ -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<number>) => {
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,
};
}