feat: New features (research, invest, gambling, new game+, trophies)
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
// Investments: nine societies in three risk tiers. Every `intervalSeconds` each
|
||||
// price moves by a random percentage within the tier's swing (low ±5 %, mid
|
||||
// ±15 %, high ±50 %), then drifts back towards its fair value (more weakly for the wilder tiers), and
|
||||
// holdings pay a small dividend. Buying and selling carries a small fee.
|
||||
|
||||
import { clamp } from './formulas.js';
|
||||
|
||||
export function holdingValue(state, id) {
|
||||
return state.invest.holdings[id] * state.invest.prices[id];
|
||||
}
|
||||
|
||||
export function portfolio(state, ctx) {
|
||||
let value = 0;
|
||||
let basis = 0;
|
||||
for (const s of ctx.investments.societies) {
|
||||
value += holdingValue(state, s.id);
|
||||
basis += state.invest.basis[s.id];
|
||||
}
|
||||
return { value, basis, profit: value - basis };
|
||||
}
|
||||
|
||||
function repriceAll(state, ctx, rng, dividendMultiplier) {
|
||||
const cfg = ctx.investments;
|
||||
const inv = state.invest;
|
||||
for (const s of cfg.societies) {
|
||||
const tier = cfg.tiers[s.tier];
|
||||
const before = inv.prices[s.id];
|
||||
let price = before * (1 + (rng() * 2 - 1) * tier.swing);
|
||||
price += (s.basePrice - price) * tier.reversion;
|
||||
price = clamp(price, s.basePrice * cfg.minRatio, s.basePrice * cfg.maxRatio);
|
||||
inv.prices[s.id] = price;
|
||||
inv.change[s.id] = price / before - 1;
|
||||
state.funds += inv.holdings[s.id] * price * tier.dividend * dividendMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
export function stepInvestments(state, ctx, dt, rng, mods) {
|
||||
state.investTimer += dt;
|
||||
while (state.investTimer >= ctx.investments.intervalSeconds) {
|
||||
state.investTimer -= ctx.investments.intervalSeconds;
|
||||
repriceAll(state, ctx, rng, mods.dividend);
|
||||
}
|
||||
}
|
||||
|
||||
/** Spend `fraction` of the current funds on society `id`. */
|
||||
export function buyShares(state, ctx, id, fraction) {
|
||||
if (!ctx.societiesById.has(id)) return false;
|
||||
const spend = state.funds * clamp(fraction, 0, 1);
|
||||
if (!(spend >= 0.01)) return false;
|
||||
state.funds -= spend;
|
||||
state.invest.holdings[id] += (spend * (1 - ctx.investments.fee)) / state.invest.prices[id];
|
||||
state.invest.basis[id] += spend;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Sell `fraction` of what is held in society `id`. */
|
||||
export function sellShares(state, ctx, id, fraction) {
|
||||
if (!ctx.societiesById.has(id)) return false;
|
||||
const inv = state.invest;
|
||||
const share = clamp(fraction, 0, 1);
|
||||
if (!(inv.holdings[id] > 0) || share <= 0) return false;
|
||||
|
||||
const proceeds = inv.holdings[id] * share * inv.prices[id] * (1 - ctx.investments.fee);
|
||||
const basisPart = inv.basis[id] * share;
|
||||
state.funds += proceeds;
|
||||
state.stats.investProfit += proceeds - basisPart;
|
||||
if (share >= 1) {
|
||||
inv.holdings[id] = 0;
|
||||
inv.basis[id] = 0;
|
||||
} else {
|
||||
inv.holdings[id] -= inv.holdings[id] * share;
|
||||
inv.basis[id] -= basisPart;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user