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}"); } }
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); }
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."); } } } }
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); }
public BuildingRepairState(PoorlyMaintainedEffect effect, Building targetBuilding) : base(effect, null) { Target = targetBuilding; // Buildings only have structure }