diff --git a/frontend/js/ui/inspectorView.js b/frontend/js/ui/inspectorView.js index c70396e..3a2b6df 100644 --- a/frontend/js/ui/inspectorView.js +++ b/frontend/js/ui/inspectorView.js @@ -1,8 +1,6 @@ -// Production - The Idle Game: Desktop Inspector & Supply Chain Analytics Panel -// Fine-grained DOM updates without innerHTML teardowns - import { PRODUCTS, SECTORS, getProduct } from '../data/products.js'; import { fmtCurrency, fmtNum } from '../core/formatters.js'; +import { computeProductNetFlow } from './production/ratesCalculator.js'; let currentMountedProductId = null; @@ -18,6 +16,7 @@ export function mountInspector(container, state, onAction) { const totalLiquidationValue = (pState.stock || 0) * product.basePrice; const halfLiquidationValue = totalLiquidationValue * 0.5; + const netFlow = computeProductNetFlow(product, state); let html = `
@@ -43,12 +42,14 @@ export function mountInspector(container, state, onAction) { ${fmtCurrency(product.basePrice)}
- Production Lines: - ${pState.lines} active + Net Supply Flow: + + ${netFlow.netRate > 0.01 ? '+' : ''}${fmtNum(netFlow.netRate, 1)}/s +
Logistics Fleets: - ${pState.logistics || 0} active + ${pState.logistics || 0} active (${fmtNum((pState.logistics || 0) * 0.75, 2)}/s)
@@ -181,6 +182,20 @@ export function updateInspector(container, state) { const btnAll = document.getElementById('insp-btn-all'); if (btnAll) btnAll.disabled = pState.stock < 1; + // Update Net Flow rate + const netFlow = computeProductNetFlow(product, state); + const elNetRate = document.getElementById('insp-net-rate'); + if (elNetRate) { + const sign = netFlow.netRate > 0.01 ? '+' : ''; + const netText = `${sign}${fmtNum(netFlow.netRate, 1)}/s`; + if (elNetRate.textContent !== netText) { + elNetRate.textContent = netText; + } + const isDeficit = netFlow.netRate < -0.05; + const isSurplus = netFlow.netRate > 0.05; + elNetRate.className = `metric-val font-mono ${isDeficit ? 'text-warning' : (isSurplus ? 'text-profit' : '')}`; + } + // Update input values if (product.inputs) { for (const inp of product.inputs) { @@ -191,3 +206,4 @@ export function updateInspector(container, state) { } } } + diff --git a/frontend/js/ui/production/facilityCard.js b/frontend/js/ui/production/facilityCard.js index a07174f..5c9d4e8 100644 --- a/frontend/js/ui/production/facilityCard.js +++ b/frontend/js/ui/production/facilityCard.js @@ -1,7 +1,7 @@ // Production UI: Individual Facility Card (HTML Mount and Fine-grained DOM update) import { getProduct, getNextMilestone, getUnitMilestoneMultiplier, SECTORS } from '../../data/products.js'; import { fmtCurrency, fmtNum } from '../../core/formatters.js'; -import { calculateMultiCost, getProductOutputRates } from './ratesCalculator.js'; +import { calculateMultiCost, getProductOutputRates, computeProductNetFlow } from './ratesCalculator.js'; export function renderFacilityCardHtml(p, pState, state, buyMode) { const sector = SECTORS.find(s => s.id === p.sector); @@ -113,6 +113,10 @@ export function renderFacilityCardHtml(p, pState, state, buyMode) { ${rates.isBottlenecked ? '0.0 (Paused)' : `${fmtNum(rates.prodRate, 1)}/s`} +
+ Net Balance: + +0.0/s +
Logistics Rev: +$${fmtNum(rates.revRate, 2)}/s @@ -125,10 +129,6 @@ export function renderFacilityCardHtml(p, pState, state, buyMode) { Logistics Auto-Sell: ${pState.logistics || 0} fleets (${fmtNum((pState.logistics || 0) * 0.75, 2)}/s)
-
- Milestone Eff: - ${milestoneMult}x Speed -
${inputsHtml} @@ -190,6 +190,20 @@ export function updateFacilityCardDom(p, pState, state, buyMode) { elRateUnits.className = `stat-value font-mono ${rates.isBottlenecked ? 'text-warning' : 'text-profit'}`; } + // Live Net Flow (Production minus Downstream Consumption minus Logistics Sales) + const netFlow = computeProductNetFlow(p, state); + const elNetFlow = document.getElementById(`net-flow-${p.id}`); + if (elNetFlow) { + const sign = netFlow.netRate > 0.01 ? '+' : ''; + const netText = `${sign}${fmtNum(netFlow.netRate, 1)}/s`; + if (elNetFlow.textContent !== netText) { + elNetFlow.textContent = netText; + } + const isDeficit = netFlow.netRate < -0.05; + const isSurplus = netFlow.netRate > 0.05; + elNetFlow.className = `stat-value font-mono ${isDeficit ? 'text-warning' : (isSurplus ? 'text-profit' : '')}`; + } + const elRateRev = document.getElementById(`rate-rev-${p.id}`); if (elRateRev) { const revText = `+$${fmtNum(rates.revRate, 2)}/s`; diff --git a/frontend/js/ui/production/ratesCalculator.js b/frontend/js/ui/production/ratesCalculator.js index 7892ecc..3e05da4 100644 --- a/frontend/js/ui/production/ratesCalculator.js +++ b/frontend/js/ui/production/ratesCalculator.js @@ -1,5 +1,4 @@ -// Production UI: Cost & Rate Calculations -import { getNextMilestone, getUnitMilestoneMultiplier } from '../../data/products.js'; +import { PRODUCTS, getNextMilestone, getUnitMilestoneMultiplier } from '../../data/products.js'; export function calculateMultiCost(baseCost, currentCount, multiplier) { const growth = 1.15; @@ -64,16 +63,32 @@ export function computeProductNetFlow(product, state) { // Calculate how much active downstream factories consume per second let consumedRate = 0; - // Import PRODUCTS dynamically from caller or check through global/state if needed - // In our architecture, caller passes or we compute from state.products and dependencies + for (const otherP of PRODUCTS) { + if (!otherP.inputs) continue; + const inpMatch = otherP.inputs.find(inp => inp.id === product.id); + if (inpMatch) { + const otherState = state.products[otherP.id]; + if (otherState && otherState.unlocked && otherState.lines > 0 && !otherState.isBottlenecked) { + const otherRates = getProductOutputRates(otherP, otherState, state); + const cyclesPerSec = otherRates.prodRate / (otherP.outputAmount * otherState.lines); + consumedRate += inpMatch.amount * otherState.lines * cyclesPerSec; + } + } + } + const logisticsUnits = pState.logistics || 0; const maxSellCapacity = logisticsUnits * 0.75; - const soldRate = Math.min(maxSellCapacity, pState.stock > 0 ? maxSellCapacity : effectiveProd); + const availableForSale = Math.max(0, effectiveProd - consumedRate); + const soldRate = Math.min(maxSellCapacity, pState.stock > 0 ? maxSellCapacity : availableForSale); + + const netRate = effectiveProd - consumedRate - soldRate; return { prodRate: effectiveProd, + consumedRate, soldRate, - netRate: effectiveProd - soldRate + netRate }; } +