Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
Mar 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions .storybook/fits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
13 changes: 11 additions & 2 deletions src/HardwareListing/HardwareListing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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,
Expand Down
41 changes: 35 additions & 6 deletions src/ShipFit/Slot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand Down Expand Up @@ -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 (
Expand All @@ -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;
Expand All @@ -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>
Expand Down
90 changes: 88 additions & 2 deletions src/ShipSnapshotProvider/ShipSnapshotProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand All @@ -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;
}[];
}
Expand All @@ -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;
Expand All @@ -76,6 +82,8 @@ export const ShipSnapshotContext = React.createContext<ShipSnapshot>({
},
addModule: () => {},
removeModule: () => {},
addCharge: () => {},
removeCharge: () => {},
changeHull: () => {},
changeFit: () => {},
setItemState: () => {},
Expand Down Expand Up @@ -115,6 +123,8 @@ export const ShipSnapshotProvider = (props: ShipSnapshotProps) => {
},
addModule: () => {},
removeModule: () => {},
addCharge: () => {},
removeCharge: () => {},
changeHull: () => {},
changeFit: () => {},
setItemState: () => {},
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Loading