|
|
@@ -99,22 +99,26 @@ async function render() {
|
|
|
|
|
|
if (!headersInitialized) {
|
|
|
const ths = document.querySelectorAll('th');
|
|
|
+ // Map our new "_base" keys to the DOM headers
|
|
|
const keys: (keyof ProfitWithMetrics | 'outputs')[] = [
|
|
|
- 'outputs', 'expertise', 'profit_per_area', 'break_even',
|
|
|
- 'capex_val', 'opex_val', 'logistics_per_area', 'market_capacity_area'
|
|
|
+ 'outputs', 'expertise', 'profit_per_base', 'break_even',
|
|
|
+ 'capex_val', 'opex_val', 'logistics_per_base', 'market_capacity_base'
|
|
|
];
|
|
|
|
|
|
ths.forEach((th, i) => {
|
|
|
if (keys[i]) {
|
|
|
th.style.cursor = 'pointer';
|
|
|
- th.title = ''; // Remove native title to prevent double-tooltips
|
|
|
+ th.title = '';
|
|
|
|
|
|
- // EXTREME DETAIL: We inject dynamic string content and custom tooltips directly into the DOM headers.
|
|
|
- // By routing these explanations into `dataset.tooltip`, the existing popover engine picks them up
|
|
|
- // automatically, rendering a clean, stylized popup on hover.
|
|
|
- if (keys[i] === 'market_capacity_area') {
|
|
|
- th.textContent = 'Market Cap (Areas)';
|
|
|
- th.dataset.tooltip = 'Click to sort.\n\nMarket Capacity: 7-day average traded volume ÷ daily output per area. Indicates how many areas you can build before saturating the market.';
|
|
|
+ if (keys[i] === 'profit_per_base') {
|
|
|
+ th.textContent = 'Profit/Base';
|
|
|
+ th.dataset.tooltip = 'Click to sort.\n\nDaily profit scaled to a full 500-area planetary base.';
|
|
|
+ } else if (keys[i] === 'logistics_per_base') {
|
|
|
+ th.textContent = 'Logistics/Base';
|
|
|
+ th.dataset.tooltip = 'Click to sort.\n\nDaily logistics volume/weight scaled to a full 500-area planetary base.';
|
|
|
+ } else if (keys[i] === 'market_capacity_base') {
|
|
|
+ th.textContent = 'Market Cap (Bases)';
|
|
|
+ th.dataset.tooltip = 'Click to sort.\n\nMarket Capacity: 7-day average traded volume ÷ daily output per base. Indicates how many full 500-area bases you can build before saturating the market.';
|
|
|
} else if (keys[i] === 'break_even') {
|
|
|
th.dataset.tooltip = 'Click to sort.\n\nBreak Even: (CapEx + 3 days of OpEx) ÷ daily profit. Includes 3 days of operating costs as working capital.';
|
|
|
} else {
|
|
|
@@ -173,10 +177,11 @@ async function render() {
|
|
|
const revenue_val = p.revenue[revenueMetric];
|
|
|
|
|
|
const profit_per_day = revenue_val - opex_val;
|
|
|
- const profit_per_area = profit_per_day / p.area;
|
|
|
+ // EXTREME DETAIL: Divide by the fraction of a base the building consumes.
|
|
|
+ const profit_per_base = profit_per_day / (p.area / 500);
|
|
|
const break_even = profit_per_day > 0 ? (capex_val + 3 * opex_val) / profit_per_day : Infinity;
|
|
|
|
|
|
- return { ...p, capex_val, opex_val, revenue_val, profit_per_day, profit_per_area, break_even };
|
|
|
+ return { ...p, capex_val, opex_val, revenue_val, profit_per_day, profit_per_base, break_even };
|
|
|
});
|
|
|
|
|
|
profitsWithMetrics.sort((a, b) => {
|
|
|
@@ -194,17 +199,22 @@ async function render() {
|
|
|
});
|
|
|
|
|
|
for (const p of profitsWithMetrics) {
|
|
|
+ const volumeRatio = p.output_per_day / p.average_traded_7d;
|
|
|
const tr = document.createElement('tr');
|
|
|
|
|
|
+ // EXTREME DETAIL: Because 1 base = 500 area, the color scales must be adjusted
|
|
|
+ // proportionately to ensure the visual gradients still render accurately.
|
|
|
+ // e.g. Profit bounds changed from 300 to 150,000.
|
|
|
+ // Market capacity bounds changed from [20, 500] to [0.04, 1].
|
|
|
tr.innerHTML = `
|
|
|
<td>${p.outputs.map(o => o.ticker).join(', ')}</td>
|
|
|
<td>${expertise[p.expertise]}</td>
|
|
|
- <td style="color: ${color(p.profit_per_area, 0, 300)}">${formatDecimal(p.profit_per_area)}</td>
|
|
|
+ <td style="color: ${color(p.profit_per_base, 0, 150000)}">${formatDecimal(p.profit_per_base)}</td>
|
|
|
<td><span style="color: ${color(p.break_even, 30, 3)}">${formatDecimal(p.break_even)}</span>d</td>
|
|
|
<td style="color: ${color(p.capex_val, 300_000, 40_000)}">${formatWhole(p.capex_val)}</td>
|
|
|
<td style="color: ${color(p.opex_val, 40_000, 1_000)}">${formatWhole(p.opex_val)}</td>
|
|
|
- <td style="color: ${color(p.logistics_per_area, 2, 0.2)}">${formatDecimal(p.logistics_per_area)}</td>
|
|
|
- <td style="color: ${color(p.market_capacity_area, 20, 500)}">${formatWhole(p.market_capacity_area)}</td>
|
|
|
+ <td style="color: ${color(p.logistics_per_base, 1000, 100)}">${formatDecimal(p.logistics_per_base)}</td>
|
|
|
+ <td style="color: ${color(p.market_capacity_base, 0.04, 1)}">${formatDecimal(p.market_capacity_base)}</td>
|
|
|
`;
|
|
|
|
|
|
const output = tr.querySelector('td')!;
|
|
|
@@ -214,10 +224,10 @@ async function render() {
|
|
|
profitCell.dataset.tooltip = formatMatPrices(p.outputs, revenueMetric, p.runs_per_day) + '\n\n' +
|
|
|
formatMatPrices(p.input_costs, opexMetric, p.runs_per_day) + '\n' +
|
|
|
`(${formatWhole(p.revenue_val)} - ${formatWhole(p.opex_val)}) = ${formatDecimal(p.profit_per_day)}\n` +
|
|
|
- `${formatDecimal(p.profit_per_day)} / ${formatWhole(p.area)} area = ${formatDecimal(p.profit_per_area)}`;
|
|
|
+ `${formatDecimal(p.profit_per_day)} / ${formatDecimal(p.area / 500)} bases = ${formatDecimal(p.profit_per_base)}`;
|
|
|
|
|
|
const marketCell = tr.querySelectorAll('td')[7];
|
|
|
- marketCell.dataset.tooltip = `Market Capacity: ${formatWhole(p.average_traded_7d)} traded/day ÷ ${formatDecimal(p.output_per_day / p.area)} produced/day/area = ${formatWhole(p.market_capacity_area)} equivalent areas`;
|
|
|
+ marketCell.dataset.tooltip = `Market Capacity: ${formatWhole(p.average_traded_7d)} traded/day ÷ ${formatDecimal(p.output_per_day / (p.area / 500))} produced/day/base = ${formatDecimal(p.market_capacity_base)} equivalent bases`;
|
|
|
|
|
|
tbody.appendChild(tr);
|
|
|
}
|
|
|
@@ -276,10 +286,10 @@ interface Profit {
|
|
|
revenue: Metrics
|
|
|
input_costs: MatPrice[]
|
|
|
runs_per_day: number
|
|
|
- logistics_per_area: number
|
|
|
+ logistics_per_base: number
|
|
|
output_per_day: number
|
|
|
average_traded_7d: number
|
|
|
- market_capacity_area: number
|
|
|
+ market_capacity_base: number
|
|
|
}
|
|
|
|
|
|
interface ProfitWithMetrics extends Profit {
|
|
|
@@ -287,7 +297,7 @@ interface ProfitWithMetrics extends Profit {
|
|
|
opex_val: number;
|
|
|
revenue_val: number;
|
|
|
profit_per_day: number;
|
|
|
- profit_per_area: number;
|
|
|
+ profit_per_base: number;
|
|
|
break_even: number;
|
|
|
}
|
|
|
|