Skip to content

Commit

Permalink
Loadout subclass search (#10428)
Browse files Browse the repository at this point in the history
  • Loading branch information
ryan-rushton authored May 25, 2024
1 parent ccba146 commit 3749993
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 7 deletions.
3 changes: 2 additions & 1 deletion config/i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,8 @@
"PartialMatch": "Shows loadouts where their name or notes has a partial match to the filter text. Search for entire phrases using quotes.",
"Season": "Shows loadouts by which season of Destiny 2 they were last modified in.",
"FashionOnly": "Shows loadouts that contain only fashion (shaders or ornaments).",
"ModsOnly": "Shows loadouts that only contain armor mods."
"ModsOnly": "Shows loadouts that only contain armor mods.",
"Subclass": "Shows loadouts whose subclass name or damage type partially matches the filter text."
},
"Filter": {
"Adept": "\\(Adept\\)",
Expand Down
7 changes: 2 additions & 5 deletions src/app/inventory/store/d2-item-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
uniqueEquipBuckets,
} from 'app/search/d2-known-values';
import { lightStats } from 'app/search/search-filter-values';
import { getDamageDefsByDamageType } from 'app/utils/definitions';
import { emptyArray, emptyObject } from 'app/utils/empty';
import { errorLog, warnLog } from 'app/utils/log';
import { countEnhancedPerks } from 'app/utils/socket-utils';
Expand Down Expand Up @@ -232,10 +233,6 @@ export interface ItemCreationContext {
itemComponents?: Partial<DestinyItemComponentSetOfint64>;
}

const damageDefsByDamageType = memoizeOne((defs: D2ManifestDefinitions) =>
_.keyBy(Object.values(defs.DamageType.getAll()), (d) => d.enumValue),
);

/**
* Process a single raw item into a DIM item.
*/
Expand Down Expand Up @@ -376,7 +373,7 @@ export function makeItem(
// Subclasses have their elemental damage type in the talent grid
(normalBucket.hash === BucketHashes.Subclass &&
itemDef.talentGrid?.hudDamageType !== undefined &&
damageDefsByDamageType(defs)[itemDef.talentGrid.hudDamageType]) ||
getDamageDefsByDamageType(defs)[itemDef.talentGrid.hudDamageType]) ||
null;

const powerCapHash =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ exports[`buildSearchConfig generates a reasonable filter map: key-value filters
"name",
"notes",
"season",
"subclass",
]
`;
63 changes: 63 additions & 0 deletions src/app/search/loadouts/search-filters/freeform.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
import { D2ManifestDefinitions } from 'app/destiny2/d2-definitions';
import { tl } from 'app/i18next-t';
import { getHashtagsFromNote } from 'app/inventory/note-hashtags';
import { getDamageTypeForSubclassDef } from 'app/inventory/subclass';
import { Loadout } from 'app/loadout-drawer/loadout-types';
import { matchText, plainString } from 'app/search/search-filters/freeform';
import { getDamageDefsByDamageType } from 'app/utils/definitions';
import { DestinyItemType } from 'bungie-api-ts/destiny2';
import _ from 'lodash';
import { FilterDefinition } from '../../filter-types';
import { quoteFilterString } from '../../query-parser';
import { LoadoutFilterContext, LoadoutSuggestionsContext } from '../loadout-filter-types';

function deduplicate<T>(someArray: (T | undefined | null)[]) {
return _.compact(Array.from(new Set(someArray)));
}

function subclassDefFromLoadout(loadout: Loadout, d2Definitions: D2ManifestDefinitions) {
for (const item of loadout.items) {
const itemDef = d2Definitions?.InventoryItem.get(item.hash);
if (itemDef?.itemType === DestinyItemType.Subclass) {
return itemDef;
}
}
}

const freeformFilters: FilterDefinition<
Loadout,
LoadoutFilterContext,
Expand All @@ -22,6 +40,51 @@ const freeformFilters: FilterDefinition<
return (loadout) => test(loadout.name);
},
},
{
keywords: ['subclass'],
description: tl('LoadoutFilter.Subclass'),
format: 'freeform',
suggestionsGenerator: ({ loadouts, d2Definitions }) => {
if (!loadouts || !d2Definitions) {
return [];
}
const damageDefs = getDamageDefsByDamageType(d2Definitions);
// TODO (ryan) filter on currently selected character. This info is currently localized
// to the page, so we need to lift that up before it can be done.
return deduplicate(
loadouts.flatMap((loadout) => {
const subclass = subclassDefFromLoadout(loadout, d2Definitions);
if (!subclass) {
return;
}
const damageType = getDamageTypeForSubclassDef(subclass)!;
// DamageType.None is 0
const damageName = damageDefs[damageType].displayProperties.name;
return [
`subclass:${quoteFilterString(subclass.displayProperties.name.toLowerCase())}`,
`subclass:${quoteFilterString(damageName.toLowerCase())}`,
];
}),
);
},
filter: ({ filterValue, language, d2Definitions }) => {
const test = matchText(filterValue, language, false);
const damageDefs = d2Definitions && getDamageDefsByDamageType(d2Definitions);
return (loadout: Loadout) => {
const subclass = d2Definitions && subclassDefFromLoadout(loadout, d2Definitions);
if (!subclass) {
return false;
}
if (test(subclass.displayProperties.name)) {
return true;
}
// DamageType.None is 0
const damageType = getDamageTypeForSubclassDef(subclass)!;
const damageName = damageDefs?.[damageType]?.displayProperties.name;
return damageName !== undefined && test(damageName);
};
},
},
{
keywords: 'notes',
description: tl('LoadoutFilter.Notes'),
Expand Down
7 changes: 7 additions & 0 deletions src/app/utils/definitions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { D2ManifestDefinitions } from 'app/destiny2/d2-definitions';
import _ from 'lodash';
import memoizeOne from 'memoize-one';

export const getDamageDefsByDamageType = memoizeOne((defs: D2ManifestDefinitions) =>
_.keyBy(Object.values(defs.DamageType.getAll()), (d) => d.enumValue),
);
3 changes: 2 additions & 1 deletion src/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,8 @@
"Name": "Shows loadouts whose name matches (exactname:) or partially matches (name:) the filter text. Search for entire phrases using quotes.",
"Notes": "Search for loadouts by their notes field.",
"PartialMatch": "Shows loadouts where their name or notes has a partial match to the filter text. Search for entire phrases using quotes.",
"Season": "Shows loadouts by which season of Destiny 2 they were last modified in."
"Season": "Shows loadouts by which season of Destiny 2 they were last modified in.",
"Subclass": "Shows loadouts whose subclass name or damage type partially matches the filter text."
},
"Loadouts": {
"Abilities": "Abilities",
Expand Down

0 comments on commit 3749993

Please sign in to comment.