From 2bfa1ceebe5c0c7f1e5044f0237339cc71021ddd Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Wed, 11 Sep 2024 12:33:45 -0700 Subject: [PATCH 1/6] Get character loadouts from api call --- src/lib/bungie_api/requests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/bungie_api/requests.ts b/src/lib/bungie_api/requests.ts index a01868f..00ad9ff 100644 --- a/src/lib/bungie_api/requests.ts +++ b/src/lib/bungie_api/requests.ts @@ -21,7 +21,7 @@ export function getProfileDataRequest(): Promise> { const destinyMembership = store.getState().destinyMembership.membership; return _get( - `/Platform/Destiny2/${destinyMembership.membershipType}/Profile/${destinyMembership.membershipId}/?components=102,200,201,300,205,302,304,305,800`, + `/Platform/Destiny2/${destinyMembership.membershipType}/Profile/${destinyMembership.membershipId}/?components=102,200,201,300,205,206,302,304,305,800`, { headers: { Authorization: `Bearer ${accessToken}`, From 8df4fd4e380dc2751f6b59aaed6b3b51d5c32d35 Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Wed, 11 Sep 2024 12:44:34 -0700 Subject: [PATCH 2/6] Added type for destiny in game loadout --- src/types/d2l-types.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/types/d2l-types.ts b/src/types/d2l-types.ts index 8918f21..9f4e5c7 100644 --- a/src/types/d2l-types.ts +++ b/src/types/d2l-types.ts @@ -86,6 +86,14 @@ export type Plug = { socketIndex?: number; }; +export type DestinyLoadout = { + colorHash: number; + iconHash: number; + nameHash: number; + armor: number[]; + subclass: number; +}; + export type Loadout = { helmet: DestinyArmor; gauntlets: DestinyArmor; From 7e51e9746a300a2999c146a7496184ce3d3d42ce Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Wed, 11 Sep 2024 13:01:45 -0700 Subject: [PATCH 3/6] Added character loadouts to store --- src/features/profile/destiny-profile.ts | 13 +++++++++++++ src/types/d2l-types.ts | 5 +++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/features/profile/destiny-profile.ts b/src/features/profile/destiny-profile.ts index 7031e71..5b4bc45 100644 --- a/src/features/profile/destiny-profile.ts +++ b/src/features/profile/destiny-profile.ts @@ -38,6 +38,7 @@ export async function getProfileData(): Promise { const characterEquipment = response.data.Response.characterEquipment.data; const characterData = response.data.Response.characters.data; const profileCollectibles = response.data.Response.profileCollectibles.data.collectibles; + const characterLoadouts = response.data.Response.characterLoadouts.data; for (const key in characterData) { const characterClass = getCharacterClass(characterData[key].classHash); @@ -54,8 +55,20 @@ export async function getProfileData(): Promise { }, subclasses: {}, exoticClassCombos: [], + loadouts: [], }; + // gather character's loadouts + for (const loadout of characterLoadouts[character.id].loadouts) { + character.loadouts.push({ + colorHash: loadout.colorHash, + iconHash: loadout.iconHash, + nameHash: loadout.nameHash, + armor: loadout.items.slice(3, 8), + subclass: loadout.items[8], + }); + } + // iterate character's equipped items for (const item of characterEquipment[key].items) { switch (item.bucketHash) { diff --git a/src/types/d2l-types.ts b/src/types/d2l-types.ts index 9f4e5c7..e5fbdde 100644 --- a/src/types/d2l-types.ts +++ b/src/types/d2l-types.ts @@ -90,8 +90,8 @@ export type DestinyLoadout = { colorHash: number; iconHash: number; nameHash: number; - armor: number[]; - subclass: number; + armor: { itemInstanceId: string; plugItemHashes: number[] }[]; + subclass: { itemInstanceId: string; plugItemHashes: number[] }; }; export type Loadout = { @@ -130,6 +130,7 @@ export type Character = { armor: ArmorBySlot; subclasses: { [key: number]: SubclassConfig | undefined }; exoticClassCombos: ExoticClassCombo[]; + loadouts: DestinyLoadout[]; }; export type Emblem = { From e6d7347d24c7e187a6ce5b6fbfda643243a9803c Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Wed, 11 Sep 2024 13:25:12 -0700 Subject: [PATCH 4/6] Add loadout identifiers to db --- src/lib/bungie_api/manifest.ts | 51 ++++++++++++++++++++++++++++++++++ src/store/db.ts | 9 ++++++ src/types/manifest-types.ts | 15 ++++++++++ 3 files changed, 75 insertions(+) diff --git a/src/lib/bungie_api/manifest.ts b/src/lib/bungie_api/manifest.ts index 25285c7..e5496dd 100644 --- a/src/lib/bungie_api/manifest.ts +++ b/src/lib/bungie_api/manifest.ts @@ -297,6 +297,57 @@ export async function updateManifest() { } } } + + const loadoutColorComponent = + response.data.Response.jsonWorldComponentContentPaths.en['DestinyLoadoutColorDefinition']; + + const loadoutColorResponse = await getManifestComponentRequest(loadoutColorComponent); + + if (loadoutColorResponse) { + for (const hash in loadoutColorResponse.data) { + const current = loadoutColorResponse.data[hash]; + + await db.manifestLoadoutColorDef.add({ + imagePath: urlPrefix + current.colorImagePath, + hash: current.hash, + index: current.index, + }); + } + } + + const loadoutIconComponent = + response.data.Response.jsonWorldComponentContentPaths.en['DestinyLoadoutIconDefinition']; + + const loadoutIconResponse = await getManifestComponentRequest(loadoutIconComponent); + + if (loadoutIconResponse) { + for (const hash in loadoutIconResponse.data) { + const current = loadoutIconResponse.data[hash]; + + await db.manifestLoadoutIconDef.add({ + imagePath: urlPrefix + current.iconImagePath, + hash: current.hash, + index: current.index, + }); + } + } + + const loadoutNameComponent = + response.data.Response.jsonWorldComponentContentPaths.en['DestinyLoadoutNameDefinition']; + + const loadoutNameResponse = await getManifestComponentRequest(loadoutNameComponent); + + if (loadoutNameResponse) { + for (const hash in loadoutNameResponse.data) { + const current = loadoutNameResponse.data[hash]; + + await db.manifestLoadoutNameDef.add({ + name: current.name, + hash: current.hash, + index: current.index, + }); + } + } } } else { throw new Error('Error retrieving manifest'); diff --git a/src/store/db.ts b/src/store/db.ts index 2d9acbb..b988af2 100644 --- a/src/store/db.ts +++ b/src/store/db.ts @@ -11,6 +11,9 @@ import { ManifestAspect, ManifestStatPlug, ManifestEntry, + ManifestLoadoutColor, + ManifestLoadoutIcon, + ManifestLoadoutName, } from '../types/manifest-types'; const db = new Dexie('manifestDb') as Dexie & { @@ -25,6 +28,9 @@ const db = new Dexie('manifestDb') as Dexie & { manifestSubclassAspectsDef: EntityTable; manifestSubclassFragmentsDef: EntityTable; manifestSubclass: EntityTable; + manifestLoadoutColorDef: EntityTable; + manifestLoadoutIconDef: EntityTable; + manifestLoadoutNameDef: EntityTable; }; db.version(1).stores({ @@ -43,6 +49,9 @@ db.version(1).stores({ 'itemHash, name, icon, secondaryIcon, category, perks, isOwned, mobilityMod, resilienceMod, recoveryMod, disciplineMod, intellectMod, strengthMod', manifestSubclass: 'itemHash, name, icon, secondaryIcon, screenshot, flavorText, damageType, class, isOwned', + manifestLoadoutColorDef: 'hash, imagePath, index', + manifestLoadoutIconDef: 'hash, imagePath, index', + manifestLoadoutNameDef: 'hash, name, index', }); export { db }; diff --git a/src/types/manifest-types.ts b/src/types/manifest-types.ts index 2129993..b7a6c7f 100644 --- a/src/types/manifest-types.ts +++ b/src/types/manifest-types.ts @@ -10,6 +10,21 @@ export interface ManifestEntry { perks?: number[]; } +interface ManifestLoadoutIdentifier { + hash: number; + index: number; +} + +export interface ManifestLoadoutName extends ManifestLoadoutIdentifier { + name: string; +} + +export interface ManifestLoadoutColor extends ManifestLoadoutIdentifier { + imagePath: string; +} + +export interface ManifestLoadoutIcon extends ManifestLoadoutColor {} + export interface ManifestArmor extends ManifestEntry { isExotic: boolean; class: CharacterClass; From b90a8b0b6f448826eea740eadb179ec7878a63b8 Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Wed, 11 Sep 2024 17:17:50 -0700 Subject: [PATCH 5/6] Added save loadout menu --- src/components/LoadoutCustomization.tsx | 1 + .../armor-mods/components/RequiredMod.tsx | 1 - .../loadouts/components/EquipLoadout.tsx | 3 +- .../loadouts/components/SaveLoadout.tsx | 179 ++++++++++++++++++ .../loadouts/hooks/use-loadout-identifiers.ts | 24 +++ .../hooks/use-selected-character-loadouts.ts | 13 ++ 6 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 src/features/loadouts/components/SaveLoadout.tsx create mode 100644 src/features/loadouts/hooks/use-loadout-identifiers.ts create mode 100644 src/features/loadouts/hooks/use-selected-character-loadouts.ts diff --git a/src/components/LoadoutCustomization.tsx b/src/components/LoadoutCustomization.tsx index fca0f7d..ade3ae3 100644 --- a/src/components/LoadoutCustomization.tsx +++ b/src/components/LoadoutCustomization.tsx @@ -7,6 +7,7 @@ import EquipLoadout from '../features/loadouts/components/EquipLoadout'; import AbilitiesModification from '../features/subclass/AbilitiesModification'; import ShareLoadout from '../features/loadouts/components/ShareLoadout'; import { SubclassConfig } from '../types/d2l-types'; +import SaveLoadout from '../features/loadouts/components/SaveLoadout'; interface LoadoutCustomizationProps { onBackClick: () => void; diff --git a/src/features/armor-mods/components/RequiredMod.tsx b/src/features/armor-mods/components/RequiredMod.tsx index 13b5605..ce5d753 100644 --- a/src/features/armor-mods/components/RequiredMod.tsx +++ b/src/features/armor-mods/components/RequiredMod.tsx @@ -1,4 +1,3 @@ -import { useState } from 'react'; import { ManifestArmorStatMod } from '../../../types/manifest-types'; import { Tooltip, styled } from '@mui/material'; import { autoEquipStatMod } from '../mod-utils'; diff --git a/src/features/loadouts/components/EquipLoadout.tsx b/src/features/loadouts/components/EquipLoadout.tsx index 300b2e0..e3282f7 100644 --- a/src/features/loadouts/components/EquipLoadout.tsx +++ b/src/features/loadouts/components/EquipLoadout.tsx @@ -22,6 +22,7 @@ import LoadingBorder from './LoadingBorder'; import FadeIn from './FadeIn'; import { equipLoadout } from '../util/loadout-utils'; import { TransitionProps } from '@mui/material/transitions'; +import SaveLoadout from './SaveLoadout'; const StyledTitle = styled(Typography)(({ theme }) => ({ paddingBottom: theme.spacing(1), @@ -268,7 +269,7 @@ const EquipLoadout: React.FC = () => { - + diff --git a/src/features/loadouts/components/SaveLoadout.tsx b/src/features/loadouts/components/SaveLoadout.tsx new file mode 100644 index 0000000..b9a4d2c --- /dev/null +++ b/src/features/loadouts/components/SaveLoadout.tsx @@ -0,0 +1,179 @@ +import { + Button, + Drawer, + Grid, + Box, + Autocomplete, + TextField, + Select, + FormControl, + InputLabel, + ImageList, + styled, +} from '@mui/material'; +import { useState } from 'react'; +import useLoadoutIdentifiers from '../hooks/use-loadout-identifiers'; +import { + ManifestLoadoutColor, + ManifestLoadoutIcon, + ManifestLoadoutName, +} from '../../../types/manifest-types'; +import useSelectedCharacterLoadouts from '../hooks/use-selected-character-loadouts'; +import { snapShotLoadoutRequest } from '../../../lib/bungie_api/requests'; +import { store } from '../../../store'; + +const LoadoutSlot = styled('img')(({ theme }) => ({ + backgroundSize: 'cover', + backgroundPosition: 'center', + width: '51%', + height: 'auto', + border: '2px outset transparent', + '&:hover': { border: '2px solid blue' }, +})); + +export default function SaveLoadout() { + const [loadoutDrawerOpen, setLoadoutDrawerOpen] = useState(false); + const [loadoutName, setLoadoutName] = useState(null); + const [loadoutColor, setLoadoutColor] = useState(null); + const [loadoutIcon, setLoadoutIcon] = useState(null); + const [identifiersSet, setIdentifiersSet] = useState(false); + + const loadoutIdentifiers = useLoadoutIdentifiers(); + const loadouts = useSelectedCharacterLoadouts(); + + function handleBackClick() { + setLoadoutDrawerOpen(false); + setLoadoutName(null); + setLoadoutColor(null); + setLoadoutIcon(null); + } + + const SetIdentifiersDrawer = ( + + + SET IDENTIFIERS + + + setLoadoutName(newValue)} + options={loadoutIdentifiers.loadoutNames} + getOptionLabel={(option) => option.name} + renderInput={(params) => } + /> + + + + COLOR + + + + + + ICON + + + + + + + + + + + ); + + const SelectLoadoutSlotDrawer = ( + + + SELECT SLOT TO OVERWRITE + + {loadouts?.map((loadout, index) => ( + + { + const characterId = store.getState().profile.selectedCharacter?.id; + + if (characterId && loadoutColor && loadoutIcon && loadoutName) + await snapShotLoadoutRequest( + String(characterId), + loadoutColor?.hash, + loadoutIcon?.hash, + index, + loadoutName?.hash + ); + + setIdentifiersSet(false); + setLoadoutDrawerOpen(false); + }} + src={ + loadoutIdentifiers.loadoutIcons.find((icon) => icon.hash === loadout.iconHash) + ?.imagePath + } + style={{ + backgroundImage: `url(${ + loadoutIdentifiers.loadoutColors.find((color) => color.hash === loadout.colorHash) + ?.imagePath + })`, + }} + /> + + ))} + + + + + ); + + return ( + <> + + + + {identifiersSet ? SelectLoadoutSlotDrawer : SetIdentifiersDrawer} + + + + ); +} diff --git a/src/features/loadouts/hooks/use-loadout-identifiers.ts b/src/features/loadouts/hooks/use-loadout-identifiers.ts new file mode 100644 index 0000000..e8d4285 --- /dev/null +++ b/src/features/loadouts/hooks/use-loadout-identifiers.ts @@ -0,0 +1,24 @@ +import { useState, useEffect } from 'react'; +import { db } from '../../../store/db'; +import { + ManifestLoadoutColor, + ManifestLoadoutIcon, + ManifestLoadoutName, +} from '../../../types/manifest-types'; + +export default function useLoadoutIdentifiers() { + const [loadoutColors, setLoadoutColors] = useState([]); + const [loadoutNames, setLoadoutNames] = useState([]); + const [loadoutIcons, setLoadoutIcons] = useState([]); + + useEffect(() => { + const gatherIdentifiers = async () => { + setLoadoutColors(await db.manifestLoadoutColorDef.toArray()); + setLoadoutIcons(await db.manifestLoadoutIconDef.toArray()); + setLoadoutNames(await db.manifestLoadoutNameDef.toArray()); + }; + gatherIdentifiers().catch(console.error); + }, []); + + return { loadoutNames, loadoutColors, loadoutIcons }; +} diff --git a/src/features/loadouts/hooks/use-selected-character-loadouts.ts b/src/features/loadouts/hooks/use-selected-character-loadouts.ts new file mode 100644 index 0000000..e98e102 --- /dev/null +++ b/src/features/loadouts/hooks/use-selected-character-loadouts.ts @@ -0,0 +1,13 @@ +import { useState, useEffect } from 'react'; +import { DestinyLoadout } from '../../../types/d2l-types'; +import { store } from '../../../store'; + +export default function useSelectedCharacterLoadouts() { + const [loadouts, setLoadouts] = useState(undefined); + + useEffect(() => { + setLoadouts(store.getState().profile.selectedCharacter?.loadouts); + }, []); + + return loadouts; +} From 25dd32b34b4de9a1ffb36744471ab309feed1d98 Mon Sep 17 00:00:00 2001 From: dragoni7 Date: Wed, 11 Sep 2024 17:19:57 -0700 Subject: [PATCH 6/6] cleaned up loadout util imports --- src/features/loadouts/util/loadout-utils.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/features/loadouts/util/loadout-utils.ts b/src/features/loadouts/util/loadout-utils.ts index 8b732a4..cfa1fd1 100644 --- a/src/features/loadouts/util/loadout-utils.ts +++ b/src/features/loadouts/util/loadout-utils.ts @@ -1,14 +1,9 @@ import { ARMOR_ARRAY, DAMAGE_TYPE } from '../../../lib/bungie_api/constants'; import { snapShotLoadoutRequest } from '../../../lib/bungie_api/requests'; -import { db } from '../../../store/db'; import { - armor, armorMods, - Character, DestinyArmor, - FilteredPermutation, Loadout, - StatName, Subclass, SubclassConfig, } from '../../../types/d2l-types'; @@ -19,9 +14,6 @@ import { ManifestPlug, ManifestStatPlug, } from '../../../types/manifest-types'; -import { filterPermutations } from '../../armor-optimization/filter-permutations'; -import { generatePermutations } from '../../armor-optimization/generate-permutations'; -import { DecodedLoadoutInfo } from '../components/findMatchingArmorSet'; import { EquipResult, setState } from '../types'; import { ArmorEquipper } from './armor-equipper'; import { SubclassEquipper } from './subclass-equipper';