mirror of
https://github.com/Kvan7/Exiled-Exchange-2.git
synced 2026-09-25 22:25:58 +00:00
update editor and extraction value
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 9.7 KiB |
@@ -1,36 +1,12 @@
|
||||
import { init, STAT_BY_REF } from "@/assets/data";
|
||||
import { init } from "@/assets/data";
|
||||
import { ItemCategory } from "@/parser";
|
||||
import { ParsedModifier } from "@/parser/advanced-mod-desc";
|
||||
import { ModifierType, StatCalculated } from "@/parser/modifiers";
|
||||
import { ModifierType } from "@/parser/modifiers";
|
||||
import { __testExports } from "@/parser/Parser";
|
||||
import { createTestItem, makeCalcStat } from "@specs/helper";
|
||||
import { setupTests } from "@specs/vitest.setup";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
function makeCalcStat(ref: string, value: number): StatCalculated {
|
||||
const stat = STAT_BY_REF(ref)!;
|
||||
|
||||
return {
|
||||
stat,
|
||||
type: ModifierType.Augment,
|
||||
sources: [
|
||||
{
|
||||
contributes: {
|
||||
value,
|
||||
},
|
||||
modifier: {
|
||||
info: {
|
||||
type: ModifierType.Augment,
|
||||
tags: [],
|
||||
},
|
||||
stats: [],
|
||||
},
|
||||
stat: {
|
||||
stat,
|
||||
},
|
||||
},
|
||||
],
|
||||
} as unknown as StatCalculated;
|
||||
}
|
||||
|
||||
describe("determineAugments", () => {
|
||||
beforeEach(async () => {
|
||||
setupTests();
|
||||
@@ -46,7 +22,7 @@ describe("determineAugments", () => {
|
||||
type: ModifierType.Augment,
|
||||
},
|
||||
} as unknown as ParsedModifier,
|
||||
makeCalcStat("#% increased Physical Damage", 36),
|
||||
[makeCalcStat("#% increased Physical Damage", 36, ModifierType.Augment)],
|
||||
);
|
||||
|
||||
expect(result.map((augment) => augment.refName)).toEqual(expectedRef);
|
||||
@@ -61,7 +37,7 @@ describe("determineAugments", () => {
|
||||
type: ModifierType.Augment,
|
||||
},
|
||||
} as unknown as ParsedModifier,
|
||||
makeCalcStat("#% to Lightning Resistance", 10),
|
||||
[makeCalcStat("#% to Lightning Resistance", 10, ModifierType.Augment)],
|
||||
);
|
||||
|
||||
expect(result.map((augment) => augment.refName)).toEqual(expectedRef);
|
||||
@@ -98,3 +74,224 @@ describe("BFS", () => {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("parseAugmentSockets", () => {
|
||||
beforeEach(async () => {
|
||||
setupTests();
|
||||
await init("en");
|
||||
});
|
||||
|
||||
it("should skip for items without sockets", () => {
|
||||
const item = createTestItem();
|
||||
|
||||
const result = __testExports.parseAugmentSockets([], item);
|
||||
expect(result).toEqual("PARSER_SKIPPED");
|
||||
});
|
||||
|
||||
it.each([
|
||||
[["Sockets: S"], 1],
|
||||
[["Sockets: S S"], 2],
|
||||
[["Sockets: S S S"], 3],
|
||||
[["Sockets: S S S S"], 4],
|
||||
[["Sockets: S S S S S"], 5],
|
||||
])(
|
||||
"%#. should add correct augment socket count(%o -> %o)",
|
||||
(lines, expected) => {
|
||||
const item = createTestItem();
|
||||
item.category = ItemCategory.Gloves;
|
||||
|
||||
const result = __testExports.parseAugmentSockets(lines, item);
|
||||
expect(result).toEqual("SECTION_PARSED");
|
||||
expect(item.augmentSockets?.normal).toEqual(1);
|
||||
expect(item.augmentSockets?.empty).toEqual(0);
|
||||
expect(item.augmentSockets?.current).toEqual(expected);
|
||||
expect(
|
||||
item.augmentSockets?.augments.every((v) => v === null),
|
||||
).toBeTruthy();
|
||||
expect(item.augmentSockets?.augments.length).toEqual(expected);
|
||||
},
|
||||
);
|
||||
|
||||
it("should add sockets on non-socket section", () => {
|
||||
const item = createTestItem();
|
||||
item.category = ItemCategory.Gloves;
|
||||
|
||||
const result = __testExports.parseAugmentSockets(["text"], item);
|
||||
expect(result).toEqual("SECTION_SKIPPED");
|
||||
expect(item.augmentSockets?.normal).toEqual(1);
|
||||
expect(item.augmentSockets?.empty).toEqual(1);
|
||||
expect(item.augmentSockets?.current).toEqual(0);
|
||||
expect(item.augmentSockets?.augments.every((v) => v === null)).toBeTruthy();
|
||||
expect(item.augmentSockets?.augments.length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("applyAugmentSockets", () => {
|
||||
beforeEach(async () => {
|
||||
setupTests();
|
||||
await init("en");
|
||||
});
|
||||
|
||||
it("should do nothing on item without sockets", () => {
|
||||
const item = createTestItem();
|
||||
|
||||
__testExports.applyAugmentSockets(item);
|
||||
expect(item).toEqual(createTestItem());
|
||||
});
|
||||
|
||||
it("should set empty on item with no augment stats", () => {
|
||||
const item = createTestItem();
|
||||
item.category = ItemCategory.Gloves;
|
||||
item.augmentSockets = {
|
||||
empty: 0,
|
||||
current: 2,
|
||||
normal: 2,
|
||||
augments: [null, null],
|
||||
};
|
||||
|
||||
__testExports.applyAugmentSockets(item);
|
||||
expect(item.augmentSockets.empty).toEqual(2);
|
||||
expect(item.augmentSockets.current).toEqual(2);
|
||||
expect(item.augmentSockets.normal).toEqual(2);
|
||||
expect(item.augmentSockets.augments.every((v) => v === null)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should apply correct augment to item", () => {
|
||||
const item = createTestItem();
|
||||
item.category = ItemCategory.Bow;
|
||||
item.augmentSockets = {
|
||||
empty: 0,
|
||||
current: 2,
|
||||
normal: 2,
|
||||
augments: [null, null],
|
||||
};
|
||||
|
||||
const stat = makeCalcStat(
|
||||
"#% increased Physical Damage",
|
||||
18,
|
||||
ModifierType.Augment,
|
||||
);
|
||||
item.statsByType = [stat];
|
||||
item.newMods = [
|
||||
{
|
||||
info: {
|
||||
type: ModifierType.Augment,
|
||||
tags: [],
|
||||
},
|
||||
stats: [stat.sources[0].stat],
|
||||
},
|
||||
];
|
||||
|
||||
__testExports.applyAugmentSockets(item);
|
||||
expect(item.augmentSockets.empty).toEqual(1);
|
||||
expect(item.augmentSockets.current).toEqual(2);
|
||||
expect(item.augmentSockets.normal).toEqual(2);
|
||||
expect(item.augmentSockets.augments[0]?.refName).toEqual(
|
||||
"Greater Iron Rune",
|
||||
);
|
||||
expect(item.augmentSockets.augments[1]).toBeNull();
|
||||
});
|
||||
|
||||
it("should apply multiple different augments to item", () => {
|
||||
const item = createTestItem();
|
||||
item.category = ItemCategory.Bow;
|
||||
item.augmentSockets = {
|
||||
empty: 0,
|
||||
current: 2,
|
||||
normal: 2,
|
||||
augments: [null, null],
|
||||
};
|
||||
|
||||
const stat1 = makeCalcStat(
|
||||
"#% increased Physical Damage",
|
||||
18,
|
||||
ModifierType.Augment,
|
||||
);
|
||||
const stat2 = makeCalcStat(
|
||||
"Bow Attacks fire # additional Arrows",
|
||||
1,
|
||||
ModifierType.Augment,
|
||||
);
|
||||
item.statsByType = [stat1, stat2];
|
||||
item.newMods = [
|
||||
{
|
||||
info: {
|
||||
type: ModifierType.Augment,
|
||||
tags: [],
|
||||
},
|
||||
stats: [stat1.sources[0].stat, stat2.sources[0].stat],
|
||||
},
|
||||
];
|
||||
|
||||
__testExports.applyAugmentSockets(item);
|
||||
expect(item.augmentSockets.empty).toEqual(0);
|
||||
expect(item.augmentSockets.current).toEqual(2);
|
||||
expect(item.augmentSockets.normal).toEqual(2);
|
||||
expect(
|
||||
item.augmentSockets.augments.some(
|
||||
(i) => i?.refName === "Greater Iron Rune",
|
||||
),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
item.augmentSockets.augments.some(
|
||||
(i) => i?.refName === "Countess Seske's Rune of Archery",
|
||||
),
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should join multiline augments", () => {
|
||||
const item = createTestItem();
|
||||
item.category = ItemCategory.Staff;
|
||||
item.augmentSockets = {
|
||||
empty: 0,
|
||||
current: 2,
|
||||
normal: 2,
|
||||
augments: [null, null],
|
||||
};
|
||||
|
||||
const stat1 = makeCalcStat(
|
||||
"Meta Skills gain #% increased Energy",
|
||||
40,
|
||||
ModifierType.Augment,
|
||||
);
|
||||
const stat2 = makeCalcStat(
|
||||
"#% increased Spirit",
|
||||
-25,
|
||||
ModifierType.Augment,
|
||||
);
|
||||
const stat3 = makeCalcStat(
|
||||
"#% increased Mana Regeneration Rate",
|
||||
20,
|
||||
ModifierType.Augment,
|
||||
);
|
||||
item.statsByType = [stat1, stat2, stat3];
|
||||
item.newMods = [
|
||||
{
|
||||
info: {
|
||||
type: ModifierType.Augment,
|
||||
tags: [],
|
||||
},
|
||||
stats: [
|
||||
stat1.sources[0].stat,
|
||||
stat2.sources[0].stat,
|
||||
stat3.sources[0].stat,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
__testExports.applyAugmentSockets(item);
|
||||
expect(item.augmentSockets.empty).toEqual(0);
|
||||
expect(item.augmentSockets.current).toEqual(2);
|
||||
expect(item.augmentSockets.normal).toEqual(2);
|
||||
expect(
|
||||
item.augmentSockets.augments.some(
|
||||
(i) => i?.refName === "Idol of the Martyr",
|
||||
),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
item.augmentSockets.augments.some(
|
||||
(i) => i?.refName === "Lesser Inspiration Rune",
|
||||
),
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1333,3 +1333,81 @@ CharmQuality.prefixCount = 1;
|
||||
CharmQuality.suffixCount = 1;
|
||||
|
||||
// #endregion CharmQuality
|
||||
|
||||
// #region BowThreeAugments
|
||||
export const BowThreeAugments = new TestItem(`Item Class: Bows
|
||||
Rarity: Rare
|
||||
Oblivion Branch
|
||||
Obliterator Bow
|
||||
--------
|
||||
Quality: +20% (augmented)
|
||||
Physical Damage: 288-534 (augmented)
|
||||
Fire Damage: 47-92 (fire)
|
||||
Critical Hit Chance: 8.45% (augmented)
|
||||
Attacks per Second: 1.27 (augmented)
|
||||
--------
|
||||
Requires: Level 78, 163 Dex
|
||||
--------
|
||||
Sockets: S S S
|
||||
--------
|
||||
Item Level: 80
|
||||
--------
|
||||
38% increased Physical Damage (rune)
|
||||
Bow Attacks fire an additional Arrow (rune)
|
||||
--------
|
||||
{ Implicit Modifier }
|
||||
50% reduced Projectile Range
|
||||
--------
|
||||
{ Prefix Modifier "Cruel" (Tier: 3) — Damage, Physical, Attack }
|
||||
151(135-154)% increased Physical Damage
|
||||
{ Desecrated Prefix Modifier "Razor-sharp" (Tier: 3) — Damage, Physical, Attack }
|
||||
Adds 21(16-24) to 39(28-42) Physical Damage
|
||||
{ Prefix Modifier "Blasting" (Tier: 3) — Damage, Elemental, Fire, Attack }
|
||||
Adds 47(47-59) to 92(74-97) Fire Damage
|
||||
{ Suffix Modifier "of Renown" (Tier: 2) — Attack, Speed }
|
||||
15(14-16)% increased Attack Speed
|
||||
{ Suffix Modifier "of the Vampire" (Tier: 1) — Life, Physical, Attack }
|
||||
Leeches 9.6(9-9.9)% of Physical Damage as Life
|
||||
{ Crafted Suffix Modifier "of Calamity" (Tier: 3) — Attack, Critical }
|
||||
+3.45(3.11-3.8)% to Critical Hit Chance
|
||||
--------
|
||||
Corrupted
|
||||
`);
|
||||
|
||||
BowThreeAugments.category = ItemCategory.Bow;
|
||||
BowThreeAugments.rarity = ItemRarity.Rare;
|
||||
BowThreeAugments.itemLevel = 80;
|
||||
BowThreeAugments.quality = 20;
|
||||
BowThreeAugments.weaponPHYSICAL = 411;
|
||||
BowThreeAugments.weaponFIRE = 69.5;
|
||||
BowThreeAugments.weaponELEMENTAL = BowThreeAugments.weaponFIRE;
|
||||
BowThreeAugments.weaponCRIT = 8.45;
|
||||
BowThreeAugments.weaponAS = 1.27;
|
||||
BowThreeAugments.requires = {
|
||||
level: 78,
|
||||
str: 0,
|
||||
dex: 163,
|
||||
int: 0,
|
||||
};
|
||||
|
||||
BowThreeAugments.info.refName = "Obliterator Bow";
|
||||
BowThreeAugments.sectionCount = 9;
|
||||
BowThreeAugments.prefixCount = 3;
|
||||
BowThreeAugments.suffixCount = 3;
|
||||
BowThreeAugments.implicitCount = 1;
|
||||
BowThreeAugments.augmentSockets = {
|
||||
empty: 0,
|
||||
current: 3,
|
||||
normal: 2,
|
||||
augments: [
|
||||
createEditorItem("Perfect Iron Rune", "20% increased Physical Damage", 20),
|
||||
createEditorItem("Greater Iron Rune", "18% increased Physical Damage", 18),
|
||||
createEditorItem(
|
||||
"Countess Seske's Rune of Archery",
|
||||
"Bow Attacks fire an additional Arrow",
|
||||
1,
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
// #endregion BowThreeAugments
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { STAT_BY_REF } from "@/assets/data";
|
||||
import { ItemCategory } from "@/parser";
|
||||
import { ParsedModifier } from "@/parser/advanced-mod-desc";
|
||||
import { ModifierType, StatCalculated } from "@/parser/modifiers";
|
||||
import { createVirtualItem, ParsedItem } from "@/parser/ParsedItem";
|
||||
import { ParsedStat } from "@/parser/stat-translations";
|
||||
import { FilterTag, StatFilter } from "@/web/price-check/filters/interfaces";
|
||||
|
||||
export function createTestStatFilter(): StatFilter {
|
||||
@@ -43,9 +48,71 @@ export function createTestItem(): ParsedItem {
|
||||
info: {
|
||||
refName: "test",
|
||||
namespace: "ITEM",
|
||||
craftable: {
|
||||
category: ItemCategory.Unknown,
|
||||
},
|
||||
name: "",
|
||||
icon: "",
|
||||
tags: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createParsedStat(
|
||||
statRef: string,
|
||||
value: number | [number, number],
|
||||
): ParsedStat {
|
||||
const stat = STAT_BY_REF(statRef)!;
|
||||
|
||||
return {
|
||||
stat,
|
||||
translation: stat.matchers[0],
|
||||
roll: {
|
||||
unscalable: false,
|
||||
dp: stat.dp || false,
|
||||
value: Array.isArray(value) ? (value[0] + value[1]) / 2 : value,
|
||||
min: Array.isArray(value) ? value[0] : value,
|
||||
max: Array.isArray(value) ? value[1] : value,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createParsedModifier(
|
||||
statRef: string,
|
||||
value: number | [number, number],
|
||||
type: ModifierType = ModifierType.Explicit,
|
||||
): ParsedModifier {
|
||||
return {
|
||||
info: {
|
||||
type,
|
||||
tags: [],
|
||||
},
|
||||
stats: [createParsedStat(statRef, value)],
|
||||
};
|
||||
}
|
||||
|
||||
export function makeCalcStat(
|
||||
ref: string,
|
||||
value: number,
|
||||
type: ModifierType = ModifierType.Explicit,
|
||||
): StatCalculated {
|
||||
const parsedStat = createParsedStat(ref, value);
|
||||
|
||||
return {
|
||||
stat: parsedStat.stat,
|
||||
type,
|
||||
sources: [
|
||||
{
|
||||
contributes: parsedStat.roll,
|
||||
modifier: {
|
||||
info: {
|
||||
type,
|
||||
tags: [],
|
||||
},
|
||||
stats: [parsedStat],
|
||||
},
|
||||
stat: parsedStat,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import { calcPropPercentile, QUALITY_STATS } from "./calc-q20";
|
||||
import { AppConfig } from "@/web/Config";
|
||||
import { buildEditorItems, getSavedAugments } from "./augment-builder";
|
||||
import { useAugment } from "@/web/price-check/item-editor/augment";
|
||||
import { combinations } from "./utils";
|
||||
|
||||
type SectionParseResult =
|
||||
| "SECTION_PARSED"
|
||||
@@ -855,14 +856,16 @@ function parseAugmentSockets(section: string[], item: ParsedItem) {
|
||||
empty: 0,
|
||||
current,
|
||||
normal: categoryMax,
|
||||
augments: Array(categoryMax).fill(null),
|
||||
augments: Array(current).fill(null),
|
||||
};
|
||||
} else {
|
||||
item.augmentSockets = {
|
||||
empty: 0,
|
||||
current,
|
||||
normal: categoryMax,
|
||||
augments: Array(categoryMax).fill(null),
|
||||
augments: Array(categoryMax > current ? categoryMax : current).fill(
|
||||
null,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1322,23 +1325,44 @@ function applyAugmentSockets(item: ParsedItem) {
|
||||
const augmentMods = item.newMods.filter(
|
||||
(mod) => mod.info.type === ModifierType.Augment,
|
||||
);
|
||||
console.log(augmentMods);
|
||||
const augmentStats = item.statsByType.filter(
|
||||
(calc) => calc.type === ModifierType.Augment,
|
||||
);
|
||||
const augments: Array<EditorItem | null> = augmentMods
|
||||
.map((mod) => {
|
||||
const stat = augmentStats.find(
|
||||
(stat) => stat.sources[0].stat === mod.stats[0],
|
||||
);
|
||||
if (!stat) return [];
|
||||
console.log(augmentStats);
|
||||
|
||||
let statCombinations = combinations(augmentStats)
|
||||
.filter((f) => f.length)
|
||||
.toSorted((a, b) => b.length - a.length);
|
||||
const augs: BaseType[][] = [];
|
||||
for (let i = 0; i < statCombinations.length; ) {
|
||||
const foundAugs = determineAugments(augmentMods[0], statCombinations[i]);
|
||||
if (foundAugs.length) {
|
||||
augs.push(foundAugs);
|
||||
for (const combStat of statCombinations[i]) {
|
||||
// drop any that we use here
|
||||
statCombinations = statCombinations.filter(
|
||||
(f) => f.find((s) => s === combStat) === undefined,
|
||||
);
|
||||
}
|
||||
i = 0;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
const augments: Array<EditorItem | null> = augs
|
||||
.map((augGroup) => {
|
||||
return buildEditorItems(
|
||||
determineAugments(mod, stat),
|
||||
augGroup,
|
||||
item.category ?? ItemCategory.Unknown,
|
||||
true,
|
||||
);
|
||||
})
|
||||
.flat();
|
||||
|
||||
console.log(augments);
|
||||
|
||||
if (
|
||||
augments.length <
|
||||
Math.max(item.augmentSockets.current, item.augmentSockets.normal)
|
||||
@@ -2133,21 +2157,58 @@ function modifiedBfs(
|
||||
|
||||
function determineAugments(
|
||||
mod: ParsedModifier,
|
||||
statCalc: StatCalculated,
|
||||
statCalcs: StatCalculated[],
|
||||
): BaseType[] {
|
||||
if (mod.info.type !== ModifierType.Augment) return [];
|
||||
const augmentAppliedValue = statCalc.sources[0].contributes?.value;
|
||||
const augmentAppliedValue = statCalcs
|
||||
.map((s) => s.sources[0].contributes?.value)
|
||||
.filter((v) => v !== undefined);
|
||||
|
||||
const augmentTradeId = statCalc.stat.trade.ids[ModifierType.Augment][0];
|
||||
const possibleAugments = AUGMENT_DATA_BY_TRADE_ID[augmentTradeId];
|
||||
// const augmentTradeId = statCalcs[0].stat.trade.ids[ModifierType.Augment][0];
|
||||
// const possibleAugments = AUGMENT_DATA_BY_TRADE_ID[augmentTradeId];
|
||||
|
||||
const allTradeIds = statCalcs.map(
|
||||
(c) => c.stat.trade.ids[ModifierType.Augment][0],
|
||||
);
|
||||
const allPossibleAugments = allTradeIds.map(
|
||||
(id) => AUGMENT_DATA_BY_TRADE_ID[id],
|
||||
);
|
||||
const augmentRefSets = allPossibleAugments.map(
|
||||
(augGroup) => new Set(augGroup.map((a) => a.refName)),
|
||||
);
|
||||
console.log(augmentRefSets);
|
||||
|
||||
const morePossibleAugments = augmentRefSets.reduce((acc, set) => {
|
||||
return set.intersection(acc);
|
||||
}, augmentRefSets[0]);
|
||||
console.log("morePossibleAugments", morePossibleAugments);
|
||||
|
||||
// intersect all possible augments
|
||||
const possibleAugments = allPossibleAugments
|
||||
.map((augGroup) =>
|
||||
augGroup.filter((aug) => morePossibleAugments.has(aug.refName)),
|
||||
)
|
||||
.find((f) => f.length);
|
||||
|
||||
console.log(possibleAugments);
|
||||
if (!possibleAugments) return [];
|
||||
|
||||
// something like "Raven-Touched"
|
||||
if (!augmentAppliedValue) {
|
||||
if (!augmentAppliedValue || !augmentAppliedValue.length) {
|
||||
const singleAugment = possibleAugments[0];
|
||||
return [ITEM_BY_REF("ITEM", singleAugment.refName)![0]];
|
||||
}
|
||||
|
||||
if (augmentAppliedValue.length > 1) {
|
||||
const likelyAugment = possibleAugments.find(
|
||||
(aug) =>
|
||||
new Set(aug.values).intersection(new Set(augmentAppliedValue)).size ===
|
||||
augmentAppliedValue.length,
|
||||
);
|
||||
if (!likelyAugment) return [];
|
||||
return [ITEM_BY_REF("ITEM", likelyAugment.refName)![0]];
|
||||
}
|
||||
|
||||
// // Calculate how many of this augment are in the item
|
||||
const availableAugmentValues = possibleAugments
|
||||
.map((augment) => {
|
||||
@@ -2166,7 +2227,7 @@ function determineAugments(
|
||||
|
||||
// BFS to find all combinations with minimum count
|
||||
const likelyValues =
|
||||
modifiedBfs(augmentAppliedValue, [], availableAugmentValues) ?? [];
|
||||
modifiedBfs(augmentAppliedValue[0], [], availableAugmentValues) ?? [];
|
||||
|
||||
return likelyValues.map((v) => {
|
||||
const augment = possibleAugments.find((aug) =>
|
||||
@@ -2206,4 +2267,6 @@ export const __testExports = {
|
||||
parseTrials,
|
||||
determineAugments,
|
||||
modifiedBfs,
|
||||
parseAugmentSockets,
|
||||
applyAugmentSockets,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export function combinations<T>(arr: T[]): T[][] {
|
||||
return arr.reduce<T[][]>(
|
||||
(acc, curr) => [...acc, ...acc.map((a) => [...a, curr])],
|
||||
[[]],
|
||||
);
|
||||
}
|
||||
@@ -356,6 +356,78 @@ export const usePoeninja = createGlobalState(() => {
|
||||
return currency;
|
||||
}
|
||||
|
||||
function toDivine(value: CurrencyValue): CurrencyValue {
|
||||
if (value.currency === "div") return value;
|
||||
if (xchgRate.value) {
|
||||
if (xchgRateCurrency.value !== value.currency) {
|
||||
throw new Error(
|
||||
`Cannot convert ${value.currency} to div, current core currency is ${xchgRateCurrency.value}`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
min: value.min / xchgRate.value,
|
||||
max: value.max / xchgRate.value,
|
||||
currency: "div",
|
||||
};
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function addPrice(
|
||||
addend1: CurrencyValue,
|
||||
addend2: CurrencyValue,
|
||||
): CurrencyValue {
|
||||
const addend1Div = toDivine(addend1);
|
||||
const addend2Div = toDivine(addend2);
|
||||
|
||||
return autoCurrency([
|
||||
addend1Div.min + addend2Div.min,
|
||||
addend1Div.max + addend2Div.max,
|
||||
]);
|
||||
}
|
||||
|
||||
function subtractPrice(
|
||||
minuend: CurrencyValue,
|
||||
subtrahend: CurrencyValue,
|
||||
): CurrencyValue {
|
||||
const minuendDiv = toDivine(minuend);
|
||||
const subtrahendDiv = toDivine(subtrahend);
|
||||
|
||||
return autoCurrency([
|
||||
minuendDiv.min - subtrahendDiv.max,
|
||||
minuendDiv.max - subtrahendDiv.min,
|
||||
]);
|
||||
}
|
||||
|
||||
function comparePrice(
|
||||
left: CurrencyValue,
|
||||
op: ">" | ">=" | "==" | "===" | "<=" | "<",
|
||||
right: CurrencyValue,
|
||||
): boolean {
|
||||
const leftDiv = toDivine(left);
|
||||
const rightDiv = toDivine(right);
|
||||
|
||||
switch (op) {
|
||||
case ">":
|
||||
return leftDiv.min > rightDiv.max;
|
||||
case ">=":
|
||||
return leftDiv.min >= rightDiv.max;
|
||||
case "==":
|
||||
return leftDiv.min === rightDiv.max && leftDiv.max === rightDiv.min;
|
||||
case "===":
|
||||
return (
|
||||
left.currency === right.currency &&
|
||||
leftDiv.min === rightDiv.max &&
|
||||
leftDiv.max === rightDiv.min
|
||||
);
|
||||
case "<=":
|
||||
return leftDiv.max <= rightDiv.min;
|
||||
case "<":
|
||||
return leftDiv.max < rightDiv.min;
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
load();
|
||||
}, RETRY_INTERVAL_MS);
|
||||
@@ -384,6 +456,10 @@ export const usePoeninja = createGlobalState(() => {
|
||||
initialLoading: () => isLoading.value && !PRICES_DB.length,
|
||||
availableCoreCurrencies: readonly(availableCoreCurrencies),
|
||||
ITEM_DROP,
|
||||
// math
|
||||
addPrice,
|
||||
subtractPrice,
|
||||
comparePrice,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="extractionProfits"
|
||||
class="p-2 border-2 rounded mt-2 flex items-center gap-1"
|
||||
:class="{
|
||||
'border-red-500 border': extractionProfits,
|
||||
'border-gray-600 border-dashed': !extractionProfits,
|
||||
}"
|
||||
>
|
||||
<div class="text-2xl">Σ</div>
|
||||
<item-quick-price
|
||||
:price="augmentPrice"
|
||||
:item-img="'/images/augments/rune.png'"
|
||||
:currency-text="false"
|
||||
:show-arrow="false"
|
||||
/>
|
||||
<i class="fa-solid fa-minus mr-2" />
|
||||
<item-quick-price
|
||||
:price="extractionOrb"
|
||||
:item-img="'/images/extractor.png'"
|
||||
:currency-text="false"
|
||||
:show-arrow="false"
|
||||
/>
|
||||
<i class="fa-solid fa-equals mr-2" />
|
||||
<item-quick-price
|
||||
:price="profitValue"
|
||||
:currency-text="false"
|
||||
:show-img="false"
|
||||
:show-arrow="false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { ParsedItem } from "@/parser";
|
||||
import { computed, defineComponent, PropType } from "vue";
|
||||
import { PricingResult } from "./pathofexile-trade";
|
||||
import { useI18nNs } from "@/web/i18n";
|
||||
import { CurrencyValue, usePoeninja } from "@/web/background/Prices";
|
||||
import ItemQuickPrice from "@/web/ui/ItemQuickPrice.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
ItemQuickPrice,
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
type: Object as PropType<ParsedItem>,
|
||||
required: true,
|
||||
},
|
||||
firstResult: {
|
||||
type: Object as PropType<PricingResult>,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { findPriceByQuery, autoCurrency, comparePrice, subtractPrice } =
|
||||
usePoeninja();
|
||||
|
||||
const extractionOrb = computed(() => {
|
||||
const price = findPriceByQuery({
|
||||
ns: "ITEM",
|
||||
name: "Orb of Extraction",
|
||||
});
|
||||
|
||||
return autoCurrency(price?.primaryValue || 99999);
|
||||
});
|
||||
|
||||
const augmentPrice = computed(() => {
|
||||
if (!props.item.augmentSockets) return autoCurrency(0);
|
||||
|
||||
const price = props.item.augmentSockets.augments.reduce(
|
||||
(sum, augment) => {
|
||||
if (!augment) return sum;
|
||||
const price = findPriceByQuery({
|
||||
ns: "ITEM",
|
||||
name: augment.refName,
|
||||
});
|
||||
if (!price) return sum;
|
||||
return sum + price.primaryValue;
|
||||
},
|
||||
0,
|
||||
);
|
||||
|
||||
return autoCurrency(price);
|
||||
});
|
||||
|
||||
const profitValue = computed(() =>
|
||||
subtractPrice(augmentPrice.value, extractionOrb.value),
|
||||
);
|
||||
|
||||
const firstResultValue = computed<CurrencyValue | null>(() => {
|
||||
if (!props.firstResult || !props.firstResult.normalizedPrice) return null;
|
||||
|
||||
const num = Number.parseFloat(props.firstResult.normalizedPrice);
|
||||
if (Number.isNaN(num)) return null;
|
||||
return {
|
||||
min: num,
|
||||
max: num,
|
||||
currency: props.firstResult.normalizedPriceCurrency!.id,
|
||||
};
|
||||
});
|
||||
|
||||
const { t } = useI18nNs("trade_result");
|
||||
return {
|
||||
t,
|
||||
extractionOrb,
|
||||
augmentPrice,
|
||||
profitValue,
|
||||
extractionProfits: computed(() => {
|
||||
if (!firstResultValue.value) return false;
|
||||
return comparePrice(firstResultValue.value, "<", profitValue.value);
|
||||
}),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -92,6 +92,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Extraction Value -->
|
||||
<extraction-value :item="item" :first-result="groupedResults.at(0)" />
|
||||
|
||||
<!-- ADDED AUGMENTS COST -->
|
||||
<div
|
||||
v-if="addedAugments && addedAugments.length"
|
||||
@@ -151,6 +154,7 @@ import TradeItem from "./TradeItem.vue";
|
||||
import { useTradeApi } from "./trade-api";
|
||||
import { GEM, GRANTS_REAL_SKILL } from "@/parser/meta";
|
||||
import ItemSumPrice from "@/web/ui/ItemSumPrice.vue";
|
||||
import ExtractionValue from "./ExtractionValue.vue";
|
||||
|
||||
const slowdown = artificialSlowdown(900);
|
||||
|
||||
@@ -158,6 +162,7 @@ const SHOW_RESULTS = 20;
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
ExtractionValue,
|
||||
ItemQuickPrice,
|
||||
ItemSumPrice,
|
||||
OnlineFilter,
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
<template>
|
||||
<div class="flex items-center gap-x-1">
|
||||
<slot name="item">
|
||||
<div class="flex items-center justify-center shrink-0" :class="imgSize">
|
||||
<div
|
||||
v-if="showImg"
|
||||
class="flex items-center justify-center shrink-0"
|
||||
:class="imgSize"
|
||||
>
|
||||
<ui-item-img :icon="itemImg" overflow-hidden />
|
||||
</div>
|
||||
</slot>
|
||||
<i class="fas fa-arrow-right text-gray-600 px-1 text-sm"></i>
|
||||
<i
|
||||
v-if="showArrow"
|
||||
class="fas fa-arrow-right text-gray-600 px-1 text-sm"
|
||||
></i>
|
||||
<div class="whitespace-nowrap overflow-hidden">
|
||||
<span v-if="approx && !isRange" class="text-gray-600 font-sans">~ </span>
|
||||
<span :class="{ [$style.golden]: isValuable, 'px-1': minText === '?' }">{{
|
||||
@@ -89,6 +96,14 @@ export default defineComponent({
|
||||
type: Object as PropType<BaseType>,
|
||||
default: undefined,
|
||||
},
|
||||
showImg: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showArrow: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const minText = computed(() =>
|
||||
|
||||
Reference in New Issue
Block a user