Skip to content

Commit

Permalink
Merge pull request #46 from dragoni7/42-add-mod-equip-logic-for-class…
Browse files Browse the repository at this point in the history
…-armor-mods

42 add mod equip logic for class armor mods
  • Loading branch information
dragoni7 authored Aug 31, 2024
2 parents 26bfde0 + 4606681 commit 7be3aa7
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 85 deletions.
212 changes: 138 additions & 74 deletions src/features/armor/components/ArmorConfig.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, SyntheticEvent } from 'react';
import { useDispatch } from 'react-redux';
import ArmorIcon from '../../../components/ArmorIcon';
import { updateLoadoutArmorMods, updateRequiredStatMods } from '../../../store/LoadoutReducer';
import { armorMods, DestinyArmor } from '../../../types/d2l-types';
import ArmorModSelector from './ArmorModSelector';
import { getModsBySlot } from '../mod-utils';
import { ManifestArmorMod, ManifestArmorStatMod } from '../../../types/manifest-types';
import { Grid } from '@mui/material';
import { Alert, Grid, Fade, Snackbar, SnackbarCloseReason } from '@mui/material';
import { useSelector } from 'react-redux';
import { RootState, store } from '../../../store';
import { PLUG_CATEGORY_HASH } from '../../../lib/bungie_api/constants';

interface ArmorConfigProps {
armor: DestinyArmor;
statMods: ManifestArmorMod[];
artificeMods: ManifestArmorMod[];
statMods: (ManifestArmorMod | ManifestArmorStatMod)[];
artificeMods: (ManifestArmorMod | ManifestArmorStatMod)[];
}

interface SnackbarMessage {
message: string;
key: number;
}

