public class LootProbabilities : MonoBehaviour { private const int MAX_LEVEL = 20; private const float MAX_RARITY = 10; [SerializeField] private ParticleSystem.MinMaxCurve lootRarityPerLevel; [SerializeField] private AnimationCurve probabilitiesDistribution; [ReadOnly, SerializeField] private LevelProbabilities[] probabilitiesPerLevel; private void OnValidate() { lootRarityPerLevel.curveMultiplier = 1f; CalculateChances(); } private void Reset() { var upperAnimationCurve = AnimationCurve.EaseInOut(0, MAX_RARITY/2, MAX_LEVEL, MAX_RARITY); var lowerAnimationCurve = AnimationCurve.EaseInOut(0, 0, MAX_LEVEL, MAX_RARITY/2); probabilitiesDistribution = new AnimationCurve(new Keyframe(0.01f, 0.01f), new Keyframe(1, 1)); lootRarityPerLevel = new ParticleSystem.MinMaxCurve(1, lowerAnimationCurve, upperAnimationCurve) { mode = ParticleSystemCurveMode.TwoCurves }; CalculateChances(); } public int GetRarity(int level, float random) => probabilitiesPerLevel[level].GetRarity(random); private void CalculateChances() { var (minRarityAtLevel,maxRarityAtLevel) = RaritySpreadPerLevel(); probabilitiesPerLevel = new LevelProbabilities[MAX_LEVEL + 1]; for (int level = 0; level <= MAX_LEVEL; level++) { probabilitiesPerLevel[level] = new LevelProbabilities(minRarityAtLevel[level], maxRarityAtLevel[level]); var chancesSum = CalculateChancesSum(minRarityAtLevel[level], maxRarityAtLevel[level]); for (int rarity = minRarityAtLevel[level]; rarity <= maxRarityAtLevel[level]; rarity++) { var chance = probabilitiesDistribution.Evaluate((float)(rarity - minRarityAtLevel[level]) / (maxRarityAtLevel[level] - minRarityAtLevel[level])); probabilitiesPerLevel[level].AddChance(rarity, chance / chancesSum); } } return; (int[],int[]) RaritySpreadPerLevel() { var minRarity = new int[MAX_LEVEL + 1]; var maxRarity = new int[MAX_LEVEL + 1]; for (var i = 0; i < MAX_LEVEL + 1; i++) { minRarity[i] = (int)lootRarityPerLevel.Evaluate(i, 0f); maxRarity[i] = (int)lootRarityPerLevel.Evaluate(i, 1f); } return (minRarity, maxRarity); } float CalculateChancesSum(int minRarity, int maxRarity) { float chancesSum = 0; for (int j = minRarity; j <= maxRarity; j++) { var chance = probabilitiesDistribution.Evaluate((float)(j - minRarity) / (maxRarity - minRarity)); chancesSum += chance; } return chancesSum; } } [Serializable] private class LevelProbabilities { [ReadOnly, SerializeField] private List rarityPercentages; internal LevelProbabilities(int minRarity, int maxRarity) => rarityPercentages = new List(maxRarity - minRarity + 1); internal void AddChance(int rarity, float chance) => rarityPercentages.Add(new RarityPercentages { rarity = rarity, chance = (float)Math.Round(Mathf.Clamp(chance * 100f, 0, 100f),2) }); internal int GetRarity(float percentageChance) { var totalChance = 0f; foreach (var chance in rarityPercentages) { totalChance += chance.chance; if (percentageChance * 100f <= totalChance) return chance.rarity; } Debug.LogException(new ArgumentOutOfRangeException(nameof(percentageChance), " greater than sum of percentages")); return -1; } [Serializable] private struct RarityPercentages { public int rarity; public float chance; } } }