Code/k/PityCounter.cs
using System;

namespace Sandbox.k;

/// <summary>
/// A pity counter is a system that increases the probability
/// of a rare event occurring after a series of failures.
/// </summary>
public class PityCounter
{
	private int _failures;
	private readonly int _maxFailures; // The guaranteed success threshold
	private readonly float _baseChance; // Base probability of success
	private readonly float _pityIncrease; // How much chance increases per failure
	
	public int Failures => _failures;
	
	/// <summary>
	/// How It Works:
	/// 1) Each failed attempt increments the pity counter.
	///	2) The chance to get a rare outcome increases slightly after each failure.
	///	3) If a guaranteed threshold (e.g., 10 fails) is reached, the next attempt must succeed.
	///	4) If a success happens before the pity limit, the counter resets to 0.
	/// </summary>
	/// <param name="baseChance">The initial probability of success (before pity increases). Value from 0 to 1</param>
	/// <param name="pityIncrease">The amount by which the success chance increases after each failure.</param>
	/// <param name="maxFailures">The number of consecutive failures required to guarantee success.</param>
	public PityCounter(float baseChance, float pityIncrease, int maxFailures)
	{
		_baseChance = baseChance;
		_pityIncrease = pityIncrease;
		_maxFailures = maxFailures;
		_failures = 0;
	}

	public bool TryTrigger(float luckMultiplier = 1f)
	{
		float currentChance = (_baseChance * luckMultiplier) + (_failures * _pityIncrease);
		// If max failures reached, guarantee success
		if (_failures >= _maxFailures || Random.Shared.Float() < currentChance)
		{
			_failures = 0; // Reset pity counter
			return true;
		}

		_failures++; // Increase pity counter on failure
		return false;
	}

	public void Reset() => _failures = 0;
}