1
0

3 Коммитууд 7158ddecb4 ... 76687078c3

Эзэн SHA1 Мессеж Огноо
  raylu 76687078c3 shipbuilding: show where to ship each mat 1 сар өмнө
  raylu 4a5422f0e5 shipbuilding: show mat consumption popover 1 сар өмнө
  raylu bb306d9e04 shipbuilding: collapse top level 1 сар өмнө

+ 46 - 5
ts/shipbuilding.ts

@@ -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,

+ 2 - 2
www/shipbuilding.html

@@ -10,8 +10,8 @@
 </head>
 <body>
 	<a href="/">← back</a>
-	<main class="shipbuilding">
-	</main>
+	<main class="shipbuilding"></main>
+	<div id="popover" popover="hint"></div>
 	<script src="shipbuilding.js"></script>
 </body>
 </html>

+ 3 - 0
www/style.css

@@ -158,6 +158,9 @@ main.ledger {
 }
 
 main.shipbuilding {
+	summary {
+		cursor: pointer;
+	}
 	.analysis-node {
 		padding-left: 40px;
 	}