Skip to content

Commit

Permalink
Merge pull request #10566 from DestinyItemManager/pathfinder
Browse files Browse the repository at this point in the history
Pathfinder
  • Loading branch information
bhollis authored Jun 28, 2024
2 parents 461dfe4 + 040140e commit 77e8ae5
Show file tree
Hide file tree
Showing 14 changed files with 286 additions and 48 deletions.
2 changes: 2 additions & 0 deletions config/i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,7 @@
"CrucibleRank": "Ranks",
"Items": "Quest Items",
"Milestones": "Milestones & Challenges",
"PaleHeartPathfinder": "Pale Heart Pathfinder",
"PercentPrestige": "{{pct}}% to reset",
"PointsUsed": "1 point used",
"PointsUsed_plural": "{{count}} points used",
Expand All @@ -1108,6 +1109,7 @@
"RecordValue": "{{value}}pts",
"Resets": "1 reset",
"Resets_plural": "{{count}} resets",
"RitualPathfinder": "Ritual Pathfinder",
"SecretTriumph": "Secret Triumph",
"StatTrackers": "Stat Trackers",
"TrackedTriumphs": "Tracked Triumphs",
Expand Down
4 changes: 1 addition & 3 deletions src/app/bungie-api/destiny2-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,12 @@ export function getStores(platform: DestinyAccount): Promise<DestinyProfileRespo
DestinyComponentType.ProfileProgression,
DestinyComponentType.Transitory,
DestinyComponentType.CharacterLoadouts,
DestinyComponentType.PresentationNodes,

// This is a lot of data and currently not used.
// DestinyComponentType.Craftables,
];

if ($featureFlags.solsticePresentationNodes) {
components.push(DestinyComponentType.PresentationNodes);
}
return getProfile(platform, ...components);
}

Expand Down
5 changes: 5 additions & 0 deletions src/app/item-popup/ItemPopupHeader.m.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
text-decoration: none;
margin: 0 !important;
padding: 0;

// Prevent the title running into the sheet close button
@include phone-portrait {
margin: 0 32px 0 0 !important;
}
}

