static void evaluateWeaponAttackOnVehicle(float expectedDamage, Weapon w, ref DamageExpectationRecord damageExpectationRecord, Vector3 attackerPosition, Vehicle targetVehicle, Vector3 targetPosition, Quaternion targetRotation)
        {
            // use hit table to figure out where this will go
            Dictionary <VehicleChassisLocations, float> locations = GetLocationDictionary(attackerPosition, targetVehicle, targetPosition, targetRotation);

            foreach (KeyValuePair <VehicleChassisLocations, float> locKVP in locations)
            {
                VehicleChassisLocations loc = locKVP.Key;
                float probability           = locKVP.Value;

                DamageExpectationRecord locRecord = new DamageExpectationRecord();
                damageExpectationRecord.AddChildRecord(probability, locRecord);

                float existingArmor          = targetVehicle.ArmorForLocation((int)loc);
                float armorThatWillBeRemoved = Mathf.Min(existingArmor, expectedDamage);
                float damageRemaining        = expectedDamage - existingArmor;
                locRecord.AddVehicleArmorDamage(armorThatWillBeRemoved, loc);

                if (damageRemaining > 0)
                {
                    // some goes in to the structure
                    float currentStructure = targetVehicle.GetCurrentStructure(loc);

                    float structureDamage = Mathf.Min(damageRemaining, currentStructure);
                    //float damageAfterStructure = damageRemaining - structureDamage;

                    locRecord.AddVehicleStructureDamage(structureDamage, loc);
                }
            }
        }
        /// <summary>
        /// Take a tree of DamageExpectationRecords and add them up into a single record with no children.
        /// </summary>
        /// <returns></returns>
        public DamageExpectationRecord Flatten()
        {
            DamageExpectationRecord newRecord = new DamageExpectationRecord();

            flattenInto(newRecord, this, 1.0f);
            return(newRecord);
        }
        static void NukeMechLocation(ref DamageExpectationRecord damageExpectationRecord, Mech targetMech, ChassisLocations loc)
        {
            // foreach component, destroy it
            Dictionary <ComponentLocator, float> componentDict = getComponentDictionary(targetMech, loc);

            foreach (KeyValuePair <ComponentLocator, float> componentKVP in componentDict)
            {
                ComponentLocator compLoc = componentKVP.Key;
                damageExpectationRecord.AddComponentDamage(2.0f, compLoc);
            }
        }
        static void NukeUnit(ref DamageExpectationRecord damageExpectationRecord, Turret targetTurret)
        {
            damageExpectationRecord.lethalProbability = 1.0f;

            List <Weapon> weaponList = targetTurret.Weapons;

            for (int slotIndex = 0; slotIndex < weaponList.Count; ++slotIndex)
            {
                ComponentLocator compLoc = new ComponentLocator(targetTurret, slotIndex);
                damageExpectationRecord.AddComponentDamage(2.0f, compLoc);
            }
        }
        static public float EvaluateFirepowerReductionFromAttack(AbstractActor attacker, Vector3 attackerPosition, ICombatant target, Vector3 targetPosition, Quaternion targetRotation, List <Weapon> weapons, MeleeAttackType attackType)
        {
            AbstractActor actor = target as AbstractActor;

            if (actor == null)
            {
                return(0.0f);
            }

            DamageExpectationRecord damageExpectationRecord = EvaluateAttack(attacker, attackerPosition, target, targetPosition, targetRotation, weapons, attackType);

            float dmg = 0.0f;
            List <ComponentLocator> weaponList = GetWeaponComponentLocatorList(actor);

            for (int weaponIndex = 0; weaponIndex < weaponList.Count; ++weaponIndex)
            {
                ComponentLocator compLoc  = weaponList[weaponIndex];
                MechComponent    mechComp = compLoc.GetComponent();
                Weapon           w        = mechComp as Weapon;

                if (w.CanFire)
                {
                    float weaponBaseDamage = w.ShotsWhenFired * w.DamagePerShot;
                    if (w.DamageLevel == ComponentDamageLevel.Functional)
                    {
                        int expDmg = Mathf.RoundToInt(damageExpectationRecord.GetComponentDamageForLocation(compLoc));
                        if (expDmg == 1)
                        {
                            // that's like half damage
                            dmg += weaponBaseDamage * 0.5f;
                        }
                        else if (expDmg > 1)
                        {
                            dmg += weaponBaseDamage;
                        }
                    }
                    else if (w.DamageLevel == ComponentDamageLevel.Penalized)
                    {
                        int expDmg = Mathf.RoundToInt(damageExpectationRecord.GetComponentDamageForLocation(compLoc));
                        if (expDmg >= 1)
                        {
                            dmg += weaponBaseDamage;
                        }
                    }
                }
            }
            return(dmg);
        }
        static void NukeUnit(ref DamageExpectationRecord damageExpectationRecord, Mech targetMech)
        {
            damageExpectationRecord.lethalProbability = 1.0f;

            // foreach location
            foreach (ChassisLocations loc in System.Enum.GetValues(typeof(ChassisLocations)))
            {
                if ((loc == ChassisLocations.All) ||
                    (loc == ChassisLocations.Arms) ||
                    (loc == ChassisLocations.Legs) ||
                    (loc == ChassisLocations.Torso) ||
                    (loc == ChassisLocations.None))
                {
                    continue;
                }
                NukeMechLocation(ref damageExpectationRecord, targetMech, loc);
            }
        }
        static void evaluateWeaponAttackOnBuilding(float expectedDamage, Weapon w, ref DamageExpectationRecord damageExpectationRecord, Vector3 attackerPosition, Building targetBuilding, Vector3 targetPosition, Quaternion targetRotation)
        {
            float existingArmor          = targetBuilding.CurrentArmor;
            float armorThatWillBeRemoved = Mathf.Min(existingArmor, expectedDamage);
            float damageRemaining        = expectedDamage - existingArmor;

            damageExpectationRecord.AddArmorDamage(armorThatWillBeRemoved, ArmorLocation.None);

            if (damageRemaining > 0)
            {
                // some goes in to the structure
                float currentStructure = targetBuilding.CurrentStructure;

                float structureDamage = Mathf.Min(damageRemaining, currentStructure);

                damageExpectationRecord.AddStructureDamage(structureDamage, ChassisLocations.None);
            }
        }
        public void flattenInto(DamageExpectationRecord target, DamageExpectationRecord source, float probability)
        {
            foreach (KeyValuePair <ComponentLocator, float> kvp in source.componentDamageDictionary)
            {
                ComponentLocator key = kvp.Key;
                float            dmg = kvp.Value;
                target.AddComponentDamage(dmg * probability, key);
            }

            foreach (ChassisLocations loc in source.chassisLocationDictionary.Keys)
            {
                float dmg = source.chassisLocationDictionary[loc];
                target.AddStructureDamage(dmg * probability, loc);
            }

            foreach (ArmorLocation loc in source.armorLocationDictionary.Keys)
            {
                float dmg = source.armorLocationDictionary[loc];
                target.AddArmorDamage(dmg * probability, loc);
            }

            foreach (VehicleChassisLocations loc in source.vehicleChassisLocationDictionary.Keys)
            {
                float dmg = source.vehicleChassisLocationDictionary[loc];
                target.AddVehicleStructureDamage(dmg * probability, loc);
            }

            foreach (VehicleChassisLocations loc in source.vehicleArmorLocationDictionary.Keys)
            {
                float dmg = source.vehicleArmorLocationDictionary[loc];
                target.AddVehicleArmorDamage(dmg * probability, loc);
            }

            target.AddPilotDamage(pilotDamage * probability);
            target.lethalProbability += lethalProbability * probability;

            for (int childIndex = 0; childIndex < source.children.Count; ++childIndex)
            {
                ChildWithProbability c = source.children[childIndex];
                flattenInto(target, c.DamageExpectationRecord, c.Probability);
            }
        }
        static void evaluateWeaponAttackOnMech(float expectedDamage, Weapon w, ref DamageExpectationRecord damageExpectationRecord, Vector3 attackerPosition, Mech targetMech, Vector3 targetPosition, Quaternion targetRotation)
        {
            // use hit table to figure out where this will go
            Dictionary <ArmorLocation, float> locations = GetLocationDictionary(attackerPosition, targetMech, targetPosition, targetRotation);

            foreach (KeyValuePair <ArmorLocation, float> locKVP in locations)
            {
                ArmorLocation loc         = locKVP.Key;
                float         probability = locKVP.Value;

                DamageExpectationRecord locRecord = new DamageExpectationRecord();
                damageExpectationRecord.AddChildRecord(probability, locRecord);

                float existingArmor          = targetMech.ArmorForLocation((int)loc);
                float armorThatWillBeRemoved = Mathf.Min(existingArmor, expectedDamage);
                float damageRemaining        = expectedDamage - existingArmor;
                locRecord.AddArmorDamage(armorThatWillBeRemoved, loc);

                ChassisLocations sLoc = MechStructureRules.GetChassisLocationFromArmorLocation((ArmorLocation)loc);

                // there's a chance this hit will be a critical hit
                if (!targetMech.IsLocationDestroyed(sLoc))
                {
                    float critChance = targetMech.Combat.CritChance.GetCritChance(targetMech, sLoc, w);

                    if (critChance > 0)
                    {
                        DamageExpectationRecord critRecord = new DamageExpectationRecord();
                        locRecord.AddChildRecord(critChance, critRecord);

                        // iterate over components, apply one point of damage to each location.

                        Dictionary <ComponentLocator, float> componentDict = getComponentDictionary(targetMech, sLoc);

                        float probOfHittingAmmo = 0.0f;
                        foreach (KeyValuePair <ComponentLocator, float> componentKVP in componentDict)
                        {
                            ComponentLocator compLoc              = componentKVP.Key;
                            MechComponent    component            = compLoc.GetComponent();
                            float            componentProbability = componentKVP.Value;

                            DamageExpectationRecord componentRecord = new DamageExpectationRecord();
                            critRecord.AddChildRecord(componentProbability, componentRecord);

                            componentRecord.AddComponentDamage(1.0f, compLoc);

                            // if this component is ammo, there's a chance we could lose this location and all child locations
                            if (component.componentType == ComponentType.AmmunitionBox)
                            {
                                AmmunitionBox abComponent   = component as AmmunitionBox;
                                int           remainingAmmo = abComponent.CurrentAmmo;
                                int           capacity      = abComponent.ammunitionBoxDef.Capacity;
                                float         percentage    = ((float)remainingAmmo) / ((float)capacity);

                                if (percentage > 0.5f)
                                {
                                    probOfHittingAmmo += componentProbability;
                                }
                            }
                        }
                        if (probOfHittingAmmo > 0.0f)
                        {
                            DamageExpectationRecord ammoBlownRecord = new DamageExpectationRecord();
                            locRecord.AddChildRecord(probOfHittingAmmo, ammoBlownRecord);

                            foreach (KeyValuePair <ComponentLocator, float> componentKVP in componentDict)
                            {
                                ComponentLocator compLoc = componentKVP.Key;
                                ammoBlownRecord.AddComponentDamage(2.0f, compLoc);
                            }
                        }
                    }
                }

                if (damageRemaining > 0)
                {
                    // some goes in to the structure
                    float currentStructure = targetMech.GetCurrentStructure(sLoc);

                    float structureDamage      = Mathf.Min(damageRemaining, currentStructure);
                    float damageAfterStructure = damageRemaining - structureDamage;

                    locRecord.AddStructureDamage(structureDamage, sLoc);

                    if (damageAfterStructure > 0)
                    {
                        // some hits a component
                        Dictionary <ComponentLocator, float> componentDict = getComponentDictionary(targetMech, sLoc);

                        float probOfHittingAmmo = 0.0f;

                        foreach (KeyValuePair <ComponentLocator, float> componentKVP in componentDict)
                        {
                            ComponentLocator compLoc              = componentKVP.Key;
                            MechComponent    component            = compLoc.GetComponent();
                            float            componentProbability = componentKVP.Value;

                            DamageExpectationRecord componentRecord = new DamageExpectationRecord();
                            locRecord.AddChildRecord(componentProbability, componentRecord);

                            componentRecord.AddComponentDamage(1.0f, compLoc);

                            // if this component is ammo, there's a chance we could lose this location and all child locations
                            if (component.componentType == ComponentType.AmmunitionBox)
                            {
                                AmmunitionBox abComponent   = component as AmmunitionBox;
                                int           remainingAmmo = abComponent.CurrentAmmo;
                                int           capacity      = abComponent.ammunitionBoxDef.Capacity;
                                float         percentage    = ((float)remainingAmmo) / ((float)capacity);

                                if (percentage > 0.5f)
                                {
                                    probOfHittingAmmo += componentProbability;
                                }
                            }
                        }

                        if (probOfHittingAmmo > 0)
                        {
                            DamageExpectationRecord ammoBlownRecord = new DamageExpectationRecord();
                            locRecord.AddChildRecord(probOfHittingAmmo, ammoBlownRecord);

                            foreach (KeyValuePair <ComponentLocator, float> componentKVP in componentDict)
                            {
                                ComponentLocator compLoc = componentKVP.Key;
                                ammoBlownRecord.AddComponentDamage(2.0f, compLoc);
                            }
                        }
                    }
                }
            }
        }
        static public DamageExpectationRecord EvaluateAttack(AbstractActor attacker, Vector3 attackerPosition, ICombatant target, Vector3 targetPosition, Quaternion targetRotation, List <Weapon> weapons, MeleeAttackType attackType)
        {
            // for all weapons in an attack

            // figure out the locations that are likely to be hit
            // use HitTable to figure this out

            // for each location, figure out the chance to
            // - do criticals (without breaching armor?)
            // - breach armor
            // - do structural damage
            // - do component damage (weapons get damaged, then destroyed)
            // - trigger ammo explosion
            // - lose the location
            // - lose sub-locations
            // - kill the mech

            // types.cs ConsolidateCriticalHitInfo
            // Mech.cs CheckForCrit
            // CombatCritChance GetCritChance

            DamageExpectationRecord root = new DamageExpectationRecord();

            for (int weaponIndex = 0; weaponIndex < weapons.Count; ++weaponIndex)
            {
                Weapon w = weapons[weaponIndex];

                // figure out chance to hit the target
                AbstractActor targetActor      = target as AbstractActor;
                bool          targetIsEvasive  = (targetActor != null) && (targetActor.IsEvasive);
                float         toHitProbability = w.GetToHitFromPosition(target, 1, attackerPosition, targetPosition, true, targetIsEvasive);

                DamageExpectationRecord weaponDamageExpectationRecord = new DamageExpectationRecord();
                root.AddChildRecord(toHitProbability, weaponDamageExpectationRecord);

                float expectedDamage = w.ShotsWhenFired * w.DamagePerShotFromPosition(attackType, attackerPosition, target);

                Mech     targetMech     = target as Mech;
                Vehicle  targetVehicle  = target as Vehicle;
                Turret   targetTurret   = target as Turret;
                Building targetBuilding = target as Building;
                if (targetMech != null)
                {
                    evaluateWeaponAttackOnMech(expectedDamage, w, ref weaponDamageExpectationRecord, attackerPosition, targetMech, targetPosition, targetRotation);
                }
                else if (targetVehicle != null)
                {
                    evaluateWeaponAttackOnVehicle(expectedDamage, w, ref weaponDamageExpectationRecord, attackerPosition, targetVehicle, targetPosition, targetRotation);
                }
                else if (targetTurret != null)
                {
                    evaluateWeaponAttackOnTurret(expectedDamage, w, ref weaponDamageExpectationRecord, attackerPosition, targetTurret, targetPosition, targetRotation);
                }
                else if (targetBuilding != null)
                {
                    evaluateWeaponAttackOnBuilding(expectedDamage, w, ref weaponDamageExpectationRecord, attackerPosition, targetBuilding, targetPosition, targetRotation);
                }
            }
            consolidateDamageExpectationRecord(ref root, target);

            return(root);
        }
 static void NukeUnit(ref DamageExpectationRecord damageExpectationRecord, Building targetBuilding)
 {
     damageExpectationRecord.lethalProbability = 1.0f;
 }
        static void consolidateDamageExpectationRecord(ref DamageExpectationRecord damageExpectationRecord, ICombatant target)
        {
            damageExpectationRecord = damageExpectationRecord.Flatten();

            Mech targetMech = target as Mech;

            if (targetMech != null)
            {
                foreach (ChassisLocations loc in System.Enum.GetValues(typeof(ChassisLocations)))
                {
                    if ((loc == ChassisLocations.All) ||
                        (loc == ChassisLocations.Arms) ||
                        (loc == ChassisLocations.Torso) ||
                        (loc == ChassisLocations.MainBody) ||
                        (loc == ChassisLocations.Legs) ||
                        (loc == ChassisLocations.None))
                    {
                        continue;
                    }
                    if (damageExpectationRecord.GetStructureDamageForLocation(loc) >= targetMech.GetCurrentStructure(loc))
                    {
                        NukeMechLocation(ref damageExpectationRecord, targetMech, loc);
                    }
                }

                // if left or right torso are destroyed, mark left or right arm as destroyed
                if (damageExpectationRecord.GetStructureDamageForLocation(ChassisLocations.LeftTorso) >= targetMech.LeftTorsoStructure)
                {
                    NukeMechLocation(ref damageExpectationRecord, targetMech, ChassisLocations.LeftArm);
                }
                if (damageExpectationRecord.GetStructureDamageForLocation(ChassisLocations.RightTorso) >= targetMech.RightTorsoStructure)
                {
                    NukeMechLocation(ref damageExpectationRecord, targetMech, ChassisLocations.RightArm);
                }

                // if both legs are destroyed, mark whole unit as destroyed
                // if either center torso structure or head structure are destroyed, mark whole unit as destroyed

                if (((damageExpectationRecord.GetStructureDamageForLocation(ChassisLocations.LeftLeg) >= targetMech.StructureForLocation((int)ChassisLocations.LeftLeg)) &&
                     (damageExpectationRecord.GetStructureDamageForLocation(ChassisLocations.RightLeg) >= targetMech.StructureForLocation((int)ChassisLocations.RightLeg))) ||
                    (damageExpectationRecord.GetStructureDamageForLocation(ChassisLocations.CenterTorso) >= targetMech.StructureForLocation((int)ChassisLocations.CenterTorso)) ||
                    (damageExpectationRecord.GetStructureDamageForLocation(ChassisLocations.Head) >= targetMech.StructureForLocation((int)ChassisLocations.Head)))
                {
                    NukeUnit(ref damageExpectationRecord, targetMech);
                }

                // TODO if the pilot is destroyed, mark whole unit as destroyed
            }

            Vehicle targetVehicle = target as Vehicle;

            if (targetVehicle != null)
            {
                foreach (VehicleChassisLocations loc in System.Enum.GetValues(typeof(VehicleChassisLocations)))
                {
                    if ((loc == VehicleChassisLocations.All) ||
                        (loc == VehicleChassisLocations.None) ||
                        (loc == VehicleChassisLocations.MainBody) ||
                        (loc == VehicleChassisLocations.Invalid))
                    {
                        continue;
                    }
                    if (damageExpectationRecord.GetVehicleStructureDamageForLocation(loc) >= targetVehicle.StructureForLocation((int)loc))
                    {
                        NukeUnit(ref damageExpectationRecord, targetVehicle);
                    }
                }
            }

            // if unit is a turret and center structure is destroyed, mark whole unit as destroyed
            Turret targetTurret = target as Turret;

            if (targetTurret != null)
            {
                if (damageExpectationRecord.GetTurretStructureDamage() >= targetTurret.StructureForLocation((int)BuildingLocation.Structure))
                {
                    NukeUnit(ref damageExpectationRecord, targetTurret);
                }
            }

            // if unit is a building and center structure is destroyed, mark whole unit as destroyed
            Building targetBuilding = target as Building;

            if (targetBuilding != null)
            {
                if (damageExpectationRecord.GetBuildingStructureDamage() >= targetBuilding.StructureForLocation((int)BuildingLocation.Structure))
                {
                    NukeUnit(ref damageExpectationRecord, targetBuilding);
                }
            }
        }
 public ChildWithProbability(float probability, DamageExpectationRecord damageExpectationRecord)
 {
     this.Probability             = probability;
     this.DamageExpectationRecord = damageExpectationRecord;
 }
 public void AddChildRecord(float probability, DamageExpectationRecord childRecord)
 {
     children.Add(new ChildWithProbability(probability, childRecord));
 }