Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add breakermod to match weapons with seasonal artifact breaker mods #8914

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[submodule "destiny-icons"]
path = destiny-icons
url = https://github.com/justrealmilk/destiny-icons
branch = master
2 changes: 1 addition & 1 deletion destiny-icons
2 changes: 2 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Next

* `breaker:` searches now match items that can have that breaker type granted by this season's artifact (whether or not the correct artifact mods are enabled). The effective breaker type from artifact mods also now shows up on item tiles and in the Armory.

## 8.35.1 <span class="changelog-date">(2024-09-01)</span>

* Fix the "track record" button not appearing on hover.
Expand Down
14 changes: 10 additions & 4 deletions src/app/armory/Armory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { Reward } from 'app/progress/Reward';
import { AppIcon, compareIcon } from 'app/shell/icons';
import { useIsPhonePortrait } from 'app/shell/selectors';
import { useThunkDispatch } from 'app/store/thunk-dispatch';
import { getItemYear } from 'app/utils/item-utils';
import { getBreakerTypeHash, getItemYear } from 'app/utils/item-utils';
import clsx from 'clsx';
import { D2EventInfo } from 'data/d2/d2-event-info-v2';
import { ItemCategoryHashes } from 'data/d2/generated-enums';
Expand Down Expand Up @@ -91,6 +91,14 @@ export default function Armory({
const screenshot = ornamentSocket?.plugged?.plugDef.screenshot || itemDef.screenshot;
const flavorText = itemDef.flavorText || itemDef.displaySource;

let breakerType = item.breakerType;
if (!breakerType) {
const breakerTypeHash = getBreakerTypeHash(item);
if (breakerTypeHash) {
breakerType = defs.BreakerType.get(breakerTypeHash);
}
}

// TODO: Show Catalyst benefits for exotics

return (
Expand All @@ -115,9 +123,7 @@ export default function Armory({
<div className={styles.headerContent}>
<div className={styles.subtitle}>
<ElementIcon element={item.element} className={styles.element} />
{item.breakerType && (
<BungieImage src={item.breakerType.displayProperties.icon} height={15} />
)}
{breakerType && <BungieImage src={breakerType.displayProperties.icon} height={15} />}
{item.destinyVersion === 2 && item.ammoType > 0 && <AmmoIcon type={item.ammoType} />}
<ItemTypeName item={item} />
{item.pursuit?.questLine && (
Expand Down
3 changes: 2 additions & 1 deletion src/app/item-popup/ItemPopupHeader.m.scss
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@

.legendary {
background-color: $legendary;
.primary {
.primary,
.breakerIcon {
opacity: 0.8;
}
}
Expand Down
19 changes: 13 additions & 6 deletions src/app/item-popup/ItemPopupHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import ElementIcon from 'app/dim-ui/ElementIcon';
import RichDestinyText from 'app/dim-ui/destiny-symbols/RichDestinyText';
import { useHotkey } from 'app/hotkeys/useHotkey';
import { t } from 'app/i18next-t';
import { useD2Definitions } from 'app/manifest/selectors';
import { D1BucketHashes } from 'app/search/d1-known-values';
import type { ItemTierName } from 'app/search/d2-known-values';
import { getBreakerTypeHash } from 'app/utils/item-utils';
import { LookupTable } from 'app/utils/util-types';
import { DestinyAmmunitionType, DestinyClass } from 'bungie-api-ts/destiny2';
import clsx from 'clsx';
Expand Down Expand Up @@ -33,13 +35,22 @@ export default function ItemPopupHeader({
/** Don't allow opening Armory from the header link */
noLink?: boolean;
}) {
const defs = useD2Definitions()!;
const [showArmory, setShowArmory] = useState(false);
useHotkey('a', t('Hotkey.Armory'), () => setShowArmory(true));

const showElementIcon = Boolean(item.element);

const linkToArmory = !noLink && item.destinyVersion === 2;

let breakerType = item.breakerType;
if (!breakerType) {
const breakerTypeHash = getBreakerTypeHash(item);
if (breakerTypeHash) {
breakerType = defs.BreakerType.get(breakerTypeHash);
}
}

return (
<button
type="button"
Expand All @@ -60,16 +71,12 @@ export default function ItemPopupHeader({
<RichDestinyText text={item.name} ownerId={item.owner} />
</h1>
)}

<div className={styles.subtitle}>
<div className={styles.type}>
<ItemTypeName item={item} className={styles.itemType} />
{item.destinyVersion === 2 && item.ammoType > 0 && <AmmoIcon type={item.ammoType} />}
{item.breakerType && (
<BungieImage
className={styles.breakerIcon}
src={item.breakerType.displayProperties.icon}
/>
{breakerType && (
<BungieImage className={styles.breakerIcon} src={breakerType.displayProperties.icon} />
)}
</div>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
declare module 'data/d2/artifact-breaker-weapon-types.json' {
const x: Record<
import('data/d2/generated-enums').BreakerTypeHashes,
import('data/d2/generated-enums').ItemCategoryHashes[]
>;
export default x;
}
7 changes: 6 additions & 1 deletion src/app/search/items/search-filters/known-values.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
DestinyClass,
DestinyRecordState,
} from 'bungie-api-ts/destiny2';
import artifactBreakerMods from 'data/d2/artifact-breaker-weapon-types.json';
import { D2EventEnum, D2EventInfo } from 'data/d2/d2-event-info-v2';
import focusingOutputs from 'data/d2/focusing-item-outputs.json';
import { BreakerTypeHashes, ItemCategoryHashes } from 'data/d2/generated-enums';
Expand Down Expand Up @@ -225,7 +226,11 @@ const knownValuesFilters: ItemFilterDefinition[] = [
if (!breakerType) {
throw new Error(`Unknown breaker type ${filterValue}`);
}
return (item) => breakerType.includes(item.breakerType?.hash as BreakerTypeHashes);
const breakingIchs = breakerType.flatMap((ty) => artifactBreakerMods[ty] || []);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tricky thing here is that not all of the artifact breaker mods may be active. Do we want to care about that? I assume most folks enable all the breaker mods, at least the basic ones, but then there's always some later column one that I ignore or haven't unlocked...

The tricky part of course being that each character has their own artifact.

Copy link
Contributor

@DHager DHager Aug 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the options have problems, but I find thinking "the principle of least surprise": Which model is easiest for users to recognize and understand?

To me, that's the one where the filter is showing consistent breaker potential via mods that may-or-may-not be unlocked yet, as opposed to "works on this character, but not on that character", or "worked yesterday, but not today", etc. In other words, filter results should only change when Bungie alters the seasonal-artifact.

return (item) =>
item.breakerType
? breakerType.includes(item.breakerType?.hash as BreakerTypeHashes)
: item.itemCategoryHashes.some((ich) => breakingIchs.includes(ich));
},
},
{
Expand Down
37 changes: 36 additions & 1 deletion src/app/utils/item-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,16 @@ import modSocketMetadata, {
} from 'app/search/specialty-modslots';
import { DamageType, DestinyClass, DestinyInventoryItemDefinition } from 'bungie-api-ts/destiny2';
import adeptWeaponHashes from 'data/d2/adept-weapon-hashes.json';
import artifactBreakerMods from 'data/d2/artifact-breaker-weapon-types.json';
import enhancedIntrinsics from 'data/d2/crafting-enhanced-intrinsics';
import { BucketHashes, PlugCategoryHashes, StatHashes, TraitHashes } from 'data/d2/generated-enums';
import {
BreakerTypeHashes,
BucketHashes,
ItemCategoryHashes,
PlugCategoryHashes,
StatHashes,
TraitHashes,
} from 'data/d2/generated-enums';
import masterworksWithCondStats from 'data/d2/masterworks-with-cond-stats.json';
import _ from 'lodash';
import { filterMap, objectifyArray } from './collections';
Expand Down Expand Up @@ -407,3 +415,30 @@ export function isShiny(item: DimItem) {
s.plugOptions.some((s) => s.plugDef.plug.plugCategoryIdentifier === 'holofoil_skins_shared'), //
);
}

const ichToBreakerType = Object.entries(artifactBreakerMods).reduce<
Partial<Record<ItemCategoryHashes, BreakerTypeHashes>>
>((memo, [breakerType, iches]) => {
const breakerTypeNum = parseInt(breakerType, 10);
for (const ich of iches) {
memo[ich] = breakerTypeNum;
}
return memo;
}, {});

/**
* Get the effective breaker type of a weapon, which is either its intrinsic
* breaker type (for some exotics) or one of the breaker types enabled by this
* season's artifact mods.
*/
export function getBreakerTypeHash(item: DimItem): number | undefined {
if (item.breakerType) {
return item.breakerType.hash;
} else if (item.bucket.inWeapons) {
for (const ich of item.itemCategoryHashes) {
if (ichToBreakerType[ich as ItemCategoryHashes]) {
return ichToBreakerType[ich as ItemCategoryHashes];
}
}
}
}
Loading