.subtitle {
Expand Down
16 changes: 8 additions & 8 deletions src/app/progress/Milestones.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { getEngramPowerBonus } from './engrams';
import { milestoneToItems } from './milestone-items';
import { getCharacterProgressions } from './selectors';

const sortPowerBonus = compareBy((powerBonus: number | undefined) => -(powerBonus ?? -1));

/**
* The list of Milestones for a character. Milestones are different from pursuits and
* represent challenges, story prompts, and other stuff you can do not represented by Pursuits.
Expand Down Expand Up @@ -59,8 +61,6 @@ export default function Milestones({
}
});

const sortPowerBonus = compareBy((powerBonus: number | undefined) => -(powerBonus ?? -1));

return (
<>
{characterProgressions && (
Expand All @@ -81,7 +81,7 @@ export default function Milestones({
</PursuitGrid>
)}
{[...milestonesByPower.keys()].sort(sortPowerBonus).map((powerBonus) => (
<div key={powerBonus}>
<div key={powerBonus ?? -1}>
<h2 className={styles.header}>
{powerBonus === undefined
? t('Progress.PowerBonusHeaderUndefined')
Expand Down Expand Up @@ -117,9 +117,9 @@ function milestonesForProfile(

const filteredMilestones = allMilestones.filter(
(milestone) =>
!milestone.availableQuests &&
!milestone.activities &&
(!milestone.vendors || milestone.rewards) &&
!milestone.availableQuests?.length &&
!milestone.activities?.length &&
(!milestone.vendors?.length || Boolean(milestone.rewards?.length)) &&
defs.Milestone.get(milestone.milestoneHash),
);

Expand All @@ -144,8 +144,8 @@ function milestonesForCharacter(
return (
def &&
(def.showInExplorer || def.showInMilestones) &&
(milestone.activities ||
!milestone.availableQuests ||
(Boolean(milestone.activities?.length) ||
!milestone.availableQuests?.length ||
milestone.availableQuests.every(
(q) =>
q.status.stepObjectives.length > 0 &&
Expand Down
101 changes: 101 additions & 0 deletions src/app/progress/Pathfinder.m.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
@use '../variables.scss' as *;

.pathfinderTree {
display: flex;
flex-direction: row;
gap: 16px;
margin: 16px 0;

@include phone-portrait {
flex-direction: column;
margin: 16px 10px;
gap: 12px;
}
}

.pathfinderRow {
composes: flexColumn from '../dim-ui/common.m.scss';
box-sizing: border-box;
gap: 8px;
flex: 1;
justify-content: center;
max-width: 250px;
min-width: 120px;
--item-size: 35px;

:global(.milestone-quest) {
align-items: center;
background: rgba(255, 255, 255, 0.05);
padding: 8px;
width: 100%;
}

@media (min-width: 541px) {
&:nth-child(n + 2) button::before {
position: absolute;
display: inline;
content: '';
font-size: 28px;
margin-left: -23px;
}
}

@media (max-width: 1270px) and (min-width: 541px) {
min-width: 60px;

:global(.milestone-quest) {
align-items: flex-start;
flex-direction: column;
}
:global(.milestone-icon) {
flex-direction: row !important;
--item-size: 25px;
max-width: fit-content !important;
margin-top: 6px;

& > div {
margin-right: 6px;
}
}
:global(.milestone-info) {
flex-direction: row !important;
width: auto !important;
}
:global(.milestone-name) {
font-size: 12px !important;
}
:global(.milestone-description) {
display: none;
}
&:not(:first-child) button::before {
transform: translateY(-8%);
}
}

@include phone-portrait {
flex-direction: row;
gap: 14px;
width: auto;
max-width: unset;

> * {
flex: 0;
width: calc(var(--item-size) + 16px) !important;
min-width: calc(var(--item-size) + 16px) !important;
}
:global(.milestone-icon) {
margin: 0;
}
:global(.milestone-info) {
display: none !important;
}
}
}

.completed {
:global(.milestone-icon) img {
border-color: $xp;
border-width: 2px;
padding: 3px;
}
}
9 changes: 9 additions & 0 deletions src/app/progress/Pathfinder.m.scss.d.ts

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

82 changes: 82 additions & 0 deletions src/app/progress/Pathfinder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { trackedTriumphsSelector } from 'app/dim-api/selectors';
import CollapsibleTitle from 'app/dim-ui/CollapsibleTitle';
import { DimItem } from 'app/inventory/item-types';
import { createItemContextSelector } from 'app/inventory/selectors';
import { DimStore } from 'app/inventory/store-types';
import { toPresentationNodeTree } from 'app/records/presentation-nodes';
import { filterMap } from 'app/utils/collections';
import { DestinyPresentationNodeDefinition, DestinyRecordState } from 'bungie-api-ts/destiny2';
import { useSelector } from 'react-redux';
import styles from './Pathfinder.m.scss';
import Pursuit from './Pursuit';
import { recordToPursuitItem } from './milestone-items';

/**
* List out all the seasonal challenges for the character, grouped out in a useful way.
*/
export default function Pathfinder({
id,
name,
presentationNode,
store,
}: {
id: string;
name: string;
presentationNode: DestinyPresentationNodeDefinition;
store: DimStore;
}) {
const itemCreationContext = useSelector(createItemContextSelector);
const nodeTree = toPresentationNodeTree(itemCreationContext, presentationNode.hash);

const allRecords = nodeTree?.childPresentationNodes?.[0]?.records?.toReversed() ?? [];

const trackedRecords = useSelector(trackedTriumphsSelector);

const acquiredRecords = new Set<number>(
filterMap(allRecords, (r) => {
// Don't show records that have been redeemed
const state = r.recordComponent.state;
const acquired = Boolean(state & DestinyRecordState.RecordRedeemed);
return acquired ? r.recordDef.hash : undefined;
}),
);

const pursuits = allRecords.map((r) =>
recordToPursuitItem(
r,
itemCreationContext.buckets,
store,
presentationNode.displayProperties.name,
trackedRecords.includes(r.recordDef.hash),
),
);

const pursuitGroups: DimItem[][] = [];
for (let i = 6; i > 0; i--) {
pursuitGroups.push(pursuits.splice(0, i));
}

return (
<section id={id} className="pathfinder">
<CollapsibleTitle title={name} sectionId={id}>
<div className={styles.pathfinderTree}>
{pursuitGroups.map((pursuits) => (
<div key={pursuits.length} className={styles.pathfinderRow}>
{pursuits.map((item) => (
<Pursuit
item={item}
key={item.index}
className={
acquiredRecords.has(item.pursuit?.recordHash ?? 0)
? styles.completed
: undefined
}
/>
))}
</div>
))}
</div>
</CollapsibleTitle>
</section>
);
}
52 changes: 40 additions & 12 deletions src/app/progress/Progress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ import { RAID_NODE } from 'app/search/d2-known-values';
import { querySelector, useIsPhonePortrait } from 'app/shell/selectors';
import { usePageTitle } from 'app/utils/hooks';
import { PanInfo, motion } from 'framer-motion';
import _ from 'lodash';
import { useState } from 'react';
import { useSelector } from 'react-redux';
import { DestinyAccount } from '../accounts/destiny-account';
import CollapsibleTitle from '../dim-ui/CollapsibleTitle';
import ErrorBoundary from '../dim-ui/ErrorBoundary';
import { Event } from './Event';
import Milestones from './Milestones';
import Pathfinder from './Pathfinder';
import styles from './Progress.m.scss';
import Pursuits from './Pursuits';
import Raids from './Raids';
Expand Down Expand Up @@ -89,24 +91,28 @@ export default function Progress({ account }: { account: DestinyAccount }) {
coreSettings?.seasonalChallengesPresentationNodeHash !== undefined &&
defs.PresentationNode.get(coreSettings.seasonalChallengesPresentationNodeHash);

const menuItems = [
const paleHeartPathfinderNode = defs.PresentationNode.get(1062988660);
const ritualsPathfinderNode = defs.PresentationNode.get(622609416);

const menuItems = _.compact([
{ id: 'ranks', title: t('Progress.CrucibleRank') },
{ id: 'trackedTriumphs', title: t('Progress.TrackedTriumphs') },
...(eventCard ? [{ id: 'event', title: eventCard.displayProperties.name }] : []),
eventCard && { id: 'event', title: eventCard.displayProperties.name },
{ id: 'milestones', title: t('Progress.Milestones') },
...(seasonalChallengesPresentationNode
? [
{
id: 'seasonal-challenges',
title: seasonalChallengesPresentationNode.displayProperties.name,
},
]
: []),
paleHeartPathfinderNode && {
id: 'paleHeartPathfinder',
title: t('Progress.PaleHeartPathfinder'),
},
ritualsPathfinderNode && { id: 'ritualPathfinder', title: t('Progress.RitualPathfinder') },
seasonalChallengesPresentationNode && {
id: 'seasonal-challenges',
title: seasonalChallengesPresentationNode.displayProperties.name,
},
{ id: 'Bounties', title: t('Progress.Bounties') },
{ id: 'Quests', title: t('Progress.Quests') },
{ id: 'Items', title: t('Progress.Items') },
...(raidNode ? [{ id: 'raids', title: raidTitle }] : []),
];
raidNode && { id: 'raids', title: raidTitle },
]);

return (
<ErrorBoundary name="Progress">
Expand Down Expand Up @@ -174,6 +180,28 @@ export default function Progress({ account }: { account: DestinyAccount }) {
</CollapsibleTitle>
</section>

{paleHeartPathfinderNode && (
<ErrorBoundary name={t('Progress.PaleHeartPathfinder')}>
<Pathfinder
id="paleHeartPathfinder"
name={t('Progress.PaleHeartPathfinder')}
presentationNode={paleHeartPathfinderNode}
store={selectedStore}
/>
</ErrorBoundary>
)}

{ritualsPathfinderNode && (
<ErrorBoundary name={t('Progress.RitualPathfinder')}>
<Pathfinder
id="ritualPathfinder"
name={t('Progress.RitualPathfinder')}
presentationNode={ritualsPathfinderNode}
store={selectedStore}
/>
</ErrorBoundary>
)}

{seasonalChallengesPresentationNode && (
<ErrorBoundary name="SeasonalChallenges">
<SeasonalChallenges
Expand Down
Loading

0 comments on commit 77e8ae5

Please sign in to comment.