ShipEdit with propulsion

docks66-json-schema
Yanick Champoux 2023-03-22 13:04:47 -04:00
parent cdb7e8ee35
commit a415278b6a
9 changed files with 112 additions and 44 deletions

View File

@ -116,7 +116,7 @@
border: 1px solid var(--indigo-dye);
}
input:not([type="checkbox"]) {
input:not([type="checkbox"]):not([type="radio"]) {
border: 0px;
border-bottom: 1px solid var(--indigo-dye);
border-radius: 0px;

View File

@ -1,13 +1,14 @@
<main>
<Identification {...ship.identification} />
<Propulsion {...ship.propulsion} />
</main>
<script>
import Identification from "./ShipEdit/Identification.svelte";
import Propulsion from "./ShipEdit/Propulsion.svelte";
import shipDux from "$lib/store/ship";
export let ship = {
identification: {},
};
export let ship = shipDux.initial;
</script>
<style>

View File

@ -25,7 +25,7 @@
export let rating = 0;
export let api = getContext("api");
$: api?.dispatch?.setEngine({ rating, advanced });
$: api?.dispatch?.setEngine?.({ rating, advanced });
</script>
<style>

View File

@ -1,6 +1,6 @@
<ShipItem {...reqs}>
<Field label="FTL drive">
{#each types as t (t)}
{#each ftlTypes as t (t)}
<label
><input type="radio" bind:group={type} value={t} />
{t}
@ -15,13 +15,13 @@
import ShipItem from "$lib/components/ShipItem.svelte";
import Field from "$lib/components/Field.svelte";
import { ftlTypes } from "$lib/store/ship/propulsion/ftl";
export let type = "none";
export let reqs = { mass: 0, cost: 0 };
export let api = getContext("api");
const types = ["none", "standard", "advanced"];
$: api?.dispatch.setFtl(type);
$: api?.dispatch.setFtlType?.(type);
</script>
<style>

View File

@ -5,43 +5,17 @@ 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];
export const initialState = {
reqs,
type: "none" as FtlType,
};
const ftl = createSlice({
name: "ftl",
initialState,
reducers: {
setFtl: (state, { payload }: PayloadAction<FtlType>) => {
state.type = payload;
name: "ftl",
initialState,
reducers: {
setFtl: (state, { payload }: PayloadAction<FtlType>) => {
state.type = payload;
},
setFtlReqs: (state, action: PayloadAction<Reqs>) => {
state.reqs = action.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

@ -0,0 +1,10 @@
import { test, expect } from "vitest";
import ship from "./ship";
test("ftlReqs reaction", () => {
const store = ship.createStore();
store.dispatch.setFtlType("standard");
expect(store.getState().propulsion.ftl.reqs.mass).toEqual(1);
expect(store.getState().identification.reqs.usedMass).toEqual(1);
});

View File

@ -1,11 +1,48 @@
import { createSelector } from "@reduxjs/toolkit";
import Updux from "updux";
import * as R from "remeda";
import identification from "./ship/identification";
import ftl, { calcFtlReqs } from "./ship/propulsion/ftl";
const shipDux = new Updux({
subduxes: {
identification,
propulsion: new Updux({
subduxes: {
ftl,
},
}),
},
});
shipDux.addReaction((api) =>
createSelector(
api.selectors.getFtlType,
api.selectors.getShipMass,
(type, mass) => api.dispatch.setFtlReqs(calcFtlReqs(type, mass))
)
);
shipDux.addReaction((api) => (state) => {
let cost = 0;
let mass = 0;
let subsystems = R.values(R.omit(state, ["identification"]));
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));
}
api.dispatch.setShipReqs({ cost, usedMass: mass });
});
export default shipDux;

View File

@ -14,16 +14,22 @@ const initial = {
const setShipClass = createAction("setShipClass", withPayload<string>());
const updateIdentification = createAction("updateIdentification");
const setShipReqs = createAction("setShipReqs", withPayload());
export const dux = new Updux({
initial,
actions: {
setShipClass,
updateIdentification,
setShipReqs,
},
selectors: {
getShipMass: (state) => state.reqs.mass,
},
});
dux.addMutation(setShipClass, (shipClass) => u({ shipClass }));
dux.addMutation(updateIdentification, (update) => u(update));
dux.addMutation(setShipReqs, (reqs) => u({ reqs }));
export default dux;

View File

@ -0,0 +1,40 @@
import { reqs, type Reqs } from "$lib/shipDux/reqs";
import Updux, { createAction, withPayload } from "updux";
import u from "@yanick/updeep-remeda";
import * as R from "remeda";
export const ftlTypes = ["none", "standard", "advanced"] as const;
type FtlType = typeof ftlTypes[number];
export const initial = {
reqs,
type: "none" as FtlType,
};
const setFtlType = createAction("setFtlType", withPayload<FtlType>());
const setFtlReqs = createAction("setFtlReqs", withPayload<Reqs>());
const dux = new Updux({
initial,
actions: { setFtlType, setFtlReqs },
selectors: {
getFtlType: R.prop<any, any>("type"),
},
});
dux.addMutation(setFtlType, (type) => u({ type }));
dux.addMutation(setFtlReqs, (reqs) => u({ reqs }));
export default dux;
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),
};
}