|
|
@@ -77,18 +77,23 @@ async function _render() {
|
|
|
renderAnalysis(analysisNodes),
|
|
|
element('p', {textContent: `total cost: ${formatWhole(cost)}`}),
|
|
|
renderProduction(expertiseGroups, production, storage, prices, recipes, buildings),
|
|
|
- renderMatList('extract', extract),
|
|
|
- renderMatList('buy', buy),
|
|
|
+ renderMatList('extract', extract, storage),
|
|
|
+ renderMatList('buy', buy, storage),
|
|
|
);
|
|
|
}
|
|
|
|
|
|
-function renderMatList(header: string, mats: Record<string, number>): HTMLElement {
|
|
|
+function renderMatList(header: string, mats: Record<string, number>, storage: Record<string, number>): HTMLElement {
|
|
|
const section = element('section');
|
|
|
section.append(element('h2', {textContent: header}));
|
|
|
const matsSorted = Object.entries(mats).sort(([a], [b]) => a.localeCompare(b));
|
|
|
- section.append(element('p', {
|
|
|
- textContent: matsSorted.map(([mat, amount]) => `${formatAmount(amount)}x${mat}`).join(', '),
|
|
|
- }));
|
|
|
+ for (const [mat, amount] of matsSorted) {
|
|
|
+ const div = element('div', {textContent: `${formatAmount(amount)}x${mat}`});
|
|
|
+ const storageText = element('span', {textContent: ` (${formatAmount(storage[mat] ?? 0)})`});
|
|
|
+ const percent = Math.min((storage[mat] ?? 0) / (amount * 2), 1);
|
|
|
+ storageText.style.color = `color-mix(in xyz, #0aa ${percent * 100}%, #f80 )`;
|
|
|
+ div.append(storageText);
|
|
|
+ section.append(div);
|
|
|
+ }
|
|
|
return section;
|
|
|
}
|
|
|
|
|
|
@@ -252,12 +257,12 @@ function renderProduction(expertiseGroups: Record<string, string[]>, production:
|
|
|
section.append(element('h3', {textContent: expertise.toLocaleLowerCase()}));
|
|
|
const shipTo: Record<string, Record<string, number>> = {};
|
|
|
for (const building of productionBuildings) {
|
|
|
- const buildingMats = element('div');
|
|
|
+ const buildingRow = element('div', {className: 'building-row'});
|
|
|
const mats = Object.entries(production[building]);
|
|
|
let buildingMins = 0;
|
|
|
for (const [mat, amount] of mats) {
|
|
|
- buildingMats.append(document.createTextNode(' '));
|
|
|
- buildingMats.append(renderProductionBuildingMat(expertise, mat, amount, storage,
|
|
|
+ buildingRow.append(document.createTextNode(' '));
|
|
|
+ buildingRow.append(renderProductionBuildingMat(expertise, mat, amount, storage,
|
|
|
matInputs, matConsumers, shipTo));
|
|
|
|
|
|
const recipe = recipes[mat];
|
|
|
@@ -267,9 +272,8 @@ function renderProduction(expertiseGroups: Record<string, string[]>, production:
|
|
|
const numBuildings = buildingMins / (24*60) / 5 / 1.605; // one ship every 5 days, 160.5% efficiency
|
|
|
const consumablesCost = buildingDailyCost(buildings[building], prices) * Math.round(Math.max(1, numBuildings));
|
|
|
totalConsumablesCost += consumablesCost;
|
|
|
- const buildingRow = element('div', {className: 'building-row',
|
|
|
- textContent: `${formatFixed(numBuildings, 1)}x${building} (${formatWhole(consumablesCost)}/d)`});
|
|
|
- buildingRow.append(buildingMats);
|
|
|
+ buildingRow.prepend(document.createTextNode(
|
|
|
+ `${formatFixed(numBuildings, 1)}x${building} (${formatWhole(consumablesCost)}/d)`));
|
|
|
section.append(buildingRow);
|
|
|
}
|
|
|
|
|
|
@@ -293,9 +297,22 @@ function renderProductionBuildingMat(expertise: string, mat: string, amount: num
|
|
|
matConsumers: Record<string, {downstreamMat: string, expertise: string, amount: number}[]>,
|
|
|
shipTo: Record<string, Record<string, number>>): HTMLElement {
|
|
|
const inStorage = storage[mat] ?? 0;
|
|
|
- const span = element('span', {textContent: `${formatAmount(amount)}x${mat} (${inStorage})`});
|
|
|
+ const wrapper = element('span');
|
|
|
+
|
|
|
+ const amountText = element('span', {textContent: `${formatAmount(amount)}x${mat}`});
|
|
|
+ wrapper.append(amountText);
|
|
|
+
|
|
|
+ const storageText = element('span', {textContent: ` (${formatAmount(inStorage)})`});
|
|
|
+ wrapper.append(storageText);
|
|
|
+
|
|
|
const percent = Math.min(inStorage / (amount * 2), 1);
|
|
|
- span.style.color = `color-mix(in xyz, #0aa ${percent * 100}%, #f80 )`;
|
|
|
+ storageText.style.color = `color-mix(in xyz, #0aa ${percent * 100}%, #f80 )`;
|
|
|
+ if (percent >= 1)
|
|
|
+ amountText.style.color = '#777';
|
|
|
+ else {
|
|
|
+ const produceable = Object.values(matInputs[mat]).every((input) => storage[input.upstreamMat] ?? 0 >= input.amount);
|
|
|
+ amountText.style.color = produceable ? '#0aa' : '#f80';
|
|
|
+ }
|
|
|
|
|
|
let tooltip = '';
|
|
|
const inputs = matInputs[mat];
|
|
|
@@ -315,8 +332,8 @@ function renderProductionBuildingMat(expertise: string, mat: string, amount: num
|
|
|
shipTo[consumer.expertise][mat] = (shipTo[consumer.expertise][mat] ?? 0) + consumer.amount;
|
|
|
}
|
|
|
}
|
|
|
- span.dataset.tooltip = tooltip;
|
|
|
- return span;
|
|
|
+ amountText.dataset.tooltip = tooltip;
|
|
|
+ return wrapper;
|
|
|
}
|
|
|
|
|
|
async function fetchStorage(): Promise<Record<string, number>> {
|