fix: Changing way to sell with demand

This commit is contained in:
gauvainboiche
2026-10-08 12:34:33 +02:00
parent 1734487f1e
commit c98995f310
7 changed files with 103 additions and 38 deletions
+17 -8
View File
@@ -45,17 +45,26 @@ export function materialBatch(mm, totalProduced, rate, factor, discount) {
const basis = Math.max(totalProduced * mm.batchShareOfTotal, rate * mm.batchSecondsOfProduction);
const doublings = Math.max(0, Math.floor(Math.log2(Math.max(1, basis / mm.minBatch))));
const size = mm.minBatch * 2 ** doublings;
const cost = Math.max(1, Math.ceil(size * materialUnitPrice(mm, totalProduced) * factor * discount));
return { size, cost };
const cents = size * materialUnitPrice(mm, totalProduced) * factor * discount;
return { size, cost: Math.max(0.01, Math.round(cents * 100) / 100) };
}
/**
* Units customers want to buy per second at `price`.
* Demand is maxed at or below the fair price and drops steeply above it, so
* extra production is only worth selling at a higher price while supply is
* short; to sell more at the fair price, the player needs more demand.
* Units customers want to buy per second at `price`: a smooth curve, with more
* demand the lower the price and less the higher it is (`referencePrice` is
* just where demand equals baseDemand x multiplier).
*/
export function demandPerSecond(market, price, multiplier) {
const overpriced = Math.max(1, price / market.fairPrice);
return (market.baseDemand * multiplier) / overpriced ** market.elasticity;
return market.baseDemand * multiplier * (market.referencePrice / price) ** market.elasticity;
}
/**
* The price at which customers buy exactly what is produced. Selling for less
* gains no volume (everything is already sold); selling for more leaves stock
* unsold. It moves with output and every demand bonus, and the UI deliberately
* does not reveal it: the player finds it from the market feedback.
*/
export function balancedPrice(market, supplyPerSecond, multiplier) {
const demandAtReference = market.baseDemand * multiplier;
return market.referencePrice * (demandAtReference / supplyPerSecond) ** (1 / market.elasticity);
}
+19 -4
View File
@@ -6,7 +6,8 @@ import { t } from '../services/i18n.js';
import { $, h, icon, refreshStaticIcons } from './dom.js';
const SLIDER_STEPS = 1000;
const TREND = { '-1': '▼', 1: '▲' }; // last move of the material price
const MARKET_TOLERANCE = 1.15; // demand within ±15 % of output counts as balanced
const TREND ={ '-1': '▼', 1: '▲' }; // last move of the material price
function setText(node, text) {
if (node.textContent !== text) node.textContent = text;
@@ -184,14 +185,28 @@ export function createView(actions) {
);
}
/**
* Feedback on the price: what customers want versus what is produced, and
* which side of the balance the player is on. It never gives the balanced
* price itself: it depends on output and every bonus, so it must be found.
*/
function marketHint(d) {
const demand = fmt.number(d.demand);
if (d.rate <= 0) return t('ui.hintDemandOnly', { demand });
const ratio = d.demand / d.rate;
let verdict = 'ui.marketBalanced';
if (ratio > MARKET_TOLERANCE) verdict = 'ui.marketShort';
else if (ratio < 1 / MARKET_TOLERANCE) verdict = 'ui.marketLong';
return `${t('ui.hintDemand', { demand, production: fmt.number(d.rate) })} ${t(verdict)}`;
}
function updateMarket(state, d) {
const { fairPrice, minPrice } = ctx.rules.market;
const { minPrice } = ctx.rules.market;
if (document.activeElement !== el.priceInput) el.priceInput.value = state.price.toFixed(2);
if (document.activeElement !== el.priceSlider) {
el.priceSlider.value = Math.round(sliderFromPrice(Math.max(state.price, minPrice)));
}
const hintKey = state.price > fairPrice ? 'ui.hintHigh' : 'ui.hintFair';
setText(el.priceHint, t(hintKey, { fair: money(fairPrice), demand: fmt.number(d.demand) }));
setText(el.priceHint, marketHint(d));
const nextLevelMultiplier = ctx.rules.marketing.demandPerLevel ** (state.marketing + 1);
setText(