feat(charge): allow adding/removing charges to fits (#66)

This commit is contained in:
Patric Stout
2024-03-03 15:52:13 +01:00
committed by GitHub
parent b9df78da6f
commit 080f418c3e
5 changed files with 159 additions and 17 deletions

View File

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

View File

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

View File

@@ -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 <TreeLeaf key={item.typeId} level={2} content={item.name} onClick={() => {}} />;
return (
<TreeLeaf
key={item.typeId}
level={2}
content={item.name}
onClick={() => 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,

View File

@@ -58,6 +58,11 @@ export const Slot = (props: { type: string; index: number; fittable: boolean; ma
</g>
</svg>
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" style={{ display: "none" }}>
<g id="uncharge">
<path style={{ fill: "none", strokeWidth: 1 }} d="M 4 6 A 8 8 0 1 1 4 14" />
<path style={{ fill: "none", strokeWidth: 1 }} d="M 11 6 L 6 10 L 11 14" />
<path style={{ fill: "none", strokeWidth: 1 }} d="M 6 10 L 16 10" />
</g>
<g id="unfit">
<path style={{ fill: "none", strokeWidth: 1 }} d="M 4 6 A 8 8 0 1 1 4 14" />
<path style={{ fill: "none", strokeWidth: 1 }} d="M 11 6 L 6 10 L 11 14" />
@@ -131,6 +136,16 @@ export const Slot = (props: { type: string; index: number; fittable: boolean; ma
[shipSnapshot, esiItem],
);
const unfitCharge = React.useCallback(
(e: React.MouseEvent<SVGSVGElement, 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 = (
<img
src={`https://images.evetech.net/types/${esiItem.type_id}/icon?size=64`}
title={eveData?.typeIDs?.[esiItem.type_id].name}
/>
);
if (esiItem.charge !== undefined) {
item = (
<img
src={`https://images.evetech.net/types/${esiItem.charge.type_id}/icon?size=64`}
title={`${eveData?.typeIDs?.[esiItem.type_id].name}\n${eveData?.typeIDs?.[esiItem.charge.type_id].name}`}
/>
);
} else {
item = (
<img
src={`https://images.evetech.net/types/${esiItem.type_id}/icon?size=64`}
title={eveData?.typeIDs?.[esiItem.type_id].name}
/>
);
}
}
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
<div className={styles.slotImage}>{item}</div>
</div>
<div className={styles.slotOptions}>
{esiItem?.charge !== undefined && (
<svg viewBox="0 0 20 20" width={20} xmlns="http://www.w3.org/2000/svg" onClick={unfitCharge}>
<use href="#uncharge" />
</svg>
)}
<svg viewBox="0 0 20 20" width={20} xmlns="http://www.w3.org/2000/svg" onClick={unfitModule}>
<use href="#unfit" />
</svg>

View File

@@ -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<number, ShipSnapshotItemAttribute>;
@@ -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<ShipSnapshot>({
},
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;