Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better Interchange bounds and GP Coin barters #954

Merged
merged 9 commits into from
Jun 24, 2024
2 changes: 1 addition & 1 deletion scripts/cache-api-data.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ try {
item.lastLowPrice = 0;
item.avg24hPrice = 0;
item.buyFor = item.buyFor.filter(buyFor => buyFor.vendor.normalizedName !== 'flea-market');
item.sellFor = item.sellFor.filter(buyFor => buyFor.vendor.normalizedName !== 'flea-market');
item.sellFor = item.sellFor.filter(sellFor => sellFor.vendor.normalizedName !== 'flea-market');
item.cached = true;
}
fs.writeFileSync('./src/data/items.json', JSON.stringify(filteredItems));
Expand Down
92 changes: 69 additions & 23 deletions src/components/barters-table/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@ import { Link } from 'react-router-dom';
import 'tippy.js/dist/tippy.css'; // optional

import DataTable from '../../components/data-table/index.js';

import useBartersData from '../../features/barters/index.js';
import useCraftsData from '../../features/crafts/index.js';
import useItemsData from '../../features/items/index.js';
import useMetaData from '../../features/meta/index.js';
import { selectAllTraders } from '../../features/settings/settingsSlice.js';

import ValueCell from '../value-cell/index.js';
import CostItemsCell from '../cost-items-cell/index.js';
import { formatCostItems, getCheapestCashPrice, getCheapestBarter } from '../../modules/format-cost-items.js';
import RewardCell from '../reward-cell/index.js';
import { isAnyDogtag, isBothDogtags } from '../../modules/dogtags.js';

import FleaMarketLoadingIcon from '../FleaMarketLoadingIcon.jsx';

import { formatCostItems, getCheapestCashPrice, getCheapestBarter } from '../../modules/format-cost-items.js';
import { isAnyDogtag, isBothDogtags } from '../../modules/dogtags.js';

import './index.css';

