|
|
@@ -1,3 +1,9 @@
|
|
|
+interface Building {
|
|
|
+ Ticker: string;
|
|
|
+ RecipeName: string;
|
|
|
+ BuildingCosts: Array<{CommodityTicker: string; Amount: number}>;
|
|
|
+}
|
|
|
+
|
|
|
interface Recipe {
|
|
|
RecipeId: string;
|
|
|
BuildingTicker: string;
|
|
|
@@ -19,26 +25,40 @@ interface RecipeProfit {
|
|
|
dailyProfit: number;
|
|
|
traded: number;
|
|
|
priceAverage: number;
|
|
|
+ buildingCost: number;
|
|
|
}
|
|
|
|
|
|
-async function fetchData(): Promise<{recipes: Recipe[]; prices: Price[]}> {
|
|
|
- const [recipesResponse, exchangesResponse] = await Promise.all([
|
|
|
+async function fetchData(): Promise<{buildings: Building[]; recipes: Recipe[]; prices: Price[]}> {
|
|
|
+ const [buildingsResponse, recipesResponse, exchangesResponse] = await Promise.all([
|
|
|
+ fetch('https://api.prunplanner.org/data/buildings'),
|
|
|
fetch('https://api.prunplanner.org/data/recipes'),
|
|
|
fetch('https://api.prunplanner.org/data/exchanges'),
|
|
|
]);
|
|
|
- const [recipes, prices] = await Promise.all([
|
|
|
+ const [buildings, recipes, prices] = await Promise.all([
|
|
|
+ buildingsResponse.json(),
|
|
|
recipesResponse.json(),
|
|
|
exchangesResponse.json(),
|
|
|
]);
|
|
|
- return {recipes, prices};
|
|
|
+ return {buildings, recipes, prices};
|
|
|
}
|
|
|
|
|
|
-function render({recipes, prices}: {recipes: Recipe[], prices: Price[]}) {
|
|
|
+function render({buildings, recipes, prices}: {buildings: Building[], recipes: Recipe[], prices: Price[]}) {
|
|
|
const priceMap = new Map<string, Price>();
|
|
|
for (const price of prices)
|
|
|
if (price.ExchangeCode == 'PP7D_IC1')
|
|
|
priceMap.set(price.MaterialTicker, price);
|
|
|
|
|
|
+ const buildingMap = new Map<string, number>();
|
|
|
+ for (const building of buildings) {
|
|
|
+ const cost = building.BuildingCosts.reduce((sum, mat) => {
|
|
|
+ const price = priceMap.get(mat.CommodityTicker);
|
|
|
+ if (price === undefined)
|
|
|
+ throw new Error(`missing price for ${mat.CommodityTicker}`);
|
|
|
+ return sum + price.PriceAverage * mat.Amount;
|
|
|
+ }, 0);
|
|
|
+ buildingMap.set(building.Ticker, cost);
|
|
|
+ }
|
|
|
+
|
|
|
const profits: RecipeProfit[] = []
|
|
|
for (const recipe of recipes) {
|
|
|
const runsPerDay = 1000 * 60 * 60 * 24 / recipe.TimeMs;
|
|
|
@@ -58,11 +78,17 @@ function render({recipes, prices}: {recipes: Recipe[], prices: Price[]}) {
|
|
|
return sum + price.PriceAverage * input.Amount;
|
|
|
}, 0);
|
|
|
const dailyProfit = (revenuePerRun - costPerRun) * runsPerDay;
|
|
|
+ const buildingCost = buildingMap.get(recipe.BuildingTicker);
|
|
|
+ if (buildingCost === undefined)
|
|
|
+ throw new Error(`missing building cost for ${recipe.BuildingTicker}`);
|
|
|
+ if (outputPrice.Traded === 0 || buildingCost > 1_200_000)
|
|
|
+ continue;
|
|
|
profits.push({
|
|
|
recipeName: recipe.RecipeName,
|
|
|
dailyProfit,
|
|
|
traded: outputPrice.Traded,
|
|
|
- priceAverage: outputPrice.PriceAverage
|
|
|
+ priceAverage: outputPrice.PriceAverage,
|
|
|
+ buildingCost,
|
|
|
});
|
|
|
}
|
|
|
profits.sort((a, b) => b.dailyProfit - a.dailyProfit);
|
|
|
@@ -77,6 +103,7 @@ function render({recipes, prices}: {recipes: Recipe[], prices: Price[]}) {
|
|
|
<td>${fmt.format(profit.dailyProfit)}</td>
|
|
|
<td>${fmt.format(profit.traded * profit.priceAverage)}</td>
|
|
|
<td>${fmt.format(profit.traded)}</td>
|
|
|
+ <td>${fmt.format(profit.buildingCost)}</td>
|
|
|
`;
|
|
|
tbody.appendChild(row);
|
|
|
}
|