diff --git a/src/app/routes/Dashboard.tsx b/src/app/routes/Dashboard.tsx index eb676ae..9b778a6 100644 --- a/src/app/routes/Dashboard.tsx +++ b/src/app/routes/Dashboard.tsx @@ -10,17 +10,16 @@ import { getDestinyMembershipId } from '../../features/membership/bungie-account import { updateMembership } from '../../store/MembershipReducer'; import { getProfileData } from '../../features/profile/destiny-profile'; import { updateProfileData } from '../../store/ProfileReducer'; -import { Character } from '../../types/d2l-types'; +import { Character, SubclassConfig } from '../../types/d2l-types'; import StatsTable from '../../features/armor-optimization/StatsTable'; import HeaderComponent from '../../components/HeaderComponent'; import ExoticSelector from '../../features/armor-optimization/ExoticSelector'; import greyBackground from '../../assets/grey.png'; -import { db } from '../../store/db'; 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 LoadoutCustomization from '../../components/LoadoutCustomization'; +import { DAMAGE_TYPE } from '../../lib/bungie_api/constants'; const PageContainer = styled('div')({ display: 'flex', @@ -103,6 +102,7 @@ const NewComponentWrapper = styled('div')({ export const Dashboard: React.FC = () => { const dispatch = useDispatch(); + const membership = useSelector((state: RootState) => state.destinyMembership.membership); const characters = useSelector((state: RootState) => state.profile.profileData.characters); const { selectedValues, selectedExotic, selectedExoticClassCombo } = useSelector( @@ -110,11 +110,14 @@ export const Dashboard: React.FC = () => { ); const [selectedCharacter, setSelectedCharacter] = useState(undefined); - const [isLoading, setIsLoading] = useState(false); - const [subclasses, setSubclasses] = useState([]); - const [selectedSubclass, setSelectedSubclass] = useState(null); - const [customizingSubclass, setCustomizingSubclass] = useState(null); - const [lastNonPrismaticSubclass, setLastNonPrismaticSubclass] = useState( + const [dataLoading, setDataLoading] = useState(true); + const [generatingPermutations, setGeneratingPermutations] = useState(false); + const [subclasses, setSubclasses] = useState< + { [key: number]: SubclassConfig | undefined } | undefined + >(undefined); + const [selectedSubclass, setSelectedSubclass] = useState(null); + const [customizingSubclass, setCustomizingSubclass] = useState(null); + const [lastNonPrismaticSubclass, setLastNonPrismaticSubclass] = useState( null ); const [showArmorCustomization, setShowArmorCustomization] = useState(false); @@ -134,22 +137,34 @@ export const Dashboard: React.FC = () => { }; updateProfile().catch(console.error); + + setDataLoading(false); }, []); useEffect(() => { if (selectedCharacter) { dispatch(resetLoadout()); dispatch(updateLoadoutCharacter(selectedCharacter)); - fetchSubclasses(selectedCharacter).then((subclassesData) => { - setSubclasses(subclassesData); - if (subclassesData.length > 0) { - setSelectedSubclass(subclassesData[0]); - const defaultSubclass = - subclassesData.find((subclass) => !subclass.name.includes('Prismatic')) || - subclassesData[0]; - setLastNonPrismaticSubclass(defaultSubclass); + + setSubclasses(selectedCharacter.subclasses); + + const keys = Object.keys(selectedCharacter.subclasses); + + for (let i = 0; i < keys.length; i++) { + if ( + selectedCharacter.subclasses[Number(keys[i])] !== undefined && + selectedCharacter.subclasses[Number(keys[i])]!.damageType !== DAMAGE_TYPE.KINETIC + ) { + setSelectedSubclass(selectedCharacter.subclasses[Number(keys[i])]!); + setLastNonPrismaticSubclass(selectedCharacter.subclasses[Number(keys[i])]!); + dispatch( + updateSubclass({ + subclass: selectedCharacter.subclasses[Number(keys[i])], + }) + ); + break; } - }); + } } }, [selectedCharacter, dispatch]); @@ -169,9 +184,9 @@ export const Dashboard: React.FC = () => { const filteredPermutations = useMemo(() => { if (permutations && selectedValues) { - setIsLoading(true); + setGeneratingPermutations(true); const filtered = filterPermutations(permutations, selectedValues); - setIsLoading(false); + setGeneratingPermutations(false); return filtered; } return null; @@ -183,22 +198,28 @@ export const Dashboard: React.FC = () => { } }; - const handleSubclassSelect = (subclass: ManifestSubclass) => { + const handleSubclassSelect = (subclass: SubclassConfig) => { setSelectedSubclass(subclass); - if (selectedCharacter && subclass.damageType in selectedCharacter.subclasses) { + dispatch( + updateSubclass({ + subclass: subclass, + }) + ); + + if (selectedCharacter) { dispatch( updateSubclass({ - subclass: selectedCharacter.subclasses[subclass.damageType]?.subclass, + subclass: selectedCharacter.subclasses[subclass.damageType], }) ); } - if (!subclass.name.includes('Prismatic')) { + if (subclass.damageType !== DAMAGE_TYPE.KINETIC) { setLastNonPrismaticSubclass(subclass); } }; - const handleSubclassRightClick = (subclass: ManifestSubclass) => { + const handleSubclassRightClick = (subclass: SubclassConfig) => { setCustomizingSubclass(subclass); setShowAbilitiesModification(true); }; @@ -215,29 +236,18 @@ export const Dashboard: React.FC = () => { setShowArmorCustomization(false); }; - const fetchSubclasses = async (character: Character): Promise => { - const data = await db.manifestSubclass - .where('class') - .equalsIgnoreCase(character.class) - .toArray(); - return data.map((item) => ({ - ...item, - itemHash: item.itemHash, - })); - }; - - return ( + return !dataLoading ? ( {showAbilitiesModification && customizingSubclass ? ( ) : showArmorCustomization ? ( ) : ( @@ -275,7 +285,7 @@ export const Dashboard: React.FC = () => {

