Show total raw input requirements with operating velocity on cards and two-line layout for Direct Inputs in inspector

This commit is contained in:
Christian 2026-10-16 11:49:26 +02:00
parent ff8983273e
commit 1212224c19
4 changed files with 48 additions and 13 deletions

View file

@ -169,6 +169,25 @@
background: var(--primary-blue-light);
}
.chain-item-row.chain-item-two-lines {
flex-direction: column;
align-items: stretch;
gap: 4px;
}
.chain-item-header-line {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.chain-item-sub-line {
display: flex;
align-items: center;
padding-left: 20px;
}
.chain-item-left {
display: flex;
align-items: center;

View file

@ -117,13 +117,17 @@ export function mountInspector(container, state, onAction) {
const parent = getProduct(inp.id);
const parentStock = state.products[inp.id]?.stock || 0;
return `
<div class="chain-item-row" data-product="${parent.id}">
<div class="chain-item-row chain-item-two-lines" data-product="${parent.id}">
<div class="chain-item-header-line">
<div class="chain-item-left">
<iconify-icon icon="${parent.icon}" style="color: ${parent.color};"></iconify-icon>
<span>${parent.name}</span>
<span class="chain-item-name">${parent.name}</span>
</div>
</div>
<div class="chain-item-sub-line">
<span class="chain-item-ratio font-mono">${inp.amount} per unit (<strong id="insp-inp-val-${inp.id}">${fmtNum(parentStock, 0)}</strong> on hand)</span>
</div>
</div>
`;
}).join('')}
</div>

View file

@ -48,17 +48,19 @@ export function renderFacilityCardHtml(p, pState, state, buyMode) {
if (!isRaw) {
inputsHtml = `
<div class="inputs-strip">
<span class="inputs-label">Inputs per line:</span>
<span class="inputs-label">Total Input Requirements (Operating Velocity):</span>
<div class="inputs-chips">
${p.inputs.map(inp => {
const inpProduct = getProduct(inp.id);
const currentStock = state.products[inp.id]?.stock || 0;
const neededPerLine = inp.amount * pState.lines;
const isLow = currentStock < neededPerLine;
const neededPerBatch = inp.amount * pState.lines;
const reqRatePerSec = inp.amount * pState.lines * (rates.cyclesPerSec || 0);
const isLow = currentStock < neededPerBatch;
return `
<span class="input-chip ${isLow ? 'low-stock' : ''}" id="chip-${p.id}-${inp.id}" title="${inpProduct?.name}">
<span class="input-chip ${isLow ? 'low-stock' : ''}" id="chip-${p.id}-${inp.id}" title="${inpProduct?.name} requirement: ${inp.amount * pState.lines} per batch, running at ${fmtNum(reqRatePerSec, 1)} units/sec consumption">
<iconify-icon icon="${inpProduct?.icon || 'lucide:box'}"></iconify-icon>
${inpProduct?.name}: <strong id="chip-val-${p.id}-${inp.id}">${fmtNum(currentStock, 0)}</strong> / ${fmtNum(neededPerLine, 0)}
<span>${inpProduct?.name}: <strong id="chip-val-${p.id}-${inp.id}">${fmtNum(currentStock, 0)}</strong> / ${fmtNum(neededPerBatch, 0)}</span>
<span class="input-chip-rate font-mono" id="chip-rate-${p.id}-${inp.id}">(${fmtNum(reqRatePerSec, 1)}/s)</span>
</span>
`;
}).join('')}
@ -309,8 +311,9 @@ export function updateFacilityCardDom(p, pState, state, buyMode) {
}
}
// Input Chips Stock
// Input Chips Stock & Real-time Rate
if (p.inputs && p.inputs.length > 0) {
const cyclesPerSec = rates.cyclesPerSec || 0;
for (const inp of p.inputs) {
const currentStock = state.products[inp.id]?.stock || 0;
const elChipVal = document.getElementById(`chip-val-${p.id}-${inp.id}`);
@ -320,6 +323,14 @@ export function updateFacilityCardDom(p, pState, state, buyMode) {
elChipVal.textContent = textVal;
}
}
const elChipRate = document.getElementById(`chip-rate-${p.id}-${inp.id}`);
if (elChipRate) {
const reqRatePerSec = inp.amount * pState.lines * cyclesPerSec;
const textRate = `(${fmtNum(reqRatePerSec, 1)}/s)`;
if (elChipRate.textContent !== textRate) {
elChipRate.textContent = textRate;
}
}
const elChip = document.getElementById(`chip-${p.id}-${inp.id}`);
if (elChip) {
const needed = inp.amount * pState.lines;

View file

@ -35,7 +35,8 @@ export function getProductOutputRates(p, pState, state) {
const isBottlenecked = !!pState.isBottlenecked;
// Output velocity: units per second
const prodRate = isBottlenecked ? 0 : (1 / baseCycleTime) * p.outputAmount * pState.lines;
const cyclesPerSec = (1 / baseCycleTime);
const prodRate = isBottlenecked ? 0 : cyclesPerSec * p.outputAmount * pState.lines;
// Logistics revenue rate: items sold passively per second * price
const logisticsUnits = pState.logistics || 0;
@ -51,7 +52,7 @@ export function getProductOutputRates(p, pState, state) {
revRate = effectiveSellRate * salePrice;
}
return { prodRate, revRate, isBottlenecked, milestoneMult };
return { prodRate, revRate, isBottlenecked, milestoneMult, baseCycleTime, cyclesPerSec };
}
/**