cs2-outnumbered/Outnumbered/Domain/SurvivalEconomy.cs
Kamal Tufekcic d701598350
All checks were successful
CI / build (push) Successful in 32s
CI / release (push) Successful in 32s
CI / lint (push) Successful in 30s
initial commit
2026-07-05 13:28:35 +03:00

48 lines
2.8 KiB
C#

using Outnumbered.Config;
namespace Outnumbered.Domain;
// Pure survival run-economy + escalation curves. The wave state machine + run banking stay engine-side; this owns the
// numbers they read.
public static class SurvivalEconomy
{
// Bots ALIVE at once this wave (simultaneous pressure), ramping from AliveBase to AliveCap.
public static int AliveForWave(int wave, SurvivalConfig c) =>
Math.Clamp(c.AliveBase + c.AlivePerWave * (wave - 1), 1, c.AliveCap);
// Total kills to clear a wave.
public static int WaveBudget(int wave, int aliveHumans, SurvivalConfig c) =>
Math.Max(1, c.BudgetBase + c.BudgetPerWave * (wave - 1) + c.BudgetPerPlayer * aliveHumans);
// Monotonic escalate-only handicap floor in t-space; -1 = no floor (idle / between runs).
public static double HandicapFloor(int wave, SurvivalConfig c) =>
// wave>=1 and Max(1,MaxNerfWave)>=1 => quotient>=0, so the lower clamp can never bind; Min(1,x) == Clamp(x,0,1) here.
wave <= 0 ? -1.0 : Math.Min(1.0, wave / (double)Math.Max(1, c.MaxNerfWave));
// Per-wave XP multiplier: ramps exponentially from x1 (wave 1) to xWinMult (the final wave). WinMult is the single
// knob (the FINAL-wave / "win" multiplier). Back-loaded by design so early waves pay ~nothing and the last few pay big.
// waveMult(w) = WinMult ^ ((w-1)/(WaveCount-1))
public static double WaveMult(int wave, SurvivalConfig c) =>
Math.Pow(c.WinMult, (wave - 1) / (double)Math.Max(1, c.WaveCount - 1));
// XP granted at the END of ONE cleared wave: raw wave XP (HP-damage + HS/crit, already xp_mult-card-scaled when banked)
// x prestige x the wave multiplier. NO per-run cap, NO XpBoost stat, NO handicap mult here — depth is the gate (you must
// survive + contribute to reach the big back-wave multipliers), and the handicap mult stays excluded so run XP can't
// re-couple to gameable K/D. 0 for a non-positive wave.
public static long WaveXpLump(double rawWaveXp, int wave, int prestige, SurvivalConfig c, ProgressionConfig p)
{
if (rawWaveXp <= 0) return 0;
double lump = rawWaveXp * ProgressionModel.PrestigeXpMultiplier(prestige, p) * WaveMult(wave, c);
return (long)Math.Floor(lump);
}
// Raw combat XP banked into the CURRENT wave's accumulator, scaled by the xp_mult card (so it compounds with the
// per-wave prestige x waveMult chain applied at grant time).
public static double AccrueWaveXp(double amount, double xpMultCardPct) =>
amount * (1.0 + xpMultCardPct / 100.0);
// Team-card squad multiplier (compounding): global_deal increases, global_take decreases.
public static double TeamMult(int level, double perPickPct, bool increase) =>
increase ? Math.Pow(1.0 + perPickPct / 100.0, level)
: Math.Pow(Math.Max(0.0, 1.0 - perPickPct / 100.0), level);
}