Skip to content
Draft
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
2 changes: 2 additions & 0 deletions config/feature-flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export function makeFeatureFlags(env: {
customStatWeights: false,
// On the Loadouts page, run Loadout Optimizer to find better tiers for loadouts.
runLoInBackground: true,
// Loadout Optimizer "Armor to Farm" planner (stat-target planner, #11832)
loFarmingPlanner: !env.release,
// Whether to allow setting in-game loadout identifiers on DIM loadouts.
editInGameLoadoutIdentifiers: false,
// Whether to sync DIM API data instead of loading everything
Expand Down
22 changes: 22 additions & 0 deletions config/i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,28 @@
"SetBonusModWarning": "This set contains an item with a configurable set bonus mod. Manually apply the correct mod to activate the desired set bonus.",
"ExoticClassItemPerks": "If you want specific perks, use searches like exactperk:\"spirit of verity\". Click perks in the Optimizer results to add or remove them from the item filter.",
"ExoticSpecialCategory": "Special",
"FarmingPlanner": "Armor to Farm (Prototype)",
"FarmingPlannerAlreadyBuildable": "Your targets are buildable from armor you already have.",
"FarmingPlannerAnyExoticMissing": "You have no available exotic, so one of the planned pieces must be an exotic drop instead.",
"FarmingPlannerEnable": "Suggest armor to farm to meet stat goals",
"FarmingPlannerExoticMissing": "You have no available copy of the chosen exotic, so its slot is planned as an ideal drop.",
"FarmingPlannerFarmExotic": "…with one of these as a new exotic drop",
"FarmingPlannerFinePrint": "Builds around the chosen exotic and keeps your best owned armor. Farmed pieces assume ideal Tier {{tier}} legendary drops.",
"FarmingPlannerFinePrintIdeal": "Plans every unpinned slot as an ideal Tier {{tier}} legendary drop (the chosen exotic included).",
"FarmingPlannerFromSet": "…with at least {{numPieces}} of these from {{set}}",
"FarmingPlannerKeep": "Combined with your:",
"FarmingPlannerKeepOwned": "Keep armor I already own",
"FarmingPlannerMod": "+10 {{stat}} mod",
"FarmingPlannerModMinor": "+5 {{stat}} mod",
"FarmingPlannerNeed_one": "You need {{count}} new piece to complete this:",
"FarmingPlannerNeed_other": "You need {{count}} new pieces to complete this:",
"FarmingPlannerNeedIdeal_one": "You need {{count}} piece to complete this:",
"FarmingPlannerNeedIdeal_other": "You need {{count}} pieces to complete this:",
"FarmingPlannerNoTargets": "Set minimum stat values to see which armor you still need to farm.",
"FarmingPlannerSetImpossible": "The required set bonuses can't fit alongside these constraints.",
"FarmingPlannerTuning": "farmed piece tuned +5 {{stat}}",
"FarmingPlannerTuningUncredited": "Set a stat to Ignore to let farmed pieces spend their tuning slot.",
"FarmingPlannerUnreachable": "Not reachable even with ideal drops ({{points}} stat points short). Closest:",
"Filter": "Settings",
"Legendary": "Legendary",
"LockItem": "Pin item",
Expand Down
17 changes: 17 additions & 0 deletions src/app/loadout-builder/LoadoutBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import CompareLoadoutsDrawer from './generated-sets/CompareLoadoutsDrawer';
import GeneratedSets from './generated-sets/GeneratedSets';
import { ReferenceConstraints } from './generated-sets/SetStats';
import { sortGeneratedSets } from './generated-sets/utils';
import HypotheticalPlanner from './hypothetical/HypotheticalPlanner';
import { filterItems } from './item-filter';
import { LoadoutBuilderAction, useLbState } from './loadout-builder-reducer';
import { useLoVendorItems } from './loadout-builder-vendors';
Expand Down Expand Up @@ -384,6 +385,22 @@ export default memo(function LoadoutBuilder({
lbDispatch={lbDispatch}
className={styles.loadoutEditSection}
/>
{$featureFlags.loFarmingPlanner && (
<HypotheticalPlanner
desiredStatRanges={desiredStatRanges}
filteredItems={filteredItems}
pinnedItems={pinnedItems}
lockedExoticHash={lockedExoticHash}
setBonuses={setBonuses}
modStatChanges={modStatChanges}
armorEnergyRules={armorEnergyRules}
autoStatMods={autoStatMods}
lockedModMap={lockedModMap}
storeId={selectedStore.id}
ownedSets={processing ? 'pending' : (resultSets?.length ?? 0) > 0 ? 'found' : 'none'}
className={styles.loadoutEditSection}
/>
)}
{isPhonePortrait && (
<div className={styles.guide}>
<ol start={2}>
Expand Down
62 changes: 62 additions & 0 deletions src/app/loadout-builder/hypothetical/HypotheticalPlanner.m.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
.verdict {
margin: 4px 0;
font-weight: bold;
}

.recipe {
list-style: none;
margin: 4px 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 4px;

li {
display: flex;
align-items: center;
gap: 6px;
}
}

.count {
font-weight: bold;
min-width: 22px;
text-align: right;
}

.icon {
width: 16px;
height: 16px;
}

.tertiary {
display: flex;
align-items: center;
gap: 4px;
margin-left: auto;
opacity: 0.7;
}

.setNote {
opacity: 0.8;
font-style: italic;
}

.keep {
margin: 4px 0;
opacity: 0.8;
}

.keepItems {
--item-size: 40px;
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-top: 4px;
}

.fineprint {
margin: 4px 0 0;
font-size: 10px;
opacity: 0.6;
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading