Skip to content

Commit ca7f154

Browse files
Merge pull request #96 from dragoni7/hover-armor-mods
Hover armor mods
2 parents 5d26a79 + f229b9a commit ca7f154

File tree

3 files changed

+223
-72
lines changed

3 files changed

+223
-72
lines changed

src/components/HoverCard.tsx

Lines changed: 127 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@ import {
66
ManifestAspect,
77
ManifestStatPlug,
88
ManifestPlug,
9+
ManifestArmorMod,
10+
ManifestArmorStatMod,
911
} from '../types/manifest-types';
1012

1113
type HoverCardItem =
1214
| ManifestSubclass
1315
| ManifestAspect
1416
| ManifestStatPlug
1517
| ManifestPlug
18+
| ManifestArmorMod
19+
| ManifestArmorStatMod
1620
| undefined
1721
| null;
1822

@@ -27,19 +31,19 @@ const HoverCardContainer = styled('div')(({ theme }) => ({
2731
right: '100%',
2832
zIndex: 1600,
2933
padding: theme.spacing(1.5),
30-
backgroundColor: 'rgba(0, 0, 0, 0.8)',
34+
backgroundColor: 'rgba(0, 0, 0, 0.9)',
3135
borderRadius: '0px',
3236
boxShadow: theme.shadows[10],
33-
width: '250px',
37+
width: '300px',
3438
pointerEvents: 'none',
3539
display: 'flex',
3640
flexDirection: 'column',
3741
gap: theme.spacing(1),
38-
fontFamily: 'Helvetica, Arial, sans-serif',
42+
fontFamily: 'Arial, sans-serif',
3943
}));
4044

