internal static float NormalDistibutionRandom(VarianceBounds bounds, int step = -1) { // compute a random number that fits a gaussian function https://en.wikipedia.org/wiki/Gaussian_function // iterative w/ limit adapted from https://natedenlinger.com/php-random-number-generator-with-normal-distribution-bell-curve/ const int iterationLimit = 10; var iterations = 0; float randomNumber; do { var rand1 = Random.value; var rand2 = Random.value; var gaussianNumber = Mathf.Sqrt(-2 * Mathf.Log(rand1)) * Mathf.Cos(2 * Mathf.PI * rand2); var mean = (bounds.max + bounds.min) / 2; randomNumber = (gaussianNumber * bounds.standardDeviation) + mean; if (step > 0) { randomNumber = Mathf.RoundToInt(randomNumber / step) * step; } iterations++; } while ((randomNumber < bounds.min || randomNumber > bounds.max) && iterations < iterationLimit); if (iterations == iterationLimit) { randomNumber = (bounds.min + bounds.max) / 2.0f; } return(randomNumber); }
// This method is used by the IL generators in IntermediateLanguageFuckery public static float VariantDamage(WeaponEffect weaponEffect, DesignMaskDef designMask) { var weapon = weaponEffect.weapon; var key = ShotMemoKey(weaponEffect); if (DamageWasAlreadyCalculated(key, out var variantDamage)) { return(variantDamage); } if (IsNonVariantWeapon(key, weapon, out var damageVariance, out var normalDamage)) { return(normalDamage); } // the following damage calcs should match with Weapon.DamagePerShotAdjusted(DesignMaskDef), with // the addition of the variance computations var damagePerShot = weapon.DamagePerShotAdjusted(); Logger.Debug( $"some damage numbers:\n" + $"weapon damage: {weapon.DamagePerShot}\n" + $"weapon damage adjusted: {weapon.DamagePerShotAdjusted()}\n" + $"stats based: {weapon.StatCollection.GetValue<float>("DamagePerShot")}" ); var bounds = new VarianceBounds( min: damagePerShot - damageVariance, max: damagePerShot + damageVariance, standardDeviation: ModSettings.StandardDeviationVarianceMultiplier * damageVariance ); var damage = Utility.NormalDistibutionRandom(bounds); var combat = Traverse.Create(weapon).Field("combat").GetValue <CombatGameState>(); var damageWDesign = damage * weapon.GetMaskDamageMultiplier(weapon.parent.occupiedDesignMask); var result = damageWDesign * weapon.GetMaskDamageMultiplier(combat.MapMetaData.biomeDesignMask); Logger.Debug( $"effect id: {key.weaponEffectId}\n" + $"hit index: {key.hitIndex}\n" + $"damage and variance: {damagePerShot}+-{damageVariance}\n" + $"damage range: {bounds.min}-{bounds.max} (std. dev. {bounds.standardDeviation}\n" + $"computed damage: {damage}\n" + $"damage w/ design mask: {damageWDesign}\n" + $"damage w/ env: {result}" ); WeaponDamageMemo[key] = result; return(WeaponDamageMemo[key]); }