public static void fillWeaponPredictRecord(ref DamagePredictRecord record, AbstractActor unit, ICombatant target, Weapon weapon, ref List <int> hitLocations, ref float AverageArmor)
        {
            CustomAmmoCategoriesLog.Log.LogWrite("fillWeaponPredictRecord " + unit.DisplayName + " target " + target.DisplayName + " weapon " + weapon.defId + "\n");
            CustomAmmoCategories.applyWeaponAmmoMode(weapon, record.Id.modeId, record.Id.ammoId);
            AbstractActor targetActor = target as AbstractActor;

            if (hitLocations == null)
            {
                hitLocations = target.GetPossibleHitLocations(unit);
                foreach (int hitLocation in hitLocations)
                {
                    CustomAmmoCategoriesLog.Log.LogWrite("Hit Location " + hitLocation + "\n");
                }
            }
            if (float.IsNaN(AverageArmor))
            {
                AverageArmor = CustomAmmoCategories.getTargetAvarageArmor(hitLocations, target);
            }
            float toHit = 0;

            if (weapon.WillFireAtTargetFromPosition(target, unit.CurrentPosition) == true)
            {
                toHit = weapon.GetToHitFromPosition(target, 1, unit.CurrentPosition, target.CurrentPosition, true, (targetActor != null) ? targetActor.IsEvasive : false, false);
            }
            if (toHit < Epsilon)
            {
                record.HeatDamageCoeff = 0f; record.NormDamageCoeff = 0f; record.PredictHeatDamage = 0f;
            }
            ;
            float coolDownCoeff    = 1.0f / (1.0f + CustomAmmoCategories.getWeaponCooldown(weapon));
            float jammCoeff        = 1.0f - CustomAmmoCategories.getWeaponFlatJammingChance(weapon);
            float damageJammCoeff  = CustomAmmoCategories.getWeaponDamageOnJamming(weapon) ? 0.5f : 1.0f;
            float damageShotsCount = (float)weapon.ShotsWhenFired;
            float damagePerShot    = weapon.DamagePerShot;
            float heatPerShot      = weapon.HeatDamagePerShot;

            if (weapon.componentDef.ComponentTags.Contains("wr-clustered_shots") || (CustomAmmoCategories.getWeaponDisabledClustering(weapon) == false))
            {
                damageShotsCount *= (float)weapon.ProjectilesPerShot;
            }
            float piercedLocationsCount = (float)CustomAmmoCategories.getWeaponPierceLocations(hitLocations, target, damagePerShot);
            float hitLocationsCount     = (hitLocations.Count > 0) ? (float)hitLocations.Count : 1.0f;
            float clusterCoeff          = 1.0f + ((piercedLocationsCount / hitLocationsCount) * damageShotsCount) * CustomAmmoCategories.Settings.ClusterAIMult;
            float pierceCoeff           = 1.0f;

            if (AverageArmor > damagePerShot)
            {
                pierceCoeff += (damagePerShot / AverageArmor) * CustomAmmoCategories.Settings.PenetrateAIMult;
            }
            record.NormDamageCoeff   = damagePerShot * damageShotsCount * toHit * coolDownCoeff * jammCoeff * damageJammCoeff * clusterCoeff * pierceCoeff;
            record.HeatDamageCoeff   = heatPerShot * damageShotsCount * toHit * jammCoeff * damageJammCoeff;
            record.PredictHeatDamage = heatPerShot * damageShotsCount * toHit;
            CustomAmmoCategoriesLog.Log.LogWrite(" toHit = " + toHit + "\n");
            CustomAmmoCategoriesLog.Log.LogWrite(" coolDownCoeff = " + coolDownCoeff + "\n");
            CustomAmmoCategoriesLog.Log.LogWrite(" jammCoeff = " + jammCoeff + "\n");
            CustomAmmoCategoriesLog.Log.LogWrite(" damageJammCoeff = " + damageJammCoeff + "\n");
            CustomAmmoCategoriesLog.Log.LogWrite(" damageShotsCount = " + damageShotsCount + "\n");
            CustomAmmoCategoriesLog.Log.LogWrite(" damagePerShot = " + damagePerShot + "\n");
            CustomAmmoCategoriesLog.Log.LogWrite(" heatPerShot = " + heatPerShot + "\n");
            CustomAmmoCategoriesLog.Log.LogWrite(" piercedLocationsCount = " + piercedLocationsCount + "\n");
            CustomAmmoCategoriesLog.Log.LogWrite(" hitLocationsCount = " + hitLocationsCount + "\n");
            CustomAmmoCategoriesLog.Log.LogWrite(" AverageArmor = " + AverageArmor + "\n");
            CustomAmmoCategoriesLog.Log.LogWrite(" clusterCoeff = " + clusterCoeff + "\n");
            CustomAmmoCategoriesLog.Log.LogWrite(" pierceCoeff = " + pierceCoeff + "\n");
            CustomAmmoCategoriesLog.Log.LogWrite(" NormDamageCoeff = " + record.NormDamageCoeff + "\n");
            CustomAmmoCategoriesLog.Log.LogWrite(" HeatDamageCoeff = " + record.HeatDamageCoeff + "\n");
        }
        public static int DecrementAmmo(Weapon instance, int stackItemUID, int StreakHitCount)
        {
            int shotsWhenFired = instance.ShotsWhenFired;

            if (StreakHitCount != 0)
            {
                if (instance.weaponDef.ComponentTags.Contains("wr-clustered_shots") || (CustomAmmoCategories.getWeaponDisabledClustering(instance) == false))
                {
                    shotsWhenFired = StreakHitCount / instance.ProjectilesPerShot;
                }
                else
                {
                    shotsWhenFired = StreakHitCount;
                }
            }
            bool streakEffect = CustomAmmoCategories.getExtWeaponDef(instance.weaponDef.Description.Id).StreakEffect;

            if ((streakEffect == false) && (StreakHitCount == 0))
            {
                StreakHitCount = shotsWhenFired;
            }
            CustomAmmoCategoriesLog.Log.LogWrite("Weapon.DecrementAmmo " + instance.UIName + " real fire count:" + StreakHitCount + "\n");
            int result = 0;

            if ((CustomAmmoCategories.getWeaponCustomAmmoCategory(instance).Index == CustomAmmoCategories.NotSetCustomAmmoCategoty.Index) || (instance.parent != null && instance.parent is Turret))
            {
                if (instance.weaponDef.ComponentTags.Contains("wr-clustered_shots") || CustomAmmoCategories.getWeaponDisabledClustering(instance))
                {
                    result = shotsWhenFired;
                }
                else
                {
                    result = shotsWhenFired * instance.ProjectilesPerShot;
                }
                CustomAmmoCategoriesLog.Log.LogWrite("  weapon has no ammo (energy or turret) " + instance.UIName + "\n");
                return(result);
            }
            int modValue;

            if (instance.InternalAmmo >= shotsWhenFired)
            {
                if (StreakHitCount != 0)
                {
                    instance.StatCollection.ModifyStat <int>(instance.uid, stackItemUID, "InternalAmmo", StatCollection.StatOperation.Int_Subtract, shotsWhenFired, -1, true);
                }
                modValue = 0;
            }
            else
            {
                modValue = shotsWhenFired - instance.InternalAmmo;
                if (StreakHitCount != 0)
                {
                    instance.StatCollection.ModifyStat <int>(instance.uid, stackItemUID, "InternalAmmo", StatCollection.StatOperation.Set, 0, -1, true);
                }
            }
            string CurrentAmmoId = "";

            if (CustomAmmoCategories.checkExistance(instance.StatCollection, CustomAmmoCategories.AmmoIdStatName) == true)
            {
                CurrentAmmoId = instance.StatCollection.GetStatistic(CustomAmmoCategories.AmmoIdStatName).Value <string>();
            }
            else
            {
                if (instance.ammoBoxes.Count > 0)
                {
                    CurrentAmmoId = instance.ammoBoxes[0].ammoDef.Description.Id;
                    CustomAmmoCategoriesLog.Log.LogWrite($"WARNING! strange behavior " + instance.UIName + " has no data in statistics. fallback to default ammo " + CurrentAmmoId + "\n");
                }
                else
                {
                    CustomAmmoCategoriesLog.Log.LogWrite($"WARNING! strange behavior " + instance.UIName + " not energy, parent no turret but no ammo boxes\n");
                    if (instance.weaponDef.ComponentTags.Contains("wr-clustered_shots") || CustomAmmoCategories.getWeaponDisabledClustering(instance))
                    {
                        result = shotsWhenFired;
                    }
                    else
                    {
                        result = shotsWhenFired * instance.ProjectilesPerShot;
                    }
                    return(result);
                }
            }
            //ExtAmmunitionDef extAmmo = CustomAmmoCategories.findExtAmmo(CurrentAmmoId);
            //ExtWeaponDef extWeapon = CustomAmmoCategories.getExtWeaponDef(__instance.weaponDef.Description.Id);
            if (modValue == 0)
            {
                if (instance.weaponDef.ComponentTags.Contains("wr-clustered_shots") || CustomAmmoCategories.getWeaponDisabledClustering(instance))
                {
                    result = shotsWhenFired;
                }
                else
                {
                    result = shotsWhenFired * instance.ProjectilesPerShot;
                }
                CustomAmmoCategoriesLog.Log.LogWrite("  fire internal ammo. projectiles:" + result + "\n");
                return(result);
            }
            for (int index = 0; index < instance.ammoBoxes.Count; ++index)
            {
                AmmunitionBox ammoBox = instance.ammoBoxes[index];
                if (ammoBox.IsFunctional == false)
                {
                    continue;
                }
                if (ammoBox.CurrentAmmo <= 0)
                {
                    continue;
                }
                if ((string.IsNullOrEmpty(CurrentAmmoId) == false) && (ammoBox.ammoDef.Description.Id != CurrentAmmoId))
                {
                    continue;
                }
                if (ammoBox.CurrentAmmo >= modValue)
                {
                    if (StreakHitCount != 0)
                    {
                        ammoBox.StatCollection.ModifyStat <int>(instance.uid, stackItemUID, "CurrentAmmo", StatCollection.StatOperation.Int_Subtract, modValue, -1, true);
                    }
                    modValue = 0;
                }
                else
                {
                    modValue -= ammoBox.CurrentAmmo;
                    if (StreakHitCount != 0)
                    {
                        ammoBox.StatCollection.ModifyStat <int>(instance.uid, stackItemUID, "CurrentAmmo", StatCollection.StatOperation.Set, 0, -1, true);
                    }
                }
            }
            if (instance.weaponDef.ComponentTags.Contains("wr-clustered_shots") || CustomAmmoCategories.getWeaponDisabledClustering(instance))
            {
                result = (instance.ShotsWhenFired - modValue);
            }
            else
            {
                result = (instance.ShotsWhenFired - modValue) * instance.ProjectilesPerShot;
            }
            CustomAmmoCategoriesLog.Log.LogWrite("  fire exteranl ammo. projectiles:" + result + "\n");
            return(result);
        }