|
|
@@ -38,7 +38,7 @@ async function render() {
|
|
|
}
|
|
|
|
|
|
async function _render() {
|
|
|
- const [allPrices, recipes, buildingList, storage] = await Promise.all([
|
|
|
+ const [allPrices, {recipes, extractables}, buildingList, storage] = await Promise.all([
|
|
|
cachedFetchJSON('https://refined-prun.github.io/refined-prices/all.json') as Promise<RawPrice[]>,
|
|
|
recipeForMats(),
|
|
|
cachedFetchJSON('https://api.prunplanner.org/data/buildings/') as Promise<Building[]>,
|
|
|
@@ -54,7 +54,7 @@ async function _render() {
|
|
|
const analysisNodes: AnalysisNode[] = [];
|
|
|
let cost = 0;
|
|
|
for (const [mat, amount] of Object.entries(blueprint)) {
|
|
|
- const node = analyzeMat(mat, amount, production, extract, buy, prices, recipes, storage);
|
|
|
+ const node = analyzeMat(mat, amount, production, extract, buy, prices, recipes, extractables, storage);
|
|
|
cost += node.cost;
|
|
|
analysisNodes.push(node);
|
|
|
}
|
|
|
@@ -99,11 +99,33 @@ function renderMatList(header: string, mats: Record<string, number>, storage: Re
|
|
|
return section;
|
|
|
}
|
|
|
|
|
|
-async function recipeForMats(): Promise<Record<string, Recipe>> {
|
|
|
- const allRecipes: Recipe[] = await cachedFetchJSON('https://api.prunplanner.org/data/recipes/');
|
|
|
+async function recipeForMats(): Promise<{recipes: Record<string, Recipe>, extractables: Set<string>}> {
|
|
|
+ const [allRecipes, materials]: [Recipe[], Material[]] = await Promise.all([
|
|
|
+ cachedFetchJSON('https://api.prunplanner.org/data/recipes/'),
|
|
|
+ cachedFetchJSON('https://api.prunplanner.org/data/materials/'),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ const extractables = new Set(materials.filter(
|
|
|
+ (m) => ['ores', 'minerals', 'liquids', 'gases'].includes(m.category_name)).map((m) => m.ticker));
|
|
|
+
|
|
|
+ const chosenRecipes: Record<string, string> = {
|
|
|
+ 'AL': '6xALO 1xO 1xC 1xFLX=>4xAL',
|
|
|
+ 'C': '4xHCP=>4xC',
|
|
|
+ 'FE': '6xFEO 1xC 1xO 1xFLX=>4xFE',
|
|
|
+ 'GL': '2xSIO 1xNA 1xSEN=>10xGL',
|
|
|
+ 'HCP': '14xH2O 1xNS=>8xHCP',
|
|
|
+ 'RE': '8xREO 1xC 1xO 1xFLX=>5xRE',
|
|
|
+ 'RG': '10xGL 15xPG 1xSEN=>10xRG',
|
|
|
+ 'SI': '3xSIO 1xC 1xO 1xFLX=>1xSI',
|
|
|
+ }
|
|
|
+
|
|
|
const matRecipes: Record<string, Recipe[]> = {}; // all ways to make a mat
|
|
|
for (const recipe of allRecipes)
|
|
|
for (const output of recipe.outputs) {
|
|
|
+ if (extractables.has(output.material_ticker))
|
|
|
+ continue;
|
|
|
+ if (chosenRecipes[output.material_ticker] && recipe.recipe_name != chosenRecipes[output.material_ticker])
|
|
|
+ continue;
|
|
|
const recipes = matRecipes[output.material_ticker];
|
|
|
if (recipes)
|
|
|
recipes.push(recipe);
|
|
|
@@ -111,15 +133,15 @@ async function recipeForMats(): Promise<Record<string, Recipe>> {
|
|
|
matRecipes[output.material_ticker] = [recipe];
|
|
|
}
|
|
|
|
|
|
- const matRecipe: Record<string, Recipe> = {}; // mats for which there's only one recipe to make
|
|
|
+ const matRecipe: Record<string, Recipe> = {};
|
|
|
for (const [mat, recipes] of Object.entries(matRecipes))
|
|
|
if (recipes.length === 1)
|
|
|
matRecipe[mat] = recipes[0];
|
|
|
- return matRecipe;
|
|
|
+ return {recipes: matRecipe, extractables};
|
|
|
}
|
|
|
|
|
|
function analyzeMat(mat: string, amount: number, production: Production, extract: Record<string, number>, buy: Record<string, number>,
|
|
|
- prices: Record<string, RawPrice>, recipes: Record<string, Recipe>, storage: Record<string, number>): AnalysisNode {
|
|
|
+ prices: Record<string, RawPrice>, recipes: Record<string, Recipe>, extractables: Set<string>, storage: Record<string, number>): AnalysisNode {
|
|
|
const price = prices[mat];
|
|
|
if (!price)
|
|
|
throw new Error(`missing price for ${mat}`);
|
|
|
@@ -141,11 +163,13 @@ function analyzeMat(mat: string, amount: number, production: Production, extract
|
|
|
};
|
|
|
}
|
|
|
|
|
|
- const recipe = recipes[mat];
|
|
|
- if (!recipe) {
|
|
|
+ if (extractables.has(mat)) {
|
|
|
extract[mat] = (extract[mat] ?? 0) + amount;
|
|
|
return {ticker: mat, amount, inStorage, acquisition: `extract`, children: [], cost: 0};
|
|
|
}
|
|
|
+ const recipe = recipes[mat];
|
|
|
+ if (!recipe)
|
|
|
+ throw new Error(`no recipe for ${mat}`);
|
|
|
|
|
|
const building = recipe.building_ticker;
|
|
|
if (!production[building])
|
|
|
@@ -158,7 +182,7 @@ function analyzeMat(mat: string, amount: number, production: Production, extract
|
|
|
const children: AnalysisNode[] = [];
|
|
|
for (const inputMat of recipe.inputs) {
|
|
|
const inputAmount = inputMat.material_amount * amount / recipe.outputs[0].material_amount;
|
|
|
- const node = analyzeMat(inputMat.material_ticker, inputAmount, production, extract, buy, prices, recipes, storage);
|
|
|
+ const node = analyzeMat(inputMat.material_ticker, inputAmount, production, extract, buy, prices, recipes, extractables, storage);
|
|
|
totalCost += node.cost;
|
|
|
children.push(node);
|
|
|
}
|
|
|
@@ -395,6 +419,11 @@ interface RecipeMat {
|
|
|
material_amount: number
|
|
|
}
|
|
|
|
|
|
+interface Material {
|
|
|
+ ticker: string
|
|
|
+ category_name: string
|
|
|
+}
|
|
|
+
|
|
|
interface RawPrice {
|
|
|
MaterialTicker: string
|
|
|
ExchangeCode: string
|