function BartersTable({ selectedTrader, nameFilter, itemFilter, showAll, useBarterIngredients, useCraftIngredients }) {
Expand All @@ -28,6 +34,8 @@ function BartersTable({ selectedTrader, nameFilter, itemFilter, showAll, useBart

const { data: barters } = useBartersData();
const { data: crafts } = useCraftsData();
const { data: items } = useItemsData();
const { data: meta } = useMetaData();

const columns = useMemo(
() => [
Expand All @@ -43,6 +51,19 @@ function BartersTable({ selectedTrader, nameFilter, itemFilter, showAll, useBart
Header: t('Cost'),
id: 'costItems',
accessor: 'costItems',
sortType: (a, b, columnId, desc) => {
if (a.values.costItems[0].id === '5d235b4d86f7742e017bc88a' && a.values.costItems[0].id === '5d235b4d86f7742e017bc88a') {
const aGPCost = a.values.costItems[0].price || 0;
const bGPCost = b.values.costItems[0].price || 0;

return aGPCost - bGPCost;
}

const aCost = a.values.cost || 0;
const bCost = b.values.cost || 0;

return aCost - bCost;
},
Cell: ({ value }) => {
return <CostItemsCell costItems={value} allowAllSources={showAll} barters={useBarterIngredients ? barters : false} crafts={useCraftIngredients ? crafts : false} />;
},
Expand All @@ -67,10 +88,10 @@ function BartersTable({ selectedTrader, nameFilter, itemFilter, showAll, useBart
id: 'savings',
accessor: (d) => Number(d.savings),
sortType: (a, b, columnId, desc) => {
const aSell = a.values.savings || (desc ? Number.MIN_SAFE_INTEGER : Number.MAX_SAFE_INTEGER);
const bSell = b.values.savings || (desc ? Number.MIN_SAFE_INTEGER : Number.MAX_SAFE_INTEGER);
const aSave = a.values.savings || (desc ? Number.MIN_SAFE_INTEGER : Number.MAX_SAFE_INTEGER);
const bSave = b.values.savings || (desc ? Number.MIN_SAFE_INTEGER : Number.MAX_SAFE_INTEGER);

return aSell - bSell;
return aSave - bSave;
},
Cell: (props) => {
if (props.row.original.cached) {
Expand All @@ -87,7 +108,18 @@ function BartersTable({ selectedTrader, nameFilter, itemFilter, showAll, useBart
Header: t('InstaProfit'),
id: 'instaProfit',
accessor: 'instaProfit',
sortType: 'basic',
sortType: (a, b, columnId, desc) => {
const aProf = a.values.instaProfit || 0;
const bProf = b.values.instaProfit || 0;
if (aProf === bProf) {
const aSave = a.values.savings || 0;
const bSave = b.values.savings || 0;

return aSave - bSave;
}

return aProf - bProf;
},
Cell: (props) => {
if (props.row.original.cached) {
return (
Expand Down Expand Up @@ -190,12 +222,7 @@ function BartersTable({ selectedTrader, nameFilter, itemFilter, showAll, useBart
continue;
}

if (
requiredItem.item.name
.toLowerCase()
.replace(/\s/g, '')
.includes('dogtag')
) {
if (requiredItem.item.normalizedName.includes('dogtag')) {
setSkippedBySettings(true);
return false;
}
Expand Down Expand Up @@ -251,10 +278,17 @@ function BartersTable({ selectedTrader, nameFilter, itemFilter, showAll, useBart
costItems.forEach((costItem) => (cost += costItem.pricePerUnit * costItem.count));

const barterRewardItem = barterRow.rewardItems[0].item;
let barterRewardContainedItem;

if (barterRewardItem.bsgCategoryId === '543be5cb4bdc2deb348b4568') { // "ammo-container"
barterRewardContainedItem = items.find(i => i.id === barterRewardItem.containsItems[0]?.item.id);
}

const bestSellTo = barterRewardItem.sellFor.reduce(
const whatWeSell = barterRewardContainedItem ? barterRewardContainedItem : barterRewardItem;
const howManyWeSell = barterRewardContainedItem ? barterRewardItem.containsItems[0].count : barterRow.rewardItems[0].count;
const bestSellTo = whatWeSell.sellFor.reduce(
(previousSellFor, currentSellFor) => {
if (currentSellFor.vendor.normalizedName === 'flea-market') {
if (currentSellFor.vendor.normalizedName === 'flea-market' && meta.flea.foundInRaidRequired) {
return previousSellFor;
}
if (currentSellFor.vendor.normalizedName === 'jaeger' && !hasJaeger) {
Expand All @@ -274,15 +308,23 @@ function BartersTable({ selectedTrader, nameFilter, itemFilter, showAll, useBart
},
);

if (isNaN(cost) && costItems.length === 1 && costItems[0].id === '5d235b4d86f7742e017bc88a') { // "gp-coin"
cost = bestSellTo.priceRUB * howManyWeSell;
const GPCoinPrice = cost / costItems[0].count;
costItems[0].price = GPCoinPrice;
costItems[0].priceRUB = GPCoinPrice;
costItems[0].pricePerUnit = GPCoinPrice;
}

const tradeData = {
costItems: costItems,
cost: cost,
instaProfit: (bestSellTo.priceRUB * barterRow.rewardItems[0].count) - cost,
instaProfit: (bestSellTo.priceRUB * howManyWeSell) - cost,
instaProfitSource: bestSellTo,
instaProfitDetails: [
{
name: bestSellTo.vendor.name,
value: bestSellTo.priceRUB * barterRow.rewardItems[0].count,
value: bestSellTo.priceRUB * howManyWeSell,
},
{
name: t('Barter cost'),
Expand All @@ -291,13 +333,13 @@ function BartersTable({ selectedTrader, nameFilter, itemFilter, showAll, useBart
],
reward: {
item: barterRewardItem,
count: barterRow.rewardItems[0].count,
source: `${barterRow.trader.name} ${t('LL{{level}}', { level: barterRow.level })}`,
sellTo: bestSellTo.vendor.name,
sellToNormalized: bestSellTo.vendor.normalizedName,
sellValue: bestSellTo.priceRUB,
taskUnlock: barterRow.taskUnlock,
isFIR: false,
count: barterRow.rewardItems[0].count,
},
cached: barterRow.cached || barterRewardItem.cached,
};
Expand All @@ -306,13 +348,15 @@ function BartersTable({ selectedTrader, nameFilter, itemFilter, showAll, useBart
tradeData.reward.sellValue = barterRewardItem.priceCustom;
tradeData.reward.sellType = 'custom';
}

//tradeData.reward.sellTo = t(tradeData.reward.sellTo)

if (barterRewardItem.bsgCategoryId === '543be5cb4bdc2deb348b4568') { // "ammo-container"
tradeData.reward.sellNote = t('Unpacked');
}

tradeData.savingsParts = [];
const cheapestPrice = getCheapestCashPrice(barterRewardItem, settings, showAll);
const cheapestBarter = getCheapestBarter(barterRewardItem, {barters, crafts: useCraftIngredients ? crafts : false, settings, allowAllSources: showAll});
if (cheapestPrice.type === 'cash-sell'){
if (cheapestPrice.type === 'cash-sell') {
//this item cannot be purchased for cash
if (cheapestBarter) {
if (cheapestBarter.priceRUB !== cost) {
Expand All @@ -323,7 +367,7 @@ function BartersTable({ selectedTrader, nameFilter, itemFilter, showAll, useBart
}
tradeData.savings = cheapestBarter.priceRUB - cost;
}
} else {
} else if (cheapestPrice.type !== 'none') {
// savings based on cheapest cash price
let sellerName = cheapestPrice.vendor.name;
if (cheapestPrice.vendor.minTraderLevel) {
Expand All @@ -333,12 +377,12 @@ function BartersTable({ selectedTrader, nameFilter, itemFilter, showAll, useBart
name: sellerName,
value: cheapestPrice.priceRUB,
});
tradeData.savings = cheapestPrice.priceRUB - Math.round(cost / barterRow.rewardItems[0].count);
tradeData.savings = cheapestPrice.priceRUB - Math.round(cost / howManyWeSell);
}
if (tradeData.savingsParts.length > 0) {
tradeData.savingsParts.push({
name: t('Barter cost'),
value: Math.round(cost / barterRow.rewardItems[0].count) * -1
value: Math.round(cost / howManyWeSell) * -1
});
}

Expand Down Expand Up @@ -374,6 +418,8 @@ function BartersTable({ selectedTrader, nameFilter, itemFilter, showAll, useBart
selectedTrader,
barters,
crafts,
items,
meta,
itemFilter,
traders,
completedQuests,
Expand Down
21 changes: 12 additions & 9 deletions src/components/item-cost/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function ItemCost({
itemId,
count,
price = 0,
vendor = {name: 'Flea Market', normalizedName: 'flea-market'},
vendor = {name: 'N/A', normalizedName: 'unknown'},
priceType = 'cash',
priceDetails,
isTool,
Expand Down Expand Up @@ -51,14 +51,17 @@ function ItemCost({
let { displayPrice, tooltip, displayImage} = useMemo(() => {
let displayPrice = '';
let tooltip = false;
let displayImage = (
<img
alt={vendor.name}
className="item-cost-barter-icon"
src={`${process.env.PUBLIC_URL}/images/traders/${vendor.normalizedName}-icon.jpg`}
loading="lazy"
/>
);
let displayImage = '';
if (vendor.normalizedName !== 'unknown') {
displayImage = (
<img
alt={vendor.name}
className="item-cost-barter-icon"
src={`${process.env.PUBLIC_URL}/images/traders/${vendor.normalizedName}-icon.jpg`}
loading="lazy"
/>
);
}
if (priceType === 'cached') {
displayPrice = count;
displayImage = (
Expand Down
4 changes: 2 additions & 2 deletions src/data/maps.json
Original file line number Diff line number Diff line change
Expand Up @@ -965,8 +965,8 @@
"transform": [0.265, 150.6, 0.265, 134.6],
"coordinateRotation": 180,
"bounds": [
[530, -439],
[-364, 452]
[532.75, -442.75],
[-364, 453.5]
],
"author": "Tarkov.dev",
"authorLink": "https://tarkov.dev",
Expand Down
2 changes: 1 addition & 1 deletion src/features/items/do-fetch-items.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ class ItemsQuery extends APIQuery {
priceRUB: 0,
currency: 'RUB',
vendor: {
name: 'unknown',
name: 'N/A',
normalizedName: 'unknown',
},
}
Expand Down
1 change: 1 addition & 0 deletions src/features/meta/do-fetch-meta.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class MetaQuery extends APIQuery {
minPlayerLevel
sellOfferFeeRate
sellRequirementFeeRate
foundInRaidRequired
}
armorMaterials(lang: ${language}) {
id
Expand Down
13 changes: 9 additions & 4 deletions src/modules/format-cost-items.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,23 @@ function getCheapestCashPrice(item, settings = {}, allowAllSources = false) {
});
if (!buySource || buySource.length === 0) {
let sellToTrader = item.sellFor.filter(sellFor => {
if (sellFor.vendor.normalizedName === 'flea-market') return false;
if (!allowAllSources && sellFor.vendor.normalizedName === 'jaeger' && !settings.jaeger) return false;
if (sellFor.vendor.normalizedName === 'flea-market')
return false;
if (!allowAllSources && sellFor.vendor.normalizedName === 'jaeger' && !settings.jaeger)
return false;
return true;
});
let sourceType = 'cash-sell';
if (sellToTrader.length > 1) {
sellToTrader = sellToTrader.reduce((prev, current) => {
return prev.priceRUB > current.priceRUB ? prev : current;
}, {priceRUB: 0});
} else {
} else if (sellToTrader.length === 1) {
sellToTrader = sellToTrader[0];
} else {
sourceType = 'none';
}
return {...sellToTrader, type: 'cash-sell', pricePerUnit: sellToTrader?.priceRUB};
return {...sellToTrader, type: sourceType, pricePerUnit: sellToTrader?.priceRUB};
} else {
if (buySource.length > 1) {
buySource = buySource.reduce((prev, current) => {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/item/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ function Item() {

const { data: quests } = useQuestsData();

const {data: maps } = useMapsData();
const { data: maps } = useMapsData();

const currentItemData = useMemo(() => {
let item = items.find(i => i.normalizedName === itemName);
Expand Down
2 changes: 1 addition & 1 deletion src/pages/quest/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function Quest() {
factionName: 'Any',
trader: {
name: t('Loading...'),
normalizedName: 'flea-market'
normalizedName: 'unknown'
},
objectives: [],
startRewards: [],
Expand Down
Loading