164 lines
7.1 KiB
C++
164 lines
7.1 KiB
C++
#include "game/sim/economy.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
#include "game/sim/numeric.h"
|
|
|
|
namespace sots::sim {
|
|
|
|
int SavingsInterest(int savings, bool ownsSystems) {
|
|
if (savings < 0 || !ownsSystems) return 0;
|
|
return Ftol(static_cast<double>(savings) * 0.01);
|
|
}
|
|
|
|
int DebtInterest(int savings) {
|
|
if (savings >= 0) return 0;
|
|
return Ftol(-static_cast<double>(savings) * 0.15);
|
|
}
|
|
|
|
int MaintenanceCost(int maintenance, double difficultyDivisor) {
|
|
const int d = Ftol(difficultyDivisor);
|
|
if (d == 0) return maintenance;
|
|
return maintenance / d;
|
|
}
|
|
|
|
int ExpenseTotal(const std::vector<ExpenseSlider>& sliders, int availableBeforeExpenses) {
|
|
std::int64_t minimums = 0;
|
|
std::int64_t extras = 0;
|
|
for (const ExpenseSlider& s : sliders) {
|
|
minimums += s.minimum;
|
|
const int span = std::max(0, s.maximum - s.minimum);
|
|
extras += ClampT(s.requested - s.minimum, 0, span);
|
|
}
|
|
const std::int64_t headroom = static_cast<std::int64_t>(availableBeforeExpenses) - minimums;
|
|
const std::int64_t total = minimums + std::min(extras, headroom);
|
|
return static_cast<int>(ClampT<std::int64_t>(total, -2147483648LL, 2147483647LL));
|
|
}
|
|
|
|
int ResearchPointsFromMoney(int researchMoney, double difficultyMult, double resMod,
|
|
double shrm, double trm, double techMult, double serverResMod,
|
|
double resScl) {
|
|
const double base = (static_cast<double>(researchMoney) / 50.0) * 1.15 * 0.5 * 0.85;
|
|
const double rp = difficultyMult * base * (resMod + shrm + trm) * techMult * serverResMod * resScl;
|
|
return Ftol(rp);
|
|
}
|
|
|
|
Budget ComputeBudget(const BudgetInputs& in, bool projected) {
|
|
Budget b;
|
|
|
|
b.savingsInterest = SavingsInterest(in.savings, in.ownsSystems);
|
|
b.debtInterest = DebtInterest(in.savings);
|
|
|
|
for (int inc : in.systemIncome) {
|
|
if (inc >= 0) b.systemIncomePositive = SaturatingAdd(b.systemIncomePositive, inc);
|
|
else b.systemIncomeNegative = SaturatingAdd(b.systemIncomeNegative, -inc);
|
|
}
|
|
b.tradeIncome = in.tradeIncome;
|
|
b.secondaryManagerIncome = in.secondaryManagerIncome;
|
|
b.shipCarriedPopIncome = in.shipCarriedPopIncome;
|
|
b.maintenance = MaintenanceCost(in.maintenance, in.maintenanceDivisor);
|
|
|
|
// Running total of the fixed lines; the slots filled later start at zero.
|
|
auto running = [&]() -> std::int64_t {
|
|
return static_cast<std::int64_t>(b.systemIncomePositive) + b.tradeIncome +
|
|
b.shipCarriedPopIncome + b.secondaryManagerIncome + b.savingsInterest +
|
|
b.bonusIncome - b.systemIncomeNegative - b.maintenance - b.researchMoneyKept -
|
|
b.debtInterest - b.construction - b.expenses - b.researchMoneyGiven -
|
|
b.savingsGiven;
|
|
};
|
|
|
|
b.expenses = ExpenseTotal(in.expenses, static_cast<int>(std::max<std::int64_t>(0, running())));
|
|
b.available = static_cast<int>(std::max<std::int64_t>(0, running()));
|
|
|
|
if (!in.isAI && b.available > 0) {
|
|
b.construction = std::min(std::max(0, in.constructionDemand), b.available);
|
|
}
|
|
const int availAfterConstruction = b.available - b.construction;
|
|
|
|
const double rate = projected ? 0.0 : in.researchRate;
|
|
b.researchMoney = std::max(0, Ftol(static_cast<double>(availAfterConstruction) * rate));
|
|
b.researchPoints = ResearchPointsFromMoney(b.researchMoney, in.researchDifficultyMult,
|
|
in.resMod, in.shrm, in.trm, in.techResearchMult,
|
|
in.serverResMod, in.resScl);
|
|
b.totalResearchPoints = std::max(0, b.researchPoints + in.tra + in.trp);
|
|
|
|
const int pct = ClampT(in.aidResearchPercent, 0, 100);
|
|
b.researchMoneyGiven = static_cast<int>(static_cast<std::int64_t>(b.researchMoney) * pct / 100);
|
|
b.researchPointsGiven = static_cast<int>(static_cast<std::int64_t>(b.totalResearchPoints) * pct / 100);
|
|
b.totalResearchPoints -= b.researchPointsGiven;
|
|
b.hasResearchAllocation = in.hasResearchTarget;
|
|
b.researchMoneyKept = b.researchMoney - b.researchMoneyGiven;
|
|
|
|
b.bonusIncome = Ftol((in.techIncomeMult - 1.0) * static_cast<double>(running()));
|
|
b.savingsGiven = static_cast<int>(std::min<std::int64_t>(std::max<std::int64_t>(running(), 0),
|
|
std::max(0, in.aidSavings)));
|
|
|
|
b.net = static_cast<int>(ClampT<std::int64_t>(running(), -2147483648LL, 2147483647LL));
|
|
return b;
|
|
}
|
|
|
|
// ---- trade ---------------------------------------------------------------------------
|
|
|
|
int TradeRoutesSupported(double civilianPop, double imperialPop, const TuningTable& t) {
|
|
double routes = 0;
|
|
if (t.TRADE_ROUTE_REQ_CIVPOPULATION > 0) routes += std::ceil(civilianPop / t.TRADE_ROUTE_REQ_CIVPOPULATION);
|
|
if (t.TRADE_ROUTE_REQ_IMPPOPULATION > 0) routes += std::ceil(imperialPop / t.TRADE_ROUTE_REQ_IMPPOPULATION);
|
|
return std::max(1, Ftol(routes));
|
|
}
|
|
|
|
int TradeRouteGrossIncome(const TradeRouteState& route, const TuningTable& t) {
|
|
if (route.ageTurns < t.TRADE_ROUTE_STARTUP_TURNS) return t.TRADE_ROUTE_STARTUP_INCOME;
|
|
|
|
const int perFreighter[3] = {t.TRADE_ROUTE_INCOME_PERFREIGHTER_CRQ,
|
|
t.TRADE_ROUTE_INCOME_PERFREIGHTER_CR,
|
|
t.TRADE_ROUTE_INCOME_PERFREIGHTER_DE};
|
|
int capLeft = t.TRADE_ROUTE_MAX_FREIGHTERS;
|
|
std::int64_t income = t.TRADE_ROUTE_MIN_INCOME;
|
|
for (int c = 0; c < 3; ++c) {
|
|
const int n = std::min(std::max(0, route.freighters[c]), std::max(0, capLeft));
|
|
income += static_cast<std::int64_t>(n) * perFreighter[c];
|
|
capLeft -= n;
|
|
}
|
|
double v = static_cast<double>(income);
|
|
v *= 1.0 + t.STATION_BONUS_TRADE_INCOME * route.tradeStationsAtSystem;
|
|
if (route.partnerAddicted) v *= t.ADDICTION_TRADE_MOD;
|
|
return Ftol(v);
|
|
}
|
|
|
|
int TradeRouteIncome(const TradeRouteState& route, bool asOwner, double difficultyTradeMult,
|
|
const TuningTable& t) {
|
|
const double share = Clamp01(t.TRADE_ROUTE_OWNERS_SHARE);
|
|
const double part = asOwner ? share : 1.0 - share;
|
|
return Ftol(static_cast<double>(TradeRouteGrossIncome(route, t)) * part * difficultyTradeMult);
|
|
}
|
|
|
|
// ---- bankruptcy -----------------------------------------------------------------------
|
|
|
|
BankruptcyLimits ComputeBankruptcyLimits(int maxIncome, const TuningTable& t) {
|
|
BankruptcyLimits l;
|
|
l.eliminationFloor = -Ftol(t.BANKRUPTCY_PROTECTION_LIMIT_FACTOR * static_cast<double>(maxIncome));
|
|
l.protectionLimit = std::max(l.eliminationFloor, -maxIncome);
|
|
return l;
|
|
}
|
|
|
|
int BankruptcyLevel(int savings, const BankruptcyLimits& limits) {
|
|
if (savings < limits.eliminationFloor) return 2;
|
|
if (savings < limits.protectionLimit) return 1;
|
|
return 0;
|
|
}
|
|
|
|
bool BankruptcyStep(BankruptcyState& state, int level, int currentTurn, const TuningTable& t) {
|
|
if (level == 0) {
|
|
state.warningLevel = 0;
|
|
state.startTurn = 0;
|
|
return false;
|
|
}
|
|
if (state.warningLevel != level) {
|
|
state.warningLevel = level;
|
|
state.startTurn = currentTurn;
|
|
}
|
|
return level == 2 && (currentTurn - state.startTurn) >= t.BANKRUPTCY_ELIMINATION_TURNS;
|
|
}
|
|
|
|
} // namespace sots::sim
|