|
|
@@ -1,4 +1,5 @@
|
|
|
import {cachedFetchJSON} from './cache';
|
|
|
+import {setupPopover} from './popover';
|
|
|
|
|
|
const BUY = new Set([
|
|
|
// definitely buy
|
|
|
@@ -38,6 +39,7 @@ const shipbuilders = [
|
|
|
|
|
|
const main = document.querySelector('main.shipbuilding')!;
|
|
|
(async () => {
|
|
|
+ setupPopover();
|
|
|
main.innerHTML = 'loading...';
|
|
|
try {
|
|
|
await render();
|
|
|
@@ -232,7 +234,7 @@ function renderAnalysisNode(node: AnalysisNode, level = 0): HTMLElement {
|
|
|
if (node.children.length === 0) {
|
|
|
el = element('div', {textContent: node.text, className: 'analysis-node'});
|
|
|
} else {
|
|
|
- el = element('details', {className: 'analysis-node', open: true});
|
|
|
+ el = element('details', {className: 'analysis-node', open: level > 0});
|
|
|
el.append(element('summary', {textContent: node.text}));
|
|
|
for (const child of node.children)
|
|
|
el.append(renderAnalysisNode(child, level + 1));
|
|
|
@@ -268,19 +270,49 @@ function renderProduction(expertiseGroups: Record<string, string[]>, production:
|
|
|
const section = element('section');
|
|
|
section.append(element('h2', {textContent: 'production'}));
|
|
|
|
|
|
+ // mat → list of {outputMat, expertise, amount} that consume it as an input
|
|
|
+ const matConsumers: Record<string, {downstreamMat: string, expertise: string, amount: number}[]> = {};
|
|
|
+ for (const [expertise, productionBuildings] of Object.entries(expertiseGroups)) {
|
|
|
+ for (const building of productionBuildings) {
|
|
|
+ for (const [mat, totalAmount] of Object.entries(production[building])) {
|
|
|
+ const recipe = recipes[mat];
|
|
|
+ const outputPerRun = recipe.outputs.find((o) => o.material_ticker === mat)!.material_amount;
|
|
|
+ for (const input of recipe.inputs) {
|
|
|
+ const ticker = input.material_ticker;
|
|
|
+ if (!matConsumers[ticker])
|
|
|
+ matConsumers[ticker] = [];
|
|
|
+ const amount = input.material_amount * totalAmount / outputPerRun;
|
|
|
+ matConsumers[ticker].push({downstreamMat: mat, expertise, amount});
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
let totalConsumablesCost = 0;
|
|
|
for (const [expertise, productionBuildings] of Object.entries(expertiseGroups)) {
|
|
|
- section.append(element('h3', {textContent: expertise}));
|
|
|
+ section.append(element('h3', {textContent: expertise.toLocaleLowerCase()}));
|
|
|
+ const shipTo: Record<string, Record<string, number>> = {};
|
|
|
for (const building of productionBuildings) {
|
|
|
const buildingMats = element('div');
|
|
|
const mats = Object.entries(production[building]);
|
|
|
let buildingMins = 0;
|
|
|
for (const [index, [mat, amount]] of mats.entries()) {
|
|
|
const traded = prices[mat]?.AverageTraded30D ?? 0;
|
|
|
- const span = element('span', {
|
|
|
- textContent: `${formatAmount(amount)}x${mat}`,
|
|
|
- });
|
|
|
+ const span = element('span', {textContent: `${formatAmount(amount)}x${mat}`});
|
|
|
span.style.color = traded > amount * 2 ? '#0cc' : '#c70';
|
|
|
+ const consumers = matConsumers[mat];
|
|
|
+ if (consumers) {
|
|
|
+ span.dataset.tooltip = consumers
|
|
|
+ .map((c) => `${formatAmount(c.amount)}x${mat} → ${c.downstreamMat} (${c.expertise.toLocaleLowerCase()})`)
|
|
|
+ .join('\n');
|
|
|
+ for (const consumer of consumers) {
|
|
|
+ if (consumer.expertise == expertise) // we aren't shipping it anywhere
|
|
|
+ continue;
|
|
|
+ if (!shipTo[consumer.expertise])
|
|
|
+ shipTo[consumer.expertise] = {};
|
|
|
+ shipTo[consumer.expertise][mat] = (shipTo[consumer.expertise][mat] ?? 0) + consumer.amount;
|
|
|
+ }
|
|
|
+ }
|
|
|
buildingMats.append(span);
|
|
|
if (index < mats.length - 1)
|
|
|
buildingMats.append(document.createTextNode(' '));
|
|
|
@@ -297,6 +329,15 @@ function renderProduction(expertiseGroups: Record<string, string[]>, production:
|
|
|
buildingRow.append(buildingMats);
|
|
|
section.append(buildingRow);
|
|
|
}
|
|
|
+
|
|
|
+ const shipToDetails = element('details');
|
|
|
+ shipToDetails.append(element('summary', {textContent: 'ship to'}));
|
|
|
+ for (const [expertise, mats] of Object.entries(shipTo)) {
|
|
|
+ const shipToRow = element('div', {textContent: expertise.toLocaleLowerCase() + ': '});
|
|
|
+ shipToRow.textContent += Object.entries(mats).map(([mat, amount]) => `${amount}x${mat}`).join(' ');
|
|
|
+ shipToDetails.append(shipToRow);
|
|
|
+ }
|
|
|
+ section.append(shipToDetails);
|
|
|
}
|
|
|
|
|
|
section.append(element('h4', {textContent: `total consumables cost: ${formatWhole(totalConsumablesCost)}/day,
|