using UnityEngine;
namespace Cielonos.MainGame.Inventory
{
///
/// 提供基于 Luck 和 Curse 的通用随机结果。
/// 返回值始终位于 0 到 1 之间;具体含义由调用者自行映射。
///
public static class Randomizer
{
private const float MinimumValue = 0f;
private const float MaximumValue = 1f;
private const float BiasScale = 0.5f;
private const float GoodChanceRange = 0.45f;
private const float RegularStandardDeviation = 0.165f;
private const float ExtremeStandardDeviation = 0.06f;
private const float ExtremeGoodMean = 0.9f;
private const float ExtremeBadMean = 0.1f;
private const int MaximumNormalRollAttempts = 4;
///
/// 获取一个不受属性影响的基础随机值。
///
public static float Roll01()
{
return Random.Range(MinimumValue, MaximumValue);
}
///
/// 根据 Luck 和 Curse 获取一个表示结果好坏的随机值。
/// 0 表示最坏结果,1 表示最好结果;两项属性均为 0 时使用中心为 0.5 的正态分布。
///
public static float RollLuckCurse01(float luck, float curse)
{
GetDistributionParameters(luck, curse, out float bias, out float goodChance, out float extremeWeight);
if (Random.value < extremeWeight)
{
float mean = Random.value < goodChance ? ExtremeGoodMean : ExtremeBadMean;
return RollTruncatedNormal(mean, ExtremeStandardDeviation);
}
return RollTruncatedNormal(0.5f + 0.25f * bias, RegularStandardDeviation);
}
private static void GetDistributionParameters(float luck, float curse, out float bias, out float goodChance,
out float extremeWeight)
{
float normalizedLuck = Mathf.Clamp01(luck / 100f);
float normalizedCurse = Mathf.Clamp01(curse / 100f);
bias = HyperbolicTangent((normalizedLuck - normalizedCurse) / BiasScale);
goodChance = 0.5f + GoodChanceRange * bias;
extremeWeight = normalizedLuck * normalizedCurse;
}
///
/// Unity 的 Mathf 未提供 Tanh,因此通过指数定义计算双曲正切。
///
private static float HyperbolicTangent(float value)
{
float exponent = Mathf.Exp(2f * value);
return (exponent - 1f) / (exponent + 1f);
}
private static float RollTruncatedNormal(float mean, float standardDeviation)
{
for (int attempt = 0; attempt < MaximumNormalRollAttempts; attempt++)
{
float value = mean + RollStandardNormal() * standardDeviation;
if (value > MinimumValue && value < MaximumValue) return value;
}
return Mathf.Clamp(mean + RollStandardNormal() * standardDeviation, MinimumValue, MaximumValue);
}
private static float RollStandardNormal()
{
float u1 = Mathf.Max(Random.value, float.Epsilon);
float u2 = Random.value;
return Mathf.Sqrt(-2f * Mathf.Log(u1)) * Mathf.Cos(2f * Mathf.PI * u2);
}
}
}