From 2bb9e986e70bc34fa5082a454623cef0651cfb96 Mon Sep 17 00:00:00 2001 From: Ben Hollis Date: Sun, 17 Nov 2024 13:33:58 -0800 Subject: [PATCH 1/4] Work on refactoring character stats --- src/app/item-feed/Highlights.tsx | 2 +- .../loadout-edit/LoadoutEditSubclass.tsx | 1 + src/app/store-stats/CharacterStats.m.scss | 137 ++++++++++++++++ .../store-stats/CharacterStats.m.scss.d.ts | 14 ++ src/app/store-stats/CharacterStats.scss | 146 ------------------ src/app/store-stats/CharacterStats.tsx | 41 +++-- src/app/store-stats/D1CharacterStats.m.scss | 16 ++ .../store-stats/D1CharacterStats.m.scss.d.ts | 9 ++ src/app/store-stats/D1CharacterStats.tsx | 9 +- 9 files changed, 210 insertions(+), 165 deletions(-) create mode 100644 src/app/store-stats/CharacterStats.m.scss create mode 100644 src/app/store-stats/CharacterStats.m.scss.d.ts delete mode 100644 src/app/store-stats/CharacterStats.scss create mode 100644 src/app/store-stats/D1CharacterStats.m.scss create mode 100644 src/app/store-stats/D1CharacterStats.m.scss.d.ts diff --git a/src/app/item-feed/Highlights.tsx b/src/app/item-feed/Highlights.tsx index 44c52a9a71..085852306f 100644 --- a/src/app/item-feed/Highlights.tsx +++ b/src/app/item-feed/Highlights.tsx @@ -12,7 +12,7 @@ import { } from 'app/utils/socket-utils'; import clsx from 'clsx'; import { BucketHashes, PlugCategoryHashes } from 'data/d2/generated-enums'; -import '../store-stats/CharacterStats.scss'; +import '../store-stats/CharacterStats.m.scss'; import styles from './Highlights.m.scss'; /** diff --git a/src/app/loadout/loadout-edit/LoadoutEditSubclass.tsx b/src/app/loadout/loadout-edit/LoadoutEditSubclass.tsx index 63eb90bcbe..f286578bdd 100644 --- a/src/app/loadout/loadout-edit/LoadoutEditSubclass.tsx +++ b/src/app/loadout/loadout-edit/LoadoutEditSubclass.tsx @@ -134,6 +134,7 @@ export default function LoadoutEditSubclass({ ) : (
{t('Loadouts.Abilities')}
))} + {subclass &&
Subclass Stats
} ); } diff --git a/src/app/store-stats/CharacterStats.m.scss b/src/app/store-stats/CharacterStats.m.scss new file mode 100644 index 0000000000..b190586787 --- /dev/null +++ b/src/app/store-stats/CharacterStats.m.scss @@ -0,0 +1,137 @@ +@use '../variables' as *; + +:global(.stat) { + flex: 1; + display: flex; + flex-direction: row; + align-items: center; + margin-right: 2px; + line-height: 16px; + white-space: nowrap; + &:last-child { + margin-right: 0; + } + img { + height: 14px; + width: 14px; + margin-right: 2px; + opacity: 1; + } +} + +/* INT/DIS/STR bars */ +:global(.stat-bars) { + width: 100%; + max-width: 230px; + display: flex; + flex-direction: row; + justify-content: space-around; + margin-top: 8px; + opacity: 1; + gap: 4px; + + @include phone-portrait { + margin-left: auto; + margin-right: auto; + align-items: center; + } + + > div { + flex: 1; + display: flex; + flex-direction: row; + } + + &:global(.destiny2) { + flex-direction: column; + gap: 0; + :global(.stat) { + flex: 0; + font-size: 11px; + color: var(--theme-header-characters-txt); + &.boostedValue { + color: $stat-modded; + font-weight: bold; + text-shadow: rgba(0, 0, 0, 0.5) 0 0 2px; + } + :global(.phone-portrait) & { + font-size: 12px; + } + } + } +} + +// D2 stats row. Used here and in the item feed. +:global(.stat-row) { + display: flex; + flex-direction: row; + place-items: center left; + + @include phone-portrait { + width: 100%; + justify-content: center; + } + + &:nth-child(n + 2) { + justify-content: space-between; + } +} + +.powerFormula { + margin-bottom: 3px; + img { + opacity: 0.6; + height: 17px; + width: 17px; + } + img[src^='data'] { + filter: invert(1); + } + > div:nth-child(2) { + display: flex; + flex-direction: row; + &::before, + &::after { + font-size: 13px; + color: var(--theme-header-characters-txt); + margin-left: 4px; + margin-right: 4px; + text-decoration: none !important; + } + &::before { + content: '='; + } + &::after { + content: '+'; + } + } +} + +.powerStat { + font-size: 125%; +} + +.tier { + font-weight: bold; +} + +.tooltipFootnote { + opacity: 0.6; + width: 80%; + margin: 10px 0 0 auto; + text-align: right; +} + +.richTooltipWrapper { + margin: 8px 0 0 0; +} + +.asterisk { + vertical-align: top; + margin-left: 2px; +} + +.dropLevel { + display: flex; + justify-content: space-between; +} diff --git a/src/app/store-stats/CharacterStats.m.scss.d.ts b/src/app/store-stats/CharacterStats.m.scss.d.ts new file mode 100644 index 0000000000..33e20e045a --- /dev/null +++ b/src/app/store-stats/CharacterStats.m.scss.d.ts @@ -0,0 +1,14 @@ +// This file is automatically generated. +// Please do not change this file! +interface CssExports { + 'asterisk': string; + 'boostedValue': string; + 'dropLevel': string; + 'powerFormula': string; + 'powerStat': string; + 'richTooltipWrapper': string; + 'tier': string; + 'tooltipFootnote': string; +} +export const cssExports: CssExports; +export default cssExports; diff --git a/src/app/store-stats/CharacterStats.scss b/src/app/store-stats/CharacterStats.scss deleted file mode 100644 index 806a46e4d0..0000000000 --- a/src/app/store-stats/CharacterStats.scss +++ /dev/null @@ -1,146 +0,0 @@ -@use '../variables' as *; - -@layer base { - /* INT/DIS/STR bars */ - .stat-bars { - width: 100%; - max-width: 230px; - display: flex; - flex-direction: row; - justify-content: space-around; - margin-top: 8px; - opacity: 1; - gap: 4px; - - @include phone-portrait { - margin-left: auto; - margin-right: auto; - align-items: center; - .stat-row { - width: 100%; - justify-content: center; - } - } - - > div { - flex: 1; - display: flex; - flex-direction: row; - } - - .stat { - flex: 1; - display: flex; - flex-direction: row; - align-items: center; - margin-right: 2px; - line-height: 16px; - white-space: nowrap; - &:last-child { - margin-right: 0; - } - .bar { - flex: 1; - height: 7px; - margin-left: 1px; - border-radius: 1px; - background-color: gray; - overflow: hidden; - .progress { - height: 100%; - background-color: white; - &.complete { - background-color: #fb9f28; - } - } - } - img { - height: 14px; - width: 14px; - margin-right: 2px; - opacity: 1; - } - } - - &.destiny2 { - flex-direction: column; - gap: 0; - - .stat-row { - display: flex; - flex-direction: row; - place-items: center left; - &.powerFormula { - margin-bottom: 3px; - img { - opacity: 0.6; - height: 17px; - width: 17px; - } - img[src^='data'] { - filter: invert(1); - } - .powerStat { - font-size: 125%; - } - > div:nth-child(2) { - display: flex; - flex-direction: row; - &::before, - &::after { - font-size: 13px; - color: var(--theme-header-characters-txt); - margin-left: 4px; - margin-right: 4px; - text-decoration: none !important; - } - &::before { - content: '='; - } - &::after { - content: '+'; - } - } - } - &:nth-child(n + 2) { - justify-content: space-between; - } - } - .stat { - flex: 0; - font-size: 11px; - color: var(--theme-header-characters-txt); - - @include phone-portrait { - font-size: 12px; - } - &.boostedValue { - color: $stat-modded; - font-weight: bold; - text-shadow: rgba(0, 0, 0, 0.5) 0 0 2px; - } - } - } - - .tier { - font-weight: bold; - } - } - .tooltipFootnote { - opacity: 0.6; - width: 80%; - margin: 10px 0 0 auto; - text-align: right; - } - .richTooltipWrapper { - margin: 8px 0 0 0; - } - .asterisk { - vertical-align: top; - margin-left: 2px; - } - .dropLevel { - display: flex; - justify-content: space-between; - } -} diff --git a/src/app/store-stats/CharacterStats.tsx b/src/app/store-stats/CharacterStats.tsx index e37109c4d5..b0b274d41b 100644 --- a/src/app/store-stats/CharacterStats.tsx +++ b/src/app/store-stats/CharacterStats.tsx @@ -17,19 +17,19 @@ import { useD2Definitions } from 'app/manifest/selectors'; import { getCharacterProgressions } from 'app/progress/selectors'; import { armorStats } from 'app/search/d2-known-values'; import { RootState } from 'app/store/types'; -import { sumBy } from 'app/utils/collections'; +import { filterMap, sumBy } from 'app/utils/collections'; import clsx from 'clsx'; import { BucketHashes, StatHashes } from 'data/d2/generated-enums'; import React from 'react'; import { useSelector } from 'react-redux'; import helmetIcon from '../../../destiny-icons/armor_types/helmet.svg'; import xpIcon from '../../images/xpIcon.svg'; -import './CharacterStats.scss'; +import styles from './CharacterStats.m.scss'; import StatTooltip from './StatTooltip'; function CharacterPower({ stats }: { stats: PowerStat[] }) { return ( -
+
{stats.map((stat) => (
-
{stat.richTooltipContent()}
+
{stat.richTooltipContent()}
)} @@ -54,10 +54,10 @@ function CharacterPower({ stats }: { stats: PowerStat[] }) { > {stat.name}
- + - {stat.problems?.hasClassified && *} + {stat.problems?.hasClassified && *}
@@ -107,13 +107,13 @@ export function PowerFormula({ storeId }: { storeId: string }) { powerFloor={Math.floor(powerLevel.maxGearPower)} />
-
+
{t('Stats.DropLevel')}*
-
* {t('General.ClickForDetails')}
+
* {t('General.ClickForDetails')}
), }; @@ -140,25 +140,36 @@ export function PowerFormula({ storeId }: { storeId: string }) { } /** - * Display each of the main stats (Resistance, Discipline, etc) for a character. The actual stat info is passed in. - * This shows stats for both loadouts and characters - anything that has a character stats list. + * Display each of the main stats (Resistance, Discipline, etc) for a character. + * This is used for both loadouts and characters - anything that has a character + * stats list. This is only used for D2. */ function CharacterStats({ stats, showTier, equippedHashes, }: { + /** + * A list of stats to display. This should contain an entry for each stat in + * `armorStats`, but if one is missing it won't be shown - you can use this to + * show a subset of stats. + */ stats: DimStore['stats']; + /** Whether to show the total tier of the set. */ showTier?: boolean; + /** + * Item hashes for equipped exotics, used to show more accurate cooldown + * tooltips. + */ equippedHashes: Set; }) { // Select only the armor stats, in the correct order - const statInfos = armorStats.map((h) => stats[h]); + const statInfos = filterMap(armorStats, (h) => stats[h]); return (
{showTier && ( -
+
{t('LoadoutBuilder.TierNumber', { tier: sumBy(statInfos, (s) => statTier(s.value)), })} @@ -185,8 +196,10 @@ function CharacterStats({ ); } +// TODO: just a plain "show stats" component + /** - * Show the stats for a DimStore. + * Show the stats for a DimStore. This is only used for D2 - D1 uses D1CharacterStats. */ export function StoreCharacterStats({ store }: { store: DimStore }) { const equippedItems = store.items.filter((i) => i.equipped); @@ -207,7 +220,7 @@ export function StoreCharacterStats({ store }: { store: DimStore }) { } /** - * Show the stats for a DIM Loadout. + * Show the stats for a DIM Loadout. This is only used for D2. */ // TODO: just take a FullyResolvedLoadout? export function LoadoutCharacterStats({ diff --git a/src/app/store-stats/D1CharacterStats.m.scss b/src/app/store-stats/D1CharacterStats.m.scss new file mode 100644 index 0000000000..2479b2a210 --- /dev/null +++ b/src/app/store-stats/D1CharacterStats.m.scss @@ -0,0 +1,16 @@ +.bar { + flex: 1; + height: 7px; + margin-left: 1px; + border-radius: 1px; + background-color: gray; + overflow: hidden; +} + +.progress { + height: 100%; + background-color: white; + &.complete { + background-color: #fb9f28; + } +} diff --git a/src/app/store-stats/D1CharacterStats.m.scss.d.ts b/src/app/store-stats/D1CharacterStats.m.scss.d.ts new file mode 100644 index 0000000000..20e5e99e86 --- /dev/null +++ b/src/app/store-stats/D1CharacterStats.m.scss.d.ts @@ -0,0 +1,9 @@ +// This file is automatically generated. +// Please do not change this file! +interface CssExports { + 'bar': string; + 'complete': string; + 'progress': string; +} +export const cssExports: CssExports; +export default cssExports; diff --git a/src/app/store-stats/D1CharacterStats.tsx b/src/app/store-stats/D1CharacterStats.tsx index 901c8d56e6..6bbdf30dcf 100644 --- a/src/app/store-stats/D1CharacterStats.tsx +++ b/src/app/store-stats/D1CharacterStats.tsx @@ -5,7 +5,8 @@ import type { DimStore } from 'app/inventory/store-types'; import { getD1CharacterStatTiers, statsWithTiers } from 'app/inventory/store/character-utils'; import { percent } from 'app/shell/formatters'; import clsx from 'clsx'; -import './CharacterStats.scss'; +import './CharacterStats.m.scss'; +import styles from './D1CharacterStats.m.scss'; interface Props { stats: DimStore['stats']; @@ -43,10 +44,10 @@ export default function D1CharacterStats({ stats }: Props) {
{getD1CharacterStatTiers(stat).map((n, index) => ( -
+
From fd8423873bfd98661099ede5347aced124d861ad Mon Sep 17 00:00:00 2001 From: Ben Hollis Date: Sun, 17 Nov 2024 23:07:23 -0800 Subject: [PATCH 2/4] More stuff --- src/app/inventory-page/PhoneStores.tsx | 4 ++- src/app/inventory/store-types.ts | 7 ++-- src/app/item-feed/Highlights.tsx | 7 +++- .../loadout-edit/LoadoutEditBucket.tsx | 2 +- .../loadout-ui/LoadoutItemCategorySection.tsx | 2 +- src/app/store-stats/CharacterStats.m.scss | 29 +++++++-------- src/app/store-stats/CharacterStats.tsx | 10 +++--- src/app/store-stats/D1CharacterStats.m.scss | 31 ++++++++++++++-- .../store-stats/D1CharacterStats.m.scss.d.ts | 2 ++ src/app/store-stats/D1CharacterStats.tsx | 35 ++++++++---------- src/app/store-stats/StatTooltip.tsx | 3 +- src/app/store-stats/StoreStats.tsx | 36 +++++++------------ 12 files changed, 93 insertions(+), 75 deletions(-) diff --git a/src/app/inventory-page/PhoneStores.tsx b/src/app/inventory-page/PhoneStores.tsx index d9d73d64f6..c93643410f 100644 --- a/src/app/inventory-page/PhoneStores.tsx +++ b/src/app/inventory-page/PhoneStores.tsx @@ -178,7 +178,9 @@ function StoresInventory({ <> {((!store.isVault && selectedCategoryId === 'Armor') || (store.isVault && selectedCategoryId === 'Inventory')) && ( - +
+ +
)} {showPostmaster && buckets.byCategory.Postmaster.map(renderBucket)} {buckets.byCategory[selectedCategoryId].map(renderBucket)} diff --git a/src/app/inventory/store-types.ts b/src/app/inventory/store-types.ts index e36bca6583..56bd98b70d 100644 --- a/src/app/inventory/store-types.ts +++ b/src/app/inventory/store-types.ts @@ -127,9 +127,12 @@ export interface DimCharacterStat { /** The localized description of the stat. */ description: string; - /** A localized description of this stat's effect. */ + /** + * A localization key for `Cooldown.${stat.effect}` that gives the description + * of this stat's effect in D1. + */ effect?: 'Grenade' | 'Melee' | 'Super'; - /** Cooldown time for the associated ability. */ + /** Cooldown time for the associated ability in D1. */ cooldown?: string; /** How this stat exactly was calculated. */ diff --git a/src/app/item-feed/Highlights.tsx b/src/app/item-feed/Highlights.tsx index 085852306f..b40112faf1 100644 --- a/src/app/item-feed/Highlights.tsx +++ b/src/app/item-feed/Highlights.tsx @@ -3,6 +3,7 @@ import { PressTip } from 'app/dim-ui/PressTip'; import { DefItemIcon } from 'app/inventory/ItemIcon'; import { DimItem, DimStat } from 'app/inventory/item-types'; import { DimPlugTooltip } from 'app/item-popup/PlugTooltip'; +import { CharacterStats } from 'app/store-stats/CharacterStats'; import { itemTypeName } from 'app/utils/item-utils'; import { getExtraIntrinsicPerkSockets, @@ -12,6 +13,7 @@ import { } from 'app/utils/socket-utils'; import clsx from 'clsx'; import { BucketHashes, PlugCategoryHashes } from 'data/d2/generated-enums'; +import { keyBy } from 'es-toolkit'; import '../store-stats/CharacterStats.m.scss'; import styles from './Highlights.m.scss'; @@ -74,7 +76,10 @@ export default function Highlights({ item }: { item: DimItem }) { return ( <> {item.bucket.hash !== BucketHashes.ClassArmor && ( -
+
+ s.statHash > 0) ?? [], (s) => s.statHash)} + />
{item.stats?.filter((s) => s.statHash > 0).map(renderStat)}
diff --git a/src/app/loadout/loadout-edit/LoadoutEditBucket.tsx b/src/app/loadout/loadout-edit/LoadoutEditBucket.tsx index 6288d4b523..312cde2a70 100644 --- a/src/app/loadout/loadout-edit/LoadoutEditBucket.tsx +++ b/src/app/loadout/loadout-edit/LoadoutEditBucket.tsx @@ -122,7 +122,7 @@ export function ArmorExtras({ return ( <> {equippedItems.length === 5 && ( -
+
{equippedItems.length === 5 && ( -
+
- + {stat.icon && }
{stat.value}
diff --git a/src/app/store-stats/D1CharacterStats.m.scss b/src/app/store-stats/D1CharacterStats.m.scss index 2479b2a210..51bb733ace 100644 --- a/src/app/store-stats/D1CharacterStats.m.scss +++ b/src/app/store-stats/D1CharacterStats.m.scss @@ -1,7 +1,34 @@ +@use '../variables' as *; + +/* INT/DIS/STR bars */ +.statBars { + width: 100%; + max-width: $emblem-width + 16px; + display: grid; + grid-template-columns: repeat(3, 1fr); + margin-top: 8px; + gap: 4px; + + @include phone-portrait { + margin-left: auto; + margin-right: auto; + } +} + +.stat { + display: grid; + grid-template-columns: 16px repeat(5, 1fr); + gap: 1px; + align-items: center; + + > img { + height: 14px; + width: 14px; + } +} + .bar { - flex: 1; height: 7px; - margin-left: 1px; border-radius: 1px; background-color: gray; overflow: hidden; diff --git a/src/app/store-stats/D1CharacterStats.m.scss.d.ts b/src/app/store-stats/D1CharacterStats.m.scss.d.ts index 20e5e99e86..bb4356cf7c 100644 --- a/src/app/store-stats/D1CharacterStats.m.scss.d.ts +++ b/src/app/store-stats/D1CharacterStats.m.scss.d.ts @@ -4,6 +4,8 @@ interface CssExports { 'bar': string; 'complete': string; 'progress': string; + 'stat': string; + 'statBars': string; } export const cssExports: CssExports; export default cssExports; diff --git a/src/app/store-stats/D1CharacterStats.tsx b/src/app/store-stats/D1CharacterStats.tsx index 6bbdf30dcf..df4eca8770 100644 --- a/src/app/store-stats/D1CharacterStats.tsx +++ b/src/app/store-stats/D1CharacterStats.tsx @@ -5,14 +5,9 @@ import type { DimStore } from 'app/inventory/store-types'; import { getD1CharacterStatTiers, statsWithTiers } from 'app/inventory/store/character-utils'; import { percent } from 'app/shell/formatters'; import clsx from 'clsx'; -import './CharacterStats.m.scss'; import styles from './D1CharacterStats.m.scss'; -interface Props { - stats: DimStore['stats']; -} - -export default function D1CharacterStats({ stats }: Props) { +export default function D1CharacterStats({ stats }: { stats: DimStore['stats'] }) { const statList = statsWithTiers.map((h) => stats[h]); const tooltips = statList.map((stat) => { if (stat) { @@ -38,22 +33,20 @@ export default function D1CharacterStats({ stats }: Props) { }); return ( -
+
{statList.map((stat, index) => ( - -
- - {getD1CharacterStatTiers(stat).map((n, index) => ( -
-
-
- ))} -
+ + + {getD1CharacterStatTiers(stat).map((n, index) => ( +
+
+
+ ))} ))}
diff --git a/src/app/store-stats/StatTooltip.tsx b/src/app/store-stats/StatTooltip.tsx index cfed495c5e..838aeb2b4c 100644 --- a/src/app/store-stats/StatTooltip.tsx +++ b/src/app/store-stats/StatTooltip.tsx @@ -8,9 +8,10 @@ import { useSelector } from 'react-redux'; import ClarityCharacterStat from './ClarityCharacterStat'; import styles from './StatTooltip.m.scss'; -interface Stat { +export interface Stat { hash: number; name: string; + icon?: string; value: number; description: string; breakdown?: DimCharacterStatChange[]; diff --git a/src/app/store-stats/StoreStats.tsx b/src/app/store-stats/StoreStats.tsx index 2d9aaa6978..f58e811975 100644 --- a/src/app/store-stats/StoreStats.tsx +++ b/src/app/store-stats/StoreStats.tsx @@ -1,7 +1,5 @@ import type { DimStore } from 'app/inventory/store-types'; import { useIsPhonePortrait } from 'app/shell/selectors'; -import clsx from 'clsx'; -import React from 'react'; import { PowerFormula, StoreCharacterStats } from '../store-stats/CharacterStats'; import AccountCurrencies from './AccountCurrencies'; import D1CharacterStats from './D1CharacterStats'; @@ -9,29 +7,19 @@ import styles from './StoreStats.m.scss'; import VaultCapacity from './VaultCapacity'; /** Render the store stats for any store type (character or vault) */ -export default function StoreStats({ - store, - style, -}: { - store: DimStore; - style?: React.CSSProperties; -}) { +export default function StoreStats({ store }: { store: DimStore }) { const isPhonePortrait = useIsPhonePortrait(); - return ( -
- {store.isVault ? ( -
- - {!isPhonePortrait && } -
- ) : store.destinyVersion === 1 ? ( - - ) : ( -
- - -
- )} + return store.isVault ? ( +
+ + {!isPhonePortrait && } +
+ ) : store.destinyVersion === 1 ? ( + + ) : ( +
+ +
); } From 01e58050e691608f69e98c9dc0b52943590b8715 Mon Sep 17 00:00:00 2001 From: Ben Hollis Date: Sun, 17 Nov 2024 23:38:27 -0800 Subject: [PATCH 3/4] More --- src/app/destiny1/loadout-builder/calculate.ts | 6 +-- src/app/inventory/store-types.ts | 19 ++------ src/app/inventory/store/character-utils.ts | 43 ++++++------------- 3 files changed, 19 insertions(+), 49 deletions(-) diff --git a/src/app/destiny1/loadout-builder/calculate.ts b/src/app/destiny1/loadout-builder/calculate.ts index 51775bf1b6..7833fa4ee8 100644 --- a/src/app/destiny1/loadout-builder/calculate.ts +++ b/src/app/destiny1/loadout-builder/calculate.ts @@ -124,21 +124,21 @@ export async function getSetBucketsStep( }, stats: { [StatHashes.Intellect]: { - hash: StatHashes.Intellect, + statHash: StatHashes.Intellect, value: 0, name: 'Intellect', description: '', icon: intellectIcon, }, [StatHashes.Discipline]: { - hash: StatHashes.Discipline, + statHash: StatHashes.Discipline, value: 0, name: 'Discipline', description: '', icon: disciplineIcon, }, [StatHashes.Strength]: { - hash: StatHashes.Strength, + statHash: StatHashes.Strength, value: 0, name: 'Strength', description: '', diff --git a/src/app/inventory/store-types.ts b/src/app/inventory/store-types.ts index 56bd98b70d..853bbd66a7 100644 --- a/src/app/inventory/store-types.ts +++ b/src/app/inventory/store-types.ts @@ -116,25 +116,12 @@ export interface DimCharacterStatChange { /** A character-level stat. */ export interface DimCharacterStat { /** The DestinyStatDefinition hash for the stat. */ - hash: number; - /** The localized name of the stat. */ - name: string; - /** An icon associated with the stat. */ - icon: string; + statHash: number; + displayProperties: DestinyDisplayPropertiesDefinition; + /** The current value of the stat. */ value: number; - /** The localized description of the stat. */ - description: string; - - /** - * A localization key for `Cooldown.${stat.effect}` that gives the description - * of this stat's effect in D1. - */ - effect?: 'Grenade' | 'Melee' | 'Super'; - /** Cooldown time for the associated ability in D1. */ - cooldown?: string; - /** How this stat exactly was calculated. */ breakdown?: DimCharacterStatChange[]; } diff --git a/src/app/inventory/store/character-utils.ts b/src/app/inventory/store/character-utils.ts index abb044d666..ae283a899d 100644 --- a/src/app/inventory/store/character-utils.ts +++ b/src/app/inventory/store/character-utils.ts @@ -2,6 +2,7 @@ import { D1ManifestDefinitions } from 'app/destiny1/d1-definitions'; import { D1Character, D1StatLabel } from 'app/destiny1/d1-manifest-types'; import { ArmorTypes } from 'app/destiny1/loadout-builder/types'; import { D1BucketHashes } from 'app/search/d1-known-values'; +import { DestinyDisplayPropertiesDefinition } from 'bungie-api-ts/destiny2'; import { BucketHashes, StatHashes } from 'data/d2/generated-enums'; import { DimCharacterStat } from '../store-types'; @@ -72,7 +73,7 @@ export function getBonus(light: number, bucketHash: ArmorTypes): number { export const statsWithTiers = [StatHashes.Discipline, StatHashes.Intellect, StatHashes.Strength]; export function getD1CharacterStatTiers(stat: DimCharacterStat) { - if (!statsWithTiers.includes(stat.hash)) { + if (!statsWithTiers.includes(stat.statHash)) { return []; } const tiers = new Array(5); @@ -106,49 +107,31 @@ export function getCharacterStatsData( continue; } + const statDef = defs.Stat.get(rawStat.statHash); const stat: DimCharacterStat = { - hash: rawStat.statHash, + statHash: rawStat.statHash, value: rawStat.value, - name: '', - description: '', - icon: '', + displayProperties: { + name: statDef.statName, // localized name + description: statDef.statDescription, + icon: statDef.icon, + hasIcon: Boolean(statDef.icon), + } as DestinyDisplayPropertiesDefinition, }; - switch (statId) { - case 'STAT_INTELLECT': - stat.effect = 'Super'; - stat.icon = defs.Stat.get(StatHashes.Intellect).icon; - break; - case 'STAT_DISCIPLINE': - stat.effect = 'Grenade'; - stat.icon = defs.Stat.get(StatHashes.Discipline).icon; - break; - case 'STAT_STRENGTH': - stat.effect = 'Melee'; - stat.icon = defs.Stat.get(StatHashes.Strength).icon; - break; - default: - break; - } - - const statDef = defs.Stat.get(stat.hash); - if (statDef) { - stat.name = statDef.statName; // localized name - stat.description = statDef.statDescription; - } - - if (statsWithTiers.includes(stat.hash)) { + if (statsWithTiers.includes(stat.statHash)) { const tier = Math.floor(Math.min(300, stat.value) / 60); if (data.peerView) { stat.cooldown = getAbilityCooldown(data.peerView.equipment[0].itemHash, statId, tier); } } - ret[stat.hash] = stat; + ret[stat.statHash] = stat; } return ret; } +// TODO: move this into the display // following code is from https://github.com/DestinyTrialsReport function getAbilityCooldown(subclass: number, ability: string, tier: number) { switch (ability) { From 621f4aee12bdea1d4b34cb1e4ab4af1d6f2c63d0 Mon Sep 17 00:00:00 2001 From: Ben Hollis Date: Sat, 25 Jan 2025 18:46:51 -0800 Subject: [PATCH 4/4] Restore layering --- src/app/store-stats/CharacterStats.m.scss | 118 +++++++++++----------- 1 file changed, 60 insertions(+), 58 deletions(-) diff --git a/src/app/store-stats/CharacterStats.m.scss b/src/app/store-stats/CharacterStats.m.scss index 233bc5c2b4..5bfcaacf0d 100644 --- a/src/app/store-stats/CharacterStats.m.scss +++ b/src/app/store-stats/CharacterStats.m.scss @@ -1,74 +1,76 @@ @use '../variables' as *; -:global(.stat) { - flex: 1; - display: flex; - flex-direction: row; - align-items: center; - margin-right: 2px; - line-height: 16px; - white-space: nowrap; - &:last-child { - margin-right: 0; - } - img { - height: 14px; - width: 14px; - margin-right: 2px; - opacity: 1; - } -} - -/* INT/DIS/STR bars */ -:global(.stat-bars) { - width: 100%; - max-width: 230px; - display: flex; - justify-content: space-around; - margin-top: 8px; - opacity: 1; - flex-direction: column; - - @include phone-portrait { - margin-left: auto; - margin-right: auto; - align-items: center; - } - - > div { +@layer base { + :global(.stat) { flex: 1; display: flex; flex-direction: row; + align-items: center; + margin-right: 2px; + line-height: 16px; + white-space: nowrap; + &:last-child { + margin-right: 0; + } + img { + height: 14px; + width: 14px; + margin-right: 2px; + opacity: 1; + } } - :global(.stat) { - flex: 0; - font-size: 11px; - color: var(--theme-header-characters-txt); - &.boostedValue { - color: $stat-modded; - font-weight: bold; - text-shadow: rgba(0, 0, 0, 0.5) 0 0 2px; + /* INT/DIS/STR bars */ + :global(.stat-bars) { + width: 100%; + max-width: 230px; + display: flex; + justify-content: space-around; + margin-top: 8px; + opacity: 1; + flex-direction: column; + + @include phone-portrait { + margin-left: auto; + margin-right: auto; + align-items: center; } - :global(.phone-portrait) & { - font-size: 12px; + + > div { + flex: 1; + display: flex; + flex-direction: row; + } + + :global(.stat) { + flex: 0; + font-size: 11px; + color: var(--theme-header-characters-txt); + &.boostedValue { + color: $stat-modded; + font-weight: bold; + text-shadow: rgba(0, 0, 0, 0.5) 0 0 2px; + } + :global(.phone-portrait) & { + font-size: 12px; + } } } -} -// D2 stats row. Used here and in the item feed. -:global(.stat-row) { - display: flex; - flex-direction: row; - place-items: center left; + // D2 stats row. Used here and in the item feed. + :global(.stat-row) { + display: flex; + flex-direction: row; + place-items: center left; - @include phone-portrait { - width: 100%; - justify-content: center; - } + @include phone-portrait { + width: 100%; + justify-content: center; + } - &:nth-child(n + 2) { - justify-content: space-between; + &:nth-child(n + 2) { + justify-content: space-between; + } } }