4145
const HoverCardTitle = styled(Typography)({
42-
fontFamily: 'Helvetica, Arial, sans-serif',
46+
fontFamily: 'Arial, sans-serif',
4347
fontWeight: 'bold',
4448
fontSize: '16px',
4549
textAlign: 'center',
@@ -54,7 +58,7 @@ const HoverCardIcon = styled('img')({
5458
});
5559

5660
const HoverCardDescription = styled(Typography)({
57-
fontFamily: 'Helvetica, Arial, sans-serif',
61+
fontFamily: 'Arial, sans-serif',
5862
fontSize: '14px',
5963
color: '#cccccc',
6064
});
@@ -74,6 +78,64 @@ const EnergyCapacitySquare = styled('div')(({ theme }) => ({
7478
marginRight: '2px',
7579
}));
7680

81+
const ModHoverCardHeader = styled(Box)({
82+
display: 'flex',
83+
flexDirection: 'column',
84+
marginBottom: '8px',
85+
});
86+
87+
const ModHoverCardTitleRow = styled(Box)({
88+
display: 'flex',
89+
justifyContent: 'space-between',
90+
alignItems: 'center',
91+
});
92+
93+
const ModHoverCardIcon = styled('img')({
94+
width: '32px',
95+
height: '32px',
96+
marginRight: '8px',
97+
});
98+
99+
const ModHoverCardTitle = styled(Typography)({
100+
fontFamily: 'Arial, sans-serif',
101+
fontWeight: 'bold',
102+
fontSize: '18px',
103+
color: '#ffffff',
104+
flexGrow: 1,
105+
});
106+
107+
const EnergyCostChip = styled(Box)(({ theme }) => ({
108+
color: '#ffffff',
109+
padding: '2px 6px',
110+
fontSize: '24px',
111+
fontWeight: 'bold',
112+
}));
113+
114+
const ModTypeLabel = styled(Typography)({
115+
fontSize: '12px',
116+
color: '#aaaaaa',
117+
marginTop: '4px',
118+
});
119+
120+
const Divider = styled('div')({
121+
width: '100%',
122+
height: '1px',
123+
background:
124+
'linear-gradient(to right, rgba(255,255,255,0), rgba(255,255,255,0.3), rgba(255,255,255,0))',
125+
margin: '8px 0',
126+
});
127+
128+
const ModDescriptionContainer = styled(Box)({
129+
display: 'flex',
130+
alignItems: 'flex-start',
131+
});
132+
133+
const ModDescription = styled(Typography)({
134+
fontSize: '14px',
135+
color: '#cccccc',
136+
flexGrow: 1,
137+
});
138+
77139
const HoverCard: React.FC<HoverCardProps> = ({ item, children }) => {
78140
const [hoverData, setHoverData] = useState<any | null>(null);
79141

@@ -103,7 +165,6 @@ const HoverCard: React.FC<HoverCardProps> = ({ item, children }) => {
103165

104166
fullData = await db.manifestSubclassFragmentsDef.where('itemHash').equals(itemHash).first();
105167
if (fullData) {
106-
// Fetch the description from manifestSandboxPerkDef
107168
const sandboxPerk = await db.manifestSandboxPerkDef
108169
.where('name')
109170
.equals(fullData.name)
@@ -122,6 +183,34 @@ const HoverCard: React.FC<HoverCardProps> = ({ item, children }) => {
122183
return;
123184
}
124185

186+
fullData = await db.manifestArmorModDef.where('itemHash').equals(itemHash).first();
187+
if (fullData) {
188+
const sandboxPerk =
189+
fullData.perks && fullData.perks.length > 0
190+
? await db.manifestSandboxPerkDef.where('itemHash').equals(fullData.perks[0]).first()
191+
: null;
192+
setHoverData({
193+
...fullData,
194+
type: 'armorMod',
195+
description: sandboxPerk ? sandboxPerk.description : 'No description available',
196+
});
197+
return;
198+
}
199+
200+
fullData = await db.manifestArmorStatModDef.where('itemHash').equals(itemHash).first();
201+
if (fullData) {
202+
const sandboxPerk =
203+
fullData.perks && fullData.perks.length > 0
204+
? await db.manifestSandboxPerkDef.where('itemHash').equals(fullData.perks[0]).first()
205+
: null;
206+
setHoverData({
207+
...fullData,
208+
type: 'armorStatMod',
209+
description: sandboxPerk ? sandboxPerk.description : 'No description available',
210+
});
211+
return;
212+
}
213+
125214
console.log('Item not found in any table');
126215
} catch (error) {
127216
console.error('Error fetching item data:', error);
@@ -142,6 +231,25 @@ const HoverCard: React.FC<HoverCardProps> = ({ item, children }) => {
142231
);
143232
};
144233

234+
const renderModContent = (data: any) => {
235+
return (
236+
<>
237+
<ModHoverCardHeader>
238+
<ModHoverCardTitleRow>
239+
<ModHoverCardTitle>{data.name}</ModHoverCardTitle>
240+
<EnergyCostChip>{data.energyCost}</EnergyCostChip>
241+
</ModHoverCardTitleRow>
242+
<ModTypeLabel>{data.type === 'armorMod' ? 'Armor Mod' : 'Armor Stat Mod'}</ModTypeLabel>
243+
</ModHoverCardHeader>
244+
<Divider />
245+
<ModDescriptionContainer>
246+
<ModHoverCardIcon src={data.icon} alt={data.name} />
247+
<ModDescription>{data.description}</ModDescription>
248+
</ModDescriptionContainer>
249+
</>
250+
);
251+
};
252+
145253
const renderDescription = () => {
146254
if (!hoverData) return null;
147255

@@ -179,6 +287,10 @@ const HoverCard: React.FC<HoverCardProps> = ({ item, children }) => {
179287
</>
180288
);
181289

290+
case 'armorMod':
291+
case 'armorStatMod':
292+
return renderModContent(hoverData);
293+
182294
default:
183295
return <HoverCardDescription></HoverCardDescription>;
184296
}
@@ -193,9 +305,15 @@ const HoverCard: React.FC<HoverCardProps> = ({ item, children }) => {
193305
{children}
194306
{hoverData && (
195307
<HoverCardContainer>
196-
<HoverCardTitle variant="h6">{hoverData.name}</HoverCardTitle>
197-
<HoverCardIcon src={hoverData.secondaryIcon || hoverData.icon} alt={hoverData.name} />
198-
{renderDescription()}
308+
{hoverData.type === 'armorMod' || hoverData.type === 'armorStatMod' ? (
309+
renderDescription()
310+
) : (
311+
<>
312+
<HoverCardTitle variant="h6">{hoverData.name}</HoverCardTitle>
313+
<HoverCardIcon src={hoverData.secondaryIcon || hoverData.icon} alt={hoverData.name} />
314+
{renderDescription()}
315+
</>
316+
)}
199317
</HoverCardContainer>
200318
)}
201319
</div>

src/features/armor-mods/components/ArmorConfig.tsx

Lines changed: 29 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,12 @@
11
import { useState, useEffect, SyntheticEvent } from 'react';
2-
import { useDispatch } from 'react-redux';
2+
import { useDispatch, useSelector } from 'react-redux';
33
import ArmorIcon from '../../../components/ArmorIcon';
44
import { updateLoadoutArmorMods, updateRequiredStatMods } from '../../../store/LoadoutReducer';
55
import { armorMods, DestinyArmor } from '../../../types/d2l-types';
66
import ArmorModSelector from './ArmorModSelector';
77
import { getModsBySlot } from '../mod-utils';
88
import { ManifestArmorMod, ManifestArmorStatMod } from '../../../types/manifest-types';
9-
import {
10-
Alert,
11-
Grid,
12-
Fade,
13-
Snackbar,
14-
SnackbarCloseReason,
15-
LinearProgress,
16-
CircularProgress,
17-
} from '@mui/material';
18-
import { useSelector } from 'react-redux';
9+
import { Alert, Grid, Fade, Snackbar, SnackbarCloseReason, CircularProgress } from '@mui/material';
1910
import { RootState, store } from '../../../store';
2011
import { PLUG_CATEGORY_HASH } from '../../../lib/bungie_api/constants';
2112

@@ -52,9 +43,14 @@ const ArmorConfig: React.FC<ArmorConfigProps> = ({ armor, statMods, artificeMods
5243
);
5344
};
5445

55-
const onSelectMod = async (mod: ManifestArmorMod | ManifestArmorStatMod, slot: number) => {
56-
let totalCost = mod.energyCost;
46+
const calculateAvailableEnergy = (currentSlot: number) => {
47+
const totalEnergyCost = selectedMods.reduce((total, mod, index) => {
48+
return index !== currentSlot ? total + mod.energyCost : total;
49+
}, 0);
50+
return 10 - totalEnergyCost; // Assuming max energy is 10
51+
};
5752

53+
const onSelectMod = async (mod: ManifestArmorMod | ManifestArmorStatMod, slot: number) => {
5854
if (selectedMods[slot].itemHash === mod.itemHash) return;
5955

6056
if (!mod.isOwned) {
@@ -73,21 +69,13 @@ const ArmorConfig: React.FC<ArmorConfigProps> = ({ armor, statMods, artificeMods
7369
return;
7470
}
7571

76-
for (const key in selectedMods) {
77-
if (Number(key) !== slot) {
78-
let statEnergyCost = armorMods.find(
79-
(mod) => mod.itemHash === selectedMods[key].itemHash
80-
)?.energyCost;
81-
let armorEnergyCost = statMods.find(
82-
(mod) => mod.itemHash === selectedMods[key].itemHash
83-
)?.energyCost;
84-
85-
totalCost += statEnergyCost ? statEnergyCost : armorEnergyCost ? armorEnergyCost : 0;
86-
}
87-
88-
if (totalCost > 10) {
89-
return;
90-
}
72+
const availableEnergy = calculateAvailableEnergy(slot);
73+
if (mod.energyCost > availableEnergy) {
74+
setSnackPack((prev) => [
75+
...prev,
76+
{ message: 'Not enough energy to equip ' + mod.name, key: new Date().getTime() },
77+
]);
78+
return;
9179
}
9280

9381
dispatch(
@@ -114,7 +102,6 @@ const ArmorConfig: React.FC<ArmorConfigProps> = ({ armor, statMods, artificeMods
114102

115103
function handleSnackbarClose(event: SyntheticEvent | Event, reason?: SnackbarCloseReason) {
116104
if (reason === 'clickaway') return;
117-
118105
setSnackbarOpen(false);
119106
}
120107

@@ -167,46 +154,43 @@ const ArmorConfig: React.FC<ArmorConfigProps> = ({ armor, statMods, artificeMods
167154
<ArmorModSelector
168155
selected={selectedMods[0]}
169156
mods={statMods}
170-
onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => {
171-
onSelectMod(mod, 0);
172-
}}
157+
onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => onSelectMod(mod, 0)}
158+
availableEnergy={calculateAvailableEnergy(0)}
173159
/>
174160
</Grid>
175161
<Grid item md={1}>
176162
<ArmorModSelector
177163
selected={selectedMods[1]}
178164
mods={armorMods}
179-
onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => {
180-
onSelectMod(mod, 1);
181-
}}
165+
onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => onSelectMod(mod, 1)}
166+
availableEnergy={calculateAvailableEnergy(1)}
182167
/>
183168
</Grid>
184169
<Grid item md={1}>
185170
<ArmorModSelector
186171
selected={selectedMods[2]}
187172
mods={armorMods}
188-
onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => {
189-
onSelectMod(mod, 2);
190-
}}
173+
onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => onSelectMod(mod, 2)}
174+
availableEnergy={calculateAvailableEnergy(2)}
191175
/>
192176
</Grid>
193177
<Grid item md={1}>
194178
<ArmorModSelector
195179
selected={selectedMods[3]}
196180
mods={armorMods}
197-
onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => {
198-
onSelectMod(mod, 3);
199-
}}
181+
onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => onSelectMod(mod, 3)}
182+
availableEnergy={calculateAvailableEnergy(3)}
200183
/>
201184
</Grid>
202185
{armor.artifice === true ? (
203186
<Grid item md={1}>
204187
<ArmorModSelector
205188
selected={selectedMods[4]}
206189
mods={artificeMods}
207-
onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => {
208-
onSelectMod(mod, 4);
209-
}}
190+
onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) =>
191+
onSelectMod(mod, 4)
192+
}
193+
availableEnergy={calculateAvailableEnergy(4)}
210194
/>
211195
</Grid>
212196
) : (
@@ -215,7 +199,7 @@ const ArmorConfig: React.FC<ArmorConfigProps> = ({ armor, statMods, artificeMods
215199
</>
216200
) : (
217201
[...Array(5).keys()].map((i) => (
218-
<Grid item md={1}>
202+
<Grid item md={1} key={i}>
219203
<CircularProgress />
220204
</Grid>
221205
))

0 commit comments

Comments
 (0)