diff --git a/package-lock.json b/package-lock.json index 74a8690..dcef099 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1945,9 +1945,9 @@ "peer": true }, "node_modules/@babel/runtime": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", - "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz", + "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2561,6 +2561,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/js": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", @@ -5251,9 +5263,9 @@ } }, "node_modules/axios": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", - "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -6546,9 +6558,9 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.10.tgz", - "integrity": "sha512-I39s6G9We7ZxbCRxTTM5XX4KJV2cfWhFbHF4kTuL0ygdEVdQXtCNGqUQ43sBOCbTC/N6dEZXoQKFHr8gp1VHrQ==", + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.11.tgz", + "integrity": "sha512-wrAKxMbVr8qhXTtIKfXqAn5SAtRZt0aXxe5P23Fh4pUAdC6XEsybGLB8P0PI4j1yYqOgUEUlzKAGDfo7rJOjcw==", "dev": true, "peerDependencies": { "eslint": ">=7" @@ -6696,6 +6708,18 @@ "node": ">=8" } }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -7067,9 +7091,9 @@ } }, "node_modules/framer-motion": { - "version": "11.3.29", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.3.29.tgz", - "integrity": "sha512-uyDuUOeOElJEA3kbkbyoTNEf75Jih1EUg0ouLKYMlGDdt/LaJPmO+FyOGAGxM2HwKhHcAoKFNveR5A8peb7yhw==", + "version": "11.3.30", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.3.30.tgz", + "integrity": "sha512-9VmqGe9OIjfMoCcs+ZsKXlv6JaG5QagKX2F1uSbkG3Z33wgjnz60Kw+CngC1M49rDYau+Y9aL+8jGagAwrbVyw==", "dependencies": { "tslib": "^2.4.0" }, @@ -11776,15 +11800,6 @@ "node": ">=6" } }, - "node_modules/stacktrace-parser/node_modules/type-fest": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", - "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", - "peer": true, - "engines": { - "node": ">=8" - } - }, "node_modules/statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -12160,9 +12175,9 @@ "peer": true }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/type-check": { "version": "0.4.0", @@ -12186,15 +12201,12 @@ } }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "peer": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/typed-array-buffer": { diff --git a/src/app/routes/Dashboard.tsx b/src/app/routes/Dashboard.tsx index 153e2aa..2ba374c 100644 --- a/src/app/routes/Dashboard.tsx +++ b/src/app/routes/Dashboard.tsx @@ -9,7 +9,13 @@ import { updateProfileData } from '../../store/ProfileReducer'; import { useDispatch, useSelector } from 'react-redux'; import { generatePermutations } from '../../features/armor-optimization/generate-permutations'; import { filterPermutations } from '../../features/armor-optimization/filter-permutations'; -import { DestinyArmor, Character, FilteredPermutation, DamageType } from '../../types/d2l-types'; +import { + DestinyArmor, + Character, + FilteredPermutation, + DamageType, + SubclassConfig, +} from '../../types/d2l-types'; import StatsTable from '../../features/armor-optimization/StatsTable'; import { RootState } from '../../store'; import HeaderComponent from '../../components/HeaderComponent'; @@ -20,7 +26,7 @@ import ArmorCustomization from '../../features/armor/components/ArmorCustomizati import { resetLoadout, updateLoadoutCharacter, updateSubclass } from '../../store/LoadoutReducer'; import { ManifestSubclass } from '../../types/manifest-types'; import SubclassCustomizationWrapper from '../../features/subclass/SubclassCustomizationWrapper'; -import { updateManifest } from '../../lib/bungie_api/Manifest'; +import { updateManifest } from '../../lib/bungie_api/manifest'; const PageContainer = styled('div')({ display: 'flex', @@ -199,11 +205,11 @@ export const Dashboard: React.FC = () => { const handleSubclassSelect = (subclass: ManifestSubclass) => { setSelectedSubclass(subclass); - if (selectedCharacter) { + + if (selectedCharacter && subclass.damageType in selectedCharacter.subclasses) { dispatch( updateSubclass({ - damageType: subclass.damageType as DamageType, - subclass: selectedCharacter.subclasses[subclass.damageType], + subclass: selectedCharacter.subclasses[subclass.damageType]?.subclass, }) ); } diff --git a/src/features/armor-optimization/NumberBoxes.tsx b/src/features/armor-optimization/NumberBoxes.tsx index b614c6b..57c10e4 100644 --- a/src/features/armor-optimization/NumberBoxes.tsx +++ b/src/features/armor-optimization/NumberBoxes.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { styled } from '@mui/material/styles'; import { Box, Paper, Grid, ButtonBase } from '@mui/material'; -import { STATS } from '../../lib/bungie_api/Constants'; +import { STATS } from '../../lib/bungie_api/constants'; const ContainerWithBorder = styled(Paper)(({ theme }) => ({ border: `1px solid ${theme.palette.divider}`, diff --git a/src/features/armor-optimization/StatsTable.tsx b/src/features/armor-optimization/StatsTable.tsx index 706369e..4845842 100644 --- a/src/features/armor-optimization/StatsTable.tsx +++ b/src/features/armor-optimization/StatsTable.tsx @@ -11,7 +11,7 @@ import { updateRequiredStatMods, } from '../../store/LoadoutReducer'; import ArmorIcon from '../../components/ArmorIcon'; -import { STAT_MOD_HASHES, STATS } from '../../lib/bungie_api/Constants'; +import { STAT_MOD_HASHES, STATS } from '../../lib/bungie_api/constants'; import { db } from '../../store/db'; import { ManifestArmorStatMod } from '../../types/manifest-types'; diff --git a/src/features/armor/components/LoadoutArmor.tsx b/src/features/armor/components/LoadoutArmor.tsx index ab0b466..49fe22e 100644 --- a/src/features/armor/components/LoadoutArmor.tsx +++ b/src/features/armor/components/LoadoutArmor.tsx @@ -68,9 +68,10 @@ const LoadoutArmor: React.FC = () => { <Box width={81} height={81}> Required Mods: </Box> - {requiredMods.map((mod) => ( + {requiredMods.map((mod, index) => ( <Box className="armor-mod-slot" + key={index} style={{ backgroundImage: `url(${mod.icon})`, }} diff --git a/src/features/loadouts/components/EquipLoadout.tsx b/src/features/loadouts/components/EquipLoadout.tsx index 4b21625..05ff0e1 100644 --- a/src/features/loadouts/components/EquipLoadout.tsx +++ b/src/features/loadouts/components/EquipLoadout.tsx @@ -1,22 +1,22 @@ -import { Backdrop, Badge, Box, Button, Grid, LinearProgress, Paper, Tooltip } from '@mui/material'; +import { Backdrop, Box, Button, Container, Grid, Paper, Tooltip } from '@mui/material'; import { store } from '../../../store'; -import { Fragment, useState } from 'react'; +import { useState } from 'react'; import { EquipResult } from '../types'; -import ArmorIcon from '../../../components/ArmorIcon'; -import { CheckRounded, Close } from '@mui/icons-material'; import { STATUS } from '../constants'; import { ArmorEquipper } from '../util/armorEquipper'; -import { DestinyArmor, Plug, SubclassConfig } from '../../../types/d2l-types'; +import { DestinyArmor, SubclassConfig } from '../../../types/d2l-types'; import React from 'react'; import { SubclassEquipper } from '../util/subclassEquipper'; -import { ManifestArmorStatMod, ManifestPlug } from '../../../types/manifest-types'; +import { ManifestArmorMod, ManifestArmorStatMod } from '../../../types/manifest-types'; import { DAMAGE_TYPE } from '../../../lib/bungie_api/constants'; +import LoadingBorder from './LoadingBorder'; +import FadeIn from './FadeIn'; const EquipLoadout: React.FC = () => { const [processing, setProcessing] = useState<any[]>([]); const [equipStep, setEquipStep] = useState<string>(''); const [open, setOpen] = useState<boolean>(false); - const [results, setResults] = useState<EquipResult[]>([]); + const [results, setResults] = useState<EquipResult[][]>([]); const [equipping, setEquipping] = useState<boolean>(false); const onEquipLoadout = async () => { @@ -36,7 +36,7 @@ const EquipLoadout: React.FC = () => { setEquipping(true); const armorEquipper = new ArmorEquipper(); const tempEquipped: any[] = []; - const tempResults: EquipResult[] = []; + const tempResults: EquipResult[][] = []; await armorEquipper.setCharacter(loadout.characterId); @@ -84,9 +84,9 @@ const EquipLoadout: React.FC = () => { const processArmor = async ( tempEquipped: DestinyArmor[], equipper: ArmorEquipper, - tempResults: EquipResult[], + tempResults: EquipResult[][], armor: DestinyArmor, - armorMods: { [key: number]: ManifestPlug | ManifestArmorStatMod } + armorMods: { [key: number]: ManifestArmorMod | ManifestArmorStatMod } ) => { tempEquipped.push(armor); setProcessing(tempEquipped); @@ -101,7 +101,7 @@ const EquipLoadout: React.FC = () => { const processSubclass = async ( tempEquipped: any[], equipper: SubclassEquipper, - tempResults: EquipResult[], + tempResults: EquipResult[][], subclassConfig: SubclassConfig ) => { tempEquipped.push(subclassConfig.subclass); @@ -160,85 +160,108 @@ const EquipLoadout: React.FC = () => { </Button> <Backdrop open={open} sx={{ color: '#fff', zIndex: (theme) => theme.zIndex.drawer + 1 }}> <Paper - elevation={1} + elevation={5} sx={{ - display: 'flex', - position: 'absolute', - width: '25vw', - height: '95vh', - backgroundColor: '#1c1c21', + width: '100%', + height: '87%', + backgroundColor: 'rgba(40,40,40,0.8)', + borderTop: '8px solid rgba(100,100,100,1.0)', + borderRadius: '0', color: 'white', }} > - <Grid - container - spacing={2} - justifyContent="space-between" - alignItems="flex-end" - paddingTop={1} - paddingLeft={1} - paddingBottom={4} - > - <Fragment> + <Grid container height="100%"> + {equipping ? ( + false + ) : ( + <Grid + item + md={12} + lg={12} + textAlign="center" + sx={{ backgroundColor: 'rgba(48,48,48,0.8)' }} + > + <FadeIn> + <h2>{equipStep}</h2> + </FadeIn> + </Grid> + )} + <Grid container item md={12} lg={12} spacing={3} py={4}> {processing.map((item, index) => ( - <Fragment> + <> {results[index] !== undefined ? ( - <Fragment> - <Grid item md={2}> - <Tooltip title={'Equipped'}> - <Badge - badgeContent={ - results[index].operationsStatus[0] === 'Success' ? ( - <CheckRounded sx={{ fontSize: 50, color: 'green' }} /> - ) : ( - <Close sx={{ fontSize: 50, color: 'red' }} /> - ) + <> + <Grid item md={2} lg={2} /> + <Grid item md={1}> + <FadeIn duration={1000}> + <Tooltip + title={ + results[index][0].status === STATUS.SUCCESS + ? 'Equipped' + : results[index][0].message } - anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} > - <ArmorIcon armor={item} size={64}></ArmorIcon> - </Badge> - </Tooltip> + <img + src={results[index][0].subject.icon} + width={72} + height={72} + style={{ + border: `4px solid ${ + results[index][0].status === STATUS.SUCCESS ? 'green' : 'red' + }`, + }} + /> + </Tooltip> + </FadeIn> </Grid> - <Grid item md={9}> - {results[index].status === STATUS.SUCCESS - ? 'Success' - : results[index]?.operationsStatus - .slice(1) - .map((error) => ( - <Tooltip title={error}> - {error === 'Success' ? ( - <CheckRounded sx={{ fontSize: 56, color: 'green' }} /> - ) : ( - <Close sx={{ fontSize: 56, color: 'red' }} /> - )} - </Tooltip> - ))} + <Grid item container md={9} gap={2}> + {results[index].slice(1).map((result, index) => ( + <Grid item md="auto"> + <FadeIn delay={100 * index} duration={1000}> + <Tooltip title={result.message}> + <img + src={result.subject.icon} + width={72} + height={72} + style={{ + border: `4px solid ${ + result.status === STATUS.SUCCESS ? 'green' : 'red' + }`, + }} + /> + </Tooltip> + </FadeIn> + </Grid> + ))} </Grid> - </Fragment> + </> ) : ( - <Grid container item justifyContent="center" spacing={3}> + <Grid container item alignItems="flex-start" spacing={1} height="100%"> <Grid item md={12} textAlign="center"> - <ArmorIcon armor={item} size={64} /> + <LoadingBorder armor={item} size={64} /> </Grid> - <Grid item md={4}> - <LinearProgress color="inherit" /> - </Grid> - <Grid item md={12} textAlign="center"> + <Grid item md={12} textAlign="center" height="100%"> {equipStep} </Grid> </Grid> )} - </Fragment> + </> ))} - {equipping ? ( - false - ) : ( - <Fragment> - <Grid item md={12} textAlign="center"> - {equipStep} - </Grid> - <Grid item md={3} textAlign="center"> + </Grid> + {equipping ? ( + false + ) : ( + <Grid + item + container + md={12} + textAlign="center" + alignItems="flex-end" + justifyContent="space-betwen" + sx={{ backgroundColor: 'rgba(0,0,0,0.8)' }} + > + <Grid item md={4}> + <FadeIn delay={200}> <Button onClick={() => { setOpen(false); @@ -249,16 +272,20 @@ const EquipLoadout: React.FC = () => { > Back </Button> - </Grid> - <Grid item md={3} textAlign="center"> + </FadeIn> + </Grid> + <Grid item md={4}> + <FadeIn delay={400}> <Button>Share</Button> - </Grid> - <Grid item md={3} textAlign="center"> + </FadeIn> + </Grid> + <Grid item md={4}> + <FadeIn delay={600}> <Button>Save in-game</Button> - </Grid> - </Fragment> - )} - </Fragment> + </FadeIn> + </Grid> + </Grid> + )} </Grid> </Paper> </Backdrop> diff --git a/src/features/loadouts/components/FadeIn.tsx b/src/features/loadouts/components/FadeIn.tsx new file mode 100644 index 0000000..76d4ae0 --- /dev/null +++ b/src/features/loadouts/components/FadeIn.tsx @@ -0,0 +1,23 @@ +import React, { ReactNode } from 'react'; +import { useSpring, animated } from 'react-spring'; + +const FadeIn: React.FC<{ children: ReactNode; duration?: number; delay?: number }> = (props: { + children: ReactNode; + duration?: number; + delay?: number; +}) => { + const [opacity, api] = useSpring( + () => ({ + from: { opacity: '0' }, + delay: props.delay ? props.delay : 0, + to: { opacity: '1' }, + + config: { duration: props.duration ? props.duration : 2000, tension: 120, friction: 14 }, + }), + [] + ); + + return <animated.div style={opacity}>{props.children}</animated.div>; +}; + +export default FadeIn; diff --git a/src/features/loadouts/components/LoadingBorder.tsx b/src/features/loadouts/components/LoadingBorder.tsx new file mode 100644 index 0000000..ece4b6b --- /dev/null +++ b/src/features/loadouts/components/LoadingBorder.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { useSpring, animated } from 'react-spring'; +import { styled } from '@mui/system'; +import { DestinyArmor } from '../../../types/d2l-types'; + +const DefaultIconContainer = styled(animated.img)({ + padding: '2px', + alignItems: 'center', + justifyContent: 'center', + marginRight: '5px', +}); + +interface LoadingBorderProps { + armor: DestinyArmor; + size?: number; +} + +const LoadingBorder: React.FC<LoadingBorderProps> = ({ armor, size }) => { + const [border, api] = useSpring( + () => ({ + from: { border: '3px solid rgba(195, 195, 195, 0)' }, + to: async (next) => { + await next({ border: '3px solid rgba(195, 195, 195, 1)' }); + await next({ border: '3px solid rgba(195, 195, 195, 0)' }); + }, + loop: true, + + config: { duration: 2000, tension: 120, friction: 14 }, + }), + [] + ); + + return ( + <DefaultIconContainer + src={armor.icon} + alt={armor.name} + width={size} + height={size} + style={border} + /> + ); +}; + +export default LoadingBorder; diff --git a/src/features/loadouts/types/index.ts b/src/features/loadouts/types/index.ts index 89ce8c8..e723dfb 100644 --- a/src/features/loadouts/types/index.ts +++ b/src/features/loadouts/types/index.ts @@ -4,6 +4,6 @@ export type EquipStatus = STATUS.SUCCESS | STATUS.FAIL; export type EquipResult = { status: EquipStatus; - operationsStatus: string[]; + message: string; subject: any; }; diff --git a/src/features/loadouts/util/armorEquipper.ts b/src/features/loadouts/util/armorEquipper.ts index 4d5efc8..e924ab6 100644 --- a/src/features/loadouts/util/armorEquipper.ts +++ b/src/features/loadouts/util/armorEquipper.ts @@ -1,12 +1,12 @@ -import { BUCKET_HASH, ITEM_LOCATIONS } from '../../../lib/bungie_api/constants'; +import { BUCKET_HASH, ERRORS, ITEM_LOCATIONS } from '../../../lib/bungie_api/constants'; import { equipItemRequest, getCharacterInventoryRequest, insertSocketPlugFreeRequest, transferItemRequest, } from '../../../lib/bungie_api/requests'; -import { DestinyArmor, Plug } from '../../../types/d2l-types'; -import { ManifestPlug, ManifestArmorStatMod } from '../../../types/manifest-types'; +import { DestinyArmor } from '../../../types/d2l-types'; +import { ManifestArmorStatMod, ManifestArmorMod } from '../../../types/manifest-types'; import { STATUS } from '../constants'; import { Equipper } from './equipper'; @@ -65,7 +65,11 @@ export class ArmorEquipper extends Equipper { } public async equipArmor(armor: DestinyArmor): Promise<void> { - this.result.subject = armor; + const result = { + status: STATUS.SUCCESS, + message: '', + subject: armor, + }; // if armor not in character inventory, transfer first if (armor.location !== ITEM_LOCATIONS.CHARACTER_INVENTORY) { @@ -80,11 +84,8 @@ export class ArmorEquipper extends Equipper { this.characterId ).catch((error) => { if (error.response) { - this.result.status = STATUS.FAIL; - this.result.operationsStatus[0] = error.response.data.ErrorStatus.replace( - /([a-z])([A-Z])/g, - '$1 $2' - ); + result.status = STATUS.FAIL; + result.message = error.response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); } }); } @@ -98,11 +99,8 @@ export class ArmorEquipper extends Equipper { this.characterId ).catch((error) => { if (error.response) { - this.result.status = STATUS.FAIL; - this.result.operationsStatus[0] = error.response.data.ErrorStatus.replace( - /([a-z])([A-Z])/g, - '$1 $2' - ); + result.status = STATUS.FAIL; + result.message = error.response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); } }); } @@ -110,52 +108,56 @@ export class ArmorEquipper extends Equipper { // equip const response = await equipItemRequest(armor.instanceHash, this.characterId).catch((error) => { if (error.response) { - this.result.status = STATUS.FAIL; - this.result.operationsStatus[0] = error.response.data.ErrorStatus.replace( - /([a-z])([A-Z])/g, - '$1 $2' - ); + result.status = STATUS.FAIL; + result.message = error.response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); } }); - if (response) - this.result.operationsStatus[0] = response.data.ErrorStatus.replace( - /([a-z])([A-Z])/g, - '$1 $2' - ); + if (response) { + result.status = STATUS.SUCCESS; + result.message = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); + } + + this.result.push(result); } public async equipArmorMods(mods: { - [key: number]: ManifestPlug | ManifestArmorStatMod; + [key: number]: ManifestArmorMod | ManifestArmorStatMod; }): Promise<void> { - if (this.result.subject) { - for (let i = 0; i < 5; i++) { - if (i === 4 && this.result.subject.artifice === false) continue; - - const response = await insertSocketPlugFreeRequest( - this.result.subject.instanceHash, - { - plugItemHash: String(mods[i].itemHash), - socketArrayType: 0, - socketIndex: i === 4 && this.result.subject.artifice === true ? 11 : i, - }, - this.characterId - ).catch((error) => { - if (error.response) { - this.result.status = STATUS.FAIL; - this.result.operationsStatus[i + 1] = error.response.data.ErrorStatus.replace( - /([a-z])([A-Z])/g, - '$1 $2' - ); - } - }); + const armor = this.result[0].subject; - if (response) - this.result.operationsStatus[i + 1] = response?.data.ErrorStatus.replace( - /([a-z])([A-Z])/g, - '$1 $2' - ); - } + if (!armor) return; + + for (let i = 0; i < 5; i++) { + const result = { + status: STATUS.SUCCESS, + message: '', + subject: mods[i], + }; + + if (i === 4 && armor.artifice === false) continue; + + const response = await insertSocketPlugFreeRequest( + armor.instanceHash, + { + plugItemHash: String(mods[i].itemHash), + socketArrayType: 0, + socketIndex: i === 4 && armor.artifice === true ? 11 : i, + }, + this.characterId + ).catch((error) => { + if (error.response) { + result.status = + error.response.data.ErrorCode === ERRORS.SOCKET_ALREADY_CONTAINS_PLUG + ? STATUS.SUCCESS + : STATUS.FAIL; + result.message = error.response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); + } + }); + + if (response) result.message = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); + + this.result.push(result); } } } diff --git a/src/features/loadouts/util/equipper.ts b/src/features/loadouts/util/equipper.ts index ed66ac2..9ef29aa 100644 --- a/src/features/loadouts/util/equipper.ts +++ b/src/features/loadouts/util/equipper.ts @@ -1,35 +1,26 @@ -import { STATUS } from '../constants'; import { EquipResult } from '../types'; export abstract class Equipper { protected characterId: number; - protected result: EquipResult; + protected result: EquipResult[]; public constructor() { this.characterId = -1; - this.result = { - status: STATUS.SUCCESS, - operationsStatus: [], - subject: undefined, - }; + this.result = []; } public setCharacter(characterId: number): void { this.characterId = characterId; } - public getResult(): EquipResult { + public getResult(): EquipResult[] { const temp = this.result; this.reset(); return temp; } public reset(): void { - this.result = { - status: STATUS.SUCCESS, - operationsStatus: [], - subject: undefined, - }; + this.result = []; } } diff --git a/src/features/loadouts/util/subclassEquipper.ts b/src/features/loadouts/util/subclassEquipper.ts index 899d194..8699533 100644 --- a/src/features/loadouts/util/subclassEquipper.ts +++ b/src/features/loadouts/util/subclassEquipper.ts @@ -1,35 +1,45 @@ +import { ERRORS } from '../../../lib/bungie_api/constants'; import { equipItemRequest, insertSocketPlugFreeRequest } from '../../../lib/bungie_api/requests'; -import { Plug, Subclass } from '../../../types/d2l-types'; +import { Subclass } from '../../../types/d2l-types'; import { ManifestAspect, ManifestPlug, ManifestStatPlug } from '../../../types/manifest-types'; import { STATUS } from '../constants'; import { Equipper } from './equipper'; export class SubclassEquipper extends Equipper { public async equipSubclass(subclass: Subclass) { - this.result.subject = subclass; + const result = { + status: STATUS.SUCCESS, + message: '', + subject: subclass, + }; const response = await equipItemRequest(subclass.instanceId, this.characterId).catch( (error) => { if (error.response) { - this.result.status = STATUS.FAIL; - this.result.operationsStatus[0] = error.response.data.ErrorStatus.replace( - /([a-z])([A-Z])/g, - '$1 $2' - ); + result.status = STATUS.FAIL; + result.message = error.response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); } } ); - if (response) - this.result.operationsStatus[0] = response.data.ErrorStatus.replace( - /([a-z])([A-Z])/g, - '$1 $2' - ); + if (response) result.message = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); + + this.result.push(result); } public async equipSubclassAbility(ability: ManifestPlug, socketArrayIndex: number) { + const subclass = this.result[0].subject; + + if (!subclass) return; + + const result = { + status: STATUS.SUCCESS, + message: '', + subject: ability, + }; + const response = await insertSocketPlugFreeRequest( - this.result.subject.instanceId, + subclass.instanceId, { plugItemHash: String(ability.itemHash), socketArrayType: 0, @@ -38,22 +48,31 @@ export class SubclassEquipper extends Equipper { this.characterId ).catch((error) => { if (error.response) { - this.result.status = STATUS.FAIL; - this.result.operationsStatus.push( - error.response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2') - ); + error.response.data.ErrorCode === ERRORS.SOCKET_ALREADY_CONTAINS_PLUG + ? STATUS.SUCCESS + : STATUS.FAIL; + result.message = error.response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); } }); - if (response) - this.result.operationsStatus.push( - response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2') - ); + if (response) result.message = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); + + this.result.push(result); } public async equipSubclassAspect(aspect: ManifestAspect, socketArrayIndex: number) { + const subclass = this.result[0].subject; + + if (!subclass) return; + + const result = { + status: STATUS.SUCCESS, + message: '', + subject: aspect, + }; + const response = await insertSocketPlugFreeRequest( - this.result.subject.instanceId, + subclass.instanceId, { plugItemHash: String(aspect.itemHash), socketArrayType: 0, @@ -62,22 +81,31 @@ export class SubclassEquipper extends Equipper { this.characterId ).catch((error) => { if (error.response) { - this.result.status = STATUS.FAIL; - this.result.operationsStatus.push( - error.response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2') - ); + error.response.data.ErrorCode === ERRORS.SOCKET_ALREADY_CONTAINS_PLUG + ? STATUS.SUCCESS + : STATUS.FAIL; + result.message = error.response.data.ErrorStatus?.replace(/([a-z])([A-Z])/g, '$1 $2'); } }); - if (response) - this.result.operationsStatus.push( - response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2') - ); + if (response) result.message = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); + + this.result.push(result); } public async equipSubclassFragments(fragment: ManifestStatPlug, socketArrayIndex: number) { + const subclass = this.result[0].subject; + + if (!subclass) return; + + const result = { + status: STATUS.SUCCESS, + message: '', + subject: fragment, + }; + const response = await insertSocketPlugFreeRequest( - this.result.subject.instanceId, + subclass.instanceId, { plugItemHash: String(fragment.itemHash), socketArrayType: 0, @@ -86,16 +114,15 @@ export class SubclassEquipper extends Equipper { this.characterId ).catch((error) => { if (error.response) { - this.result.status = STATUS.FAIL; - this.result.operationsStatus.push( - error.response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2') - ); + error.response.data.ErrorCode === ERRORS.SOCKET_ALREADY_CONTAINS_PLUG + ? STATUS.SUCCESS + : STATUS.FAIL; + result.message = error.response.data.ErrorStatus?.replace(/([a-z])([A-Z])/g, '$1 $2'); } }); - if (response) - this.result.operationsStatus.push( - response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2') - ); + if (response) result.message = response.data.ErrorStatus.replace(/([a-z])([A-Z])/g, '$1 $2'); + + this.result.push(result); } } diff --git a/src/features/profile/destiny-profile.ts b/src/features/profile/destiny-profile.ts index c2cccd5..75b048d 100644 --- a/src/features/profile/destiny-profile.ts +++ b/src/features/profile/destiny-profile.ts @@ -54,391 +54,6 @@ export async function getProfileData(): Promise<ProfileData> { classItem: [], }, subclasses: {}, - equippedLoadout: { - helmet: { - intellect: 0, - discipline: 0, - resilience: 0, - mobility: 0, - strength: 0, - recovery: 0, - instanceHash: '', - itemHash: '', - artifice: undefined, - masterwork: false, - exotic: undefined, - class: undefined, - type: '', - socket: undefined, - location: 0, - icon: '', - name: '', - }, - gauntlets: { - intellect: 0, - discipline: 0, - resilience: 0, - mobility: 0, - strength: 0, - recovery: 0, - instanceHash: '', - itemHash: '', - artifice: undefined, - masterwork: false, - exotic: undefined, - class: undefined, - type: '', - socket: undefined, - location: 0, - icon: '', - name: '', - }, - chestArmor: { - intellect: 0, - discipline: 0, - resilience: 0, - mobility: 0, - strength: 0, - recovery: 0, - instanceHash: '', - itemHash: '', - artifice: undefined, - masterwork: false, - exotic: undefined, - class: undefined, - type: '', - socket: undefined, - location: 0, - icon: '', - name: '', - }, - legArmor: { - intellect: 0, - discipline: 0, - resilience: 0, - mobility: 0, - strength: 0, - recovery: 0, - instanceHash: '', - itemHash: '', - artifice: undefined, - masterwork: false, - exotic: undefined, - class: undefined, - type: '', - socket: undefined, - location: 0, - icon: '', - name: '', - }, - classArmor: { - intellect: 0, - discipline: 0, - resilience: 0, - mobility: 0, - strength: 0, - recovery: 0, - instanceHash: '', - itemHash: '', - artifice: undefined, - masterwork: false, - exotic: undefined, - class: undefined, - type: '', - socket: undefined, - location: 0, - icon: '', - name: '', - }, - helmetMods: [ - { - itemHash: 1980618587, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 1078080765, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 1078080765, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 1078080765, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 4173924323, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - ], - gauntletMods: [ - { - itemHash: 1980618587, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 3820147479, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 3820147479, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 3820147479, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 4173924323, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - ], - chestArmorMods: [ - { - itemHash: 1980618587, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 1803434835, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 1803434835, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 1803434835, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 4173924323, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - ], - legArmorMods: [ - { - itemHash: 1980618587, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 2269836811, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 2269836811, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 2269836811, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 4173924323, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - ], - classArmorMods: [ - { - itemHash: 1980618587, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 3200810407, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 3200810407, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 3200810407, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 4173924323, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - ], - characterId: characterData[key].characterId, - subclassConfig: { - subclass: { - instanceId: '', - screenshot: '', - damageType: 0, - isOwned: false, - class: '', - itemHash: 0, - name: '', - icon: '', - }, - damageType: 1, - super: EMPTY_MANIFEST_PLUG, - aspects: [EMPTY_ASPECT, EMPTY_ASPECT], - fragments: [ - EMPTY_FRAGMENT, - EMPTY_FRAGMENT, - EMPTY_FRAGMENT, - EMPTY_FRAGMENT, - EMPTY_FRAGMENT, - ], - classAbility: null, - meleeAbility: null, - movementAbility: null, - grenade: null, - }, - requiredStatMods: [], - }, }; // iterate character's equipped items @@ -480,16 +95,30 @@ export async function getProfileData(): Promise<ProfileData> { screenshot: subclass.icon, isOwned: subclass.isOwned, }; - character.subclasses[subclass.damageType] = s; + + character.subclasses[subclass.damageType] = { + subclass: s, + damageType: 1, + super: EMPTY_MANIFEST_PLUG, + aspects: [EMPTY_ASPECT, EMPTY_ASPECT], + fragments: [ + EMPTY_FRAGMENT, + EMPTY_FRAGMENT, + EMPTY_FRAGMENT, + EMPTY_FRAGMENT, + EMPTY_FRAGMENT, + ], + classAbility: null, + meleeAbility: null, + movementAbility: null, + grenade: null, + }; // set equipped loadout subclass config - character.equippedLoadout.subclassConfig.subclass = s; - character.equippedLoadout.subclassConfig.damageType = - subclass.damageType as DamageType; const subclassSockets = itemComponents.sockets.data[item.itemInstanceId]?.sockets; /*if (subclassSockets) { - character.equippedLoadout.subclassConfig.classAbility = { + character.subclasses[subclass.damageType].classAbility = { plugItemHash: subclassSockets[0].plugHash, socketArrayType: 0, socketIndex: 1, @@ -570,28 +199,24 @@ export async function getProfileData(): Promise<ProfileData> { case BUCKET_HASH.HELMET: { const helmet = await buildDestinyArmor(itemComponents, item, character.class, 'helmet'); character.armor.helmet.push(helmet); - character.equippedLoadout.helmet = helmet; continue; } case BUCKET_HASH.GAUNTLETS: { const arms = await buildDestinyArmor(itemComponents, item, character.class, 'arms'); character.armor.arms.push(arms); - character.equippedLoadout.gauntlets = arms; continue; } case BUCKET_HASH.CHEST_ARMOR: { const chest = await buildDestinyArmor(itemComponents, item, character.class, 'chest'); character.armor.chest.push(chest); - character.equippedLoadout.chestArmor = chest; continue; } case BUCKET_HASH.LEG_ARMOR: { const legs = await buildDestinyArmor(itemComponents, item, character.class, 'legs'); character.armor.legs.push(legs); - character.equippedLoadout.legArmor = legs; continue; } @@ -603,7 +228,6 @@ export async function getProfileData(): Promise<ProfileData> { 'class' ); character.armor.classItem.push(classItem); - character.equippedLoadout.classArmor = classItem; continue; } } @@ -620,7 +244,7 @@ export async function getProfileData(): Promise<ProfileData> { const subclass = await subclassQuery.first(); if (subclass) { - character.subclasses[subclass.damageType] = { + const s: Subclass = { instanceId: item.itemInstanceId, itemHash: subclass.itemHash, damageType: subclass.damageType, @@ -630,6 +254,24 @@ export async function getProfileData(): Promise<ProfileData> { screenshot: subclass.icon, isOwned: subclass.isOwned, }; + + character.subclasses[subclass.damageType] = { + subclass: s, + damageType: 1, + super: EMPTY_MANIFEST_PLUG, + aspects: [EMPTY_ASPECT, EMPTY_ASPECT], + fragments: [ + EMPTY_FRAGMENT, + EMPTY_FRAGMENT, + EMPTY_FRAGMENT, + EMPTY_FRAGMENT, + EMPTY_FRAGMENT, + ], + classAbility: null, + meleeAbility: null, + movementAbility: null, + grenade: null, + }; } continue; } @@ -837,6 +479,16 @@ export async function getProfileData(): Promise<ProfileData> { .where('itemHash') .equals(plug.plugItemHash) .modify({ isOwned: true }); + + await db.manifestSubclassAspectsDef + .where('itemHash') + .equals(plug.plugItemHash) + .modify({ isOwned: true }); + + await db.manifestSubclassFragmentsDef + .where('itemHash') + .equals(plug.plugItemHash) + .modify({ isOwned: true }); } } } diff --git a/src/features/subclass/SubclassCustomizationWrapper.tsx b/src/features/subclass/SubclassCustomizationWrapper.tsx index d7d8132..4e37cea 100644 --- a/src/features/subclass/SubclassCustomizationWrapper.tsx +++ b/src/features/subclass/SubclassCustomizationWrapper.tsx @@ -3,7 +3,7 @@ import AbilitiesModification from './AbilitiesModification'; import './SubclassCustomizationWrapper.css'; import { Button, Box } from '@mui/material'; import { styled } from '@mui/material/styles'; -import { ManifestSubclass } from '../types/manifest-types'; +import { ManifestSubclass } from '../../types/manifest-types'; interface SubclassCustomizationWrapperProps { onBackClick: () => void; diff --git a/src/lib/bungie_api/Constants.ts b/src/lib/bungie_api/Constants.ts index ed370c4..99864df 100644 --- a/src/lib/bungie_api/Constants.ts +++ b/src/lib/bungie_api/Constants.ts @@ -1,4 +1,3 @@ -import { Stats } from 'fs'; import { ManifestPlug, ManifestAspect, ManifestStatPlug } from '../../types/manifest-types'; export enum API_CREDENTIALS { @@ -60,9 +59,327 @@ export const STATS: string[] = [ 'strength', ]; +export module EMPTY_SOCKETS { + export const HELMET = [ + { + itemHash: 1980618587, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2487827355, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/80d2e1a2a76fdea2752beb141084dcf8.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 1078080765, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2912171003, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/5af87586c92c673aa783543d8fd2d8fd.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 1078080765, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2912171003, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/5af87586c92c673aa783543d8fd2d8fd.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 1078080765, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2912171003, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/5af87586c92c673aa783543d8fd2d8fd.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 4173924323, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 3773173029, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/20133fd6a0df6abaa14bbe5bb3e19856.png', + energyCost: 0, + collectibleHash: -1, + }, + ]; + + export const GAUNTLETS = [ + { + itemHash: 1980618587, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2487827355, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/80d2e1a2a76fdea2752beb141084dcf8.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 3820147479, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2912171003, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/5af87586c92c673aa783543d8fd2d8fd.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 3820147479, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2912171003, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/5af87586c92c673aa783543d8fd2d8fd.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 3820147479, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2912171003, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/5af87586c92c673aa783543d8fd2d8fd.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 4173924323, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 3773173029, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/20133fd6a0df6abaa14bbe5bb3e19856.png', + energyCost: 0, + collectibleHash: -1, + }, + ]; + + export const CHEST_ARMOR = [ + { + itemHash: 1980618587, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2487827355, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/80d2e1a2a76fdea2752beb141084dcf8.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 1803434835, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2912171003, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/5af87586c92c673aa783543d8fd2d8fd.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 1803434835, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2912171003, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/5af87586c92c673aa783543d8fd2d8fd.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 1803434835, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2912171003, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/5af87586c92c673aa783543d8fd2d8fd.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 4173924323, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 3773173029, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/20133fd6a0df6abaa14bbe5bb3e19856.png', + energyCost: 0, + collectibleHash: -1, + }, + ]; + + export const LEG_ARMOR = [ + { + itemHash: 1980618587, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2487827355, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/80d2e1a2a76fdea2752beb141084dcf8.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 2269836811, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2912171003, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/5af87586c92c673aa783543d8fd2d8fd.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 2269836811, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2912171003, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/5af87586c92c673aa783543d8fd2d8fd.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 2269836811, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2912171003, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/5af87586c92c673aa783543d8fd2d8fd.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 4173924323, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 3773173029, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/20133fd6a0df6abaa14bbe5bb3e19856.png', + energyCost: 0, + collectibleHash: -1, + }, + ]; + + export const CLASS_ARMOR = [ + { + itemHash: 1980618587, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2487827355, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/80d2e1a2a76fdea2752beb141084dcf8.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 3200810407, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2912171003, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/5af87586c92c673aa783543d8fd2d8fd.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 3200810407, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2912171003, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/5af87586c92c673aa783543d8fd2d8fd.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 3200810407, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 2912171003, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/5af87586c92c673aa783543d8fd2d8fd.jpg', + energyCost: 0, + collectibleHash: -1, + }, + { + itemHash: 4173924323, + perkName: '', + perkDescription: '', + perkIcon: '', + category: 3773173029, + isOwned: true, + name: 'Empty Mod Socket', + icon: 'https://bungie.net/common/destiny2_content/icons/20133fd6a0df6abaa14bbe5bb3e19856.png', + energyCost: 0, + collectibleHash: -1, + }, + ]; +} + export enum ERRORS { INVALID_EQUIP_LOCATION = 1671, CANNOT_FIND_ITEM = 1623, + SOCKET_ALREADY_CONTAINS_PLUG = 1679, } export enum STAT_MOD_HASHES { diff --git a/src/store/LoadoutReducer.tsx b/src/store/LoadoutReducer.tsx index 0dec289..a774371 100644 --- a/src/store/LoadoutReducer.tsx +++ b/src/store/LoadoutReducer.tsx @@ -1,18 +1,12 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { Character, DamageType, DestinyArmor, Loadout, Plug, Subclass } from '../types/d2l-types'; +import { Character, DamageType, DestinyArmor, Loadout, Subclass } from '../types/d2l-types'; import { - DAMAGE_TYPE, EMPTY_ASPECT, EMPTY_FRAGMENT, EMPTY_MANIFEST_PLUG, + EMPTY_SOCKETS, } from '../lib/bungie_api/constants'; -import { ManifestArmorStatMod, ManifestPlug } from '../types/manifest-types'; - -const EMPTY_PLUG: Plug = { - plugItemHash: '', - socketArrayType: 0, - socketIndex: -1, -}; +import { ManifestArmorMod, ManifestArmorStatMod } from '../types/manifest-types'; export interface InitialState { loadout: Loadout; @@ -115,266 +109,11 @@ const initialState: InitialState = { name: '', }, requiredStatMods: [], - helmetMods: [ - { - itemHash: 1980618587, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 1078080765, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 1078080765, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 1078080765, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 4173924323, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - ], - gauntletMods: [ - { - itemHash: 1980618587, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 3820147479, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 3820147479, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 3820147479, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 4173924323, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - ], - chestArmorMods: [ - { - itemHash: 1980618587, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 1803434835, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 1803434835, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 1803434835, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 4173924323, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - ], - legArmorMods: [ - { - itemHash: 1980618587, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 2269836811, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 2269836811, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 2269836811, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 4173924323, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - ], - classArmorMods: [ - { - itemHash: 1980618587, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 3200810407, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 3200810407, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 3200810407, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - { - itemHash: 4173924323, - perkName: '', - perkDescription: '', - perkIcon: '', - category: 0, - isOwned: false, - name: '', - icon: '', - }, - ], + helmetMods: EMPTY_SOCKETS.HELMET, + gauntletMods: EMPTY_SOCKETS.GAUNTLETS, + chestArmorMods: EMPTY_SOCKETS.CHEST_ARMOR, + legArmorMods: EMPTY_SOCKETS.LEG_ARMOR, + classArmorMods: EMPTY_SOCKETS.CLASS_ARMOR, characterId: 0, subclassConfig: { subclass: { @@ -418,7 +157,7 @@ export const loadoutConfigSlice = createSlice({ action: PayloadAction<{ armorType: string; slot: number; - plug: ManifestPlug | ManifestArmorStatMod; + plug: ManifestArmorMod | ManifestArmorStatMod; }> ) => { switch (action.payload.armorType) { @@ -454,21 +193,26 @@ export const loadoutConfigSlice = createSlice({ state.loadout.legArmorMods = initialState.loadout.legArmorMods; state.loadout.classArmorMods = initialState.loadout.classArmorMods; }, - updateSubclass: ( - state, - action: PayloadAction<{ damageType: DamageType; subclass: Subclass }> - ) => { - state.loadout.subclassConfig = { - subclass: action.payload.subclass, - damageType: action.payload.damageType, - super: EMPTY_MANIFEST_PLUG, - aspects: [EMPTY_ASPECT, EMPTY_ASPECT], - fragments: [EMPTY_FRAGMENT, EMPTY_FRAGMENT, EMPTY_FRAGMENT, EMPTY_FRAGMENT, EMPTY_FRAGMENT], - classAbility: null, - meleeAbility: null, - movementAbility: null, - grenade: null, - }; + updateSubclass: (state, action: PayloadAction<{ subclass: Subclass | undefined }>) => { + if (action.payload.subclass !== undefined) { + state.loadout.subclassConfig = { + subclass: action.payload.subclass, + damageType: action.payload.subclass.damageType as DamageType, + super: EMPTY_MANIFEST_PLUG, + aspects: [EMPTY_ASPECT, EMPTY_ASPECT], + fragments: [ + EMPTY_FRAGMENT, + EMPTY_FRAGMENT, + EMPTY_FRAGMENT, + EMPTY_FRAGMENT, + EMPTY_FRAGMENT, + ], + classAbility: null, + meleeAbility: null, + movementAbility: null, + grenade: null, + }; + } }, updateSubclassMods: (state, action: PayloadAction<{ category: string; mods: any[] }>) => { const { category, mods } = action.payload; @@ -505,7 +249,6 @@ export const loadoutConfigSlice = createSlice({ }, updateLoadoutCharacter: (state, action: PayloadAction<Character>) => { state.loadout.characterId = action.payload.id; - state.loadout.subclassConfig = action.payload.equippedLoadout.subclassConfig; }, resetLoadout: () => initialState, }, diff --git a/src/types/d2l-types.ts b/src/types/d2l-types.ts index 56cd071..e6e0eef 100644 --- a/src/types/d2l-types.ts +++ b/src/types/d2l-types.ts @@ -82,8 +82,7 @@ export type Character = { class: CharacterClass; emblem?: Emblem; armor: ArmorBySlot; - subclasses: { [key: number]: Subclass }; - equippedLoadout: Loadout; + subclasses: { [key: number]: SubclassConfig | undefined }; }; export type Emblem = {