From cdf46c80b5a66018c89b61975fc14bc524e00158 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 3 Mar 2024 15:21:49 +0100 Subject: [PATCH] feat(charge): allow adding/removing charges to fits --- .storybook/fits.ts | 30 +++++-- package.json | 2 +- src/HardwareListing/HardwareListing.tsx | 13 ++- src/ShipFit/Slot.tsx | 41 +++++++-- .../ShipSnapshotProvider.tsx | 90 ++++++++++++++++++- 5 files changed, 159 insertions(+), 17 deletions(-) diff --git a/.storybook/fits.ts b/.storybook/fits.ts index cb3405d..75a6706 100644 --- a/.storybook/fits.ts +++ b/.storybook/fits.ts @@ -111,32 +111,50 @@ export const fullFit = { { "flag": 27, "quantity": 1, - "type_id": 25715 + "type_id": 25715, + "charge": { + "type_id": 20308 + } }, { "flag": 28, "quantity": 1, - "type_id": 25715 + "type_id": 25715, + "charge": { + "type_id": 20308 + } }, { "flag": 29, "quantity": 1, - "type_id": 25715 + "type_id": 25715, + "charge": { + "type_id": 20308 + } }, { "flag": 30, "quantity": 1, - "type_id": 25715 + "type_id": 25715, + "charge": { + "type_id": 20308 + } }, { "flag": 31, "quantity": 1, - "type_id": 25715 + "type_id": 25715, + "charge": { + "type_id": 20308 + } }, { "flag": 32, "quantity": 1, - "type_id": 25715 + "type_id": 25715, + "charge": { + "type_id": 20308 + } }, { "flag": 33, diff --git a/package.json b/package.json index 7c545eb..0d3037f 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "typescript-plugin-css-modules": "^5.0.2" }, "peerDependencies": { - "@eveshipfit/dogma-engine": "^2.5.1", + "@eveshipfit/dogma-engine": "^2.6.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/src/HardwareListing/HardwareListing.tsx b/src/HardwareListing/HardwareListing.tsx index 2a5aa43..0c2b9c4 100644 --- a/src/HardwareListing/HardwareListing.tsx +++ b/src/HardwareListing/HardwareListing.tsx @@ -50,7 +50,14 @@ const ModuleGroup = (props: { level: number; group: ListingGroup; hideGroup?: bo .sort((a, b) => a.meta - b.meta || a.name.localeCompare(b.name)) .map((item) => { if (item.slotType === "charge") { - return {}} />; + return ( + shipSnapShot.addCharge(item.typeId)} + /> + ); } else { const slotType = item.slotType; return ( @@ -152,7 +159,9 @@ export const HardwareListing = () => { setModulesWithCharges(newModulesWithCharges); - /* Reset the filter, as the ship is changed. */ + /* If the moduleWithCharge filter was set, validate if it is still valid. */ + if (newModulesWithCharges.find((charge) => charge.typeId === filter.moduleWithCharge?.typeId) !== undefined) return; + setFilter({ ...filter, moduleWithCharge: undefined, diff --git a/src/ShipFit/Slot.tsx b/src/ShipFit/Slot.tsx index 9427e57..45a63f8 100644 --- a/src/ShipFit/Slot.tsx +++ b/src/ShipFit/Slot.tsx @@ -58,6 +58,11 @@ export const Slot = (props: { type: string; index: number; fittable: boolean; ma + + + + + @@ -131,6 +136,16 @@ export const Slot = (props: { type: string; index: number; fittable: boolean; ma [shipSnapshot, esiItem], ); + const unfitCharge = React.useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + if (!shipSnapshot?.loaded || !esiItem) return; + + shipSnapshot.removeCharge(esiItem.flag); + }, + [shipSnapshot, esiItem], + ); + /* Not fittable and nothing fitted; no need to render the slot. */ if (esiItem === undefined && !props.fittable) { return ( @@ -143,12 +158,21 @@ export const Slot = (props: { type: string; index: number; fittable: boolean; ma } if (esiItem !== undefined) { - item = ( - - ); + if (esiItem.charge !== undefined) { + item = ( + + ); + } else { + item = ( + + ); + } } const state = esiItem?.state === "Passive" && esiItem?.max_state !== "Passive" ? "Offline" : esiItem?.state; @@ -160,6 +184,11 @@ export const Slot = (props: { type: string; index: number; fittable: boolean; ma
{item}
+ {esiItem?.charge !== undefined && ( + + + + )} diff --git a/src/ShipSnapshotProvider/ShipSnapshotProvider.tsx b/src/ShipSnapshotProvider/ShipSnapshotProvider.tsx index 421635c..fde5d7f 100644 --- a/src/ShipSnapshotProvider/ShipSnapshotProvider.tsx +++ b/src/ShipSnapshotProvider/ShipSnapshotProvider.tsx @@ -21,6 +21,7 @@ export interface ShipSnapshotItem { type_id: number; quantity: number; flag: number; + charge: ShipSnapshotItem | undefined; state: "Passive" | "Online" | "Active" | "Overload"; max_state: "Passive" | "Online" | "Active" | "Overload"; attributes: Map; @@ -32,9 +33,12 @@ export interface EsiFit { description: string; ship_type_id: number; items: { - flag: number; type_id: number; quantity: number; + flag: number; + charge?: { + type_id: number; + }; state?: string; }[]; } @@ -59,6 +63,8 @@ interface ShipSnapshot { addModule: (typeId: number, slot: ShipSnapshotSlotsType | "dronebay") => void; removeModule: (flag: number) => void; + addCharge: (chargeTypeId: number) => void; + removeCharge: (flag: number) => void; changeHull: (typeId: number) => void; changeFit: (fit: EsiFit) => void; setItemState: (flag: number, state: string) => void; @@ -76,6 +82,8 @@ export const ShipSnapshotContext = React.createContext({ }, addModule: () => {}, removeModule: () => {}, + addCharge: () => {}, + removeCharge: () => {}, changeHull: () => {}, changeFit: () => {}, setItemState: () => {}, @@ -115,6 +123,8 @@ export const ShipSnapshotProvider = (props: ShipSnapshotProps) => { }, addModule: () => {}, removeModule: () => {}, + addCharge: () => {}, + removeCharge: () => {}, changeHull: () => {}, changeFit: () => {}, setItemState: () => {}, @@ -203,6 +213,80 @@ export const ShipSnapshotProvider = (props: ShipSnapshotProps) => { }); }, []); + const addCharge = React.useCallback( + (chargeTypeId: number) => { + const chargeSize = + eveData.typeDogma?.[chargeTypeId]?.dogmaAttributes.find( + (attr) => attr.attributeID === eveData.attributeMapping?.chargeSize, + )?.value ?? -1; + const groupID = eveData.typeIDs?.[chargeTypeId]?.groupID ?? -1; + + setCurrentFit((oldFit: EsiFit | undefined) => { + if (oldFit === undefined) return undefined; + + const newItems = []; + + for (let item of oldFit.items) { + /* If the module has size restrictions, ensure the charge matches. */ + const moduleChargeSize = eveData.typeDogma?.[item.type_id]?.dogmaAttributes.find( + (attr) => attr.attributeID === eveData.attributeMapping?.chargeSize, + )?.value; + if (moduleChargeSize !== undefined && moduleChargeSize !== chargeSize) { + newItems.push(item); + } + + /* Check if the charge fits in this module; if so, assign it. */ + for (const attr of eveData.typeDogma?.[item.type_id]?.dogmaAttributes ?? []) { + switch (attr.attributeID) { + case eveData.attributeMapping?.chargeGroup1: + case eveData.attributeMapping?.chargeGroup2: + case eveData.attributeMapping?.chargeGroup3: + case eveData.attributeMapping?.chargeGroup4: + case eveData.attributeMapping?.chargeGroup5: + if (attr.value === groupID) { + item = { + ...item, + charge: { + type_id: chargeTypeId, + }, + }; + } + break; + } + } + + newItems.push(item); + } + + return { + ...oldFit, + items: newItems, + }; + }); + }, + [eveData], + ); + + const removeCharge = React.useCallback((flag: number) => { + setCurrentFit((oldFit: EsiFit | undefined) => { + if (oldFit === undefined) return undefined; + + return { + ...oldFit, + items: oldFit.items.map((item) => { + if (item.flag === flag) { + return { + ...item, + charge: undefined, + }; + } + + return item; + }), + }; + }); + }, []); + const changeHull = React.useCallback( (typeId: number) => { const hullName = eveData?.typeIDs?.[typeId].name; @@ -222,12 +306,14 @@ export const ShipSnapshotProvider = (props: ShipSnapshotProps) => { ...oldSnapshot, addModule, removeModule, + addCharge, + removeCharge, changeHull, changeFit: setCurrentFit, setItemState, setName, })); - }, [addModule, removeModule, changeHull, setItemState, setName]); + }, [addModule, removeModule, addCharge, removeCharge, changeHull, setItemState, setName]); React.useEffect(() => { if (!dogmaEngine.loaded) return;