diff --git a/.env_example b/.env_example
index f31fad95..ff5509a4 100644
--- a/.env_example
+++ b/.env_example
@@ -3,4 +3,5 @@ D2AP_BUNGIE_CLIENT_ID=
D2AP_BUNGIE_CLIENT_SECRET=
D2AP_FEATURE_ENABLE_MODSLOT_LIMITATION=0
-D2AP_FEATURE_ENABLE_ZERO_WASTE=0
\ No newline at end of file
+D2AP_FEATURE_ENABLE_ZERO_WASTE=0
+D2AP_FEATURE_ENABLE_GUARDIAN_GAMES_FEATURES=1
\ No newline at end of file
diff --git a/.prettierrc.json b/.prettierrc.json
index b16b260b..13d9e051 100644
--- a/.prettierrc.json
+++ b/.prettierrc.json
@@ -1,5 +1,5 @@
{
- "tabWidth": 4,
+ "tabWidth": 2,
"useTabs": false,
"singleQuote": false,
"semi": true,
diff --git a/package.json b/package.json
index d2470f3e..60088cd2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "d2-armor-picker",
- "version": "2.3.1",
+ "version": "2.3.2",
"scripts": {
"ng": "ng",
"config": "ts-node set-env.ts",
diff --git a/set-env.ts b/set-env.ts
index 202669ac..b5a01037 100644
--- a/set-env.ts
+++ b/set-env.ts
@@ -2,18 +2,18 @@ const writeFile = require("fs").writeFile;
const production = process.env.PRODUCTION === "1";
const beta_branch = process.env.BETA === "1";
-const version = "2.3.1";
+const version = "2.3.2";
// Configure Angular `environment.ts` file path
const targetPath = production
- ? "./src/environments/environment.prod.ts"
- : beta_branch
- ? "./src/environments/environment.prod.ts"
- : "./src/environments/environment.ts";
+ ? "./src/environments/environment.prod.ts"
+ : beta_branch
+ ? "./src/environments/environment.prod.ts"
+ : "./src/environments/environment.ts";
// Load node modules
require("dotenv").config({
- path: production ? ".env" : beta_branch ? ".env_beta" : ".env_dev",
+ path: production ? ".env" : beta_branch ? ".env_beta" : ".env_dev",
});
const revision = require("child_process").execSync("git rev-parse --short HEAD").toString().trim();
@@ -21,27 +21,28 @@ const revision = require("child_process").execSync("git rev-parse --short HEAD")
var version_tag = production ? "" : beta_branch ? "-beta-" + revision : "-dev-" + revision;
const data = {
- version: version + version_tag,
- revision: revision,
- production: production,
- beta: beta_branch,
- apiKey: process.env.D2AP_BUNGIE_API_KEY,
- clientId: process.env.D2AP_BUNGIE_CLIENT_ID,
- client_secret: process.env.D2AP_BUNGIE_CLIENT_SECRET,
- nodeEnv: process.env.NODE_ENV,
- offlineMode: false,
- featureFlags: {
- enableModslotLimitation: process.env.D2AP_FEATURE_ENABLE_MODSLOT_LIMITATION == "1",
- enableZeroWaste: process.env.D2AP_FEATURE_ENABLE_ZERO_WASTE == "1",
- },
+ version: version + version_tag,
+ revision: revision,
+ production: production,
+ beta: beta_branch,
+ apiKey: process.env.D2AP_BUNGIE_API_KEY,
+ clientId: process.env.D2AP_BUNGIE_CLIENT_ID,
+ client_secret: process.env.D2AP_BUNGIE_CLIENT_SECRET,
+ nodeEnv: process.env.NODE_ENV,
+ offlineMode: false,
+ featureFlags: {
+ enableModslotLimitation: process.env.D2AP_FEATURE_ENABLE_MODSLOT_LIMITATION == "1",
+ enableZeroWaste: process.env.D2AP_FEATURE_ENABLE_ZERO_WASTE == "1",
+ enableGuardianGamesFeatures: process.env.D2AP_FEATURE_ENABLE_GUARDIAN_GAMES_FEATURES == "1",
+ },
};
// `environment.ts` file structure
const envConfigFile = `export const environment = ${JSON.stringify(data, null, 2)};`;
writeFile(targetPath, envConfigFile, (err: NodeJS.ErrnoException | null) => {
- if (err) {
- throw console.error(err);
- } else {
- console.log(`Angular environment.ts file generated correctly at ${targetPath} \n`);
- }
+ if (err) {
+ throw console.error(err);
+ } else {
+ console.log(`Angular environment.ts file generated correctly at ${targetPath} \n`);
+ }
});
diff --git a/src/app/components/authenticated-v2/settings/desired-mod-limit-selection/slot-limitation-selection/slot-limitation-selection.component.html b/src/app/components/authenticated-v2/settings/desired-mod-limit-selection/slot-limitation-selection/slot-limitation-selection.component.html
index 6204629c..d46e60cd 100644
--- a/src/app/components/authenticated-v2/settings/desired-mod-limit-selection/slot-limitation-selection/slot-limitation-selection.component.html
+++ b/src/app/components/authenticated-v2/settings/desired-mod-limit-selection/slot-limitation-selection/slot-limitation-selection.component.html
@@ -1,268 +1,234 @@
-
-
-
-
-
-
-
-
= 1"
- [class.hovered-higher]="hoveredSlot > -1 && hoveredSlot < 1"
- [class.hovered]="hoveredSlot >= 1"
- [class.blocked]="featureDisabled"
- class="energy-bar">
-
= 2"
- [class.hovered-higher]="hoveredSlot > -1 && hoveredSlot < 2"
- [class.hovered]="hoveredSlot >= 2"
- [class.blocked]="featureDisabled"
- class="energy-bar">
-
= 3"
- [class.hovered-higher]="hoveredSlot > -1 && hoveredSlot < 3"
- [class.hovered]="hoveredSlot >= 3"
- [class.blocked]="featureDisabled"
- class="energy-bar">
-
= 4"
- [class.hovered-higher]="hoveredSlot > -1 && hoveredSlot < 4"
- [class.hovered]="hoveredSlot >= 4"
- [class.blocked]="featureDisabled"
- class="energy-bar">
-
-1 && hoveredSlot < 5"
- [class.hovered]="hoveredSlot === 5"
- [class.blocked]="featureDisabled"
- class="energy-bar">
-
-
-
-1 ? 5 - hoveredSlot : maximumModSlots as slot">
-
-
-
= 3" [class.minor]="slot >= 1 && slot < 3" class="mod-info">
-

-

-
-
-
-
-
-
= 4" [class.minor]="slot >= 2 && slot < 4" class="mod-info">
-

-

-
-
-
-
-
-
= 3" [class.minor]="slot >= 1 && slot < 3" class="mod-info">
-

-

-
-
-
-
-
-
= 3" [class.minor]="slot >= 1 && slot < 3" class="mod-info">
-

-

-
-
-
-
-
-
= 4" [class.minor]="slot >= 2 && slot < 4" class="mod-info">
-

-

-
-
-
-
-
-
= 4" [class.minor]="slot >= 2 && slot < 4" class="mod-info">
-

-

-
-
-
-
-
+
+
+
+
+
+
+
+
= 1"
+ [class.hovered-higher]="hoveredSlot > -1 && hoveredSlot < 1"
+ [class.hovered]="hoveredSlot >= 1"
+ [class.blocked]="featureDisabled"
+ class="energy-bar">
+
= 2"
+ [class.hovered-higher]="hoveredSlot > -1 && hoveredSlot < 2"
+ [class.hovered]="hoveredSlot >= 2"
+ [class.blocked]="featureDisabled"
+ class="energy-bar">
+
= 3"
+ [class.hovered-higher]="hoveredSlot > -1 && hoveredSlot < 3"
+ [class.hovered]="hoveredSlot >= 3"
+ [class.blocked]="featureDisabled"
+ class="energy-bar">
+
= 4"
+ [class.hovered-higher]="hoveredSlot > -1 && hoveredSlot < 4"
+ [class.hovered]="hoveredSlot >= 4"
+ [class.blocked]="featureDisabled"
+ class="energy-bar">
+
-1 && hoveredSlot < 5"
+ [class.hovered]="hoveredSlot === 5"
+ [class.blocked]="featureDisabled"
+ class="energy-bar">
-
-
- {{ i }}
-
-
-
-
-
-
- arrow_drop_down
-
-
-
- lock_open
- lock
-
-
-
-
-
-
-
+
+ -1 ? 5 - hoveredSlot : maximumModSlots as slot">
+
+
+
= 3" [class.minor]="slot >= 1 && slot < 3" class="mod-info">
+

+

+
+
+
+
+
+
= 4" [class.minor]="slot >= 2 && slot < 4" class="mod-info">
+

+

+
+
+
+
+
+
= 3" [class.minor]="slot >= 1 && slot < 3" class="mod-info">

+ class="mod-icon mod-major"
+ src="https://bungie.net/common/destiny2_content/icons/9d54e2149f945b2c298020da443b70fa.png" />

+ class="mod-icon mod-minor"
+ src="https://bungie.net/common/destiny2_content/icons/8fa2d4e4c82586668210e12c5115575a.png" />
+
+
+
+
+
+
= 3" [class.minor]="slot >= 1 && slot < 3" class="mod-info">

+ class="mod-icon mod-major"
+ src="https://bungie.net/common/destiny2_content/icons/07f2361532c79e773909220e5884ab07.png" />

+ class="mod-icon mod-minor"
+ src="https://bungie.net/common/destiny2_content/icons/ec0b298ec4dac0023604e467a58c3868.png" />
+
+
+
+
+
+
= 4" [class.minor]="slot >= 2 && slot < 4" class="mod-info">

-
+ class="mod-icon mod-major"
+ src="https://bungie.net/common/destiny2_content/icons/18054408a5fc068f2384c6c31a183423.png" />
+

+
+
+
+
+
+
= 4" [class.minor]="slot >= 2 && slot < 4" class="mod-info">
+

+

+
+
+
+
+
+
+
+
+ {{ i }}
+
+
+
+
+
+
+ arrow_drop_down
+
+
+
+ lock_open
+ lock
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/components/authenticated-v2/settings/desired-mod-limit-selection/slot-limitation-selection/slot-limitation-selection.component.ts b/src/app/components/authenticated-v2/settings/desired-mod-limit-selection/slot-limitation-selection/slot-limitation-selection.component.ts
index baceca09..4cf6a0e6 100644
--- a/src/app/components/authenticated-v2/settings/desired-mod-limit-selection/slot-limitation-selection/slot-limitation-selection.component.ts
+++ b/src/app/components/authenticated-v2/settings/desired-mod-limit-selection/slot-limitation-selection/slot-limitation-selection.component.ts
@@ -11,122 +11,149 @@ import { takeUntil } from "rxjs/operators";
import { environment } from "../../../../../../environments/environment";
@Component({
- selector: "app-slot-limitation-selection",
- templateUrl: "./slot-limitation-selection.component.html",
- styleUrls: ["./slot-limitation-selection.component.scss"],
+ selector: "app-slot-limitation-selection",
+ templateUrl: "./slot-limitation-selection.component.html",
+ styleUrls: ["./slot-limitation-selection.component.scss"],
})
export class SlotLimitationSelectionComponent implements OnInit, OnDestroy {
- readonly featureDisabled = !environment.featureFlags.enableModslotLimitation;
- readonly ArmorSlot = ArmorSlot;
- readonly ArmorPerkOrSlotNames = ArmorPerkOrSlotNames;
- readonly ArmorPerkOrSlot = ArmorPerkOrSlot;
- readonly ModRange = new Array(MAXIMUM_STAT_MOD_AMOUNT + 1);
- selection: number = MAXIMUM_STAT_MOD_AMOUNT;
-
- @Input()
- slot: ArmorSlot = ArmorSlot.ArmorSlotHelmet;
- @Output()
- possible: EventEmitter
= new EventEmitter();
-
- isPossible: boolean = true;
- configSelectedClass: DestinyClass = DestinyClass.Titan;
- armorPerk: ArmorPerkOrSlot = ArmorPerkOrSlot.None;
- armorPerkLock: boolean = false;
- maximumModSlots: number = 5;
-
- hoveredSlot: number = -1;
-
- disabled: boolean = false;
-
- constructor(
- public config: ConfigurationService,
- public inventory: InventoryService,
- private db: DatabaseService
- ) {}
-
- public async runPossibilityCheck() {
- const mustCheckArmorPerk = this.armorPerkLock && this.armorPerk != ArmorPerkOrSlot.None;
- if (mustCheckArmorPerk) {
- var applicablePerk = await this.db.inventoryArmor
- .where("clazz")
- .equals(this.configSelectedClass)
- .and((f) => f.slot == this.slot)
- .and((f) => f.perk == this.armorPerk)
- .count();
- this.isPossible = applicablePerk > 0;
- } else {
- this.isPossible = true;
- }
- this.possible.next(this.isPossible);
+ readonly featureDisabled = !environment.featureFlags.enableModslotLimitation;
+ readonly ArmorSlot = ArmorSlot;
+ readonly ArmorPerkOrSlotNames = ArmorPerkOrSlotNames;
+ readonly ArmorPerkOrSlot = ArmorPerkOrSlot;
+ readonly ModRange = new Array(MAXIMUM_STAT_MOD_AMOUNT + 1);
+ selection: number = MAXIMUM_STAT_MOD_AMOUNT;
+
+ @Input()
+ slot: ArmorSlot = ArmorSlot.ArmorSlotHelmet;
+ @Output()
+ possible: EventEmitter = new EventEmitter();
+
+ isPossible: boolean = true;
+ configSelectedClass: DestinyClass = DestinyClass.Titan;
+ armorPerk: ArmorPerkOrSlot = ArmorPerkOrSlot.None;
+ armorPerkLock: boolean = false;
+ maximumModSlots: number = 5;
+
+ hoveredSlot: number = -1;
+
+ disabled: boolean = false;
+
+ readonly availableArmorPerks = [
+ ArmorPerkOrSlot.None,
+ ArmorPerkOrSlot.PerkQueensFavor,
+ ArmorPerkOrSlot.SlotRootOfNightmares,
+ ArmorPerkOrSlot.SlotKingsFall,
+ ArmorPerkOrSlot.SlotVowOfTheDisciple,
+ ArmorPerkOrSlot.SlotVaultOfGlass,
+ ArmorPerkOrSlot.SlotDeepStoneCrypt,
+ ArmorPerkOrSlot.SlotGardenOfSalvation,
+ ArmorPerkOrSlot.SlotLastWish,
+ ArmorPerkOrSlot.SlotNightmare,
+ ArmorPerkOrSlot.SlotArtifice,
+ ArmorPerkOrSlot.PerkIronBanner,
+ ArmorPerkOrSlot.PerkUniformedOfficer,
+ ArmorPerkOrSlot.PerkPlunderersTrappings,
+ ArmorPerkOrSlot.SeraphSensorArray,
+ ];
+
+ constructor(
+ public config: ConfigurationService,
+ public inventory: InventoryService,
+ private db: DatabaseService
+ ) {}
+
+ public async runPossibilityCheck() {
+ const mustCheckArmorPerk = this.armorPerkLock && this.armorPerk != ArmorPerkOrSlot.None;
+ if (mustCheckArmorPerk) {
+ var applicablePerk = await this.db.inventoryArmor
+ .where("clazz")
+ .equals(this.configSelectedClass)
+ .and((f) => f.slot == this.slot)
+ .and((f) => f.perk == this.armorPerk)
+ .count();
+ this.isPossible = applicablePerk > 0;
+ } else {
+ this.isPossible = true;
}
-
- get slotName(): string {
- switch (this.slot) {
- case ArmorSlot.ArmorSlotHelmet:
- return "Helmet";
- case ArmorSlot.ArmorSlotGauntlet:
- return "Gauntlet";
- case ArmorSlot.ArmorSlotChest:
- return "Chest";
- case ArmorSlot.ArmorSlotLegs:
- return "Leg";
- case ArmorSlot.ArmorSlotClass:
- return "Class Item";
- default:
- return "";
- }
- }
-
- ngOnInit(): void {
- this.config.configuration.pipe(takeUntil(this.ngUnsubscribe)).subscribe(async (c) => {
- var mustRunPossibilityCheck =
- this.configSelectedClass != (c.characterClass as unknown as DestinyClass) ||
- this.selection != c.maximumModSlots[this.slot].value ||
- this.armorPerk != c.armorPerks[this.slot].value ||
- this.armorPerkLock != c.armorPerks[this.slot].fixed ||
- this.maximumModSlots != c.maximumModSlots[this.slot].value;
-
- this.configSelectedClass = c.characterClass as unknown as DestinyClass;
- this.selection = c.maximumModSlots[this.slot].value;
- this.armorPerk = c.armorPerks[this.slot].value;
- this.armorPerkLock = c.armorPerks[this.slot].fixed;
- this.maximumModSlots = c.maximumModSlots[this.slot].value;
-
- this.disabled =
- (await this.inventory.getExoticsForClass(c.characterClass))
- .filter((x) => c.selectedExotics.indexOf(x.item.hash) > -1)
- .map((e) => e.item.slot)
- .indexOf(this.slot) > -1;
-
- if (mustRunPossibilityCheck) await this.runPossibilityCheck();
- });
- }
-
- toggleArmorPerkLock() {
- this.config.modifyConfiguration((c) => {
- c.armorPerks[this.slot].fixed = !c.armorPerks[this.slot].fixed;
- });
- }
-
- setArmorPerk(perk: ArmorPerkOrSlot) {
- if (this.armorPerk != perk)
- this.config.modifyConfiguration((c) => {
- c.armorPerks[this.slot].value = perk;
- });
- }
-
- setValue(i: number) {
- if (this.featureDisabled) return;
- if (this.maximumModSlots == i) return;
-
- this.maximumModSlots = i;
- this.config.modifyConfiguration((c) => (c.maximumModSlots[this.slot].value = i));
+ this.possible.next(this.isPossible);
+ }
+
+ get slotName(): string {
+ switch (this.slot) {
+ case ArmorSlot.ArmorSlotHelmet:
+ return "Helmet";
+ case ArmorSlot.ArmorSlotGauntlet:
+ return "Gauntlet";
+ case ArmorSlot.ArmorSlotChest:
+ return "Chest";
+ case ArmorSlot.ArmorSlotLegs:
+ return "Leg";
+ case ArmorSlot.ArmorSlotClass:
+ return "Class Item";
+ default:
+ return "";
}
-
- private ngUnsubscribe = new Subject();
-
- ngOnDestroy() {
- this.ngUnsubscribe.next();
- this.ngUnsubscribe.complete();
+ }
+
+ ngOnInit(): void {
+ this.config.configuration.pipe(takeUntil(this.ngUnsubscribe)).subscribe(async (c) => {
+ var mustRunPossibilityCheck =
+ this.configSelectedClass != (c.characterClass as unknown as DestinyClass) ||
+ this.selection != c.maximumModSlots[this.slot].value ||
+ this.armorPerk != c.armorPerks[this.slot].value ||
+ this.armorPerkLock != c.armorPerks[this.slot].fixed ||
+ this.maximumModSlots != c.maximumModSlots[this.slot].value;
+
+ this.configSelectedClass = c.characterClass as unknown as DestinyClass;
+ this.selection = c.maximumModSlots[this.slot].value;
+ this.armorPerk = c.armorPerks[this.slot].value;
+ this.armorPerkLock = c.armorPerks[this.slot].fixed;
+ this.maximumModSlots = c.maximumModSlots[this.slot].value;
+
+ this.disabled =
+ (await this.inventory.getExoticsForClass(c.characterClass))
+ .filter((x) => c.selectedExotics.indexOf(x.item.hash) > -1)
+ .map((e) => e.item.slot)
+ .indexOf(this.slot) > -1;
+
+ if (mustRunPossibilityCheck) await this.runPossibilityCheck();
+ });
+ }
+
+ ngAfterViewInit(): void {
+ if (
+ environment.featureFlags.enableGuardianGamesFeatures &&
+ this.slot === ArmorSlot.ArmorSlotClass
+ ) {
+ this.availableArmorPerks.splice(1, 0, ArmorPerkOrSlot.GuardianGamesClassItem);
}
+ }
+
+ toggleArmorPerkLock() {
+ this.config.modifyConfiguration((c) => {
+ c.armorPerks[this.slot].fixed = !c.armorPerks[this.slot].fixed;
+ });
+ }
+
+ setArmorPerk(perk: ArmorPerkOrSlot) {
+ if (this.armorPerk != perk)
+ this.config.modifyConfiguration((c) => {
+ c.armorPerks[this.slot].value = perk;
+ });
+ }
+
+ setValue(i: number) {
+ if (this.featureDisabled) return;
+ if (this.maximumModSlots == i) return;
+
+ this.maximumModSlots = i;
+ this.config.modifyConfiguration((c) => (c.maximumModSlots[this.slot].value = i));
+ }
+
+ private ngUnsubscribe = new Subject();
+
+ ngOnDestroy() {
+ this.ngUnsubscribe.next();
+ this.ngUnsubscribe.complete();
+ }
}
diff --git a/src/app/data/changelog.ts b/src/app/data/changelog.ts
index efaa2793..bbff9d32 100644
--- a/src/app/data/changelog.ts
+++ b/src/app/data/changelog.ts
@@ -1,863 +1,874 @@
export enum ChangelogEntryType {
- ADD,
- REMOVE,
- MODIFIED,
+ ADD,
+ REMOVE,
+ MODIFIED,
}
export interface ChangelogEntry {
- type: ChangelogEntryType;
- text: string;
- issues: string[] | undefined;
+ type: ChangelogEntryType;
+ text: string;
+ issues: string[] | undefined;
}
export const CHANGELOG_DATA = [
- {
- version: "2.3.1",
- date: "April 2, 2023",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "Feature: Added the possibility to only show builds that contain an exotic.",
- issues: [],
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added 'Root of Nightmares' modslot filter.",
- issues: [],
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added 'Queen's Favor' seasonal perk filter.",
- issues: [],
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added text indicating that only fragments that affect stats are shown.",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed deprecated mods being sent when opening the loadout in DIM. Also adds artifice mods now.",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed an issue where the tool did not put artifice mods on the class item if you forced the class item to be artifice.....",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed an issue where the default selection was the titan class, even if you had no titan characters.",
- issues: [],
- },
- ],
- },
- {
- version: "2.3.0",
- date: "March 14, 2023",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "Automatically adds artifice mods to your armor. This replaces minor and major mods where possible.",
- issues: [],
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added the amount of used artifice mods to the result table overview (next to the mods). They are not calculated into the 'mod cost' column.",
- issues: [],
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added new fragments, including Strand.",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Adapted modslot cost of Resilience and Recovery mods.",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Introduced more modules for asynchronous loading. This is more a speed improvement than a feature.",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Reduced the maximum limit of reported results from 50,000 to 30,000. Note that D2AP still calculates every result, it just does not report them. This is a major speedup, and you usually should not even realize the change.",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Increased the visual contrast of major mods in the results table. This means it is now easier to distinguish major from minor mods.",
- issues: [],
- },
- {
- type: ChangelogEntryType.REMOVE,
- text: "Removed the elemental affinity completely.",
- issues: [],
- },
- {
- type: ChangelogEntryType.REMOVE,
- text: "DISABLED the 'Zero Waste' feature. It will be re-enabled in the future.",
- issues: [],
- },
- {
- type: ChangelogEntryType.REMOVE,
- text: "DISABLED the 'Modslot Limitation' feature. It will be re-enabled in the future.",
- issues: [],
- },
- ],
- },
- {
- version: "2.2.16",
- date: "December 7, 2022",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "Added Ember of Torches (with -10 Discipline).",
- issues: [],
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added Retrofit mods (mobility and resilience).",
- issues: [],
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added armor perk 'Seraph Sensor Array' to the dropdown.",
- issues: [],
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added a Game2Give message. Let's support the little lights together!",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed artifice modslot.",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Membership data is cached longer to make things faster and to ease the Bungie API.",
- issues: [],
- },
- ],
- },
- {
- version: "2.2.15",
- date: "October 18, 2022",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "Added FOTL masks.",
- issues: [],
- },
- ],
- },
- {
- version: "2.2.13",
- date: "September 23, 2022",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "Added an advanced setting to replace the 'Tiers' column with a 'Max Tiers' column. This is adds the amount of open modslots to the column, but ignores mod limitations at the moment. A T32 build without mods will now show T37.",
- issues: [],
- },
- ],
- },
- {
- version: "2.2.12",
- date: "September 11, 2022",
- entries: [
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Changed the layout to force the settings (left) and results (right) to be next to each other. This means that the page is more mobile approachable. This is also the first step to a more flexible layout.",
- issues: [],
- },
- ],
- },
- {
- version: "2.2.11",
- date: "September 10, 2022",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "Added a display for your owned upgrade materials in the character overview.",
- issues: [],
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added a notification to warn you when you create a modslot limitation that yields no results. Note that this will not (yet) show invalid combinations over all armor, just for the given slot you selected it in.",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Reduced size of exotic icons so that the left side will not grow on Titan class.",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "The 'Performance Optimization' settings will now always re-enable after a reload to prevent your browser being stuck in a crash-loop. (This is for you, iOS Safari users)",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Changed wording of the 'Performance Optimization' setting to prevent people from using it incorrectly.",
- issues: [],
- },
- ],
- },
- {
- version: "2.2.10",
- date: "September 2, 2022",
- entries: [
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Updated Artifice Modslot Hash after the most recent hotfix.",
- issues: [],
- },
- ],
- },
- {
- version: "2.2.9",
- date: "September 1, 2022",
- entries: [
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Tooltips do now not obstruct the clicks of stats. This fixes the iOS stat selection issue.",
- issues: [],
- },
- ],
- },
- {
- version: "2.2.8c",
- date: "August 24, 2022",
- entries: [
- {
- type: ChangelogEntryType.MODIFIED,
- text: "The manifest is now (again) automatically updated on version changes. This fixes your artifice problems at the beginning of a new season.",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Added subclass hashes for Arc. This means that it now transfers to DIM.",
- issues: [],
- },
- ],
- },
- {
- version: "2.2.8b",
- date: "August 24, 2022",
- entries: [
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed Artifice modslots. You might need to wait a bit, alternatively delete the database (Account section) or re-log.",
- issues: [],
- },
- ],
- },
- {
- version: "2.2.8a",
- date: "August 23, 2022",
- entries: [
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Updated grenade and melee cooldowns for arc.",
- issues: [],
- },
- ],
- },
- {
- version: "2.2.8",
- date: "August 23, 2022",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "Added Arc 3.0 fragments.",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed tooltip issues on iOS devices.",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed an infinite loading issue.",
- issues: [],
- },
- ],
- },
- {
- version: "2.2.7",
- date: "June 25, 2022",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "Added a link to my Discord bot Crayon.",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed an issue where a large number of stored configurations would drastically slow down the app.",
- issues: [],
- },
- ],
- },
- {
- version: "2.2.6",
- date: "June 8, 2022",
- entries: [
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed an issue where the elemental selection would not give the correct results under very specific conditions.",
- issues: [],
- },
- ],
- },
- {
- version: "2.2.5",
- date: "June 7, 2022",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "Added all of the new ability cooldowns.",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Completely reworked the ability cooldown tooltips. They now show the difference to the currently selected tier.",
- issues: [],
- },
- ],
- },
- {
- version: "2.2.4",
- date: "June 3, 2022",
- entries: [
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed the export to DIM. Thanks to bhollis for the fix!",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "The manifest now automatically updates when Bungie updated it too, except just after a fixed timespan.",
- issues: [],
- },
- ],
- },
- {
- version: "2.2.3",
- date: "May 24, 2022",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "Added Solar 3.0",
- issues: [],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Adapted artifice armor to the new Season.",
- issues: [],
- },
- ],
- },
- {
- version: "2.2.2",
- date: "Mar 9, 2022",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "See what mods do by hovering over their name.",
- issues: ["D2AP-41"],
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added Vow of the Disciple armor.",
- issues: ["D2AP-35"],
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added 'Uniformed Officer' armor.",
- issues: ["D2AP-35"],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed Scatter grenades being Tier 3 (were T4 before).",
- issues: ["D2AP-39"],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Changed the color of the login button.",
- issues: ["D2AP-37"],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "When a selected character class does not exist, the tool will now select the first available class as default.",
- },
- ],
- },
- {
- version: "2.2.1",
- date: "Feb 22, 2022",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "Added a switch to select between Stasis and Void 3.0 fragments. Stasis is enabled per default to ensure backwards compatibility of saved configurations.",
- issues: ["D2AP-10"],
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added a debug section to the (new) account settings page. The account settings page does not do much yet, I just wanted to deploy the debug functions.",
- issues: ["D2AP-23"],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Modified super cooldowns for Witch Queen.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Added a minor text to the exotic overview that explains that exotics not in the inventory are shown in grayscale. You can also no longer select those.",
- issues: ["D2AP-17"],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Minor changes in how the database behaves on logout. It now deletes the inventory when you log out, but still keeps the manifest.",
- issues: ["D2AP-27"],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Users are now logged out when the Bungie.Net API is down.",
- issues: ["D2AP-34"],
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed Charge Harvester and Echo of Persistence incorrectly reducing Discipline instead of recovery when used on a Warlock.",
- },
- ],
- },
- {
- version: "2.2.0",
- date: "Feb 09, 2022",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "Added the ability to select armor perks and 5th slots. Just like the elements you can optionally enforce it to be on a certain slot. Useful if you want to build Iron Banner armor or utilize artifice modslots.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added the ability to limit the available points on each armor item for stat mods. This allows you to limit the kind of stat mods that are usable. You can now say 'do not use major intellect mods'.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added the ability to lock stat tiers. Previously you could only set 'Use Tier 3 or higher', now you can optionally set 'Enforce Tier 3'. This is useful for example if you want to enforce T3 mobility on a Titan.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added a summary of important configuration choices to the result header to improve readability.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added a button to open the DIM Loadout Builder with the current settings.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added an option that forces the correct element on non-masterworked armor pieces. This is per default enabled.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added cooldowns for class abilities (Dodge, Barricade, Rift).",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added visual indicators for minor and major mods in the overview table. The whole visualization is also more compact now.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added visual cursor indicator to every clickable input, e.g. in the exotic, element and perk/mod selection.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added a (very basic) item tooltip.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Show the required material cost for each item. Note that this ignores your class item.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added a stat summary table to the stat detail view. This allows you to easily share a stat distribution with others.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added 'Clear this section' button to each configuration section.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added support for your class items. You won't see a lot of this, except when you use the slot and element limitation in specific ways.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Detailed information is now in expandable containers to improve readability.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Show the seasonal icon for every item too.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added info text to the table headers.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "The character selection now shows the existing characters - and their emblems too!",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added Discord and another Ko-Fi link. You can find them in the character selection.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added a changelog popup right at the start of the page. It only appears when a new update occured. You can always trigger it by clicking the current version number.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Now utilizes three webworkers instead of one. The process is simple, but generally speeds up the results by a huge margin.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added an Armor Investigation tab for data scientists.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added a sidenav for smaller devices and reworked the top menubar.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "The detailed result table now shows the perk of an selected item, if it has one. This also applies to class items, if necessary.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Hovering over exotics in the exotic selection now displays their perk description.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added an advanced setting to disable white, green and blue armor.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added an advanced setting to ignore sunset armor.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Changed the word 'Permutation' to 'Combination' wherever it has been used.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Clicking on a setting that is already set does not re-trigger the calculation now. For example, selecting T3 recovery when it is already at T3 now does nothing.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "The default for all stats is now tier 0 instead of tier 1, to make it consistent with the clear buttons.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "The default setting for 'ignore non masterworked elements' is now 'off'.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "The 'time required' number now measures the time from before the webworkers are spawned until all webworkers are done. Previously it only monitored the time required INSIDE the webworker, so it may show slower times.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed the width of the 'Exotic' header in the results table. This means that it is now farther away from the 'Mobility' column.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Completely reworked the elemental affinity selection. Per default it is now not fixed to a certain armor slot, but using a toggle button you can simply do so again.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "The results header now screams at you in bright red letters when no results are found.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "The alternating rows of the results details table have now a lighter color to make it easier to read.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed ability cooldowns for melee and grenade at tier 7 and tier 8.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Cooldowns now are shown in MM:SS instead of plain seconds.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Completely rewrote the core logic of D2ArmorPicker for the changes mentioned above.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Exotics you do not have in your vault or inventory are now greyed out.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed an issue where exotics were not shown at your first login.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Changed the color of important buttons so that they are easier to read.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "The icons of items in the results are now loaded asynchronous from their hashes. This saves a lot of memory, as I do not have to send two icon URLs for each item - for each result. I will further improve this in a later version.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Maximum table output is now limited to 50 results (instead of 200). It still defaults to 20.}",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Completely reworked the visuals of the changelog. It is now more pleasing to the eye.",
- },
- ] as ChangelogEntry[],
- },
- {
- version: "2.1.4",
- date: "Dec 29, 2021",
- entries: [
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Item stats are now built from their plugs and InvestmentStats (in case of some exotics). This fixes potentially invalid item stats when you use mods like Powerful Friends or Protective Light. A big thanks to u/deangaudet for reminding me that the API also reports the plugs of an item.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Only save necessary item types of the manifest (namely mods and armor). Also, save twhe investmentStats now.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Sped up the item update step.",
- },
- ],
- },
- {
- version: "2.1.3",
- date: "Dec 16, 2021",
- entries: [
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Added new stat cooldowns",
- },
- ],
- },
- {
- version: "2.1.2",
- date: "Dec 1, 2021",
- entries: [
- {
- type: ChangelogEntryType.MODIFIED,
- text: "The changelog is now in a scrollable box.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Changed buymeacoffee to a ko-fi link. This way we can keep the PayPal support!",
- },
- ],
- },
- {
- version: "2.1.1",
- date: "Nov 3, 2021",
- entries: [
- {
- type: ChangelogEntryType.REMOVE,
- text: "Removed the HALLOWEEN SPECIAL feature. Maybe something like this will return some day?",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added tooltip to the item icons in the detailed overview to show the name of the item.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed an issue where the list of exotics did not load at the first login.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Saved configurations now contain the current software version for future reference.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Minor code quality improvements",
- },
- ],
- },
- {
- version: "2.1.0 (major)",
- date: "Oct 29, 2021",
- entries: [
- {
- type: ChangelogEntryType.REMOVE,
- text: "Completely removed V1 of the tool. If you had any problems with V2, you had over three months to report them.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "The stat selection now displays which stat tiers are added by stat mods (PF/RL) or stasis fragments in the configuration.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "You can now export (and import) individual configurations as well as all stored configurations at once.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added a navigation bar to the title bar. On smaller screens it is replaced by two buttons in the character selection.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added more details to the last step of the 'What to do now?' section. It now lists the fragments and mods you selected.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Optimized code and reduced overall page size.",
- },
- ],
- },
- {
- version: "2.0.16",
- date: "Oct 26, 2021",
- entries: [
- {
- type: ChangelogEntryType.MODIFIED,
- text:
- "Completely rewrote the core logic in order to fix the memory issues. " +
- "The tool will now no longer crash when you have many armor items, but it's slightly slower than the previous approach. " +
- "I tested it with 600 items - it works and does not crash, but takes up to a minute. " +
- "Make sure to never get that much armor and to lock an exotic right away.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "The rewrite also fixed the issue where the tool did not work in Safari, or more generally, on Mac and iPhone.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "The rewrite also fixed an issue where items could not be found when an result update was triggered while the inventory was updated.",
- },
- ],
- },
- {
- version: "2.0.15",
- date: "Oct 24, 2021",
- entries: [
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Updated the visual display of the cluster page. It displays the stats in a better way now.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed an issue with login, where you were automatically logged in again and could not switch accounts.",
- },
- ],
- },
- {
- version: "2.0.14",
- date: "Oct 21, 2021",
- entries: [
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Updated URLs to the mobility, resilience and recovery images, as Bungie decided to change their URLs in today's hotfix.",
- },
- ],
- },
- {
- version: "2.0.13",
- date: "Oct 20, 2021",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "Added a chart to the armor clustering page, showing each clusters average stats.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Stats over 100 are now seen as wasted.",
- },
- ],
- },
- {
- version: "2.0.12",
- date: "Oct 18, 2021",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "Added an experimental armor clustering feature.",
- },
- ],
- },
- {
- version: "2.0.11",
- date: "Oct 13, 2021",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "HALLOWEEN SPECIAL! Added (temporary) filter for halloween masks! This will be removed after the event.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fix: Clear the results if you switch character and no possible permutations can be found.",
- },
- ],
- },
- {
- version: "2.0.10",
- date: "Oct 10, 2021",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "Added 'Equip Items' button to the detailed item overview.",
- },
- ],
- },
- {
- version: "2.0.9",
- date: "Oct 6, 2021",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "Added a detailed description of the steps required to build a selected result.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added a button to disable all four armor pieces at once.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Fixed an issue where the permutations were not updated after 'Ignore armor elemental affinities on masterworked armor' was changed.",
- },
- ],
- },
- {
- version: "2.0.8",
- date: "Oct 5, 2021",
- entries: [
- {
- type: ChangelogEntryType.ADD,
- text: "Added this changelog to the help page.",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Added 'Move to Inventory' button (beta).",
- },
- {
- type: ChangelogEntryType.ADD,
- text: "Split up 'Assume items are masterworked' into three settings: Class Items, Legendaries, Exotics",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "'Try to optimize wasted stats' is now active per default.",
- },
- {
- type: ChangelogEntryType.MODIFIED,
- text: "Introduced an item buffer in the results component to further reduce memory usage.",
- },
- ],
- },
+ {
+ version: "2.3.2",
+ date: "May 7, 2023",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Feature: Added 'Guardian Games' class item type to the dropdown. ",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.3.1",
+ date: "April 2, 2023",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Feature: Added the possibility to only show builds that contain an exotic.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added 'Root of Nightmares' modslot filter.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added 'Queen's Favor' seasonal perk filter.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added text indicating that only fragments that affect stats are shown.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed deprecated mods being sent when opening the loadout in DIM. Also adds artifice mods now.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed an issue where the tool did not put artifice mods on the class item if you forced the class item to be artifice.....",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed an issue where the default selection was the titan class, even if you had no titan characters.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.3.0",
+ date: "March 14, 2023",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Automatically adds artifice mods to your armor. This replaces minor and major mods where possible.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added the amount of used artifice mods to the result table overview (next to the mods). They are not calculated into the 'mod cost' column.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added new fragments, including Strand.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Adapted modslot cost of Resilience and Recovery mods.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Introduced more modules for asynchronous loading. This is more a speed improvement than a feature.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Reduced the maximum limit of reported results from 50,000 to 30,000. Note that D2AP still calculates every result, it just does not report them. This is a major speedup, and you usually should not even realize the change.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Increased the visual contrast of major mods in the results table. This means it is now easier to distinguish major from minor mods.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.REMOVE,
+ text: "Removed the elemental affinity completely.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.REMOVE,
+ text: "DISABLED the 'Zero Waste' feature. It will be re-enabled in the future.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.REMOVE,
+ text: "DISABLED the 'Modslot Limitation' feature. It will be re-enabled in the future.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.2.16",
+ date: "December 7, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added Ember of Torches (with -10 Discipline).",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added Retrofit mods (mobility and resilience).",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added armor perk 'Seraph Sensor Array' to the dropdown.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added a Game2Give message. Let's support the little lights together!",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed artifice modslot.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Membership data is cached longer to make things faster and to ease the Bungie API.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.2.15",
+ date: "October 18, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added FOTL masks.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.2.13",
+ date: "September 23, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added an advanced setting to replace the 'Tiers' column with a 'Max Tiers' column. This is adds the amount of open modslots to the column, but ignores mod limitations at the moment. A T32 build without mods will now show T37.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.2.12",
+ date: "September 11, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Changed the layout to force the settings (left) and results (right) to be next to each other. This means that the page is more mobile approachable. This is also the first step to a more flexible layout.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.2.11",
+ date: "September 10, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added a display for your owned upgrade materials in the character overview.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added a notification to warn you when you create a modslot limitation that yields no results. Note that this will not (yet) show invalid combinations over all armor, just for the given slot you selected it in.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Reduced size of exotic icons so that the left side will not grow on Titan class.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "The 'Performance Optimization' settings will now always re-enable after a reload to prevent your browser being stuck in a crash-loop. (This is for you, iOS Safari users)",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Changed wording of the 'Performance Optimization' setting to prevent people from using it incorrectly.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.2.10",
+ date: "September 2, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Updated Artifice Modslot Hash after the most recent hotfix.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.2.9",
+ date: "September 1, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Tooltips do now not obstruct the clicks of stats. This fixes the iOS stat selection issue.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.2.8c",
+ date: "August 24, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "The manifest is now (again) automatically updated on version changes. This fixes your artifice problems at the beginning of a new season.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Added subclass hashes for Arc. This means that it now transfers to DIM.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.2.8b",
+ date: "August 24, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed Artifice modslots. You might need to wait a bit, alternatively delete the database (Account section) or re-log.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.2.8a",
+ date: "August 23, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Updated grenade and melee cooldowns for arc.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.2.8",
+ date: "August 23, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added Arc 3.0 fragments.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed tooltip issues on iOS devices.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed an infinite loading issue.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.2.7",
+ date: "June 25, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added a link to my Discord bot Crayon.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed an issue where a large number of stored configurations would drastically slow down the app.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.2.6",
+ date: "June 8, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed an issue where the elemental selection would not give the correct results under very specific conditions.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.2.5",
+ date: "June 7, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added all of the new ability cooldowns.",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Completely reworked the ability cooldown tooltips. They now show the difference to the currently selected tier.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.2.4",
+ date: "June 3, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed the export to DIM. Thanks to bhollis for the fix!",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "The manifest now automatically updates when Bungie updated it too, except just after a fixed timespan.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.2.3",
+ date: "May 24, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added Solar 3.0",
+ issues: [],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Adapted artifice armor to the new Season.",
+ issues: [],
+ },
+ ],
+ },
+ {
+ version: "2.2.2",
+ date: "Mar 9, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "See what mods do by hovering over their name.",
+ issues: ["D2AP-41"],
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added Vow of the Disciple armor.",
+ issues: ["D2AP-35"],
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added 'Uniformed Officer' armor.",
+ issues: ["D2AP-35"],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed Scatter grenades being Tier 3 (were T4 before).",
+ issues: ["D2AP-39"],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Changed the color of the login button.",
+ issues: ["D2AP-37"],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "When a selected character class does not exist, the tool will now select the first available class as default.",
+ },
+ ],
+ },
+ {
+ version: "2.2.1",
+ date: "Feb 22, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added a switch to select between Stasis and Void 3.0 fragments. Stasis is enabled per default to ensure backwards compatibility of saved configurations.",
+ issues: ["D2AP-10"],
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added a debug section to the (new) account settings page. The account settings page does not do much yet, I just wanted to deploy the debug functions.",
+ issues: ["D2AP-23"],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Modified super cooldowns for Witch Queen.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Added a minor text to the exotic overview that explains that exotics not in the inventory are shown in grayscale. You can also no longer select those.",
+ issues: ["D2AP-17"],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Minor changes in how the database behaves on logout. It now deletes the inventory when you log out, but still keeps the manifest.",
+ issues: ["D2AP-27"],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Users are now logged out when the Bungie.Net API is down.",
+ issues: ["D2AP-34"],
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed Charge Harvester and Echo of Persistence incorrectly reducing Discipline instead of recovery when used on a Warlock.",
+ },
+ ],
+ },
+ {
+ version: "2.2.0",
+ date: "Feb 09, 2022",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added the ability to select armor perks and 5th slots. Just like the elements you can optionally enforce it to be on a certain slot. Useful if you want to build Iron Banner armor or utilize artifice modslots.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added the ability to limit the available points on each armor item for stat mods. This allows you to limit the kind of stat mods that are usable. You can now say 'do not use major intellect mods'.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added the ability to lock stat tiers. Previously you could only set 'Use Tier 3 or higher', now you can optionally set 'Enforce Tier 3'. This is useful for example if you want to enforce T3 mobility on a Titan.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added a summary of important configuration choices to the result header to improve readability.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added a button to open the DIM Loadout Builder with the current settings.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added an option that forces the correct element on non-masterworked armor pieces. This is per default enabled.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added cooldowns for class abilities (Dodge, Barricade, Rift).",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added visual indicators for minor and major mods in the overview table. The whole visualization is also more compact now.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added visual cursor indicator to every clickable input, e.g. in the exotic, element and perk/mod selection.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added a (very basic) item tooltip.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Show the required material cost for each item. Note that this ignores your class item.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added a stat summary table to the stat detail view. This allows you to easily share a stat distribution with others.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added 'Clear this section' button to each configuration section.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added support for your class items. You won't see a lot of this, except when you use the slot and element limitation in specific ways.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Detailed information is now in expandable containers to improve readability.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Show the seasonal icon for every item too.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added info text to the table headers.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "The character selection now shows the existing characters - and their emblems too!",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added Discord and another Ko-Fi link. You can find them in the character selection.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added a changelog popup right at the start of the page. It only appears when a new update occured. You can always trigger it by clicking the current version number.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Now utilizes three webworkers instead of one. The process is simple, but generally speeds up the results by a huge margin.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added an Armor Investigation tab for data scientists.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added a sidenav for smaller devices and reworked the top menubar.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "The detailed result table now shows the perk of an selected item, if it has one. This also applies to class items, if necessary.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Hovering over exotics in the exotic selection now displays their perk description.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added an advanced setting to disable white, green and blue armor.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added an advanced setting to ignore sunset armor.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Changed the word 'Permutation' to 'Combination' wherever it has been used.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Clicking on a setting that is already set does not re-trigger the calculation now. For example, selecting T3 recovery when it is already at T3 now does nothing.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "The default for all stats is now tier 0 instead of tier 1, to make it consistent with the clear buttons.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "The default setting for 'ignore non masterworked elements' is now 'off'.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "The 'time required' number now measures the time from before the webworkers are spawned until all webworkers are done. Previously it only monitored the time required INSIDE the webworker, so it may show slower times.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed the width of the 'Exotic' header in the results table. This means that it is now farther away from the 'Mobility' column.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Completely reworked the elemental affinity selection. Per default it is now not fixed to a certain armor slot, but using a toggle button you can simply do so again.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "The results header now screams at you in bright red letters when no results are found.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "The alternating rows of the results details table have now a lighter color to make it easier to read.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed ability cooldowns for melee and grenade at tier 7 and tier 8.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Cooldowns now are shown in MM:SS instead of plain seconds.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Completely rewrote the core logic of D2ArmorPicker for the changes mentioned above.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Exotics you do not have in your vault or inventory are now greyed out.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed an issue where exotics were not shown at your first login.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Changed the color of important buttons so that they are easier to read.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "The icons of items in the results are now loaded asynchronous from their hashes. This saves a lot of memory, as I do not have to send two icon URLs for each item - for each result. I will further improve this in a later version.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Maximum table output is now limited to 50 results (instead of 200). It still defaults to 20.}",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Completely reworked the visuals of the changelog. It is now more pleasing to the eye.",
+ },
+ ] as ChangelogEntry[],
+ },
+ {
+ version: "2.1.4",
+ date: "Dec 29, 2021",
+ entries: [
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Item stats are now built from their plugs and InvestmentStats (in case of some exotics). This fixes potentially invalid item stats when you use mods like Powerful Friends or Protective Light. A big thanks to u/deangaudet for reminding me that the API also reports the plugs of an item.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Only save necessary item types of the manifest (namely mods and armor). Also, save twhe investmentStats now.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Sped up the item update step.",
+ },
+ ],
+ },
+ {
+ version: "2.1.3",
+ date: "Dec 16, 2021",
+ entries: [
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Added new stat cooldowns",
+ },
+ ],
+ },
+ {
+ version: "2.1.2",
+ date: "Dec 1, 2021",
+ entries: [
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "The changelog is now in a scrollable box.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Changed buymeacoffee to a ko-fi link. This way we can keep the PayPal support!",
+ },
+ ],
+ },
+ {
+ version: "2.1.1",
+ date: "Nov 3, 2021",
+ entries: [
+ {
+ type: ChangelogEntryType.REMOVE,
+ text: "Removed the HALLOWEEN SPECIAL feature. Maybe something like this will return some day?",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added tooltip to the item icons in the detailed overview to show the name of the item.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed an issue where the list of exotics did not load at the first login.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Saved configurations now contain the current software version for future reference.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Minor code quality improvements",
+ },
+ ],
+ },
+ {
+ version: "2.1.0 (major)",
+ date: "Oct 29, 2021",
+ entries: [
+ {
+ type: ChangelogEntryType.REMOVE,
+ text: "Completely removed V1 of the tool. If you had any problems with V2, you had over three months to report them.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "The stat selection now displays which stat tiers are added by stat mods (PF/RL) or stasis fragments in the configuration.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "You can now export (and import) individual configurations as well as all stored configurations at once.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added a navigation bar to the title bar. On smaller screens it is replaced by two buttons in the character selection.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added more details to the last step of the 'What to do now?' section. It now lists the fragments and mods you selected.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Optimized code and reduced overall page size.",
+ },
+ ],
+ },
+ {
+ version: "2.0.16",
+ date: "Oct 26, 2021",
+ entries: [
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text:
+ "Completely rewrote the core logic in order to fix the memory issues. " +
+ "The tool will now no longer crash when you have many armor items, but it's slightly slower than the previous approach. " +
+ "I tested it with 600 items - it works and does not crash, but takes up to a minute. " +
+ "Make sure to never get that much armor and to lock an exotic right away.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "The rewrite also fixed the issue where the tool did not work in Safari, or more generally, on Mac and iPhone.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "The rewrite also fixed an issue where items could not be found when an result update was triggered while the inventory was updated.",
+ },
+ ],
+ },
+ {
+ version: "2.0.15",
+ date: "Oct 24, 2021",
+ entries: [
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Updated the visual display of the cluster page. It displays the stats in a better way now.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed an issue with login, where you were automatically logged in again and could not switch accounts.",
+ },
+ ],
+ },
+ {
+ version: "2.0.14",
+ date: "Oct 21, 2021",
+ entries: [
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Updated URLs to the mobility, resilience and recovery images, as Bungie decided to change their URLs in today's hotfix.",
+ },
+ ],
+ },
+ {
+ version: "2.0.13",
+ date: "Oct 20, 2021",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added a chart to the armor clustering page, showing each clusters average stats.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Stats over 100 are now seen as wasted.",
+ },
+ ],
+ },
+ {
+ version: "2.0.12",
+ date: "Oct 18, 2021",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added an experimental armor clustering feature.",
+ },
+ ],
+ },
+ {
+ version: "2.0.11",
+ date: "Oct 13, 2021",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "HALLOWEEN SPECIAL! Added (temporary) filter for halloween masks! This will be removed after the event.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fix: Clear the results if you switch character and no possible permutations can be found.",
+ },
+ ],
+ },
+ {
+ version: "2.0.10",
+ date: "Oct 10, 2021",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added 'Equip Items' button to the detailed item overview.",
+ },
+ ],
+ },
+ {
+ version: "2.0.9",
+ date: "Oct 6, 2021",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added a detailed description of the steps required to build a selected result.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added a button to disable all four armor pieces at once.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Fixed an issue where the permutations were not updated after 'Ignore armor elemental affinities on masterworked armor' was changed.",
+ },
+ ],
+ },
+ {
+ version: "2.0.8",
+ date: "Oct 5, 2021",
+ entries: [
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added this changelog to the help page.",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Added 'Move to Inventory' button (beta).",
+ },
+ {
+ type: ChangelogEntryType.ADD,
+ text: "Split up 'Assume items are masterworked' into three settings: Class Items, Legendaries, Exotics",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "'Try to optimize wasted stats' is now active per default.",
+ },
+ {
+ type: ChangelogEntryType.MODIFIED,
+ text: "Introduced an item buffer in the results component to further reduce memory usage.",
+ },
+ ],
+ },
];
diff --git a/src/app/data/enum/armor-stat.ts b/src/app/data/enum/armor-stat.ts
index e864eef0..c5284d23 100644
--- a/src/app/data/enum/armor-stat.ts
+++ b/src/app/data/enum/armor-stat.ts
@@ -2,198 +2,204 @@ import { EnumDictionary } from "../types/EnumDictionary";
import { DestinyEnergyType } from "bungie-api-ts/destiny2";
export enum StatModifier {
- NONE,
- MINOR_MOBILITY,
- MAJOR_MOBILITY,
- ARTIFICE_MOBILITY,
- MINOR_RESILIENCE,
- MAJOR_RESILIENCE,
- ARTIFICE_RESILIENCE,
- MINOR_RECOVERY,
- MAJOR_RECOVERY,
- ARTIFICE_RECOVERY,
- MINOR_DISCIPLINE,
- MAJOR_DISCIPLINE,
- ARTIFICE_DISCIPLINE,
- MINOR_INTELLECT,
- MAJOR_INTELLECT,
- ARTIFICE_INTELLECT,
- MINOR_STRENGTH,
- MAJOR_STRENGTH,
- ARTIFICE_STRENGTH,
+ NONE,
+ MINOR_MOBILITY,
+ MAJOR_MOBILITY,
+ ARTIFICE_MOBILITY,
+ MINOR_RESILIENCE,
+ MAJOR_RESILIENCE,
+ ARTIFICE_RESILIENCE,
+ MINOR_RECOVERY,
+ MAJOR_RECOVERY,
+ ARTIFICE_RECOVERY,
+ MINOR_DISCIPLINE,
+ MAJOR_DISCIPLINE,
+ ARTIFICE_DISCIPLINE,
+ MINOR_INTELLECT,
+ MAJOR_INTELLECT,
+ ARTIFICE_INTELLECT,
+ MINOR_STRENGTH,
+ MAJOR_STRENGTH,
+ ARTIFICE_STRENGTH,
}
export enum ArmorStat {
- Mobility,
- Resilience,
- Recovery,
- Discipline,
- Intellect,
- Strength,
+ Mobility,
+ Resilience,
+ Recovery,
+ Discipline,
+ Intellect,
+ Strength,
}
// Stat Enum, bonus, cost, mod hash
export const STAT_MOD_VALUES: EnumDictionary = {
- [StatModifier.NONE]: [ArmorStat.Strength, 0, 0, 0],
- [StatModifier.MINOR_MOBILITY]: [ArmorStat.Mobility, 5, 1, 1703647492],
- [StatModifier.MAJOR_MOBILITY]: [ArmorStat.Mobility, 10, 3, 4183296050],
- [StatModifier.ARTIFICE_MOBILITY]: [ArmorStat.Mobility, 3, 0, 2322202118],
- [StatModifier.MINOR_RESILIENCE]: [ArmorStat.Resilience, 5, 2, 2532323436],
- [StatModifier.MAJOR_RESILIENCE]: [ArmorStat.Resilience, 10, 4, 1180408010],
- [StatModifier.ARTIFICE_RESILIENCE]: [ArmorStat.Resilience, 3, 0, 199176566],
- [StatModifier.MINOR_RECOVERY]: [ArmorStat.Recovery, 5, 2, 1237786518],
- [StatModifier.MAJOR_RECOVERY]: [ArmorStat.Recovery, 10, 4, 4204488676],
- [StatModifier.ARTIFICE_RECOVERY]: [ArmorStat.Recovery, 3, 0, 539459624],
- [StatModifier.MINOR_DISCIPLINE]: [ArmorStat.Discipline, 5, 1, 4021790309],
- [StatModifier.MAJOR_DISCIPLINE]: [ArmorStat.Discipline, 10, 3, 1435557120],
- [StatModifier.ARTIFICE_DISCIPLINE]: [ArmorStat.Discipline, 3, 0, 617569843],
- [StatModifier.MINOR_INTELLECT]: [ArmorStat.Intellect, 5, 2, 350061697],
- [StatModifier.MAJOR_INTELLECT]: [ArmorStat.Intellect, 10, 4, 2724608735],
- [StatModifier.ARTIFICE_INTELLECT]: [ArmorStat.Intellect, 3, 0, 3160845295],
- [StatModifier.MINOR_STRENGTH]: [ArmorStat.Strength, 5, 1, 2639422088],
- [StatModifier.MAJOR_STRENGTH]: [ArmorStat.Strength, 10, 3, 4287799666],
- [StatModifier.ARTIFICE_STRENGTH]: [ArmorStat.Strength, 3, 0, 2507624050],
+ [StatModifier.NONE]: [ArmorStat.Strength, 0, 0, 0],
+ [StatModifier.MINOR_MOBILITY]: [ArmorStat.Mobility, 5, 1, 1703647492],
+ [StatModifier.MAJOR_MOBILITY]: [ArmorStat.Mobility, 10, 3, 4183296050],
+ [StatModifier.ARTIFICE_MOBILITY]: [ArmorStat.Mobility, 3, 0, 2322202118],
+ [StatModifier.MINOR_RESILIENCE]: [ArmorStat.Resilience, 5, 2, 2532323436],
+ [StatModifier.MAJOR_RESILIENCE]: [ArmorStat.Resilience, 10, 4, 1180408010],
+ [StatModifier.ARTIFICE_RESILIENCE]: [ArmorStat.Resilience, 3, 0, 199176566],
+ [StatModifier.MINOR_RECOVERY]: [ArmorStat.Recovery, 5, 2, 1237786518],
+ [StatModifier.MAJOR_RECOVERY]: [ArmorStat.Recovery, 10, 4, 4204488676],
+ [StatModifier.ARTIFICE_RECOVERY]: [ArmorStat.Recovery, 3, 0, 539459624],
+ [StatModifier.MINOR_DISCIPLINE]: [ArmorStat.Discipline, 5, 1, 4021790309],
+ [StatModifier.MAJOR_DISCIPLINE]: [ArmorStat.Discipline, 10, 3, 1435557120],
+ [StatModifier.ARTIFICE_DISCIPLINE]: [ArmorStat.Discipline, 3, 0, 617569843],
+ [StatModifier.MINOR_INTELLECT]: [ArmorStat.Intellect, 5, 2, 350061697],
+ [StatModifier.MAJOR_INTELLECT]: [ArmorStat.Intellect, 10, 4, 2724608735],
+ [StatModifier.ARTIFICE_INTELLECT]: [ArmorStat.Intellect, 3, 0, 3160845295],
+ [StatModifier.MINOR_STRENGTH]: [ArmorStat.Strength, 5, 1, 2639422088],
+ [StatModifier.MAJOR_STRENGTH]: [ArmorStat.Strength, 10, 3, 4287799666],
+ [StatModifier.ARTIFICE_STRENGTH]: [ArmorStat.Strength, 3, 0, 2507624050],
};
export const ArmorStatNames: EnumDictionary = {
- [ArmorStat.Mobility]: "Mobility",
- [ArmorStat.Resilience]: "Resilience",
- [ArmorStat.Recovery]: "Recovery",
- [ArmorStat.Discipline]: "Discipline",
- [ArmorStat.Intellect]: "Intellect",
- [ArmorStat.Strength]: "Strength",
+ [ArmorStat.Mobility]: "Mobility",
+ [ArmorStat.Resilience]: "Resilience",
+ [ArmorStat.Recovery]: "Recovery",
+ [ArmorStat.Discipline]: "Discipline",
+ [ArmorStat.Intellect]: "Intellect",
+ [ArmorStat.Strength]: "Strength",
};
export const ArmorStatIconUrls: EnumDictionary = {
- [ArmorStat.Mobility]:
- "https://www.bungie.net/common/destiny2_content/icons/e26e0e93a9daf4fdd21bf64eb9246340.png",
- [ArmorStat.Resilience]:
- "https://www.bungie.net/common/destiny2_content/icons/202ecc1c6febeb6b97dafc856e863140.png",
- [ArmorStat.Recovery]:
- "https://www.bungie.net/common/destiny2_content/icons/128eee4ee7fc127851ab32eac6ca91cf.png",
- [ArmorStat.Discipline]:
- "https://www.bungie.net/common/destiny2_content/icons/ca62128071dc254fe75891211b98b237.png",
- [ArmorStat.Intellect]:
- "https://www.bungie.net/common/destiny2_content/icons/59732534ce7060dba681d1ba84c055a6.png",
- [ArmorStat.Strength]:
- "https://www.bungie.net/common/destiny2_content/icons/c7eefc8abbaa586eeab79e962a79d6ad.png",
+ [ArmorStat.Mobility]:
+ "https://www.bungie.net/common/destiny2_content/icons/e26e0e93a9daf4fdd21bf64eb9246340.png",
+ [ArmorStat.Resilience]:
+ "https://www.bungie.net/common/destiny2_content/icons/202ecc1c6febeb6b97dafc856e863140.png",
+ [ArmorStat.Recovery]:
+ "https://www.bungie.net/common/destiny2_content/icons/128eee4ee7fc127851ab32eac6ca91cf.png",
+ [ArmorStat.Discipline]:
+ "https://www.bungie.net/common/destiny2_content/icons/ca62128071dc254fe75891211b98b237.png",
+ [ArmorStat.Intellect]:
+ "https://www.bungie.net/common/destiny2_content/icons/59732534ce7060dba681d1ba84c055a6.png",
+ [ArmorStat.Strength]:
+ "https://www.bungie.net/common/destiny2_content/icons/c7eefc8abbaa586eeab79e962a79d6ad.png",
};
type Literal = `${T}`;
export type ArmorStatLiteral = Literal;
export enum SpecialArmorStat {
- ClassAbilityRegenerationStat = 10,
+ ClassAbilityRegenerationStat = 10,
}
export enum ArmorPerkOrSlot {
- None,
- SlotNightmare,
- SlotArtifice,
- SlotLastWish,
- SlotGardenOfSalvation,
- SlotDeepStoneCrypt,
- SlotVaultOfGlass,
- PerkIronBanner,
- PerkUniformedOfficer,
- SlotVowOfTheDisciple,
- SlotKingsFall,
- PerkPlunderersTrappings,
- SeraphSensorArray,
- SlotRootOfNightmares,
- PerkQueensFavor,
- COUNT,
+ None,
+ SlotNightmare,
+ SlotArtifice,
+ SlotLastWish,
+ SlotGardenOfSalvation,
+ SlotDeepStoneCrypt,
+ SlotVaultOfGlass,
+ PerkIronBanner,
+ PerkUniformedOfficer,
+ SlotVowOfTheDisciple,
+ SlotKingsFall,
+ PerkPlunderersTrappings,
+ SeraphSensorArray,
+ SlotRootOfNightmares,
+ PerkQueensFavor,
+ // A special case just for guardian games class items.
+ GuardianGamesClassItem,
+ COUNT,
}
export const ArmorPerkOrSlotNames: EnumDictionary = {
- [ArmorPerkOrSlot.None]: "None",
- [ArmorPerkOrSlot.SlotNightmare]: "Nightmare Hunt Modslot",
- [ArmorPerkOrSlot.SlotArtifice]: "Artifice Modslot",
- [ArmorPerkOrSlot.SlotLastWish]: "Last Wish Modslot",
- [ArmorPerkOrSlot.SlotGardenOfSalvation]: "Garden of Salvation Modslot",
- [ArmorPerkOrSlot.SlotDeepStoneCrypt]: "Deep Stone Crypt Modslot",
- [ArmorPerkOrSlot.SlotVaultOfGlass]: "Vault of Glass Modslot",
- [ArmorPerkOrSlot.SlotVowOfTheDisciple]: "Vow of the Disciple Modslot",
- [ArmorPerkOrSlot.PerkIronBanner]: "Iron Banner Perk",
- [ArmorPerkOrSlot.PerkUniformedOfficer]: "Uniformed Officer",
- [ArmorPerkOrSlot.SlotKingsFall]: "King's Fall Modslot",
- [ArmorPerkOrSlot.PerkPlunderersTrappings]: "Plunderer's Trappings",
- [ArmorPerkOrSlot.SeraphSensorArray]: "Seraph Sensor Array",
- [ArmorPerkOrSlot.SlotRootOfNightmares]: "Root of Nightmares Modslot",
- [ArmorPerkOrSlot.PerkQueensFavor]: "Queen's Favor",
- [ArmorPerkOrSlot.COUNT]: "",
+ [ArmorPerkOrSlot.None]: "None",
+ [ArmorPerkOrSlot.GuardianGamesClassItem]: "Guardian Games",
+ [ArmorPerkOrSlot.SlotNightmare]: "Nightmare Hunt Modslot",
+ [ArmorPerkOrSlot.SlotArtifice]: "Artifice Modslot",
+ [ArmorPerkOrSlot.SlotLastWish]: "Last Wish Modslot",
+ [ArmorPerkOrSlot.SlotGardenOfSalvation]: "Garden of Salvation Modslot",
+ [ArmorPerkOrSlot.SlotDeepStoneCrypt]: "Deep Stone Crypt Modslot",
+ [ArmorPerkOrSlot.SlotVaultOfGlass]: "Vault of Glass Modslot",
+ [ArmorPerkOrSlot.SlotVowOfTheDisciple]: "Vow of the Disciple Modslot",
+ [ArmorPerkOrSlot.PerkIronBanner]: "Iron Banner Perk",
+ [ArmorPerkOrSlot.PerkUniformedOfficer]: "Uniformed Officer",
+ [ArmorPerkOrSlot.SlotKingsFall]: "King's Fall Modslot",
+ [ArmorPerkOrSlot.PerkPlunderersTrappings]: "Plunderer's Trappings",
+ [ArmorPerkOrSlot.SeraphSensorArray]: "Seraph Sensor Array",
+ [ArmorPerkOrSlot.SlotRootOfNightmares]: "Root of Nightmares Modslot",
+ [ArmorPerkOrSlot.PerkQueensFavor]: "Queen's Favor",
+ [ArmorPerkOrSlot.COUNT]: "",
};
export const ArmorPerkOrSlotIcons: EnumDictionary = {
- [ArmorPerkOrSlot.None]: "https://www.bungie.net/img/misc/missing_icon_d2.png",
- //[ArmorPerkOrSlot.None]: "https://www.bungie.net/common/destiny2_content/icons/58afd7d17e7b58883b94fd5ba2e66b76.png",
- [ArmorPerkOrSlot.SlotNightmare]:
- "https://www.bungie.net/common/destiny2_content/icons/94fe19fb98ae33e79921e3a8aa07800f.jpg",
- [ArmorPerkOrSlot.SlotArtifice]:
- "https://bungie.net/common/destiny2_content/icons/74aeb2f3d7bc16a31a6924822f850184.png",
- [ArmorPerkOrSlot.SlotLastWish]:
- "https://bungie.net/common/destiny2_content/icons/c70116144be386def9e675d76dacfe64.png",
- [ArmorPerkOrSlot.SlotGardenOfSalvation]:
- "https://www.bungie.net/common/destiny2_content/icons/94fe19fb98ae33e79921e3a8aa07800f.jpg",
- [ArmorPerkOrSlot.SlotDeepStoneCrypt]:
- "https://bungie.net/common/destiny2_content/icons/3c14e3c3a747a7487c76f38602b9e2fe.png",
- [ArmorPerkOrSlot.SlotVaultOfGlass]:
- "https://bungie.net/common/destiny2_content/icons/9603e0d01826d7ab97ce1b1bf3eb3c96.png",
- [ArmorPerkOrSlot.SlotVowOfTheDisciple]:
- "https://www.bungie.net//common/destiny2_content/icons/1f66fa02b19f40e6ce5d8336c7ed5a00.png",
- [ArmorPerkOrSlot.PerkIronBanner]:
- "https://bungie.net/common/destiny2_content/icons/DestinyActivityModeDefinition_fe57052d7cf971f7502daa75a2ca2437.png",
- [ArmorPerkOrSlot.PerkUniformedOfficer]:
- "https://bungie.net/common/destiny2_content/icons/b39b83dd5ea3d9144e4e63f103af8b46.png",
- [ArmorPerkOrSlot.SlotKingsFall]:
- "https://www.bungie.net/common/destiny2_content/icons/bc809878e0c2ed8fd32feb62aaae690c.png",
- [ArmorPerkOrSlot.PerkPlunderersTrappings]:
- "https://www.bungie.net/common/destiny2_content/icons/d7ad8979dab2f4544e2cfb66f262f7d1.png",
- [ArmorPerkOrSlot.SeraphSensorArray]:
- "https://www.bungie.net/common/destiny2_content/icons/d7ad8979dab2f4544e2cfb66f262f7d1.png",
- [ArmorPerkOrSlot.SlotRootOfNightmares]:
- "https://www.bungie.net/common/destiny2_content/icons/9694158ef08d416ab091062629b6b7ec.png",
- [ArmorPerkOrSlot.PerkQueensFavor]:
- "https://www.bungie.net/common/destiny2_content/icons/d64dc503b9a88c179635e777c30db86c.png",
- [ArmorPerkOrSlot.COUNT]: "",
+ [ArmorPerkOrSlot.None]: "https://www.bungie.net/img/misc/missing_icon_d2.png",
+ //[ArmorPerkOrSlot.None]: "https://www.bungie.net/common/destiny2_content/icons/58afd7d17e7b58883b94fd5ba2e66b76.png",
+ [ArmorPerkOrSlot.GuardianGamesClassItem]:
+ "https://www.bungie.net/common/destiny2_content/icons/DestinyEventCardDefinition_ce6c2cf855dce694bcc89803b6bc44b7.png",
+ [ArmorPerkOrSlot.SlotNightmare]:
+ "https://www.bungie.net/common/destiny2_content/icons/94fe19fb98ae33e79921e3a8aa07800f.jpg",
+ [ArmorPerkOrSlot.SlotArtifice]:
+ "https://bungie.net/common/destiny2_content/icons/74aeb2f3d7bc16a31a6924822f850184.png",
+ [ArmorPerkOrSlot.SlotLastWish]:
+ "https://bungie.net/common/destiny2_content/icons/c70116144be386def9e675d76dacfe64.png",
+ [ArmorPerkOrSlot.SlotGardenOfSalvation]:
+ "https://www.bungie.net/common/destiny2_content/icons/94fe19fb98ae33e79921e3a8aa07800f.jpg",
+ [ArmorPerkOrSlot.SlotDeepStoneCrypt]:
+ "https://bungie.net/common/destiny2_content/icons/3c14e3c3a747a7487c76f38602b9e2fe.png",
+ [ArmorPerkOrSlot.SlotVaultOfGlass]:
+ "https://bungie.net/common/destiny2_content/icons/9603e0d01826d7ab97ce1b1bf3eb3c96.png",
+ [ArmorPerkOrSlot.SlotVowOfTheDisciple]:
+ "https://www.bungie.net//common/destiny2_content/icons/1f66fa02b19f40e6ce5d8336c7ed5a00.png",
+ [ArmorPerkOrSlot.PerkIronBanner]:
+ "https://bungie.net/common/destiny2_content/icons/DestinyActivityModeDefinition_fe57052d7cf971f7502daa75a2ca2437.png",
+ [ArmorPerkOrSlot.PerkUniformedOfficer]:
+ "https://bungie.net/common/destiny2_content/icons/b39b83dd5ea3d9144e4e63f103af8b46.png",
+ [ArmorPerkOrSlot.SlotKingsFall]:
+ "https://www.bungie.net/common/destiny2_content/icons/bc809878e0c2ed8fd32feb62aaae690c.png",
+ [ArmorPerkOrSlot.PerkPlunderersTrappings]:
+ "https://www.bungie.net/common/destiny2_content/icons/d7ad8979dab2f4544e2cfb66f262f7d1.png",
+ [ArmorPerkOrSlot.SeraphSensorArray]:
+ "https://www.bungie.net/common/destiny2_content/icons/d7ad8979dab2f4544e2cfb66f262f7d1.png",
+ [ArmorPerkOrSlot.SlotRootOfNightmares]:
+ "https://www.bungie.net/common/destiny2_content/icons/9694158ef08d416ab091062629b6b7ec.png",
+ [ArmorPerkOrSlot.PerkQueensFavor]:
+ "https://www.bungie.net/common/destiny2_content/icons/d64dc503b9a88c179635e777c30db86c.png",
+ [ArmorPerkOrSlot.COUNT]: "",
};
export const ArmorPerkOrSlotDIMText: EnumDictionary = {
- [ArmorPerkOrSlot.None]: "",
- [ArmorPerkOrSlot.SlotNightmare]: "modslot:nightmare",
- [ArmorPerkOrSlot.SlotArtifice]: 'perkname:"artifice armor"',
- [ArmorPerkOrSlot.SlotLastWish]: "modslot:lastwish",
- [ArmorPerkOrSlot.SlotGardenOfSalvation]: "modslot:gardenofsalvation",
- [ArmorPerkOrSlot.SlotDeepStoneCrypt]: "modslot:deepstonecrypt",
- [ArmorPerkOrSlot.SlotVaultOfGlass]: "modslot:vaultofglass",
- [ArmorPerkOrSlot.SlotVowOfTheDisciple]: "source:vow",
- [ArmorPerkOrSlot.PerkIronBanner]: 'perkname:"iron lord\'s pride"',
- [ArmorPerkOrSlot.PerkUniformedOfficer]: 'perkname:"Uniformed Officer"',
- [ArmorPerkOrSlot.SlotKingsFall]: "modslot:kingsfall",
- [ArmorPerkOrSlot.PerkPlunderersTrappings]: 'perkname:"plunderer\'s trappings"',
- [ArmorPerkOrSlot.SeraphSensorArray]: 'perkname:"seraph sensor array"',
- [ArmorPerkOrSlot.SlotRootOfNightmares]: "modslot:rootofnightmares",
- [ArmorPerkOrSlot.PerkQueensFavor]: 'perkname:"queen\'s favor"',
- [ArmorPerkOrSlot.COUNT]: "",
+ [ArmorPerkOrSlot.None]: "",
+ [ArmorPerkOrSlot.GuardianGamesClassItem]: "(hash:537041732 or hash:366019830 or hash:1013401891)",
+ [ArmorPerkOrSlot.SlotNightmare]: "modslot:nightmare",
+ [ArmorPerkOrSlot.SlotArtifice]: 'perkname:"artifice armor"',
+ [ArmorPerkOrSlot.SlotLastWish]: "modslot:lastwish",
+ [ArmorPerkOrSlot.SlotGardenOfSalvation]: "modslot:gardenofsalvation",
+ [ArmorPerkOrSlot.SlotDeepStoneCrypt]: "modslot:deepstonecrypt",
+ [ArmorPerkOrSlot.SlotVaultOfGlass]: "modslot:vaultofglass",
+ [ArmorPerkOrSlot.SlotVowOfTheDisciple]: "source:vow",
+ [ArmorPerkOrSlot.PerkIronBanner]: 'perkname:"iron lord\'s pride"',
+ [ArmorPerkOrSlot.PerkUniformedOfficer]: 'perkname:"Uniformed Officer"',
+ [ArmorPerkOrSlot.SlotKingsFall]: "modslot:kingsfall",
+ [ArmorPerkOrSlot.PerkPlunderersTrappings]: 'perkname:"plunderer\'s trappings"',
+ [ArmorPerkOrSlot.SeraphSensorArray]: 'perkname:"seraph sensor array"',
+ [ArmorPerkOrSlot.SlotRootOfNightmares]: "modslot:rootofnightmares",
+ [ArmorPerkOrSlot.PerkQueensFavor]: 'perkname:"queen\'s favor"',
+ [ArmorPerkOrSlot.COUNT]: "",
};
export const ArmorAffinityNames: EnumDictionary = {
- [DestinyEnergyType.Any]: "Any",
- [DestinyEnergyType.Arc]: "Arc",
- [DestinyEnergyType.Thermal]: "Solar",
- [DestinyEnergyType.Void]: "Void",
- [DestinyEnergyType.Stasis]: "Stasis",
- [DestinyEnergyType.Ghost]: "Ghost",
- [DestinyEnergyType.Subclass]: "Subclass",
+ [DestinyEnergyType.Any]: "Any",
+ [DestinyEnergyType.Arc]: "Arc",
+ [DestinyEnergyType.Thermal]: "Solar",
+ [DestinyEnergyType.Void]: "Void",
+ [DestinyEnergyType.Stasis]: "Stasis",
+ [DestinyEnergyType.Ghost]: "Ghost",
+ [DestinyEnergyType.Subclass]: "Subclass",
};
export const ArmorAffinityIcons: EnumDictionary = {
- [DestinyEnergyType.Any]: "",
- [DestinyEnergyType.Arc]:
- "https://www.bungie.net/common/destiny2_content/icons/DestinyEnergyTypeDefinition_092d066688b879c807c3b460afdd61e6.png",
- [DestinyEnergyType.Thermal]:
- "https://www.bungie.net/common/destiny2_content/icons/DestinyEnergyTypeDefinition_2a1773e10968f2d088b97c22b22bba9e.png",
- [DestinyEnergyType.Void]:
- "https://www.bungie.net/common/destiny2_content/icons/DestinyEnergyTypeDefinition_ceb2f6197dccf3958bb31cc783eb97a0.png",
- [DestinyEnergyType.Stasis]:
- "https://www.bungie.net/common/destiny2_content/icons/DestinyEnergyTypeDefinition_530c4c3e7981dc2aefd24fd3293482bf.png",
- [DestinyEnergyType.Ghost]: "",
- [DestinyEnergyType.Subclass]: "",
+ [DestinyEnergyType.Any]: "",
+ [DestinyEnergyType.Arc]:
+ "https://www.bungie.net/common/destiny2_content/icons/DestinyEnergyTypeDefinition_092d066688b879c807c3b460afdd61e6.png",
+ [DestinyEnergyType.Thermal]:
+ "https://www.bungie.net/common/destiny2_content/icons/DestinyEnergyTypeDefinition_2a1773e10968f2d088b97c22b22bba9e.png",
+ [DestinyEnergyType.Void]:
+ "https://www.bungie.net/common/destiny2_content/icons/DestinyEnergyTypeDefinition_ceb2f6197dccf3958bb31cc783eb97a0.png",
+ [DestinyEnergyType.Stasis]:
+ "https://www.bungie.net/common/destiny2_content/icons/DestinyEnergyTypeDefinition_530c4c3e7981dc2aefd24fd3293482bf.png",
+ [DestinyEnergyType.Ghost]: "",
+ [DestinyEnergyType.Subclass]: "",
};
diff --git a/src/app/services/bungie-api.service.ts b/src/app/services/bungie-api.service.ts
index eb2092e4..5ba3e03c 100644
--- a/src/app/services/bungie-api.service.ts
+++ b/src/app/services/bungie-api.service.ts
@@ -1,16 +1,16 @@
import { Injectable } from "@angular/core";
import {
- DestinyClass,
- DestinyComponentType,
- DestinyInventoryItemDefinition,
- DestinyItemSocketState,
- equipItem,
- getDestinyManifest,
- getDestinyManifestSlice,
- getItem,
- getProfile,
- HttpClientConfig,
- transferItem,
+ DestinyClass,
+ DestinyComponentType,
+ DestinyInventoryItemDefinition,
+ DestinyItemSocketState,
+ equipItem,
+ getDestinyManifest,
+ getDestinyManifestSlice,
+ getItem,
+ getProfile,
+ HttpClientConfig,
+ transferItem,
} from "bungie-api-ts/destiny2";
import { getMembershipDataForCurrentUser } from "bungie-api-ts/user";
import { AuthService } from "./auth.service";
@@ -25,650 +25,641 @@ import { ArmorPerkOrSlot } from "../data/enum/armor-stat";
import { ConfigurationService } from "./configuration.service";
@Injectable({
- providedIn: "root",
+ providedIn: "root",
})
export class BungieApiService {
- config_assumeEveryLegendaryIsArtifice = false;
-
- constructor(
- private authService: AuthService,
- private http: HttpClient,
- private db: DatabaseService,
- private config: ConfigurationService
- ) {
- this.config.configuration.subscribe(async (config) => {
- this.config_assumeEveryLegendaryIsArtifice = config.assumeEveryLegendaryIsArtifice;
- });
+ config_assumeEveryLegendaryIsArtifice = false;
+
+ constructor(
+ private authService: AuthService,
+ private http: HttpClient,
+ private db: DatabaseService,
+ private config: ConfigurationService
+ ) {
+ this.config.configuration.subscribe(async (config) => {
+ this.config_assumeEveryLegendaryIsArtifice = config.assumeEveryLegendaryIsArtifice;
+ });
+ }
+
+ async $httpWithoutKey(config: HttpClientConfig) {
+ return this.http
+ .get(config.url, {
+ params: config.params,
+ })
+ .toPromise();
+ }
+
+ async $httpPost(config: HttpClientConfig) {
+ return this.http
+ .post(config.url, config.body, {
+ params: config.params,
+ headers: {
+ "X-API-Key": environment.apiKey,
+ Authorization: "Bearer " + this.authService.accessToken,
+ },
+ })
+ .toPromise()
+ .catch(async (err) => {
+ console.error(err);
+ });
+ }
+
+ async $http(config: HttpClientConfig) {
+ return this.http
+ .get(config.url, {
+ params: config.params,
+ headers: {
+ "X-API-Key": environment.apiKey,
+ Authorization: "Bearer " + this.authService.accessToken,
+ },
+ })
+ .toPromise()
+ .catch(async (err) => {
+ console.error(err);
+ if (environment.offlineMode) {
+ console.debug("Offline mode, ignoring API error");
+ return;
+ }
+ if (err.error?.ErrorStatus == "SystemDisabled") {
+ console.info("System is disabled. Revoking auth, must re-login");
+ await this.authService.logout();
+ }
+ if (err.ErrorStatus != "Internal Server Error") {
+ console.info("API-Error");
+ //await this.authService.logout();
+ }
+ // TODO: go to login page
+ });
+ }
+
+ async getCharacters() {
+ let destinyMembership = await this.getMembershipDataForCurrentUser();
+ if (!destinyMembership) {
+ await this.authService.logout();
+ return [];
}
- async $httpWithoutKey(config: HttpClientConfig) {
- return this.http
- .get(config.url, {
- params: config.params,
- })
- .toPromise();
+ const profile = await getProfile((d) => this.$http(d), {
+ components: [DestinyComponentType.Characters],
+ membershipType: destinyMembership.membershipType,
+ destinyMembershipId: destinyMembership.membershipId,
+ });
+
+ return (
+ Object.values(profile?.Response.characters.data || {}).map((d) => {
+ return {
+ characterId: d.characterId,
+ clazz: d.classType as DestinyClass,
+ emblemUrl: d.emblemBackgroundPath,
+ lastPlayed: Date.parse(d.dateLastPlayed),
+ };
+ }) || []
+ );
+ }
+
+ async transferItem(
+ itemInstanceId: string,
+ targetCharacter: string,
+ equip = false
+ ): Promise {
+ let destinyMembership = await this.getMembershipDataForCurrentUser();
+ if (!destinyMembership) {
+ await this.authService.logout();
+ return false;
}
- async $httpPost(config: HttpClientConfig) {
- return this.http
- .post(config.url, config.body, {
- params: config.params,
- headers: {
- "X-API-Key": environment.apiKey,
- Authorization: "Bearer " + this.authService.accessToken,
- },
- })
- .toPromise()
- .catch(async (err) => {
- console.error(err);
- });
+ let r1 = await getItem((d) => this.$http(d), {
+ membershipType: destinyMembership.membershipType,
+ destinyMembershipId: destinyMembership.membershipId,
+ itemInstanceId: itemInstanceId,
+ components: [DestinyComponentType.ItemCommonData],
+ });
+
+ let transferResult = false;
+
+ if (!r1) return false;
+ if (r1.Response.characterId != targetCharacter) {
+ if (r1.Response.item.data?.location != 2) {
+ await this.moveItemToVault(r1.Response.item.data?.itemInstanceId || "");
+ r1 = await getItem((d) => this.$http(d), {
+ membershipType: destinyMembership.membershipType,
+ destinyMembershipId: destinyMembership.membershipId,
+ itemInstanceId: itemInstanceId,
+ components: [DestinyComponentType.ItemCommonData],
+ });
+ }
+
+ const payload = {
+ characterId: targetCharacter,
+ membershipType: 3,
+ itemId: r1?.Response.item.data?.itemInstanceId || "",
+ itemReferenceHash: r1?.Response.item.data?.itemHash || 0,
+ stackSize: 1,
+ transferToVault: false,
+ };
+
+ transferResult = !!(await transferItem((d) => this.$httpPost(d), payload));
}
-
- async $http(config: HttpClientConfig) {
- return this.http
- .get(config.url, {
- params: config.params,
- headers: {
- "X-API-Key": environment.apiKey,
- Authorization: "Bearer " + this.authService.accessToken,
- },
- })
- .toPromise()
- .catch(async (err) => {
- console.error(err);
- if (environment.offlineMode) {
- console.debug("Offline mode, ignoring API error");
- return;
- }
- if (err.error?.ErrorStatus == "SystemDisabled") {
- console.info("System is disabled. Revoking auth, must re-login");
- await this.authService.logout();
- }
- if (err.ErrorStatus != "Internal Server Error") {
- console.info("API-Error");
- //await this.authService.logout();
- }
- // TODO: go to login page
- });
+ if (equip) {
+ let equipPayload = {
+ characterId: targetCharacter,
+ membershipType: 3,
+ stackSize: 1,
+ itemId: r1?.Response.item.data?.itemInstanceId || "",
+ itemReferenceHash: r1?.Response.item.data?.itemHash || 0,
+ };
+ transferResult = !!(await equipItem((d) => this.$httpPost(d), equipPayload));
}
- async getCharacters() {
- let destinyMembership = await this.getMembershipDataForCurrentUser();
- if (!destinyMembership) {
- await this.authService.logout();
- return [];
- }
-
- const profile = await getProfile((d) => this.$http(d), {
- components: [DestinyComponentType.Characters],
- membershipType: destinyMembership.membershipType,
- destinyMembershipId: destinyMembership.membershipId,
- });
+ return transferResult;
+ }
- return (
- Object.values(profile?.Response.characters.data || {}).map((d) => {
- return {
- characterId: d.characterId,
- clazz: d.classType as DestinyClass,
- emblemUrl: d.emblemBackgroundPath,
- lastPlayed: Date.parse(d.dateLastPlayed),
- };
- }) || []
- );
+ async moveItemToVault(itemInstanceId: string) {
+ console.info("moveItemToVault", itemInstanceId);
+ let destinyMembership = await this.getMembershipDataForCurrentUser();
+ if (!destinyMembership) {
+ await this.authService.logout();
+ return;
}
- async transferItem(
- itemInstanceId: string,
- targetCharacter: string,
- equip = false
- ): Promise {
- let destinyMembership = await this.getMembershipDataForCurrentUser();
- if (!destinyMembership) {
- await this.authService.logout();
- return false;
- }
+ const r1 = await getItem((d) => this.$http(d), {
+ membershipType: destinyMembership.membershipType,
+ destinyMembershipId: destinyMembership.membershipId,
+ itemInstanceId: itemInstanceId,
+ components: [DestinyComponentType.ItemCommonData],
+ });
+
+ const payload = {
+ characterId: r1?.Response.characterId || "",
+ membershipType: 3,
+ itemId: r1?.Response.item.data?.itemInstanceId || "",
+ itemReferenceHash: r1?.Response.item.data?.itemHash || 0,
+ stackSize: 1,
+ transferToVault: true,
+ };
+
+ await transferItem((d) => this.$httpPost(d), payload);
+ }
+
+ async getMembershipDataForCurrentUser() {
+ var membershipData = JSON.parse(localStorage.getItem("auth-membershipInfo") || "null");
+ var membershipDataAge = JSON.parse(localStorage.getItem("auth-membershipInfo-date") || "0");
+ if (membershipData && Date.now() - membershipDataAge < 1000 * 60 * 60 * 24) {
+ console.log("getMembershipDataForCurrentUser -> loading cached! ");
+ return membershipData;
+ }
- let r1 = await getItem((d) => this.$http(d), {
- membershipType: destinyMembership.membershipType,
- destinyMembershipId: destinyMembership.membershipId,
- itemInstanceId: itemInstanceId,
- components: [DestinyComponentType.ItemCommonData],
+ console.info("BungieApiService", "getMembershipDataForCurrentUser");
+ let response = await getMembershipDataForCurrentUser((d) => this.$http(d));
+ let memberships = response?.Response.destinyMemberships;
+ console.info("Memberships:", memberships);
+ memberships = memberships.filter(
+ (m) => m.crossSaveOverride == 0 || m.crossSaveOverride == m.membershipType
+ );
+ console.info("Filtered Memberships:", memberships);
+
+ let result = null;
+ if (memberships?.length == 1) {
+ // This guardian only has one account linked, so we can proceed as normal
+ result = memberships?.[0];
+ } else {
+ // This guardian has multiple accounts linked.
+ // Fetch the last login time for each account, and use the one that was most recently used.
+ let lastLoggedInProfileIndex: any = 0;
+ let lastPlayed = 0;
+ for (let id in memberships) {
+ const membership = memberships?.[id];
+ const profile = await getProfile((d) => this.$http(d), {
+ components: [DestinyComponentType.Profiles],
+ membershipType: membership.membershipType,
+ destinyMembershipId: membership.membershipId,
});
-
- let transferResult = false;
-
- if (!r1) return false;
- if (r1.Response.characterId != targetCharacter) {
- if (r1.Response.item.data?.location != 2) {
- await this.moveItemToVault(r1.Response.item.data?.itemInstanceId || "");
- r1 = await getItem((d) => this.$http(d), {
- membershipType: destinyMembership.membershipType,
- destinyMembershipId: destinyMembership.membershipId,
- itemInstanceId: itemInstanceId,
- components: [DestinyComponentType.ItemCommonData],
- });
- }
-
- const payload = {
- characterId: targetCharacter,
- membershipType: 3,
- itemId: r1?.Response.item.data?.itemInstanceId || "",
- itemReferenceHash: r1?.Response.item.data?.itemHash || 0,
- stackSize: 1,
- transferToVault: false,
- };
-
- transferResult = !!(await transferItem((d) => this.$httpPost(d), payload));
- }
- if (equip) {
- let equipPayload = {
- characterId: targetCharacter,
- membershipType: 3,
- stackSize: 1,
- itemId: r1?.Response.item.data?.itemInstanceId || "",
- itemReferenceHash: r1?.Response.item.data?.itemHash || 0,
- };
- transferResult = !!(await equipItem((d) => this.$httpPost(d), equipPayload));
+ if (!!profile && profile.Response?.profile.data?.dateLastPlayed) {
+ let date = Date.parse(profile.Response?.profile.data?.dateLastPlayed);
+ if (date > lastPlayed) {
+ lastPlayed = date;
+ lastLoggedInProfileIndex = id;
+ }
}
-
- return transferResult;
+ }
+ console.info(
+ "getMembershipDataForCurrentUser",
+ "Selected membership data for the last logged in membership."
+ );
+ result = memberships?.[lastLoggedInProfileIndex];
+ }
+ localStorage.setItem("auth-membershipInfo", JSON.stringify(result));
+ localStorage.setItem("auth-membershipInfo-date", JSON.stringify(Date.now()));
+ return result;
+ }
+
+ async updateArmorItems(force = false) {
+ if (environment.offlineMode) {
+ console.info("BungieApiService", "updateArmorItems", "offline mode, skipping");
+ return;
}
- async moveItemToVault(itemInstanceId: string) {
- console.info("moveItemToVault", itemInstanceId);
- let destinyMembership = await this.getMembershipDataForCurrentUser();
- if (!destinyMembership) {
- await this.authService.logout();
- return;
- }
-
- const r1 = await getItem((d) => this.$http(d), {
- membershipType: destinyMembership.membershipType,
- destinyMembershipId: destinyMembership.membershipId,
- itemInstanceId: itemInstanceId,
- components: [DestinyComponentType.ItemCommonData],
- });
-
- const payload = {
- characterId: r1?.Response.characterId || "",
- membershipType: 3,
- itemId: r1?.Response.item.data?.itemInstanceId || "",
- itemReferenceHash: r1?.Response.item.data?.itemHash || 0,
- stackSize: 1,
- transferToVault: true,
- };
+ if (!force && localStorage.getItem("LastArmorUpdate"))
+ if (localStorage.getItem("last-armor-db-name") == this.db.inventoryArmor.db.name)
+ if (
+ Date.now() - Number.parseInt(localStorage.getItem("LastArmorUpdate") || "0") <
+ (1000 * 3600) / 2
+ )
+ return;
+ let destinyMembership = await this.getMembershipDataForCurrentUser();
+ if (!destinyMembership) {
+ await this.authService.logout();
+ return;
+ }
- await transferItem((d) => this.$httpPost(d), payload);
+ console.info("BungieApiService", "getProfile");
+ let profile = await getProfile((d) => this.$http(d), {
+ components: [
+ DestinyComponentType.CharacterEquipment,
+ DestinyComponentType.CharacterInventories,
+ DestinyComponentType.ProfileCurrencies,
+ DestinyComponentType.ProfileInventories,
+ DestinyComponentType.ItemStats,
+ DestinyComponentType.ItemInstances,
+ DestinyComponentType.ItemPerks,
+ DestinyComponentType.ItemSockets,
+ DestinyComponentType.ItemPlugStates,
+ ],
+ membershipType: destinyMembership.membershipType,
+ destinyMembershipId: destinyMembership.membershipId,
+ });
+
+ let allItems = profile.Response.profileInventory.data?.items || [];
+ for (let charI in profile.Response.characterEquipment.data) {
+ let i = profile.Response.characterEquipment.data[charI].items;
+ allItems = allItems.concat(i);
+ }
+ for (let charI in profile.Response.characterInventories.data) {
+ let i = profile.Response.characterInventories.data[charI].items;
+ allItems = allItems.concat(i);
}
- async getMembershipDataForCurrentUser() {
- var membershipData = JSON.parse(localStorage.getItem("auth-membershipInfo") || "null");
- var membershipDataAge = JSON.parse(localStorage.getItem("auth-membershipInfo-date") || "0");
- if (membershipData && Date.now() - membershipDataAge < 1000 * 60 * 60 * 24) {
- console.log("getMembershipDataForCurrentUser -> loading cached! ");
- return membershipData;
+ // get amount of materials
+ // 3853748946 enhancement core
+ // 4257549984 enhancement prism
+ // 4257549985 Ascendant Shard
+ var materials = allItems
+ .filter((k) => [3853748946, 4257549984, 4257549985].indexOf(k.itemHash!) > -1)
+ .reduce((previousValue, currentValue) => {
+ if (!(currentValue.itemHash.toString() in previousValue)) {
+ previousValue[currentValue.itemHash] = 0;
}
-
- console.info("BungieApiService", "getMembershipDataForCurrentUser");
- let response = await getMembershipDataForCurrentUser((d) => this.$http(d));
- let memberships = response?.Response.destinyMemberships;
- console.info("Memberships:", memberships);
- memberships = memberships.filter(
- (m) => m.crossSaveOverride == 0 || m.crossSaveOverride == m.membershipType
- );
- console.info("Filtered Memberships:", memberships);
-
- let result = null;
- if (memberships?.length == 1) {
- // This guardian only has one account linked, so we can proceed as normal
- result = memberships?.[0];
- } else {
- // This guardian has multiple accounts linked.
- // Fetch the last login time for each account, and use the one that was most recently used.
- let lastLoggedInProfileIndex: any = 0;
- let lastPlayed = 0;
- for (let id in memberships) {
- const membership = memberships?.[id];
- const profile = await getProfile((d) => this.$http(d), {
- components: [DestinyComponentType.Profiles],
- membershipType: membership.membershipType,
- destinyMembershipId: membership.membershipId,
- });
- if (!!profile && profile.Response?.profile.data?.dateLastPlayed) {
- let date = Date.parse(profile.Response?.profile.data?.dateLastPlayed);
- if (date > lastPlayed) {
- lastPlayed = date;
- lastLoggedInProfileIndex = id;
- }
+ previousValue[currentValue.itemHash] += currentValue.quantity;
+ return previousValue;
+ }, {} as any);
+ let glimmerEntry =
+ profile.Response.profileCurrencies.data?.items.filter((k) => k.itemHash == 3159615086) || [];
+ if (glimmerEntry.length > 0) materials["3159615086"] = glimmerEntry[0].quantity;
+ else materials["3159615086"] = 0;
+ let legShardEntry =
+ profile.Response.profileCurrencies.data?.items.filter((k) => k.itemHash == 1022552290) || [];
+ if (legShardEntry.length > 0) materials["1022552290"] = legShardEntry[0].quantity;
+ else materials["1022552290"] = 0;
+ localStorage.setItem("stored-materials", JSON.stringify(materials));
+
+ let ids = Array.from(new Set(allItems.map((d) => d.itemHash)));
+ // Do not search directly in the DB, as it is VERY slow.
+ let manifestArmor = await this.db.manifestArmor.toArray();
+ const cx = manifestArmor.filter((d) => ids.indexOf(d.hash) > -1);
+ const modsData = manifestArmor.filter((d) => d.itemType == 19);
+ let res = Object.fromEntries(cx.map((_) => [_.hash, _]));
+ let mods = Object.fromEntries(modsData.map((_) => [_.hash, _]));
+
+ let r =
+ allItems
+ //.filter(d => ids.indexOf(d.itemHash) > -1)
+ .filter((d) => !!d.itemInstanceId)
+ .filter((d) => {
+ let statData = profile.Response.itemComponents.stats.data || {};
+ let stats = statData[d.itemInstanceId || ""]?.stats || {};
+ return !!stats[392767087];
+ })
+ .filter((d) => {
+ // remove sunset items
+ let instanceData = profile.Response.itemComponents.instances.data || {};
+ let instance = instanceData[d.itemInstanceId || ""] || {};
+ return !!instance.energy;
+ })
+ .map((d) => {
+ let instanceData = profile.Response.itemComponents.instances.data || {};
+ let instance = instanceData[d.itemInstanceId || ""] || {};
+
+ let r = Object.assign(
+ {
+ itemInstanceId: d.itemInstanceId || "",
+ masterworked: !!instance.energy && instance.energy.energyCapacity == 10,
+ energyLevel: !!instance.energy ? instance.energy.energyCapacity : 0,
+ mobility: 0,
+ resilience: 0,
+ recovery: 0,
+ discipline: 0,
+ intellect: 0,
+ strength: 0,
+ },
+ res[d.itemHash]
+ ) as IInventoryArmor;
+ (r.id as any) = undefined;
+
+ // HALLOWEEN MASKS
+ if (d.itemHash == 2545426109 || d.itemHash == 199733460 || d.itemHash == 3224066584)
+ r.slot = ArmorSlot.ArmorSlotHelmet;
+ // /HALLOWEEN MASKS
+
+ var investmentStats: { [id: number]: number } = {
+ 2996146975: 0,
+ 392767087: 0,
+ 1943323491: 0,
+ 1735777505: 0,
+ 144602215: 0,
+ 4244567218: 0,
+ };
+ // Intrinsics
+ if (res[d.itemHash] && res[d.itemHash].investmentStats) {
+ for (let newStats of res[d.itemHash].investmentStats) {
+ if (newStats.statTypeHash in investmentStats)
+ investmentStats[newStats.statTypeHash] += newStats.value;
+ }
+ }
+ if (r.slot != ArmorSlot.ArmorSlotClass) {
+ const destinyItemSocketsComponents = profile.Response.itemComponents.sockets.data || {};
+ // check if d.itemInstanceId is in destinyItemSocketsComponents
+ if (d.itemInstanceId && d.itemInstanceId in destinyItemSocketsComponents) {
+ const sockets: DestinyItemSocketState[] =
+ destinyItemSocketsComponents[d.itemInstanceId || ""].sockets;
+ var plugs = [
+ sockets[6].plugHash,
+ sockets[7].plugHash,
+ sockets[8].plugHash,
+ sockets[9].plugHash,
+ ];
+ r.statPlugHashes = plugs;
+ var plm = plugs.map((k) => mods[k || ""]).filter((k) => k != null);
+ for (let entry of plm) {
+ for (let newStats of entry.investmentStats) {
+ if (newStats.statTypeHash in investmentStats)
+ investmentStats[newStats.statTypeHash] += newStats.value;
}
+ }
+ } else {
+ console.error(
+ "Sockets data does not contain the correct item instance ID",
+ d.itemInstanceId,
+ profile.Response.itemComponents.sockets.data
+ );
}
- console.info(
- "getMembershipDataForCurrentUser",
- "Selected membership data for the last logged in membership."
- );
- result = memberships?.[lastLoggedInProfileIndex];
- }
- localStorage.setItem("auth-membershipInfo", JSON.stringify(result));
- localStorage.setItem("auth-membershipInfo-date", JSON.stringify(Date.now()));
- return result;
+ }
+ r.mobility = investmentStats[2996146975];
+ r.resilience = investmentStats[392767087];
+ r.recovery = investmentStats[1943323491];
+ r.discipline = investmentStats[1735777505];
+ r.intellect = investmentStats[144602215];
+ r.strength = investmentStats[4244567218];
+
+ // Take a look if it really has the artifice perk
+ if (r.perk == ArmorPerkOrSlot.SlotArtifice) {
+ let statData = profile.Response.itemComponents.perks.data || {};
+ let perks = (statData[d.itemInstanceId || ""] || {})["perks"] || [];
+ const hasPerk = perks.filter((p) => p.perkHash == 229248542).length > 0;
+ if (!hasPerk) r.perk = ArmorPerkOrSlot.None;
+ }
+
+ if (!r.isExotic && this.config_assumeEveryLegendaryIsArtifice)
+ r.perk = ArmorPerkOrSlot.SlotArtifice;
+
+ return r as IInventoryArmor;
+ }) || [];
+
+ r = r.filter((k) => !k["statPlugHashes"] || k["statPlugHashes"][0] != null);
+
+ // Now add the stuff to the db..
+ await this.db.inventoryArmor.clear();
+ await this.db.inventoryArmor.bulkAdd(r);
+ localStorage.setItem("LastArmorUpdate", Date.now().toString());
+ localStorage.setItem("last-armor-db-name", this.db.inventoryArmor.db.name);
+
+ return r;
+ }
+
+ private getArmorPerk(v: DestinyInventoryItemDefinition): ArmorPerkOrSlot {
+ // Guardian Games
+ if (
+ environment.featureFlags.enableGuardianGamesFeatures &&
+ (v.hash === 1013401891 || v.hash === 366019830 || v.hash == 537041732)
+ )
+ return ArmorPerkOrSlot.GuardianGamesClassItem;
+
+ if ((v.sockets?.socketEntries.filter((d) => d.reusablePlugSetHash == 1259) || []).length > 0)
+ return ArmorPerkOrSlot.SlotArtifice;
+
+ if (
+ (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 4144354978) || []).length >
+ 0
+ )
+ return ArmorPerkOrSlot.SlotRootOfNightmares;
+ if (
+ (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 1728096240) || []).length >
+ 0
+ )
+ return ArmorPerkOrSlot.SlotKingsFall;
+ if (
+ (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 1679876242) || []).length >
+ 0
+ )
+ return ArmorPerkOrSlot.SlotLastWish;
+ if (
+ (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 3738398030) || []).length >
+ 0
+ )
+ return ArmorPerkOrSlot.SlotVaultOfGlass;
+ if (
+ (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 706611068) || []).length >
+ 0
+ )
+ return ArmorPerkOrSlot.SlotGardenOfSalvation;
+ if (
+ (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 4055462131) || []).length >
+ 0
+ )
+ return ArmorPerkOrSlot.SlotDeepStoneCrypt;
+ if (
+ (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 2447143568) || []).length >
+ 0
+ )
+ return ArmorPerkOrSlot.SlotVowOfTheDisciple;
+
+ if (
+ (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 1101259514) || []).length >
+ 0
+ )
+ return ArmorPerkOrSlot.PerkQueensFavor;
+ if (
+ (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 1180997867) || []).length >
+ 0
+ )
+ return ArmorPerkOrSlot.SlotNightmare;
+ if (
+ (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 2472875850) || []).length >
+ 0
+ )
+ return ArmorPerkOrSlot.PerkIronBanner;
+ if (
+ (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 2392155347) || []).length >
+ 0
+ )
+ return ArmorPerkOrSlot.PerkUniformedOfficer;
+ if (
+ (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 400659041) || []).length >
+ 0
+ )
+ return ArmorPerkOrSlot.PerkPlunderersTrappings;
+ if (
+ (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 3525583702) || []).length >
+ 0
+ )
+ return ArmorPerkOrSlot.SeraphSensorArray;
+
+ return ArmorPerkOrSlot.None;
+ }
+
+ async updateManifest(force = false) {
+ if (environment.offlineMode) {
+ console.info("BungieApiService", "updateManifest", "offline mode, skipping");
+ return;
}
- async updateArmorItems(force = false) {
- if (environment.offlineMode) {
- console.info("BungieApiService", "updateArmorItems", "offline mode, skipping");
+ var destinyManifest = null;
+ if (
+ !force &&
+ localStorage.getItem("LastManifestUpdate") &&
+ localStorage.getItem("last-manifest-revision")
+ ) {
+ if (localStorage.getItem("last-manifest-revision") == environment.revision) {
+ if (
+ Date.now() - Number.parseInt(localStorage.getItem("LastManifestUpdate") || "0") >
+ 1000 * 3600 * 0.25
+ ) {
+ destinyManifest = await getDestinyManifest((d) => this.$http(d));
+ const version = destinyManifest.Response.version;
+ if (localStorage.getItem("last-manifest-version") == version) {
+ console.debug(
+ "bungieApiService - updateManifest",
+ "Abort updateManifest due to fitting ManifestVersion"
+ );
return;
+ }
}
-
- if (!force && localStorage.getItem("LastArmorUpdate"))
- if (localStorage.getItem("last-armor-db-name") == this.db.inventoryArmor.db.name)
- if (
- Date.now() - Number.parseInt(localStorage.getItem("LastArmorUpdate") || "0") <
- (1000 * 3600) / 2
- )
- return;
- let destinyMembership = await this.getMembershipDataForCurrentUser();
- if (!destinyMembership) {
- await this.authService.logout();
+ if (localStorage.getItem("last-manifest-db-name") == this.db.manifestArmor.db.name)
+ if (
+ Date.now() - Number.parseInt(localStorage.getItem("LastManifestUpdate") || "0") <
+ 1000 * 3600 * 24
+ ) {
+ console.debug(
+ "bungieApiService - updateManifest",
+ "Abort updateManifest due to fitting Date"
+ );
return;
- }
-
- console.info("BungieApiService", "getProfile");
- let profile = await getProfile((d) => this.$http(d), {
- components: [
- DestinyComponentType.CharacterEquipment,
- DestinyComponentType.CharacterInventories,
- DestinyComponentType.ProfileCurrencies,
- DestinyComponentType.ProfileInventories,
- DestinyComponentType.ItemStats,
- DestinyComponentType.ItemInstances,
- DestinyComponentType.ItemPerks,
- DestinyComponentType.ItemSockets,
- DestinyComponentType.ItemPlugStates,
- ],
- membershipType: destinyMembership.membershipType,
- destinyMembershipId: destinyMembership.membershipId,
- });
-
- let allItems = profile.Response.profileInventory.data?.items || [];
- for (let charI in profile.Response.characterEquipment.data) {
- let i = profile.Response.characterEquipment.data[charI].items;
- allItems = allItems.concat(i);
- }
- for (let charI in profile.Response.characterInventories.data) {
- let i = profile.Response.characterInventories.data[charI].items;
- allItems = allItems.concat(i);
- }
-
- // get amount of materials
- // 3853748946 enhancement core
- // 4257549984 enhancement prism
- // 4257549985 Ascendant Shard
- var materials = allItems
- .filter((k) => [3853748946, 4257549984, 4257549985].indexOf(k.itemHash!) > -1)
- .reduce((previousValue, currentValue) => {
- if (!(currentValue.itemHash.toString() in previousValue)) {
- previousValue[currentValue.itemHash] = 0;
- }
- previousValue[currentValue.itemHash] += currentValue.quantity;
- return previousValue;
- }, {} as any);
- let glimmerEntry =
- profile.Response.profileCurrencies.data?.items.filter(
- (k) => k.itemHash == 3159615086
- ) || [];
- if (glimmerEntry.length > 0) materials["3159615086"] = glimmerEntry[0].quantity;
- else materials["3159615086"] = 0;
- let legShardEntry =
- profile.Response.profileCurrencies.data?.items.filter(
- (k) => k.itemHash == 1022552290
- ) || [];
- if (legShardEntry.length > 0) materials["1022552290"] = legShardEntry[0].quantity;
- else materials["1022552290"] = 0;
- localStorage.setItem("stored-materials", JSON.stringify(materials));
-
- let ids = Array.from(new Set(allItems.map((d) => d.itemHash)));
- // Do not search directly in the DB, as it is VERY slow.
- let manifestArmor = await this.db.manifestArmor.toArray();
- const cx = manifestArmor.filter((d) => ids.indexOf(d.hash) > -1);
- const modsData = manifestArmor.filter((d) => d.itemType == 19);
- let res = Object.fromEntries(cx.map((_) => [_.hash, _]));
- let mods = Object.fromEntries(modsData.map((_) => [_.hash, _]));
-
- let r =
- allItems
- //.filter(d => ids.indexOf(d.itemHash) > -1)
- .filter((d) => !!d.itemInstanceId)
- .filter((d) => {
- let statData = profile.Response.itemComponents.stats.data || {};
- let stats = statData[d.itemInstanceId || ""]?.stats || {};
- return !!stats[392767087];
- })
- .filter((d) => {
- // remove sunset items
- let instanceData = profile.Response.itemComponents.instances.data || {};
- let instance = instanceData[d.itemInstanceId || ""] || {};
- return !!instance.energy;
- })
- .map((d) => {
- let instanceData = profile.Response.itemComponents.instances.data || {};
- let instance = instanceData[d.itemInstanceId || ""] || {};
-
- let r = Object.assign(
- {
- itemInstanceId: d.itemInstanceId || "",
- masterworked: !!instance.energy && instance.energy.energyCapacity == 10,
- energyLevel: !!instance.energy ? instance.energy.energyCapacity : 0,
- mobility: 0,
- resilience: 0,
- recovery: 0,
- discipline: 0,
- intellect: 0,
- strength: 0,
- },
- res[d.itemHash]
- ) as IInventoryArmor;
- (r.id as any) = undefined;
-
- // HALLOWEEN MASKS
- if (
- d.itemHash == 2545426109 ||
- d.itemHash == 199733460 ||
- d.itemHash == 3224066584
- )
- r.slot = ArmorSlot.ArmorSlotHelmet;
- // /HALLOWEEN MASKS
-
- var investmentStats: { [id: number]: number } = {
- 2996146975: 0,
- 392767087: 0,
- 1943323491: 0,
- 1735777505: 0,
- 144602215: 0,
- 4244567218: 0,
- };
- // Intrinsics
- if (res[d.itemHash] && res[d.itemHash].investmentStats) {
- for (let newStats of res[d.itemHash].investmentStats) {
- if (newStats.statTypeHash in investmentStats)
- investmentStats[newStats.statTypeHash] += newStats.value;
- }
- }
- if (r.slot != ArmorSlot.ArmorSlotClass) {
- const destinyItemSocketsComponents =
- profile.Response.itemComponents.sockets.data || {};
- // check if d.itemInstanceId is in destinyItemSocketsComponents
- if (d.itemInstanceId && d.itemInstanceId in destinyItemSocketsComponents) {
- const sockets: DestinyItemSocketState[] =
- destinyItemSocketsComponents[d.itemInstanceId || ""].sockets;
- var plugs = [
- sockets[6].plugHash,
- sockets[7].plugHash,
- sockets[8].plugHash,
- sockets[9].plugHash,
- ];
- r.statPlugHashes = plugs;
- var plm = plugs.map((k) => mods[k || ""]).filter((k) => k != null);
- for (let entry of plm) {
- for (let newStats of entry.investmentStats) {
- if (newStats.statTypeHash in investmentStats)
- investmentStats[newStats.statTypeHash] += newStats.value;
- }
- }
- } else {
- console.error(
- "Sockets data does not contain the correct item instance ID",
- d.itemInstanceId,
- profile.Response.itemComponents.sockets.data
- );
- }
- }
- r.mobility = investmentStats[2996146975];
- r.resilience = investmentStats[392767087];
- r.recovery = investmentStats[1943323491];
- r.discipline = investmentStats[1735777505];
- r.intellect = investmentStats[144602215];
- r.strength = investmentStats[4244567218];
-
- // Take a look if it really has the artifice perk
- if (r.perk == ArmorPerkOrSlot.SlotArtifice) {
- let statData = profile.Response.itemComponents.perks.data || {};
- let perks = (statData[d.itemInstanceId || ""] || {})["perks"] || [];
- const hasPerk = perks.filter((p) => p.perkHash == 229248542).length > 0;
- if (!hasPerk) r.perk = ArmorPerkOrSlot.None;
- }
-
- if (!r.isExotic && this.config_assumeEveryLegendaryIsArtifice)
- r.perk = ArmorPerkOrSlot.SlotArtifice;
-
- return r as IInventoryArmor;
- }) || [];
-
- r = r.filter((k) => !k["statPlugHashes"] || k["statPlugHashes"][0] != null);
-
- // Now add the stuff to the db..
- await this.db.inventoryArmor.clear();
- await this.db.inventoryArmor.bulkAdd(r);
- localStorage.setItem("LastArmorUpdate", Date.now().toString());
- localStorage.setItem("last-armor-db-name", this.db.inventoryArmor.db.name);
-
- return r;
+ }
+ }
}
- private getArmorPerk(v: DestinyInventoryItemDefinition): ArmorPerkOrSlot {
- if (
- (v.sockets?.socketEntries.filter((d) => d.reusablePlugSetHash == 1259) || []).length > 0
- )
- return ArmorPerkOrSlot.SlotArtifice;
-
- if (
- (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 4144354978) || [])
- .length > 0
- )
- return ArmorPerkOrSlot.SlotRootOfNightmares;
- if (
- (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 1728096240) || [])
- .length > 0
- )
- return ArmorPerkOrSlot.SlotKingsFall;
- if (
- (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 1679876242) || [])
- .length > 0
- )
- return ArmorPerkOrSlot.SlotLastWish;
- if (
- (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 3738398030) || [])
- .length > 0
- )
- return ArmorPerkOrSlot.SlotVaultOfGlass;
- if (
- (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 706611068) || [])
- .length > 0
- )
- return ArmorPerkOrSlot.SlotGardenOfSalvation;
- if (
- (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 4055462131) || [])
- .length > 0
- )
- return ArmorPerkOrSlot.SlotDeepStoneCrypt;
- if (
- (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 2447143568) || [])
- .length > 0
- )
- return ArmorPerkOrSlot.SlotVowOfTheDisciple;
-
- if (
- (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 1101259514) || [])
- .length > 0
- )
- return ArmorPerkOrSlot.PerkQueensFavor;
- if (
- (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 1180997867) || [])
- .length > 0
- )
- return ArmorPerkOrSlot.SlotNightmare;
- if (
- (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 2472875850) || [])
- .length > 0
- )
- return ArmorPerkOrSlot.PerkIronBanner;
- if (
- (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 2392155347) || [])
- .length > 0
- )
- return ArmorPerkOrSlot.PerkUniformedOfficer;
- if (
- (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 400659041) || [])
- .length > 0
- )
- return ArmorPerkOrSlot.PerkPlunderersTrappings;
- if (
- (v.sockets?.socketEntries.filter((d) => d.singleInitialItemHash == 3525583702) || [])
- .length > 0
- )
- return ArmorPerkOrSlot.SeraphSensorArray;
-
- return ArmorPerkOrSlot.None;
- }
-
- async updateManifest(force = false) {
- if (environment.offlineMode) {
- console.info("BungieApiService", "updateManifest", "offline mode, skipping");
- return;
+ if (destinyManifest == null) destinyManifest = await getDestinyManifest((d) => this.$http(d));
+ const version = destinyManifest.Response.version;
+
+ const manifestTables = await getDestinyManifestSlice((d) => this.$httpWithoutKey(d), {
+ destinyManifest: destinyManifest.Response,
+ tableNames: ["DestinyInventoryItemDefinition"],
+ language: "en",
+ });
+
+ console.log(
+ "manifestTables.DestinyInventoryItemDefinition",
+ manifestTables.DestinyInventoryItemDefinition
+ );
+
+ let entries = Object.entries(manifestTables.DestinyInventoryItemDefinition)
+ .filter(([k, v]) => {
+ if (v.itemType == 19) return true; // mods
+ if (v.itemType == 2) return true; // armor
+ if (v.inventory?.bucketTypeHash == 3448274439) return true; // helmets, required for festival masks
+ if (v.inventory?.bucketTypeHash == 3551918588) return true; // gauntlets
+ if (v.inventory?.bucketTypeHash == 14239492) return true; // chest
+ if (v.inventory?.bucketTypeHash == 20886954) return true; // leg
+ return false;
+ })
+ .map(([k, v]) => {
+ let slot = ArmorSlot.ArmorSlotNone;
+ if ((v.itemCategoryHashes?.indexOf(45) || -1) > -1) slot = ArmorSlot.ArmorSlotHelmet;
+ if ((v.itemCategoryHashes?.indexOf(46) || -1) > -1) slot = ArmorSlot.ArmorSlotGauntlet;
+ if ((v.itemCategoryHashes?.indexOf(47) || -1) > -1) slot = ArmorSlot.ArmorSlotChest;
+ if ((v.itemCategoryHashes?.indexOf(48) || -1) > -1) slot = ArmorSlot.ArmorSlotLegs;
+ if ((v.itemCategoryHashes?.indexOf(49) || -1) > -1) slot = ArmorSlot.ArmorSlotClass;
+
+ const isArmor2 =
+ (v.sockets?.socketEntries.filter((d) => {
+ return (
+ d.socketTypeHash == 2512726577 || // general
+ d.socketTypeHash == 1108765570 || // arms
+ d.socketTypeHash == 959256494 || // chest
+ d.socketTypeHash == 2512726577 || // class
+ d.socketTypeHash == 3219375296 || // legs
+ d.socketTypeHash == 968742181
+ ); // head
+ }).length || []) > 0;
+
+ const isExotic = v.inventory?.tierTypeName == "Exotic" ? 1 : 0;
+ let exoticPerkHash = null;
+ if (isExotic) {
+ const perks =
+ v.sockets?.socketEntries
+ .filter((s) => s.socketTypeHash == 965959289)
+ .map((d) => d.singleInitialItemHash) || [];
+ exoticPerkHash = perks[0];
}
- var destinyManifest = null;
- if (
- !force &&
- localStorage.getItem("LastManifestUpdate") &&
- localStorage.getItem("last-manifest-revision")
- ) {
- if (localStorage.getItem("last-manifest-revision") == environment.revision) {
- if (
- Date.now() -
- Number.parseInt(localStorage.getItem("LastManifestUpdate") || "0") >
- 1000 * 3600 * 0.25
- ) {
- destinyManifest = await getDestinyManifest((d) => this.$http(d));
- const version = destinyManifest.Response.version;
- if (localStorage.getItem("last-manifest-version") == version) {
- console.debug(
- "bungieApiService - updateManifest",
- "Abort updateManifest due to fitting ManifestVersion"
- );
- return;
- }
- }
- if (localStorage.getItem("last-manifest-db-name") == this.db.manifestArmor.db.name)
- if (
- Date.now() -
- Number.parseInt(localStorage.getItem("LastManifestUpdate") || "0") <
- 1000 * 3600 * 24
- ) {
- console.debug(
- "bungieApiService - updateManifest",
- "Abort updateManifest due to fitting Date"
- );
- return;
- }
- }
- }
-
- if (destinyManifest == null)
- destinyManifest = await getDestinyManifest((d) => this.$http(d));
- const version = destinyManifest.Response.version;
-
- const manifestTables = await getDestinyManifestSlice((d) => this.$httpWithoutKey(d), {
- destinyManifest: destinyManifest.Response,
- tableNames: ["DestinyInventoryItemDefinition"],
- language: "en",
- });
-
- console.log(
- "manifestTables.DestinyInventoryItemDefinition",
- manifestTables.DestinyInventoryItemDefinition
- );
-
- let entries = Object.entries(manifestTables.DestinyInventoryItemDefinition)
- .filter(([k, v]) => {
- if (v.itemType == 19) return true; // mods
- if (v.itemType == 2) return true; // armor
- if (v.inventory?.bucketTypeHash == 3448274439) return true; // helmets, required for festival masks
- if (v.inventory?.bucketTypeHash == 3551918588) return true; // gauntlets
- if (v.inventory?.bucketTypeHash == 14239492) return true; // chest
- if (v.inventory?.bucketTypeHash == 20886954) return true; // leg
- return false;
- })
- .map(([k, v]) => {
- let slot = ArmorSlot.ArmorSlotNone;
- if ((v.itemCategoryHashes?.indexOf(45) || -1) > -1)
- slot = ArmorSlot.ArmorSlotHelmet;
- if ((v.itemCategoryHashes?.indexOf(46) || -1) > -1)
- slot = ArmorSlot.ArmorSlotGauntlet;
- if ((v.itemCategoryHashes?.indexOf(47) || -1) > -1) slot = ArmorSlot.ArmorSlotChest;
- if ((v.itemCategoryHashes?.indexOf(48) || -1) > -1) slot = ArmorSlot.ArmorSlotLegs;
- if ((v.itemCategoryHashes?.indexOf(49) || -1) > -1) slot = ArmorSlot.ArmorSlotClass;
-
- const isArmor2 =
- (v.sockets?.socketEntries.filter((d) => {
- return (
- d.socketTypeHash == 2512726577 || // general
- d.socketTypeHash == 1108765570 || // arms
- d.socketTypeHash == 959256494 || // chest
- d.socketTypeHash == 2512726577 || // class
- d.socketTypeHash == 3219375296 || // legs
- d.socketTypeHash == 968742181
- ); // head
- }).length || []) > 0;
-
- const isExotic = v.inventory?.tierTypeName == "Exotic" ? 1 : 0;
- let exoticPerkHash = null;
- if (isExotic) {
- const perks =
- v.sockets?.socketEntries
- .filter((s) => s.socketTypeHash == 965959289)
- .map((d) => d.singleInitialItemHash) || [];
- exoticPerkHash = perks[0];
- }
-
- var sunsetPowerCaps = [
- 1862490585, // 1260
- 1862490584, // 1060
- 1862490584, // 1060
- 1862490583, // 1060
- 2471437758, // 1010
- ];
- // if every entry is sunset, so is this item.
- var isSunset =
- v.quality?.versions.filter((k) => sunsetPowerCaps.includes(k.powerCapHash))
- .length == v.quality?.versions.length;
-
- return {
- hash: v.hash,
- icon: v.displayProperties.icon,
- watermarkIcon: (v.quality?.displayVersionWatermarkIcons || [null])[0],
- name: v.displayProperties.name,
- description: v.displayProperties.description,
- clazz: v.classType,
- armor2: isArmor2,
- slot: slot,
- isExotic: isExotic,
- isSunset: isSunset,
- rarity: v.inventory?.tierType,
- exoticPerkHash: exoticPerkHash,
- itemType: v.itemType,
- itemSubType: v.itemSubType,
- investmentStats: v.investmentStats,
- perk: this.getArmorPerk(v),
- } as IManifestArmor;
- });
-
- await this.db.manifestArmor.clear();
- await this.db.manifestArmor.bulkPut(entries);
- localStorage.setItem("LastManifestUpdate", Date.now().toString());
- localStorage.setItem("last-manifest-db-name", this.db.manifestArmor.db.name);
- localStorage.setItem("last-manifest-revision", environment.revision);
- localStorage.setItem("last-manifest-version", version);
-
- return manifestTables;
- }
+ var sunsetPowerCaps = [
+ 1862490585, // 1260
+ 1862490584, // 1060
+ 1862490584, // 1060
+ 1862490583, // 1060
+ 2471437758, // 1010
+ ];
+ // if every entry is sunset, so is this item.
+ var isSunset =
+ v.quality?.versions.filter((k) => sunsetPowerCaps.includes(k.powerCapHash)).length ==
+ v.quality?.versions.length;
+
+ return {
+ hash: v.hash,
+ icon: v.displayProperties.icon,
+ watermarkIcon: (v.quality?.displayVersionWatermarkIcons || [null])[0],
+ name: v.displayProperties.name,
+ description: v.displayProperties.description,
+ clazz: v.classType,
+ armor2: isArmor2,
+ slot: slot,
+ isExotic: isExotic,
+ isSunset: isSunset,
+ rarity: v.inventory?.tierType,
+ exoticPerkHash: exoticPerkHash,
+ itemType: v.itemType,
+ itemSubType: v.itemSubType,
+ investmentStats: v.investmentStats,
+ perk: this.getArmorPerk(v),
+ } as IManifestArmor;
+ });
+
+ await this.db.manifestArmor.clear();
+ await this.db.manifestArmor.bulkPut(entries);
+ localStorage.setItem("LastManifestUpdate", Date.now().toString());
+ localStorage.setItem("last-manifest-db-name", this.db.manifestArmor.db.name);
+ localStorage.setItem("last-manifest-revision", environment.revision);
+ localStorage.setItem("last-manifest-version", version);
+
+ return manifestTables;
+ }
}