Game/DesertPumpGame.Pressure.cs

Partial class for DesertPumpGame handling the pressure mechanic and surge logic. It defines tuning constants, pressure state, derived properties (bonus, stage, text), surge event tracking, and methods to tick decay, bump pressure, roll for surges, and set pressure for testing.

NetworkingFile Access
namespace DesertPump;

public sealed partial class DesertPumpGame
{
	// The two things that make one click different from the next: pressure, which
	// rewards keeping a rhythm going, and surges, which occasionally pay out several
	// clicks at once.
	//
	// Nothing sits above the class declaration on purpose. s&box turns a comment there
	// into a [Description] attribute, and this type already gets one from the comment
	// over in DesertPumpGame.cs - two of them is warning SB2000.

	// ---- tuning -------------------------------------------------------------
	//
	// Both of these are flat multipliers on manual pumping only. They shift the curve,
	// they don't bend it - nothing here compounds with anything else, which is what
	// separates it from the upgrade tracks that ran the economy away three times over.
	//
	// Measured, not guessed: tools/balance.py mirrors these constants in click_bonus()
	// and reports the cost. As shipped a player clicking flat out finishes in about 37
	// hours against 48 before, and a casual one in 142 against 157. Set PressureMaxBonus
	// to 0 and SurgeMultiplier to 1 to get the old curve back exactly.

	/// <summary>Manual pumps needed to go from cold to full pressure with no decay.</summary>
	public const float PressureClicksToFull = 8f;

	/// <summary>Seconds from full pressure back to nothing once you stop clicking.</summary>
	public const float PressureDecaySeconds = 3.5f;

	/// <summary>Extra litres per click at full pressure, as a fraction of the base.</summary>
	public const float PressureMaxBonus = 0.2f;

	/// <summary>
	/// Chance a manual pump comes up a surge instead. Deliberately rare and large rather
	/// than frequent and small - the same average payout either way, but one reads as an
	/// event worth looking up for and the other reads as the numbers being noisy. At four
	/// clicks a second this is one surge every twelve seconds or so.
	/// </summary>
	public const float SurgeChance = 0.02f;

	/// <summary>What a surge multiplies that one click by.</summary>
	public const float SurgeMultiplier = 7f;

	// ---- state --------------------------------------------------------------

	/// <summary>
	/// 0-1. Rises with every manual pump and bleeds away when you stop, so it reads as
	/// a rhythm you're holding rather than a resource you're spending.
	/// </summary>
	public float Pressure { get; private set; }

	/// <summary>What manual pumping is multiplied by right now.</summary>
	public float PressureBonus => 1f + Pressure * PressureMaxBonus;

	/// <summary>True while pressure is pinned at the top.</summary>
	public bool PressureHot => Pressure >= 0.995f;

	/// <summary>
	/// 0-3. The HUD colours the gauge off this rather than off the raw float, so the
	/// bands stay put if the tuning above moves.
	/// </summary>
	public int PressureStage => Pressure switch
	{
		>= 0.995f => 3,
		>= 0.66f => 2,
		>= 0.33f => 1,
		_ => 0
	};

	/// <summary>"+25%" - what pressure is currently adding.</summary>
	public string PressureBonusText => $"+{(PressureBonus - 1f) * 100f:0}%";

	/// <summary>
	/// Fired instead of <see cref="Pumped"/> when a click comes up a surge, with the
	/// litres it was worth. The two are mutually exclusive - exactly one fires per pump.
	/// </summary>
	public Action<double> Surged { get; set; }

	/// <summary>Surges in this run. Never saved - it's a session curiosity, not progress.</summary>
	public int SurgeCount { get; private set; }

	/// <summary>Bleed pressure off. Called every frame from the main update.</summary>
	void TickPressure()
	{
		if ( Pressure <= 0f )
			return;

		Pressure = Math.Max( 0f, Pressure - Time.Delta / PressureDecaySeconds );
	}

	/// <summary>One manual pump's worth of pressure.</summary>
	void BumpPressure()
	{
		Pressure = Math.Min( 1f, Pressure + 1f / PressureClicksToFull );
	}

	/// <summary>Does this click come up a surge?</summary>
	bool RollSurge() => System.Random.Shared.NextSingle() < SurgeChance;

	/// <summary>
	/// Force the gauge somewhere, for testing the HUD without clicking eight times.
	/// Not reachable from the game itself.
	/// </summary>
	public void SetPressure( float value )
	{
		Pressure = Math.Clamp( value, 0f, 1f );
	}
}