Skip to content

Commit

Permalink
Merge pull request #10868 from FlaminSarge/enhanceable-perks
Browse files Browse the repository at this point in the history
feat: add is:enhancementready search and allow is:enhancedperk
  • Loading branch information
FlaminSarge authored Dec 23, 2024
2 parents 4b09b44 + 60e9531 commit 3961846
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 46 deletions.
7 changes: 4 additions & 3 deletions config/i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,10 @@
"DupePerks": "Shows items whose perks are either a duplicate of, or a subset of, another item of the same type.",
"Energy": "Shows items that have energy capacity (Armor 2.0).",
"Engrams": "Shows engrams.",
"EnhancedPerk": "Shows weapons that have the specified number of enhanced perks.",
"Enhanced": "Shows weapons based on their enhancement tier.",
"Enhanceable": "Shows weapons that can be enhanced.",
"Enhanced": "Shows weapons based on their enhancement tier.",
"EnhancedPerk": "Shows weapons that have the specified number of enhanced perk columns.",
"EnhancementReady": "Shows weapons that have reached level thresholds for perk enhancement.",
"Equipment": "Items that can be equipped.",
"Equipped": "Items that are currently equipped on a character.",
"Event": "Shows items from which event in Destiny 2 they appeared in.",
Expand Down Expand Up @@ -1523,4 +1524,4 @@
},
"wrong-level": "wrong-level",
"no-space": "no-space"
}
}
5 changes: 5 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
## Next

* Added `is:enhancementready` search that finds weapons which have reached level thresholds to enhance perks
* For crafted weapons, this looks at the level thresholds of the enhanced versions of the weapon's current perks
* For enhanced weapons, this looks at whether the next tier of enhancement is selectable on the weapon
* Updated `enhancedperk` search to allow `is:enhancedperk` to find any weapons with already-enhanced perk columns

## 8.52.0 <span class="changelog-date">(2024-12-22)</span>

## 8.51.0 <span class="changelog-date">(2024-12-15)</span>
Expand Down
44 changes: 22 additions & 22 deletions src/app/inventory/item-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,28 @@ export interface DimPlugSet {
/** A precomputed list of plug hashes that can not roll on current versions of the item. */
readonly plugHashesThatCannotRoll: number[];
readonly plugHashesThatCanRoll: number[];

// "Why not just determine craftingData at the plug level?" you ask.
// Well, we cache/de-dupe plugs on a per-hash basis, so the
// DimPlug for Demolitionist should always be a reference the same object.

// Additional metadata about Demolitionist, that's only applicable when
// it's inside this plugSet, should live with the plugSet.

// yes, the de-dupe thing is not strictly true, due to cannotCurrentlyRoll property...
// but that's its own mess that needs cleanup. cannotCurrentlyRoll could definitely be
// determined at runtime. there are *very* few places it's needed.
// a good TO-DO for later.

/**
* If populated, this plugSet seems to belong to a crafted weapon.
*
* For rendering purposes, the child plugs in the owning socket's plugOptions
* ought to share a little more about their material/level requirements.
*
* This property holds that metadata, keyed by plugHash.
*/
craftingData?: { [plugHash: number]: DestinyPlugItemCraftingRequirements | undefined };
}

export interface DimSocket {
Expand Down Expand Up @@ -498,28 +520,6 @@ export interface DimSocket {
*/
plugSet?: DimPlugSet;

// "Why not just determine craftingData at the plug level?" you ask.
// Well, we cache/de-dupe plugs on a per-hash basis, so the
// DimPlug for Demolitionist should always be a reference the same object.

// Additional metadata about Demolitionist, that's only applicable when
// it's inside this socket, should live with the socket.

// yes, the de-dupe thing is not strictly true, due to cannotCurrentlyRoll property...
// but that's its own mess that needs cleanup. cannotCurrentlyRoll could definitely be
// determined at runtime. there are *very* few places it's needed.
// a good TO-DO for later.

/**
* If populated, this socket seems to belong to a crafted weapon.
*
* For rendering purposes, the child plugs in this socket's plugOptions
* ought to share a little more about their material/level requirements.
*
* This property holds that metadata, keyed by plugHash.
*/
craftingData?: { [plugHash: number]: DestinyPlugItemCraftingRequirements | undefined };

/**
* The plug item hash used to reset this plug to an empty default plug.
* This is a heuristic improvement over singleInitialItemHash, but it's
Expand Down
2 changes: 1 addition & 1 deletion src/app/inventory/store/crafted.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const craftedSocketCategoryHash = 3583996951;
export const mementoSocketCategoryHash = 3201856887;

/** the socket containing the enhancement tier plugs */
const enhancementSocketHash = 4251072212;
export const enhancementSocketHash = 4251072212;
const plugHashToEnhancementTier: HashLookup<number> = {
2728416798: 1,
2728416797: 2,
Expand Down
23 changes: 9 additions & 14 deletions src/app/inventory/store/sockets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,17 +288,6 @@ function buildDefinedSocket(
// The currently equipped plug, if any
const reusablePlugs: DimPlug[] = [];

let craftingData: DimSocket['craftingData'];
function addCraftingReqs(plugEntry: DestinyItemSocketEntryPlugItemRandomizedDefinition) {
if (
plugEntry.craftingRequirements &&
(plugEntry.craftingRequirements.materialRequirementHashes.length ||
plugEntry.craftingRequirements.unlockRequirements.length)
) {
(craftingData ??= {})[plugEntry.plugItemHash] = plugEntry.craftingRequirements;
}
}

// We only build a larger list of plug options if this is a perk socket, since users would
// only want to see (and search) the plug options for perks. For other socket types (mods, shaders, etc.)
// we will only populate plugOptions with the currently inserted plug.
Expand All @@ -315,7 +304,6 @@ function buildDefinedSocket(

if (built && !isUncraftableEnhancedPerk(built, reusablePlug.craftingRequirements)) {
reusablePlugs.push(built);
addCraftingReqs(reusablePlug);
}
}
}
Expand Down Expand Up @@ -357,7 +345,6 @@ function buildDefinedSocket(
!isUncraftableEnhancedPerk(built, randomPlug.craftingRequirements)
) {
reusablePlugs.push(built);
addCraftingReqs(randomPlug);
}
}
}
Expand Down Expand Up @@ -455,7 +442,6 @@ function buildDefinedSocket(
isReusable,
isMod,
socketDefinition: socketDef,
craftingData,
};
}