Armour Combinations

- {isLoading ? ( + {generatingPermutations ? (

Loading...

) : filteredPermutations ? ( { )}
+ ) : ( +
loading...
); }; diff --git a/src/assets/subclass-icons/2.png b/src/assets/subclass-icons/2.png new file mode 100644 index 0000000..73efff3 Binary files /dev/null and b/src/assets/subclass-icons/2.png differ diff --git a/src/assets/subclass-icons/3.png b/src/assets/subclass-icons/3.png new file mode 100644 index 0000000..b06ca4f Binary files /dev/null and b/src/assets/subclass-icons/3.png differ diff --git a/src/assets/subclass-icons/4.png b/src/assets/subclass-icons/4.png new file mode 100644 index 0000000..e467494 Binary files /dev/null and b/src/assets/subclass-icons/4.png differ diff --git a/src/assets/subclass-icons/6.png b/src/assets/subclass-icons/6.png new file mode 100644 index 0000000..14ba9f3 Binary files /dev/null and b/src/assets/subclass-icons/6.png differ diff --git a/src/assets/subclass-icons/7.png b/src/assets/subclass-icons/7.png new file mode 100644 index 0000000..d641f4b Binary files /dev/null and b/src/assets/subclass-icons/7.png differ diff --git a/src/components/LoadoutCustomization.tsx b/src/components/LoadoutCustomization.tsx index 768b7f7..7f5b393 100644 --- a/src/components/LoadoutCustomization.tsx +++ b/src/components/LoadoutCustomization.tsx @@ -5,13 +5,13 @@ import { styled } from '@mui/material/styles'; import ModCustomization from '../features/armor-mods/components/ModCustomization'; import EquipLoadout from '../features/loadouts/components/EquipLoadout'; import AbilitiesModification from '../features/subclass/AbilitiesModification'; -import { ManifestSubclass } from '../types/manifest-types'; import ShareLoadout from '../features/loadouts/components/ShareLoadout'; +import { SubclassConfig } from '../types/d2l-types'; interface LoadoutCustomizationProps { onBackClick: () => void; screenshot: string; - subclass: ManifestSubclass; + subclass: SubclassConfig; } const TransparentButton = styled(Button)(({ theme }) => ({ diff --git a/src/components/SingleDiamondButton.tsx b/src/components/SingleDiamondButton.tsx index 1ecf23c..6670cef 100644 --- a/src/components/SingleDiamondButton.tsx +++ b/src/components/SingleDiamondButton.tsx @@ -2,12 +2,15 @@ import React, { useState, useEffect, useCallback } from 'react'; import { useSpring, animated, config, to } from 'react-spring'; import './SingleDiamondButton.css'; import { ManifestSubclass } from '../types/manifest-types'; +import { SubclassConfig } from '../types/d2l-types'; +import { DAMAGE_TYPE } from '../lib/bungie_api/constants'; +import { damp } from 'three/src/math/MathUtils'; interface SingleDiamondButtonProps { - subclasses: ManifestSubclass[]; - selectedSubclass: ManifestSubclass | null; - onSubclassSelect: (subclass: ManifestSubclass) => void; - onSubclassRightClick: (subclass: ManifestSubclass) => void; + subclasses: { [key: number]: SubclassConfig | undefined } | undefined; + selectedSubclass: SubclassConfig | null; + onSubclassSelect: (subclass: SubclassConfig) => void; + onSubclassRightClick: (subclass: SubclassConfig) => void; } const SingleDiamondButton: React.FC = ({ @@ -16,10 +19,10 @@ const SingleDiamondButton: React.FC = ({ onSubclassSelect, onSubclassRightClick, }) => { - const [currentSubclass, setCurrentSubclass] = useState(null); - const [lastNonPrismaticSubclass, setLastNonPrismaticSubclass] = useState( - null - ); + const [currentSubclass, setCurrentSubclass] = useState(undefined); + const [lastNonPrismaticSubclass, setLastNonPrismaticSubclass] = useState< + SubclassConfig | undefined + >(undefined); const [isPrismaticActive, setIsPrismaticActive] = useState(false); const [isAccelerating, setIsAccelerating] = useState(false); const [isOblong, setIsOblong] = useState(false); @@ -85,29 +88,40 @@ const SingleDiamondButton: React.FC = ({ } }, [isAccelerating, scaleApi]); - const getDefaultSubclass = () => { - return subclasses.find((subclass) => !subclass.name.includes('Prismatic')) || subclasses[0]; - }; + function getDefaultSubclass(): SubclassConfig | undefined { + if (subclasses) { + const keys = Object.keys(subclasses); + for (let i = 0; i < keys.length; i++) { + if ( + subclasses[Number(keys[i])] !== undefined && + subclasses[Number(keys[i])]?.damageType !== DAMAGE_TYPE.KINETIC + ) + return subclasses[Number(keys[i])]!; + } + } + + return undefined; + } useEffect(() => { if (selectedSubclass) { setCurrentSubclass(selectedSubclass); - setIsPrismaticActive(selectedSubclass.name.includes('Prismatic')); - if (!selectedSubclass.name.includes('Prismatic')) { + setIsPrismaticActive(selectedSubclass.subclass.damageType === DAMAGE_TYPE.KINETIC); + if (selectedSubclass.subclass.damageType !== DAMAGE_TYPE.KINETIC) { setLastNonPrismaticSubclass(selectedSubclass); } else if (!lastNonPrismaticSubclass) { setLastNonPrismaticSubclass(getDefaultSubclass()); } - } else if (subclasses.length > 0) { + } else if (subclasses) { const defaultSubclass = getDefaultSubclass(); setCurrentSubclass(defaultSubclass); setLastNonPrismaticSubclass(defaultSubclass); - onSubclassSelect(defaultSubclass); + onSubclassSelect(defaultSubclass!); } }, [selectedSubclass, subclasses, onSubclassSelect, lastNonPrismaticSubclass]); - const handleSelect = (subclass: ManifestSubclass) => { - if (subclass.name.includes('Prismatic')) { + const handleSelect = (subclass: SubclassConfig) => { + if (subclass.damageType === DAMAGE_TYPE.KINETIC) { setIsPrismaticActive(true); } else { setIsPrismaticActive(false); @@ -125,16 +139,11 @@ const SingleDiamondButton: React.FC = ({ } }; - const handleRightClick = (event: React.MouseEvent, subclass: ManifestSubclass) => { + const handleRightClick = (event: React.MouseEvent, subclass: SubclassConfig) => { event.preventDefault(); onSubclassRightClick(subclass); }; - const prismaticSubclass = subclasses.find((subclass) => subclass.name.includes('Prismatic')); - const nonPrismaticSubclasses = subclasses - .filter((subclass) => !subclass.name.includes('Prismatic') && subclass !== currentSubclass) - .slice(0, 4); - const RotatingShape = ({ rotationOffset = 0 }: { rotationOffset?: number }) => ( = ({
{!isPrismaticActive && (
- {nonPrismaticSubclasses.map((subclass, index) => ( -
handleSelect(subclass)} - onContextMenu={(event) => handleRightClick(event, subclass)} - > - {subclass.name} -
- ))} + {subclasses && + [ + DAMAGE_TYPE.ARC, + DAMAGE_TYPE.SOLAR, + DAMAGE_TYPE.STASIS, + DAMAGE_TYPE.STRAND, + DAMAGE_TYPE.VOID, + ] + .filter((key) => Number(key) !== selectedSubclass?.damageType) + .map((damageType, index) => ( +
{ + if (damageType in subclasses) handleSelect(subclasses[Number(damageType)]!); + }} + onContextMenu={(event) => { + if (damageType in subclasses && selectedSubclass?.damageType === damageType) + handleRightClick(event, subclasses[Number(damageType)]!); + }} + > + {String(damageType)} +
+ ))}
)} {isPrismaticActive ? ( @@ -170,22 +198,32 @@ const SingleDiamondButton: React.FC = ({
handleRightClick(event, currentSubclass!)} + onContextMenu={(event) => { + if (selectedSubclass?.damageType === DAMAGE_TYPE.KINETIC) + handleRightClick(event, currentSubclass!); + }} >
- {currentSubclass!.name} + {currentSubclass!.subclass.name}
handleSelect(lastNonPrismaticSubclass!)} - onContextMenu={(event) => handleRightClick(event, lastNonPrismaticSubclass!)} + onContextMenu={(event) => { + if (selectedSubclass?.damageType !== DAMAGE_TYPE.KINETIC) + handleRightClick(event, lastNonPrismaticSubclass!); + }} > {lastNonPrismaticSubclass!.name}
@@ -194,28 +232,40 @@ const SingleDiamondButton: React.FC = ({ <>
handleRightClick(event, currentSubclass!)} + onContextMenu={(event) => { + if (selectedSubclass?.damageType !== DAMAGE_TYPE.KINETIC) + handleRightClick(event, currentSubclass!); + }} > {currentSubclass && ( - {currentSubclass.name} + {currentSubclass.subclass.name} )}
- {prismaticSubclass && ( + {subclasses !== undefined && subclasses[DAMAGE_TYPE.KINETIC] ? (
handleSelect(prismaticSubclass)} - onContextMenu={(event) => handleRightClick(event, prismaticSubclass)} + onClick={() => handleSelect(subclasses[DAMAGE_TYPE.KINETIC]!)} + onContextMenu={(event) => { + if (selectedSubclass?.damageType === DAMAGE_TYPE.KINETIC) + handleRightClick(event, subclasses[DAMAGE_TYPE.KINETIC]!); + }} >
{prismaticSubclass.name}
+ ) : ( +
)} )} diff --git a/src/features/profile/destiny-profile.ts b/src/features/profile/destiny-profile.ts index b278eff..3926d4b 100644 --- a/src/features/profile/destiny-profile.ts +++ b/src/features/profile/destiny-profile.ts @@ -2,6 +2,7 @@ import { ARMOR, BUCKET_HASH, COLLECTIBLE_OWNED, + DAMAGE_TYPE, EMPTY_ASPECT, EMPTY_FRAGMENT, EMPTY_MANIFEST_PLUG, @@ -15,6 +16,7 @@ import { armor, Character, CharacterClass, + DamageType, DestinyArmor, Emblem, ProfileData, @@ -76,121 +78,7 @@ export async function getProfileData(): Promise { } case BUCKET_HASH.SUBCLASS: { - const subclassQuery = db.manifestSubclass.where('itemHash').equals(item.itemHash); - - await subclassQuery.modify({ isOwned: true }); - - const subclass = await subclassQuery.first(); - - if (subclass) { - const s: Subclass = { - instanceId: item.itemInstanceId, - itemHash: subclass.itemHash, - damageType: subclass.damageType, - name: subclass.name, - class: subclass.class, - icon: subclass.icon, - 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, - }; - - // set equipped loadout subclass config - const subclassSockets = itemComponents.sockets.data[item.itemInstanceId]?.sockets; - - /*if (subclassSockets) { - character.subclasses[subclass.damageType].classAbility = { - plugItemHash: subclassSockets[0].plugHash, - socketArrayType: 0, - socketIndex: 1, - }; - character.equippedLoadout.subclassConfig.movementAbility = { - plugItemHash: subclassSockets[1].plugHash, - socketArrayType: 0, - socketIndex: 2, - }; - character.equippedLoadout.subclassConfig.super = { - plugItemHash: subclassSockets[2].plugHash, - socketArrayType: 0, - socketIndex: 0, - }; - character.equippedLoadout.subclassConfig.meleeAbility = { - plugItemHash: subclassSockets[3].plugHash, - socketArrayType: 0, - socketIndex: 3, - }; - character.equippedLoadout.subclassConfig.grenade = { - plugItemHash: subclassSockets[4].plugHash, - socketArrayType: 0, - socketIndex: 4, - }; - - if (character.equippedLoadout.subclassConfig.damageType === DAMAGE_TYPE.KINETIC) { - character.equippedLoadout.subclassConfig.aspects = [ - { - plugItemHash: subclassSockets[7].plugHash, - socketArrayType: 0, - socketIndex: 7, - }, - { - plugItemHash: subclassSockets[8].plugHash, - socketArrayType: 0, - socketIndex: 8, - }, - ]; - - character.equippedLoadout.subclassConfig.fragments = subclassSockets - .slice(9, subclassSockets.length) - .map((p: any, index: number): Plug => { - return { - plugItemHash: p.plugHash, - socketArrayType: 0, - socketIndex: 9 + index, - }; - }); - } else { - character.equippedLoadout.subclassConfig.aspects = [ - { - plugItemHash: subclassSockets[5].plugHash, - socketArrayType: 0, - socketIndex: 5, - }, - { - plugItemHash: subclassSockets[6].plugHash, - socketArrayType: 0, - socketIndex: 6, - }, - ]; - - character.equippedLoadout.subclassConfig.fragments = subclassSockets - .slice(7, subclassSockets.length) - .map((p: any, index: number): Plug => { - return { - plugItemHash: p.plugHash, - socketArrayType: 7 + 0, - socketIndex: index, - }; - }); - } - }*/ - } + buildSubclassConfig(item, character, itemComponents); continue; } @@ -275,42 +163,7 @@ export async function getProfileData(): Promise { for (const item of characterInventories[key].items) { switch (item.bucketHash) { case BUCKET_HASH.SUBCLASS: { - const subclassQuery = db.manifestSubclass.where('itemHash').equals(item.itemHash); - - await subclassQuery.modify({ isOwned: true }); - - const subclass = await subclassQuery.first(); - - if (subclass) { - const s: Subclass = { - instanceId: item.itemInstanceId, - itemHash: subclass.itemHash, - damageType: subclass.damageType, - name: subclass.name, - class: subclass.class, - icon: subclass.icon, - 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, - }; - } + buildSubclassConfig(item, character, itemComponents); continue; } @@ -545,6 +398,130 @@ export async function getProfileData(): Promise { return profile; } +async function buildSubclassConfig(item: any, character: Character, itemComponents: any) { + const subclassQuery = db.manifestSubclass.where('itemHash').equals(item.itemHash); + + await subclassQuery.modify({ isOwned: true }); + + const subclass = await subclassQuery.first(); + + if (subclass) { + const s: Subclass = { + instanceId: item.itemInstanceId, + itemHash: subclass.itemHash, + damageType: subclass.damageType, + name: subclass.name, + class: subclass.class, + icon: subclass.icon, + screenshot: subclass.screenshot, + isOwned: subclass.isOwned, + }; + + character.subclasses[subclass.damageType] = { + subclass: s, + damageType: 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, + }; + + // set equipped loadout subclass config + const subclassSockets = itemComponents.sockets.data[item.itemInstanceId]?.sockets; + + if (subclassSockets && character.subclasses[subclass.damageType] !== undefined) { + const classAbility = await db.manifestSubclassModDef + .where('itemHash') + .equals(subclassSockets[0].plugHash) + .first(); + + if (classAbility) character.subclasses[subclass.damageType]!.classAbility = classAbility; + + const movementAbility = await db.manifestSubclassModDef + .where('itemHash') + .equals(subclassSockets[1].plugHash) + .first(); + + if (movementAbility) + character.subclasses[subclass.damageType]!.movementAbility = movementAbility; + + const superAbility = await db.manifestSubclassModDef + .where('itemHash') + .equals(subclassSockets[2].plugHash) + .first(); + + if (superAbility) character.subclasses[subclass.damageType]!.super = superAbility; + + const meleeAbility = await db.manifestSubclassModDef + .where('itemHash') + .equals(subclassSockets[3].plugHash) + .first(); + + if (meleeAbility) character.subclasses[subclass.damageType]!.meleeAbility = meleeAbility; + + const grenade = await db.manifestSubclassModDef + .where('itemHash') + .equals(subclassSockets[4].plugHash) + .first(); + + if (grenade) character.subclasses[subclass.damageType]!.grenade = grenade; + + if (subclass.damageType === DAMAGE_TYPE.KINETIC) { + const firstAspect = await db.manifestSubclassAspectsDef + .where('itemHash') + .equals(subclassSockets[7].plugHash) + .first(); + + if (firstAspect) character.subclasses[subclass.damageType]!.aspects[0] = firstAspect; + + const secondAspect = await db.manifestSubclassAspectsDef + .where('itemHash') + .equals(subclassSockets[8].plugHash) + .first(); + + if (secondAspect) character.subclasses[subclass.damageType]!.aspects[1] = secondAspect; + + const fragments = subclassSockets.slice(9, subclassSockets.length); + for (let i = 0; i < fragments.length; i++) { + const fragment = await db.manifestSubclassFragmentsDef + .where('itemHash') + .equals(fragments[i].plugHash) + .first(); + + if (fragment) character.subclasses[subclass.damageType]!.fragments[i] = fragment; + } + } else { + const firstAspect = await db.manifestSubclassAspectsDef + .where('itemHash') + .equals(subclassSockets[5].plugHash) + .first(); + + if (firstAspect) character.subclasses[subclass.damageType]!.aspects[0] = firstAspect; + + const secondAspect = await db.manifestSubclassAspectsDef + .where('itemHash') + .equals(subclassSockets[6].plugHash) + .first(); + + if (secondAspect) character.subclasses[subclass.damageType]!.aspects[1] = secondAspect; + + const fragments = subclassSockets.slice(7, subclassSockets.length); + for (let i = 0; i < fragments.length; i++) { + const fragment = await db.manifestSubclassFragmentsDef + .where('itemHash') + .equals(fragments[i].plugHash) + .first(); + + if (fragment) character.subclasses[subclass.damageType]!.fragments[i] = fragment; + } + } + } + } +} + async function buildDestinyArmor( itemComponents: any, item: any, diff --git a/src/features/subclass/AbilitiesModification.tsx b/src/features/subclass/AbilitiesModification.tsx index 791df24..7757f32 100644 --- a/src/features/subclass/AbilitiesModification.tsx +++ b/src/features/subclass/AbilitiesModification.tsx @@ -5,17 +5,17 @@ import { Box, Container } from '@mui/system'; import { PLUG_CATEGORY_HASH } from '../../lib/bungie_api/subclass-constants'; import { RootState } from '../../store'; import { db } from '../../store/db'; -import { updateSubclassMods } from '../../store/LoadoutReducer'; +import { updateSubclass, updateSubclassMods } from '../../store/LoadoutReducer'; import { ManifestSubclass, ManifestPlug, ManifestAspect, ManifestStatPlug, } from '../../types/manifest-types'; -import { DamageType } from '../../types/d2l-types'; +import { DamageType, SubclassConfig } from '../../types/d2l-types'; interface AbilitiesModificationProps { - subclass: ManifestSubclass; + subclass: SubclassConfig; } export const EMPTY_MANIFEST_PLUG: ManifestPlug = { @@ -153,7 +153,7 @@ const StyledTitle = styled(Typography)(({ theme }) => ({ width: '40%', })); -const fetchMods = async (subclass: ManifestSubclass) => { +const fetchMods = async (subclass: SubclassConfig) => { const modsData: { [key: string]: (ManifestPlug | ManifestAspect | ManifestStatPlug)[] } = { SUPERS: [], CLASS_ABILITIES: [], @@ -164,7 +164,7 @@ const fetchMods = async (subclass: ManifestSubclass) => { FRAGMENTS: [], }; - const classType = subclass.class.toUpperCase() as keyof typeof PLUG_CATEGORY_HASH; + const classType = subclass.subclass.class.toUpperCase() as keyof typeof PLUG_CATEGORY_HASH; const damageType = subclassTypeMap[ subclass.damageType as DamageType ] as keyof (typeof PLUG_CATEGORY_HASH)[typeof classType]; @@ -217,6 +217,7 @@ const fetchMods = async (subclass: ManifestSubclass) => { return modsData; } }; + const AbilitiesModification: React.FC = ({ subclass }) => { const [mods, setMods] = useState<{ [key: string]: (ManifestPlug | ManifestAspect | ManifestStatPlug)[]; @@ -426,16 +427,14 @@ const AbilitiesModification: React.FC = ({ subclass } return ( - + - {subclass.name} + {subclass.subclass.name} - {renderModCategory('SUPERS', loadout.super)} - ABILITIES @@ -446,7 +445,6 @@ const AbilitiesModification: React.FC = ({ subclass {renderModCategory('GRENADES', loadout.grenade)} - ASPECTS @@ -457,7 +455,6 @@ const AbilitiesModification: React.FC = ({ subclass ))} - FRAGMENTS diff --git a/src/features/subclass/SubclassCustomizationWrapper.tsx b/src/features/subclass/SubclassCustomizationWrapper.tsx index 4e37cea..47d7650 100644 --- a/src/features/subclass/SubclassCustomizationWrapper.tsx +++ b/src/features/subclass/SubclassCustomizationWrapper.tsx @@ -4,10 +4,11 @@ import './SubclassCustomizationWrapper.css'; import { Button, Box } from '@mui/material'; import { styled } from '@mui/material/styles'; import { ManifestSubclass } from '../../types/manifest-types'; +import { SubclassConfig } from '../../types/d2l-types'; interface SubclassCustomizationWrapperProps { onBackClick: () => void; - subclass: ManifestSubclass; + subclass: SubclassConfig; screenshot: string; } @@ -29,7 +30,7 @@ const SubclassCustomizationWrapper: React.FC screenshot, }) => { return ( -
@@ -41,8 +42,10 @@ const SubclassCustomizationWrapper: React.FC Back - -
+ + + +
); }; diff --git a/src/store/LoadoutReducer.tsx b/src/store/LoadoutReducer.tsx index 75d5f66..f60dbeb 100644 --- a/src/store/LoadoutReducer.tsx +++ b/src/store/LoadoutReducer.tsx @@ -1,5 +1,12 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { Character, DamageType, DestinyArmor, Loadout, Subclass } from '../types/d2l-types'; +import { + Character, + DamageType, + DestinyArmor, + Loadout, + Subclass, + SubclassConfig, +} from '../types/d2l-types'; import { EMPTY_ASPECT, EMPTY_FRAGMENT, @@ -196,25 +203,9 @@ export const loadoutConfigSlice = createSlice({ state.loadout.legArmorMods = initialState.loadout.legArmorMods; state.loadout.classArmorMods = initialState.loadout.classArmorMods; }, - updateSubclass: (state, action: PayloadAction<{ subclass: Subclass | undefined }>) => { + updateSubclass: (state, action: PayloadAction<{ subclass: SubclassConfig | 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, - }; + state.loadout.subclassConfig = action.payload.subclass; } }, updateSubclassMods: (state, action: PayloadAction<{ category: string; mods: any[] }>) => {