const ArmorConfig: React.FC<ArmorConfigProps> = ({ armor, statMods, artificeMods }) => {
const [snackPack, setSnackPack] = useState<readonly SnackbarMessage[]>([]);
const [snackbarOpen, setSnackbarOpen] = useState<boolean>(false);
const [messageInfo, setMessageInfo] = useState<SnackbarMessage | undefined>(undefined);
const [armorMods, setArmorMods] = useState<(ManifestArmorMod | ManifestArmorStatMod)[]>([]);
const selectedMods: (ManifestArmorMod | ManifestArmorStatMod)[] = useSelector(
(state: RootState) => state.loadoutConfig.loadout[(armor.type + 'Mods') as armorMods]
Expand All @@ -38,6 +46,25 @@ const ArmorConfig: React.FC<ArmorConfigProps> = ({ armor, statMods, artificeMods

const onSelectMod = async (mod: ManifestArmorMod | ManifestArmorStatMod, slot: number) => {
let totalCost = mod.energyCost;

if (selectedMods[slot].itemHash === mod.itemHash) return;

if (!mod.isOwned) {
setSnackPack((prev) => [
...prev,
{ message: 'You do not own ' + mod.name, key: new Date().getTime() },
]);
return;
}

if ('unique' in mod && mod.unique && selectedMods.includes(mod)) {
setSnackPack((prev) => [
...prev,
{ message: mod.name + ' is unique. Only equip one copy', key: new Date().getTime() },
]);
return;
}

for (const key in selectedMods) {
if (Number(key) !== slot) {
let statEnergyCost = armorMods.find(
Expand All @@ -63,96 +90,133 @@ const ArmorConfig: React.FC<ArmorConfigProps> = ({ armor, statMods, artificeMods
})
);

const requiredMods = store.getState().loadoutConfig.loadout.requiredStatMods;

if (
mod.category === PLUG_CATEGORY_HASH.ARMOR_MODS.STAT_ARMOR_MODS ||
(requiredMods.length > 0 && mod.category === PLUG_CATEGORY_HASH.ARMOR_MODS.STAT_ARMOR_MODS) ||
mod.category === PLUG_CATEGORY_HASH.ARMOR_MODS.ARTIFICE_ARMOR_MODS
) {
const newRequired = [...store.getState().loadoutConfig.loadout.requiredStatMods];
const newRequired = [...requiredMods];
const idx = newRequired.findIndex((required) => required.mod === selectedMods[slot]);
newRequired[idx] = { mod: newRequired[idx].mod, equipped: false };

dispatch(updateRequiredStatMods(newRequired));
}
};

function handleSnackbarClose(event: SyntheticEvent | Event, reason?: SnackbarCloseReason) {
if (reason === 'clickaway') return;

setSnackbarOpen(false);
}

function handleExited() {
setMessageInfo(undefined);
}

useEffect(() => {
updateMods().catch(console.error);
}, []);

useEffect(() => {
if (snackPack.length && !messageInfo) {
setMessageInfo({ ...snackPack[0] });
setSnackPack((prev) => prev.slice(1));
setSnackbarOpen(true);
} else if (snackPack.length && messageInfo && snackbarOpen) {
setSnackbarOpen(false);
}
}, [snackPack, messageInfo, snackbarOpen]);

return (
<Grid
item
container
columns={{ md: 7 }}
alignItems="center"
justifyContent="center"
alignContent="flex-start"
>
<Grid item md={1} textAlign="end">
<ArmorIcon armor={armor} />
</Grid>
<Grid item md={1}>
<hr
style={{
opacity: 0.7,
border: 'none',
width: '90%',
height: '2px',
color: 'rgba(255, 255, 255, 0.5)',
backgroundColor: 'rgba(255, 255, 255, 0.5)',
}}
/>
</Grid>
<Grid item md={1}>
<ArmorModSelector
selected={selectedMods[0]}
mods={statMods}
onSelectMod={(mod: ManifestArmorMod) => {
onSelectMod(mod, 0);
}}
/>
</Grid>
<Grid item md={1}>
<ArmorModSelector
selected={selectedMods[1]}
mods={armorMods}
onSelectMod={(mod: ManifestArmorMod) => {
onSelectMod(mod, 1);
}}
/>
</Grid>
<Grid item md={1}>
<ArmorModSelector
selected={selectedMods[2]}
mods={armorMods}
onSelectMod={(mod: ManifestArmorMod) => {
onSelectMod(mod, 2);
}}
/>
</Grid>
<Grid item md={1}>
<ArmorModSelector
selected={selectedMods[3]}
mods={armorMods}
onSelectMod={(mod: ManifestArmorMod) => {
onSelectMod(mod, 3);
}}
/>
</Grid>
{armor.artifice === true ? (
<>
<Grid
item
container
columns={{ md: 7 }}
alignItems="center"
justifyContent="center"
alignContent="flex-start"
>
<Grid item md={1} textAlign="end">
<ArmorIcon armor={armor} />
</Grid>
<Grid item md={1}>
<hr
style={{
opacity: 0.7,
border: 'none',
width: '90%',
height: '2px',
color: 'rgba(255, 255, 255, 0.5)',
backgroundColor: 'rgba(255, 255, 255, 0.5)',
}}
/>
</Grid>
<Grid item md={1}>
<ArmorModSelector
selected={selectedMods[4]}
mods={artificeMods}
onSelectMod={(mod: ManifestArmorMod) => {
onSelectMod(mod, 4);
selected={selectedMods[0]}
mods={statMods}
onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => {
onSelectMod(mod, 0);
}}
/>
</Grid>
) : (
<Grid item md={1} />
)}
</Grid>
<Grid item md={1}>
<ArmorModSelector
selected={selectedMods[1]}
mods={armorMods}
onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => {
onSelectMod(mod, 1);
}}
/>
</Grid>
<Grid item md={1}>
<ArmorModSelector
selected={selectedMods[2]}
mods={armorMods}
onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => {
onSelectMod(mod, 2);
}}
/>
</Grid>
<Grid item md={1}>
<ArmorModSelector
selected={selectedMods[3]}
mods={armorMods}
onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => {
onSelectMod(mod, 3);
}}
/>
</Grid>
{armor.artifice === true ? (
<Grid item md={1}>
<ArmorModSelector
selected={selectedMods[4]}
mods={artificeMods}
onSelectMod={(mod: ManifestArmorMod | ManifestArmorStatMod) => {
onSelectMod(mod, 4);
}}
/>
</Grid>
) : (
<Grid item md={1} />
)}
</Grid>
<Snackbar
key={messageInfo ? messageInfo.key : undefined}
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
open={snackbarOpen}
onClose={handleSnackbarClose}
autoHideDuration={3000}
TransitionComponent={Fade}
TransitionProps={{ onExited: handleExited }}
>
<Alert onClose={handleSnackbarClose} severity="error" variant="filled">
{messageInfo ? messageInfo.message : undefined}
</Alert>
</Snackbar>
</>
);
};

Expand Down
10 changes: 4 additions & 6 deletions src/features/armor/components/ArmorModSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Box } from '@mui/system';
import { ManifestArmorMod, ManifestArmorStatMod } from '../../../types/manifest-types';
import { useState } from 'react';

import { Tooltip } from '@mui/material';

interface ModSelectorProps {
Expand Down Expand Up @@ -29,22 +29,20 @@ const ArmorModSelector: React.FC<ModSelectorProps> = ({ selected, mods, onSelect
maxWidth: '91px',
width: '58%',
height: 'auto',
backgroundColor: 'rgba(60, 60, 60, 0.45)',
backgroundColor: 'rgba(10, 10, 10, 0.8)',
}}
/>
<Box className="submenu-grid">
{mods.map((mod) => (
<Tooltip title={mod.name} placement="top" disableInteractive>
<div
<Box
key={mod.itemHash}
className="submenu-item"
style={{
backgroundImage: `url(${mod.isOwned ? mod.icon : lockedModIcon})`,
}}
onClick={() => {
if (selected.itemHash !== mod.itemHash && mod.isOwned) {
onSelectMod(mod);
}
onSelectMod(mod);
}}
/>
</Tooltip>
Expand Down
5 changes: 3 additions & 2 deletions src/features/armor/components/ModCustomization.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import useArtificeMods from '../hooks/use-artifice-mods';
import useStatMods from '../hooks/use-stat-mods';
import RequiredMod from './RequiredMod';
import { useSelector } from 'react-redux';
import { ManifestArmorMod, ManifestArmorStatMod } from '../../../types/manifest-types';

const StyledTitle = styled(Typography)(({ theme }) => ({
paddingBottom: theme.spacing(1),
Expand All @@ -23,8 +24,8 @@ const StyledSubTitle = styled(Typography)(({ theme }) => ({

const ModCustomization: React.FC = () => {
const currentConfig = store.getState().loadoutConfig.loadout;
const statMods = useStatMods();
const artificeMods = useArtificeMods();
const statMods: (ManifestArmorMod | ManifestArmorStatMod)[] = useStatMods();
const artificeMods: (ManifestArmorMod | ManifestArmorStatMod)[] = useArtificeMods();
const requiredMods = useSelector(
(state: RootState) => state.loadoutConfig.loadout.requiredStatMods
);
Expand Down
2 changes: 0 additions & 2 deletions src/features/loadouts/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { armor } from '../types';

export enum STATUS {
SUCCESS = 1,
FAIL = 0,
Expand Down
5 changes: 5 additions & 0 deletions src/lib/bungie_api/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ export async function updateManifest() {
perkName: '',
perkDescription: '',
perkIcon: '',
unique: current.tooltipNotifications.some(
(notification: any) =>
notification.displayString ===
'Equipping additional copies of this mod provides no benefit.'
),
});
}
} else if (current.itemCategoryHashes.includes(ITEM_CATEGORY_HASHES.SUBCLASS_MODS)) {
Expand Down
2 changes: 1 addition & 1 deletion src/store/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ db.version(2).stores({
manifestExoticArmorCollection: 'itemHash, name, icon, class, slot, isOwned, collectibleHash',
manifestEmblemDef: 'itemHash, name, icon, secondaryOverlay, secondarySpecial',
manifestArmorModDef:
'itemHash, name, icon, category, perkName, perkDescription, perkIcon, isOwned, collectibleHash',
'itemHash, name, icon, category, perkName, perkDescription, perkIcon, isOwned, unique, collectibleHash',
manifestArmorStatModDef:
'itemHash, name, icon, category, perkName, perkDescription, perkIcon, isOwned, collectibleHash, mobilityMod, resilienceMod, recoveryMod, discipline, intellect, strength',
manifestSubclassModDef:
Expand Down
1 change: 1 addition & 0 deletions src/types/manifest-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export interface ManifestAspect extends ManifestPlug {
export interface ManifestArmorMod extends ManifestPlug {
energyCost: number;
collectibleHash: number;
unique: boolean;
}

export interface ManifestArmorStatMod extends ManifestStatPlug {
Expand Down

0 comments on commit 7be3aa7

Please sign in to comment.