Esempio n. 1
0
 public RepairState(PoorlyMaintainedEffect effect, UnitRollCfg rollCfg)
 {
     if (effect == null || effect.EffectData == null ||
         effect.EffectData.poorlyMaintainedEffectData != null ||
         rollCfg == null)
     {
         if (effect.EffectData.poorlyMaintainedEffectData.armorMod == 0.25f)
         {
             effectRating = 25;
             stateRolls   = Mod.Random.Next(rollCfg.PM25_MinRolls, rollCfg.PM25_MaxRolls);
             Mod.Log.Debug?.Write($"25% effect supplied, stateRolls = {stateRolls}");
         }
         else if (effect.EffectData.poorlyMaintainedEffectData.armorMod == 0.5f)
         {
             effectRating = 50;
             stateRolls   = Mod.Random.Next(rollCfg.PM50_MinRolls, rollCfg.PM50_MaxRolls);
             Mod.Log.Debug?.Write($"50% effect supplied, stateRolls = {stateRolls}");
         }
         else if (effect.EffectData.poorlyMaintainedEffectData.armorMod == 0.75f)
         {
             effectRating = 75;
             stateRolls   = Mod.Random.Next(rollCfg.PM75_MinRolls, rollCfg.PM75_MaxRolls);
             Mod.Log.Debug?.Write($"75% effect supplied, stateRolls = {stateRolls}");
         }
         else
         {
             Mod.Log.Debug?.Write($"Unknown effect, stateRolls = {stateRolls}");
         }
     }
     else
     {
         Mod.Log.Debug?.Write($"Unknown effect or config stateRolls = {stateRolls}");
     }
 }
Esempio n. 2
0
        static bool Prefix(PoorlyMaintainedEffect __instance, Building targetBuilding)
        {
            Mod.Log.Trace?.Write("PME:AETB - entered.");
            //BuildingRepairState repairState = RepairsHelper.GetRepairState(__instance, targetBuilding);

            // Note that OnEffectBegin will invoke *every* ApplyEffects, and expects the ApplyEfect to check that the target isn't null.
            if (targetBuilding == null)
            {
                return(false);
            }

            return(true);
        }
Esempio n. 3
0
        public MechRepairState(PoorlyMaintainedEffect effect, Mech targetMech) : base(effect, Mod.Config.DamageRollsConfig.MechRolls)
        {
            this.Target = targetMech;

            // See https://github.com/BattletechModders/MechEngineer/issues/181

            // Parse the list of components on the target. Determine the max engine hits, engine components, etc
            ComponentSummary compSummary = AnalyzeComponents(targetMech);

            int engineHits = 0;
            int gyroHits   = 0;


            for (int i = 0; i < stateRolls; i++)
            {
                int  killSwitch = 0;
                bool isResolved = false;
                while (!isResolved)
                {
                    int         randIdx     = Mod.Random.Next(0, 9); // Number of indexes in the themeConfig
                    ThemeConfig themeConfig = ModState.CurrentTheme;
                    DamageType  damageType  = themeConfig.MechTable[randIdx];

                    switch (damageType)
                    {
                    case DamageType.Skill:
                        PilotSkillHits++;
                        isResolved = true;
                        Mod.Log.Debug?.Write($"  {i} is damageType: {damageType}.");
                        break;

                    case DamageType.Gyro:
                        // Only accept 1 gyro hit, then fallback
                        if (compSummary.GyroParts.Count > 0 && gyroHits < compSummary.MaxGyroHits - 1)
                        {
                            MechComponent gyroComp = compSummary.GyroParts.GetRandomElement <MechComponent>();
                            DamagedComponents.Add(gyroComp);
                            compSummary.GyroParts.Remove(gyroComp);
                            gyroHits++;
                            isResolved = true;
                            Mod.Log.Debug?.Write($"  {i} is damageType: {damageType}.");
                        }
                        break;

                    case DamageType.Engine:
                        // Only accept 2 engine hits, then fallback
                        if (compSummary.EngineParts.Count > 0 && engineHits < compSummary.MaxEngineHits - 1)
                        {
                            MechComponent engineComp = compSummary.EngineParts.GetRandomElement <MechComponent>();
                            DamagedComponents.Add(engineComp);
                            compSummary.EngineParts.Remove(engineComp);
                            engineHits++;
                            isResolved = true;
                            Mod.Log.Debug?.Write($"  {i} is damageType: {damageType}.");
                        }
                        break;

                    case DamageType.HeatSink:
                        if (compSummary.HeatSinks.Count > 0)
                        {
                            MechComponent heatSink = compSummary.HeatSinks.GetRandomElement <MechComponent>();
                            DamagedComponents.Add(heatSink);
                            compSummary.HeatSinks.Remove(heatSink);
                            isResolved = true;
                            Mod.Log.Debug?.Write($"  {i} is damageType: {damageType}.");
                        }
                        break;

                    case DamageType.AmmoBox:
                        if (compSummary.AmmoBoxes.Count > 0)
                        {
                            AmmunitionBox ammoBox = compSummary.AmmoBoxes.GetRandomElement <AmmunitionBox>();
                            DamagedComponents.Add(ammoBox);
                            compSummary.AmmoBoxes.Remove(ammoBox);
                            isResolved = true;
                            Mod.Log.Debug?.Write($"  {i} is damageType: {damageType}.");
                        }
                        break;

                    case DamageType.Component:
                        if (compSummary.Components.Count > 0)
                        {
                            MechComponent component = compSummary.Components.GetRandomElement <MechComponent>();
                            DamagedComponents.Add(component);
                            compSummary.Components.Remove(component);
                            isResolved = true;
                            Mod.Log.Debug?.Write($"  {i} is damageType: {damageType}.");
                        }
                        break;

                    case DamageType.Weapon:
                        // Always leave at least one weapon
                        if (compSummary.Weapons.Count > 1)
                        {
                            MechComponent weapon = compSummary.Weapons.GetRandomElement <MechComponent>();
                            DamagedComponents.Add(weapon);
                            compSummary.Weapons.Remove(weapon);
                            isResolved = true;
                            Mod.Log.Debug?.Write($"  {i} is damageType: {damageType}.");
                        }
                        break;

                    case DamageType.Structure:
                        StructureHits++;
                        isResolved = true;
                        Mod.Log.Debug?.Write($"  {i} is damageType: {damageType}.");
                        break;

                    case DamageType.Armor:
                        ArmorHits++;
                        isResolved = true;
                        Mod.Log.Debug?.Write($"  {i} is damageType: {damageType}.");
                        break;
                    }

                    killSwitch++;

                    if (killSwitch > 30)
                    {
                        Mod.Log.Info?.Write("Too many iterating, stopping and moving forward.");
                    }
                }
            }
        }