Expand Down Expand Up @@ -814,6 +800,7 @@ function buildCachedDimPlugSet(defs: D2ManifestDefinitions, plugSetHash: number)

const plugs: DimPlug[] = [];
const defPlugSet = defs.PlugSet.get(plugSetHash);
let craftingData: DimPlugSet['craftingData'];
for (const plugEntry of defPlugSet.reusablePlugItems) {
// Deprecated mods should not actually be in any PlugSets, but here we are
// https://github.com/Bungie-net/api/issues/1801
Expand All @@ -823,6 +810,13 @@ function buildCachedDimPlugSet(defs: D2ManifestDefinitions, plugSetHash: number)
plugs.push(plug);
}
}
if (
plugEntry.craftingRequirements &&
(plugEntry.craftingRequirements.materialRequirementHashes.length ||
plugEntry.craftingRequirements.unlockRequirements.length)
) {
(craftingData ??= {})[plugEntry.plugItemHash] = plugEntry.craftingRequirements;
}
}
const [cant, can] = partition(plugs, (p) => plugCannotCurrentlyRoll(plugs, p.plugDef.hash));
const dimPlugSet: DimPlugSet = {
Expand All @@ -833,6 +827,7 @@ function buildCachedDimPlugSet(defs: D2ManifestDefinitions, plugSetHash: number)
)?.plugItemHash,
plugHashesThatCannotRoll: cant.map((p) => p.plugDef.hash),
plugHashesThatCanRoll: can.map((p) => p.plugDef.hash),
craftingData: craftingData,
};
reusablePlugSetCache[plugSetHash] = dimPlugSet;

Expand Down
2 changes: 1 addition & 1 deletion src/app/item-popup/Plug.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export function PerkCircleWithTooltip({
item={item}
plug={plug}
wishlistRoll={wishlistRoll}
craftingData={socketInfo.craftingData?.[plug.plugDef.hash]}
craftingData={socketInfo.plugSet?.craftingData?.[plug.plugDef.hash]}
/>
);

Expand Down
2 changes: 2 additions & 0 deletions src/app/search/__snapshots__/search-config.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ exports[`buildSearchConfig generates a reasonable filter map: is filters 1`] = `
"engrams",
"enhanceable",
"enhanced",
"enhancedperk",
"enhancementready",
"equipment",
"equippable",
"equipped",
Expand Down
45 changes: 40 additions & 5 deletions src/app/search/items/search-filters/sockets.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { tl } from 'app/i18next-t';
import { enhancementSocketHash } from 'app/inventory/store/crafted';
import {
DEFAULT_GLOW,
DEFAULT_ORNAMENTS,
Expand Down Expand Up @@ -28,6 +29,7 @@ import {
PlugCategoryHashes,
SocketCategoryHashes,
} from 'data/d2/generated-enums';
import perkToEnhanced from 'data/d2/trait-to-enhanced-trait.json';
import { ItemFilterDefinition } from '../item-filter-types';

export const modslotFilter = {
Expand Down Expand Up @@ -299,12 +301,17 @@ const socketFilters: ItemFilterDefinition[] = [
{
keywords: 'enhancedperk',
description: tl('Filter.EnhancedPerk'),
format: 'range',
format: ['simple', 'range'],
destinyVersion: 2,
filter:
({ compare }) =>
(item) =>
item.sockets && compare!(countEnhancedPerks(item.sockets)),
filter: ({ lhs, compare }) => {
if (compare) {
return (item) => item.sockets && compare(countEnhancedPerks(item.sockets));
}
if (lhs === 'is') {
return (item) => item.sockets && countEnhancedPerks(item.sockets) > 0;
}
return (_item) => false;
},
},
{
keywords: 'enhanceable',
Expand Down Expand Up @@ -337,6 +344,34 @@ const socketFilters: ItemFilterDefinition[] = [
return (_item) => false;
},
},
{
keywords: 'enhancementready',
description: tl('Filter.EnhancementReady'),
destinyVersion: 2,
filter: () => (item) => {
if (!item.crafted || !item.craftedInfo) {
return false;
}
if (item.crafted === 'enhanced') {
return item.sockets?.allSockets
.find((s) => s.socketDefinition.socketTypeHash === enhancementSocketHash)
?.reusablePlugItems?.some((p) => p.canInsert);
}
if (item.crafted === 'crafted') {
return item.sockets?.allSockets.some((s) => {
const enhancedPerk = perkToEnhanced[s.plugged?.plugDef.hash || 0] || 0;
return (
enhancedPerk &&
s.plugSet?.plugHashesThatCanRoll.includes(enhancedPerk) &&
s.plugSet?.craftingData &&
(s.plugSet?.craftingData?.[enhancedPerk]?.requiredLevel || 0) <=
(item.craftedInfo?.level || 0)
);
});
}
return false;
},
},
{
keywords: 'retiredperk',
description: tl('Filter.RetiredPerk'),
Expand Down

0 comments on commit 3961846

Please sign in to comment.