sml
This commit is contained in:
parent
bf99b8dd4e
commit
1b9a5ab253
@ -8,6 +8,8 @@ releases:
|
|||||||
changes:
|
changes:
|
||||||
- desc: add SMRs
|
- desc: add SMRs
|
||||||
type: feat
|
type: feat
|
||||||
|
- desc: add SMLs
|
||||||
|
type: feat
|
||||||
- version: 3.1.0
|
- version: 3.1.0
|
||||||
changes:
|
changes:
|
||||||
- desc: add version and changelog to the about section
|
- desc: add version and changelog to the about section
|
||||||
|
40
src/lib/components/ShipEdit/MissileMagazines.svelte
Normal file
40
src/lib/components/ShipEdit/MissileMagazines.svelte
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{#if magazines.length > 0}
|
||||||
|
<ShipItem {...reqs}>
|
||||||
|
<div>missile magazines</div>
|
||||||
|
<div class="magazines">
|
||||||
|
{#each magazines as magazine (magazine.id)}
|
||||||
|
<Magazine {...magazine} unused={magazine.launchers.length === 0} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</ShipItem>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getContext } from "svelte";
|
||||||
|
|
||||||
|
import ShipItem from "$lib/components/ShipItem.svelte";
|
||||||
|
import Magazine from "./MissileMagazines/Magazine.svelte";
|
||||||
|
|
||||||
|
export let magazines = [];
|
||||||
|
|
||||||
|
let reqs = { cost: 0, mass: 0 };
|
||||||
|
|
||||||
|
const api = getContext("api");
|
||||||
|
|
||||||
|
$: {
|
||||||
|
reqs = { cost: 0, mass: 0 };
|
||||||
|
magazines.forEach(({ reqs: r }) => {
|
||||||
|
reqs.cost += r.cost;
|
||||||
|
reqs.mass += r.mass;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMagazine = () => {};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.magazines {
|
||||||
|
display: flex;
|
||||||
|
margin-left: 2em;
|
||||||
|
}
|
||||||
|
</style>
|
42
src/lib/components/ShipEdit/MissileMagazines/Magazine.svelte
Normal file
42
src/lib/components/ShipEdit/MissileMagazines/Magazine.svelte
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<div class="magazine">
|
||||||
|
<div class="field label">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
min={unused ? 0 : 1}
|
||||||
|
disabled={unused}
|
||||||
|
bind:value={maxAmmo}
|
||||||
|
/>
|
||||||
|
<label class="active">magazine {id}</label>
|
||||||
|
</div>
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox" bind:checked={extended} disabled={unused} />
|
||||||
|
<span>extended range</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getContext } from "svelte";
|
||||||
|
|
||||||
|
export let id = 1;
|
||||||
|
export let maxAmmo = 0;
|
||||||
|
export let extended = false;
|
||||||
|
export let unused = false;
|
||||||
|
|
||||||
|
export let api = getContext("api");
|
||||||
|
|
||||||
|
$: api?.dispatch?.setMissileMagazine?.(id, maxAmmo, extended);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.magazine {
|
||||||
|
max-width: 20em;
|
||||||
|
margin-right: 2em;
|
||||||
|
}
|
||||||
|
.magazine input {
|
||||||
|
max-width: 8em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.field {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
@ -3,15 +3,18 @@
|
|||||||
|
|
||||||
<ADFC {...adfc} />
|
<ADFC {...adfc} />
|
||||||
|
|
||||||
|
<MissileMagazines magazines={missileMagazines} />
|
||||||
|
|
||||||
<AddWeapon />
|
<AddWeapon />
|
||||||
|
|
||||||
{#each weapons as weapon (weapon.id)}
|
{#each weapons as weapon (weapon.id)}
|
||||||
<Weapon {...weapon} />
|
<Weapon {...weapon} nbrMissileMagazines={missileMagazines.length} />
|
||||||
{/each}
|
{/each}
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte";
|
import { getContext } from "svelte";
|
||||||
|
import u from "@yanick/updeep-remeda";
|
||||||
|
|
||||||
import Section from "$lib/components/Section.svelte";
|
import Section from "$lib/components/Section.svelte";
|
||||||
|
|
||||||
@ -21,11 +24,28 @@
|
|||||||
import Firecons from "./Weaponry/Firecons.svelte";
|
import Firecons from "./Weaponry/Firecons.svelte";
|
||||||
import ADFC from "./Weaponry/ADFC.svelte";
|
import ADFC from "./Weaponry/ADFC.svelte";
|
||||||
import AddWeapon from "./Weaponry/AddWeapon.svelte";
|
import AddWeapon from "./Weaponry/AddWeapon.svelte";
|
||||||
|
import MissileMagazines from "./MissileMagazines.svelte";
|
||||||
|
import Weapon from "./Weaponry/Weapon/index.svelte";
|
||||||
|
|
||||||
export let firecons = {};
|
export let firecons = {};
|
||||||
export let adfc = {};
|
export let adfc = {};
|
||||||
|
export let missileMagazines = [];
|
||||||
import Weapon from "./Weaponry/Weapon/index.svelte";
|
|
||||||
|
|
||||||
export let weapons = [];
|
export let weapons = [];
|
||||||
|
|
||||||
|
$: missileMagazines = addLaunchersToMagazines(missileMagazines, weapons);
|
||||||
|
|
||||||
|
function addLaunchersToMagazines(magazines, weapons) {
|
||||||
|
return u.map(magazines, (mag) =>
|
||||||
|
u(mag, {
|
||||||
|
launchers: weapons.filter(
|
||||||
|
u.matches({
|
||||||
|
specs: {
|
||||||
|
type: "sml",
|
||||||
|
missileMagazineId: mag.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -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"],
|
||||||
|
},
|
||||||
|
};
|
@ -0,0 +1,8 @@
|
|||||||
|
<Hst.Story title="ShipEdit/Weaponry/Weapons/SML">
|
||||||
|
<SML />
|
||||||
|
</Hst.Story>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export let Hst;
|
||||||
|
import SML from "./index.svelte";
|
||||||
|
</script>
|
74
src/lib/components/ShipEdit/Weaponry/Weapon/SML/index.svelte
Normal file
74
src/lib/components/ShipEdit/Weaponry/Weapon/SML/index.svelte
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<span>salvo missile launcher</span>
|
||||||
|
|
||||||
|
<div class="arcs">
|
||||||
|
<Arcs
|
||||||
|
size={48}
|
||||||
|
selected={arcs}
|
||||||
|
on:clickArc={({ detail }) => setFirstArc(detail)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field label suffix">
|
||||||
|
<select bind:value={missileMagazineId}>
|
||||||
|
{#each availableMissileMagazineIds as id}
|
||||||
|
<option value={id}>{id}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
<label class:active={true}>magazine</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import u from "@yanick/updeep-remeda";
|
||||||
|
import { createEventDispatcher } from "svelte";
|
||||||
|
import memoize from "memoize-one";
|
||||||
|
|
||||||
|
import Arcs from "../Arcs.svelte";
|
||||||
|
import {
|
||||||
|
weaponTypes,
|
||||||
|
arcs as allArcs,
|
||||||
|
} from "$lib/store/ship/weaponry/rules.ts";
|
||||||
|
|
||||||
|
export let arcs = ["F", "FS", "FP"];
|
||||||
|
export let missileMagazineId;
|
||||||
|
|
||||||
|
export let nbrMissileMagazines = 1;
|
||||||
|
$: availableMissileMagazineIds = Array.from({ length: nbrMissileMagazines })
|
||||||
|
.fill(1)
|
||||||
|
.map((_, i) => i + 1);
|
||||||
|
|
||||||
|
const nbrArcs = 3;
|
||||||
|
|
||||||
|
let firstArc = allArcs[0];
|
||||||
|
const setFirstArc = (a) => (firstArc = a);
|
||||||
|
|
||||||
|
$: arcs = setArcs(firstArc, nbrArcs);
|
||||||
|
|
||||||
|
function setArcs(firstArc, nbrArcs) {
|
||||||
|
let first_index = allArcs.findIndex((arc) => arc === firstArc);
|
||||||
|
if (first_index === -1) first_index = 0;
|
||||||
|
|
||||||
|
return Array.from({ length: nbrArcs }).map(
|
||||||
|
(_dummy, i) => allArcs[(first_index + i) % allArcs.length]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
const memoChange = memoize((missileMagazineId, ...arcs) =>
|
||||||
|
dispatch("change", {
|
||||||
|
missileMagazineId,
|
||||||
|
arcs,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
$: memoChange(missileMagazineId, ...arcs);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.arcs {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
label span {
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,79 @@
|
|||||||
|
<span>salvo missile launcher</span>
|
||||||
|
|
||||||
|
<div class="arcs">
|
||||||
|
<Arcs
|
||||||
|
size={48}
|
||||||
|
selected={arcs}
|
||||||
|
on:clickArc={({ detail }) => setFirstArc(detail)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field label suffix">
|
||||||
|
<select bind:value={missileMagazineId}>
|
||||||
|
{#each availableMissileMagazineIds as id}
|
||||||
|
<option value={id}>{id}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
<label class:active={true}>magazine</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import u from "@yanick/updeep-remeda";
|
||||||
|
import { createEventDispatcher } from "svelte";
|
||||||
|
import memoize from "memoize-one";
|
||||||
|
|
||||||
|
import Arcs from "../Arcs.svelte";
|
||||||
|
import {
|
||||||
|
weaponTypes,
|
||||||
|
arcs as allArcs,
|
||||||
|
} from "$lib/store/ship/weaponry/rules.ts";
|
||||||
|
|
||||||
|
export let arcs = ["F", "FS", "FP"];
|
||||||
|
<<<<<<< HEAD
|
||||||
|
export let missileMagazineId;
|
||||||
|
=======
|
||||||
|
export let availableMissileMagazineIds = [1];
|
||||||
|
export let missileMagazineId = 1;
|
||||||
|
>>>>>>> a5a09f9 (storybook is back)
|
||||||
|
|
||||||
|
export let nbrMissileMagazines = 1;
|
||||||
|
$: availableMissileMagazineIds = Array.from({ length: nbrMissileMagazines })
|
||||||
|
.fill(1)
|
||||||
|
.map((_, i) => i + 1);
|
||||||
|
|
||||||
|
const nbrArcs = 3;
|
||||||
|
|
||||||
|
let firstArc = allArcs[0];
|
||||||
|
const setFirstArc = (a) => (firstArc = a);
|
||||||
|
|
||||||
|
$: arcs = setArcs(firstArc, nbrArcs);
|
||||||
|
|
||||||
|
function setArcs(firstArc, nbrArcs) {
|
||||||
|
let first_index = allArcs.findIndex((arc) => arc === firstArc);
|
||||||
|
if (first_index === -1) first_index = 0;
|
||||||
|
|
||||||
|
return Array.from({ length: nbrArcs }).map(
|
||||||
|
(_dummy, i) => allArcs[(first_index + i) % allArcs.length]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
const memoChange = memoize((missileMagazineId, ...arcs) =>
|
||||||
|
dispatch("change", {
|
||||||
|
missileMagazineId,
|
||||||
|
arcs,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
$: memoChange(missileMagazineId, ...arcs);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.arcs {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
label span {
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
@ -2,7 +2,12 @@
|
|||||||
<div class="weapon_row">
|
<div class="weapon_row">
|
||||||
<a on:click={remove}><i>Delete</i> </a>
|
<a on:click={remove}><i>Delete</i> </a>
|
||||||
|
|
||||||
<svelte:component this={component[type]} {...specs} on:change={update} />
|
<svelte:component
|
||||||
|
this={component[type]}
|
||||||
|
{...specs}
|
||||||
|
{nbrMissileMagazines}
|
||||||
|
on:change={update}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ShipItem>
|
</ShipItem>
|
||||||
|
|
||||||
@ -21,6 +26,7 @@
|
|||||||
import Torpedo from "./Torpedo/index.svelte";
|
import Torpedo from "./Torpedo/index.svelte";
|
||||||
import Missile from "./HeavyMissile/index.svelte";
|
import Missile from "./HeavyMissile/index.svelte";
|
||||||
import SalvoMissileRack from "./SalvoMissileRack.svelte";
|
import SalvoMissileRack from "./SalvoMissileRack.svelte";
|
||||||
|
import SalvoMissileLauncher from "./SML/index.svelte";
|
||||||
|
|
||||||
const component = {
|
const component = {
|
||||||
beam: Beam,
|
beam: Beam,
|
||||||
@ -32,11 +38,13 @@
|
|||||||
torpedo: Torpedo,
|
torpedo: Torpedo,
|
||||||
heavyMissile: Missile,
|
heavyMissile: Missile,
|
||||||
smr: SalvoMissileRack,
|
smr: SalvoMissileRack,
|
||||||
|
sml: SalvoMissileLauncher,
|
||||||
};
|
};
|
||||||
|
|
||||||
export let reqs = {};
|
export let reqs = {};
|
||||||
export let specs = {};
|
export let specs = {};
|
||||||
export let id;
|
export let id;
|
||||||
|
export let nbrMissileMagazines = 0;
|
||||||
|
|
||||||
const api = getContext("api");
|
const api = getContext("api");
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import { armorDux } from "./ship/structure/armor";
|
|||||||
import { fireconsDux } from "./ship/weaponry/firecons";
|
import { fireconsDux } from "./ship/weaponry/firecons";
|
||||||
import { adfcDux } from "./ship/weaponry/adfc";
|
import { adfcDux } from "./ship/weaponry/adfc";
|
||||||
import { weaponsDux } from "./ship/weaponry/weapons";
|
import { weaponsDux } from "./ship/weaponry/weapons";
|
||||||
|
import { weaponryDux } from "./ship/weaponry";
|
||||||
|
|
||||||
if (typeof process !== "undefined") {
|
if (typeof process !== "undefined") {
|
||||||
process.env.UPDEEP_MODE = "dangerously_never_freeze";
|
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<typeof shipDux.initialState>("restore");
|
const restore = createPayloadAction<typeof shipDux.initialState>("restore");
|
||||||
const importShip =
|
const importShip =
|
||||||
createPayloadAction<typeof shipDux.initialState>("importShip");
|
createPayloadAction<typeof shipDux.initialState>("importShip");
|
||||||
@ -68,7 +60,7 @@ const shipDux = new Updux({
|
|||||||
structure,
|
structure,
|
||||||
propulsion,
|
propulsion,
|
||||||
carrier: carrierDux,
|
carrier: carrierDux,
|
||||||
weaponry,
|
weaponry: weaponryDux,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
52
src/lib/store/ship/weaponry/index.test.ts
Normal file
52
src/lib/store/ship/weaponry/index.test.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
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);
|
||||||
|
|
||||||
|
console.log(store.getState());
|
||||||
|
|
||||||
|
expect(store.getState().weapons[1].specs.missileMagazineId).toEqual(2);
|
||||||
|
expect(store.getState().missileMagazines[1].id).toEqual(2);
|
||||||
|
});
|
116
src/lib/store/ship/weaponry/index.ts
Normal file
116
src/lib/store/ship/weaponry/index.ts
Normal file
@ -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<number>(
|
||||||
|
"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]);
|
||||||
|
});
|
@ -40,6 +40,18 @@ type SalvoMissileRack = {
|
|||||||
extended: boolean;
|
extended: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type MissileMagazine = {
|
||||||
|
id: number;
|
||||||
|
maxAmmo: number;
|
||||||
|
extended: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SalvoMissileLauncher = {
|
||||||
|
type: "salvoMissileLauncher";
|
||||||
|
arcs: Arc[];
|
||||||
|
missileMagazineId: number;
|
||||||
|
};
|
||||||
|
|
||||||
type Graser = {
|
type Graser = {
|
||||||
type: "graser";
|
type: "graser";
|
||||||
weaponClass: 1 | 2 | 3;
|
weaponClass: 1 | 2 | 3;
|
||||||
@ -60,7 +72,8 @@ export type Weapon =
|
|||||||
| Needle
|
| Needle
|
||||||
| Graser
|
| Graser
|
||||||
| Torpedo
|
| Torpedo
|
||||||
| HeavyMissile;
|
| HeavyMissile
|
||||||
|
| SalvoMissileLauncher;
|
||||||
|
|
||||||
export const weaponTypes = [
|
export const weaponTypes = [
|
||||||
{
|
{
|
||||||
@ -155,6 +168,16 @@ export const weaponTypes = [
|
|||||||
type: "smr",
|
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 {
|
export function weaponReqs(weapon): Reqs {
|
||||||
|
@ -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"] }],
|
||||||
|
},
|
||||||
|
};
|
@ -0,0 +1,59 @@
|
|||||||
|
<div class="sml">
|
||||||
|
<div class="launchers">
|
||||||
|
{#each launchers as launcher, i (i)}
|
||||||
|
<div>
|
||||||
|
<SMR {...launcher} />
|
||||||
|
<div class="line"> </div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<div class="magazine">
|
||||||
|
{#each Array.from({ length: magazine.maxAmmo }).fill(1) as a, i (i)}
|
||||||
|
<img src="/icons/missile.svg" width="18" />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{#if magazine.extended}
|
||||||
|
<div>extended range</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SMR from "../SMR/index.svelte";
|
||||||
|
|
||||||
|
export let launchers = [];
|
||||||
|
|
||||||
|
/** the missile magazine feeding the launchers */
|
||||||
|
export let magazine = {};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.sml {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.launchers {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.launchers > div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.line {
|
||||||
|
width: 0.2em;
|
||||||
|
background-color: black;
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
.magazine {
|
||||||
|
border: 0.2em black solid;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
padding: 0.3em;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
}
|
||||||
|
</style>
|
@ -7,6 +7,7 @@ import Graser from "./Graser/index.svelte";
|
|||||||
import Torpedo from "./Torpedo/index.svelte";
|
import Torpedo from "./Torpedo/index.svelte";
|
||||||
import HeavyMissile from "./HeavyMissile/index.svelte";
|
import HeavyMissile from "./HeavyMissile/index.svelte";
|
||||||
import SalvoMissileRack from "./SMR/index.svelte";
|
import SalvoMissileRack from "./SMR/index.svelte";
|
||||||
|
import SalvoMissileLauncher from "./SML/index.svelte";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
torpedo: Torpedo,
|
torpedo: Torpedo,
|
||||||
@ -18,4 +19,5 @@ export default {
|
|||||||
needle: Needlebeam,
|
needle: Needlebeam,
|
||||||
heavyMissile: HeavyMissile,
|
heavyMissile: HeavyMissile,
|
||||||
smr: SalvoMissileRack,
|
smr: SalvoMissileRack,
|
||||||
|
sml: SalvoMissileLauncher,
|
||||||
};
|
};
|
||||||
|
@ -15,6 +15,15 @@
|
|||||||
<HeavyMissiles {heavyMissiles} />
|
<HeavyMissiles {heavyMissiles} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="weapon-group">
|
||||||
|
{#each Object.keys(smls) as magId (magId)}
|
||||||
|
<SML
|
||||||
|
launchers={smls[magId].map(R.prop("specs"))}
|
||||||
|
magazine={magazines.find(({ id }) => id == magId)}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
<Beams {beams} />
|
<Beams {beams} />
|
||||||
|
|
||||||
<Weapons {weapons} />
|
<Weapons {weapons} />
|
||||||
@ -61,6 +70,8 @@
|
|||||||
import Beams from "./Weapons/Beams.svelte";
|
import Beams from "./Weapons/Beams.svelte";
|
||||||
import HeavyMissiles from "./Weapons/HeavyMissiles.svelte";
|
import HeavyMissiles from "./Weapons/HeavyMissiles.svelte";
|
||||||
import SalvoMissileRack from "./Weapons/SMR/index.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 identification = {};
|
||||||
export let propulsion = {};
|
export let propulsion = {};
|
||||||
@ -76,11 +87,14 @@
|
|||||||
weapons,
|
weapons,
|
||||||
u.matches({
|
u.matches({
|
||||||
specs: {
|
specs: {
|
||||||
type: (t) => ["smr", "pds", "beam", "heavyMissile"].includes(t),
|
type: (t) => ["sml", "smr", "pds", "beam", "heavyMissile"].includes(t),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$: magazines = weaponry.missileMagazines;
|
||||||
|
$: console.log({ magazines });
|
||||||
|
|
||||||
$: pds = (weaponry?.weapons ?? []).filter(
|
$: pds = (weaponry?.weapons ?? []).filter(
|
||||||
u.matches({ specs: { type: "pds" } })
|
u.matches({ specs: { type: "pds" } })
|
||||||
);
|
);
|
||||||
@ -93,6 +107,14 @@
|
|||||||
$: smrs = (weaponry?.weapons ?? []).filter(
|
$: smrs = (weaponry?.weapons ?? []).filter(
|
||||||
u.matches({ specs: { type: "smr" } })
|
u.matches({ specs: { type: "smr" } })
|
||||||
);
|
);
|
||||||
|
$: console.log(
|
||||||
|
(weaponry?.weapons ?? []).filter(u.matches({ specs: { type: "sml" } }))
|
||||||
|
);
|
||||||
|
$: smls = R.groupBy(
|
||||||
|
(weaponry?.weapons ?? []).filter(u.matches({ specs: { type: "sml" } })),
|
||||||
|
({ specs: { missileMagazineId } }) => missileMagazineId
|
||||||
|
);
|
||||||
|
$: console.log({ smls });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -7,7 +7,7 @@ import git from "git-describe";
|
|||||||
/** @type {import('vite').UserConfig} */
|
/** @type {import('vite').UserConfig} */
|
||||||
const config = {
|
const config = {
|
||||||
plugins: [sveltekit()],
|
plugins: [sveltekit()],
|
||||||
// publicDir: "./static",
|
publicDir: "./static",
|
||||||
ssr: {},
|
ssr: {},
|
||||||
optimizeDeps: {},
|
optimizeDeps: {},
|
||||||
define: {
|
define: {
|
||||||
|
Loading…
Reference in New Issue
Block a user