Esempio n. 4
0
        static bool Prefix(PoorlyMaintainedEffect __instance, Mech targetMech)
        {
            Mod.Log.Trace?.Write("PME:AETM - entered.");

            // Note that OnEffectBegin will invoke *every* ApplyEffects, and expects the ApplyEfect to check that the target isn't null.
            if (targetMech == null)
            {
                return(false);
            }

            Mod.Log.Info?.Write($" Applying PoorlyMaintainedEffect to unit: {CombatantUtils.Label(targetMech)}");
            ModState.SuppressShowActorSequences = true;

            WeaponHitInfo hitInfo = new WeaponHitInfo(-1, -1, -1, -1, "", "", -1,
                                                      null, null, null, null, null, null, null,
                                                      new AttackDirection[] { AttackDirection.FromFront }, null, null, null);

            // Apply any component damage first
            StringBuilder   componentDamageSB = new StringBuilder();
            MechRepairState repairState       = new MechRepairState(__instance, targetMech);

            foreach (MechComponent mc in repairState.DamagedComponents)
            {
                if (mc.componentType == ComponentType.AmmunitionBox)
                {
                    AmmunitionBox ab            = (AmmunitionBox)mc;
                    float         ammoReduction = Mod.Random.Next(
                        (int)(Mod.Config.PerHitPenalties.MinAmmoRemaining * 100f),
                        (int)(Mod.Config.PerHitPenalties.MaxAmmoRemaining * 100f)
                        ) / 100f;
                    int newAmmo = (int)Math.Floor(ab.CurrentAmmo * ammoReduction);
                    Mod.Log.Info?.Write($"Reducing ammoBox: {mc.UIName} from {ab.CurrentAmmo} x {ammoReduction} = {newAmmo}");
                    ab.StatCollection.Set <int>(ModStats.AmmoBoxCurrentAmmo, newAmmo);
                }
                else
                {
                    Mod.Log.Info?.Write($"Damaging component: {mc.UIName}");
                    mc.DamageComponent(hitInfo, ComponentDamageLevel.Destroyed, false);
                }
                Text localText = new Text(" - {0}\n", new object[] { mc.UIName });
                componentDamageSB.Append(localText.ToString());
            }

            int armorOrStructHeadHits            = 0;
            HashSet <ArmorLocation> armorHitLocs = new HashSet <ArmorLocation>();

            // Then apply any armor hits
            for (int i = 0; i < repairState.ArmorHits; i++)
            {
                ArmorLocation location = LocationHelper.GetRandomMechArmorLocation();
                armorHitLocs.Add(location);
                float maxArmor       = targetMech.GetMaxArmor(location);
                float maxDamageRatio = Mod.Random.Next(
                    (int)(Mod.Config.PerHitPenalties.MinArmorLoss * 100),
                    (int)(Mod.Config.PerHitPenalties.MaxArmorLoss * 100)
                    ) / 100f;
                float damage = (float)Math.Floor(maxArmor * maxDamageRatio);
                if (targetMech.GetCurrentArmor(location) - damage < 0)
                {
                    damage = targetMech.GetCurrentArmor(location);
                }
                Mod.Log.Info?.Write($"Reducing armor in location {location} by {maxDamageRatio}% for {damage} points");

                if (damage != 0)
                {
                    targetMech.StatCollection.ModifyStat <float>(hitInfo.attackerId, hitInfo.stackItemUID,
                                                                 targetMech.GetStringForArmorLocation(location),
                                                                 StatCollection.StatOperation.Float_Subtract, damage, -1, true);
                }

                if (location == ArmorLocation.Head)
                {
                    armorOrStructHeadHits++;
                }
            }

            // We don't limit to armor damage locations here so we can represent that armor is easily scavenged
            HashSet <ChassisLocations> structHitLocs = new HashSet <ChassisLocations>();

            for (int i = 0; i < repairState.StructureHits; i++)
            {
                ChassisLocations location = LocationHelper.GetRandomMechStructureLocation();
                structHitLocs.Add(location);
                float maxStructure   = targetMech.GetMaxStructure(location);
                float maxDamageRatio = Mod.Random.Next(
                    (int)(Mod.Config.PerHitPenalties.MinStructureLoss * 100),
                    (int)(Mod.Config.PerHitPenalties.MaxStructureLoss * 100)
                    ) / 100f;
                float damage = (float)Math.Floor(maxStructure * maxDamageRatio);
                if (targetMech.GetCurrentStructure(location) - damage < 1)
                {
                    // Never allow a hit to completely remove a limb or location
                    damage = targetMech.GetCurrentStructure(location) - 1;
                }
                Mod.Log.Info?.Write($"Reducing structure in location {location} by {maxDamageRatio}% for {damage} points");

                if (damage != 0)
                {
                    targetMech.StatCollection.ModifyStat <float>(hitInfo.attackerId, hitInfo.stackItemUID,
                                                                 targetMech.GetStringForStructureLocation(location),
                                                                 StatCollection.StatOperation.Float_Subtract, damage, -1, true);
                }

                targetMech.UpdateLocationDamageLevel(location, hitInfo.attackerId, hitInfo.stackItemUID);

                if (location == ChassisLocations.Head)
                {
                    armorOrStructHeadHits++;
                }
            }

            PilotHelper.ApplyPilotHealthDamage(targetMech, hitInfo, armorOrStructHeadHits, out string pilotHealthTooltipText);
            PilotHelper.ApplyPilotSkillDamage(targetMech, hitInfo, repairState.PilotSkillHits, out string pilotSkillDamageTooltipText);

            // Build the tooltip
            StringBuilder descSB = new StringBuilder();

            if (repairState.ArmorHits > 0)
            {
                Text localText = new Text(Mod.Config.LocalizedText[ModConfig.LT_TT_DAMAGE_ARMOR]);
                descSB.Append(localText.ToString());
                foreach (ArmorLocation hitLoc in armorHitLocs)
                {
                    Text locationText = new Text(" - {0}\n", new object[] { hitLoc });
                    descSB.Append(locationText.ToString());
                }
            }
            if (repairState.StructureHits > 0)
            {
                Text localText = new Text(Mod.Config.LocalizedText[ModConfig.LT_TT_DAMAGE_STRUCTURE]);
                descSB.Append(localText.ToString());
                foreach (ChassisLocations hitLoc in structHitLocs)
                {
                    Text locationText = new Text(" - {0}\n", new object[] { hitLoc });
                    descSB.Append(locationText.ToString());
                }
            }
            if (componentDamageSB.Length > 0)
            {
                Text localText = new Text(Mod.Config.LocalizedText[ModConfig.LT_TT_DAMAGE_COMP]);
                descSB.Append(localText.ToString());
                descSB.Append(componentDamageSB.ToString());
            }
            if (pilotHealthTooltipText != null)
            {
                Text localText = new Text(Mod.Config.LocalizedText[ModConfig.LT_TT_DAMAGE_PILOT]);
                descSB.Append(localText.ToString());
                descSB.Append(pilotHealthTooltipText);
            }
            if (pilotSkillDamageTooltipText != null)
            {
                Text localText = new Text(Mod.Config.LocalizedText[ModConfig.LT_TT_DAMAGE_SKILL]);
                descSB.Append(localText.ToString());
                descSB.Append(pilotSkillDamageTooltipText.ToString());
            }

            Text titleText = new Text(ModState.CurrentTheme.Label, new object[] { repairState.effectRating });

            __instance.EffectData.Description = new BaseDescriptionDef("PoorlyMaintained",
                                                                       titleText.ToString(), descSB.ToString(), __instance.EffectData.Description.Icon);

            ModState.SuppressShowActorSequences = false;
            return(false);
        }
Esempio n. 5
0
        public BuildingRepairState(PoorlyMaintainedEffect effect, Building targetBuilding) : base(effect, null)
        {
            Target = targetBuilding;

            // Buildings only have structure
        }