Ejemplo n.º 1
0
        private void SetDamageLevel(WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel)
        {
            SetDamageLevel(component, hitInfo, damageLevel);

            if (HasLinked)
            {
                var id = LinkedScopedId();

                Control.Logger.Debug?.Log($"HasLinked id={id}");
                foreach (var otherMechComponent in actor.allComponents)
                {
                    if (otherMechComponent.DamageLevel == ComponentDamageLevel.Destroyed)
                    {
                        continue;
                    }

                    var otherCriticals = otherMechComponent.Criticals();
                    if (!otherCriticals.HasLinked)
                    {
                        continue;
                    }

                    var otherId = otherCriticals.LinkedScopedId();
                    if (id == otherId)
                    {
                        SetDamageLevel(otherMechComponent, hitInfo, damageLevel);
                    }
                }
            }
Ejemplo n.º 2
0
        public static void LogHitLocations(WeaponHitInfo hitInfo)
        {
            try
            {
                string output = "---\n";
                output += $"[Utilities_LogHitLocations] Clustered hits: {hitInfo.hitLocations.Length}\n";
                for (int i = 0; i < hitInfo.hitLocations.Length; i++)
                {
                    int location = hitInfo.hitLocations[i];
                    var chassisLocationFromArmorLocation = MechStructureRules.GetChassisLocationFromArmorLocation((ArmorLocation)location);

                    if (location == 0 || location == 65536)
                    {
                        output += $"[Utilities_LogHitLocations] HitLocation {i}: NONE/INVALID({location})";
                    }
                    else
                    {
                        output += $"[Utilities_LogHitLocations] HitLocation {i}: {chassisLocationFromArmorLocation}({location})";
                    }

                    output += (i < hitInfo.hitLocations.Length - 1) ? "\n" : "\n---";
                }
                Logger.Info(output, false);
            }
            catch (Exception e)
            {
                Logger.Error(e);
            }
        }
        internal static void OverrideApplyStructureStatDamage(
            this Mech mech,
            ChassisLocations location,
            float damage,
            WeaponHitInfo hitInfo
            )
        {
            try
            {
                if (IsInternalExplosion)
                {
                    var properties = ComponentExplosionHandler.Shared.GetCASEProperties(currentMech, (int)location);
                    if (properties.MaximumDamage.HasValue)
                    {
                        var directDamage = Mathf.Min(damage, properties.MaximumDamage.Value);
                        var backDamage   = damage - directDamage;
                        //Control.mod.Logger.LogDebug($"reducing structure damage from {damage} to {directDamage} in {Mech.GetAbbreviatedChassisLocation(location)}");
                        damage = directDamage;

                        if (backDamage > 0)
                        {
                            currentMech.PublishFloatieMessage("EXPLOSION REDIRECTED");

                            if ((location & ChassisLocations.Torso) > 0)
                            {
                                ArmorLocation armorLocation;
                                switch (location)
                                {
                                case ChassisLocations.LeftTorso:
                                    armorLocation = ArmorLocation.LeftTorsoRear;
                                    break;

                                case ChassisLocations.RightTorso:
                                    armorLocation = ArmorLocation.RightTorsoRear;
                                    break;

                                default:
                                    armorLocation = ArmorLocation.CenterTorsoRear;
                                    break;
                                }

                                var armor = mech.GetCurrentArmor(armorLocation);
                                if (armor > 0)
                                {
                                    var armorDamage = Mathf.Min(backDamage, armor);
                                    //Control.mod.Logger.LogDebug($"added blowout armor damage {armorDamage} to {Mech.GetLongArmorLocation(armorLocation)}");

                                    mech.ApplyArmorStatDamage(armorLocation, armorDamage, hitInfo);
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Control.mod.Logger.LogError(e);
            }
            mech.ApplyStructureStatDamage(location, damage, hitInfo);
        }
Ejemplo n.º 4
0
        static void Prefix(Mech __instance, int originalHitLoc, WeaponHitInfo hitInfo, ArmorLocation aLoc, Weapon weapon,
                           float totalArmorDamage, float directStructureDamage, int hitIndex, AttackImpactQuality impactQuality, DamageType damageType)
        {
            if (aLoc == ArmorLocation.Head)
            {
                Boolean DirectHit = false;

                //we do some quick calculation of damage to see if it's an armor hit or an structure hit
                float currentArmor = Math.Max(__instance.GetCurrentArmor(aLoc), 0f); //either it has armor remaining or it's got nothing left

                float remainingDamage = totalArmorDamage - currentArmor;             // subtract our armour damage by totalArmorDamage: if it's more than 0, we calculate for structure damage

                float structureDamage = directStructureDamage;
                if (remainingDamage > 0f)
                {
                    structureDamage += remainingDamage;
                }
                if (structureDamage >= 0f) // need to account for direct structure damage
                {
                    DirectHit = true;
                }
                if (!DirectHit && totalArmorDamage < LessHeadInjuries.Settings.ArmorHitDamageMinimum)
                {
                    //remainign damage less or equal to zero mean no structure penetration. Treat as an armour hit.
                    LessHeadInjuries.IgnoreNextHeadHit.Add(__instance.pilot);
                }
                else if (DirectHit && structureDamage < LessHeadInjuries.Settings.StructureHitDamageMinimum)
                {
                    LessHeadInjuries.IgnoreNextHeadHit.Add(__instance.pilot);
                }
            }
        }
        static bool Prefix(ref WeaponHitInfo hitInfo, int groupIdx, int weaponIdx, Weapon weapon, float toHitChance,
                           float prevDodgedDamage, AttackDirector.AttackSequence __instance)
        {
            if (!weapon.weaponDef.ComponentTags.Contains(CLUSTER_TAG))
            {
                return(true);
            }
            Logger.Debug("had the cluster tag");
            var newNumberOfShots      = weapon.ProjectilesPerShot * hitInfo.numberOfShots;
            var originalNumberOfShots = hitInfo.numberOfShots;

            hitInfo.numberOfShots  = newNumberOfShots;
            hitInfo.toHitRolls     = new float[newNumberOfShots];
            hitInfo.locationRolls  = new float[newNumberOfShots];
            hitInfo.dodgeRolls     = new float[newNumberOfShots];
            hitInfo.dodgeSuccesses = new bool[newNumberOfShots];
            hitInfo.hitLocations   = new int[newNumberOfShots];
            hitInfo.hitPositions   = new Vector3[newNumberOfShots];
            hitInfo.hitVariance    = new int[newNumberOfShots];
            hitInfo.hitQualities   = new AttackImpactQuality[newNumberOfShots];
            AttackSequenceGetClusteredHits.Invoke(
                __instance,
                new object[] { hitInfo, groupIdx, weaponIdx, weapon, toHitChance, prevDodgedDamage }
                );

            PrintHitLocations(hitInfo);
            hitInfo.numberOfShots = originalNumberOfShots;
            return(false);
        }
Ejemplo n.º 6
0
        // crit engine reduces speed
        // destroyed engine destroys CT
        public static bool Prefix(MechComponent __instance, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel, bool applyEffects)
        {
            try
            {
                if (!EngineCrits.ProcessWeaponHit(__instance, hitInfo, damageLevel, applyEffects, MechCheckForCritPatch.MessageAdditions))
                {
                    MechCheckForCritPatch.Message = null;
                    return(false);
                }

                if (!Structure.ProcessWeaponHit(__instance, hitInfo, damageLevel, applyEffects))
                {
                    MechCheckForCritPatch.Message = null;
                    return(false);
                }

                if (!Armor.ProcessWeaponHit(__instance, hitInfo, damageLevel, applyEffects))
                {
                    MechCheckForCritPatch.Message = null;
                    return(false);
                }
            }
            catch (Exception e)
            {
                Control.mod.Logger.LogError(e);
            }

            return(true);
        }
        private static void PrintHitLocations(WeaponHitInfo hitInfo)
        {
            if (!Core.ModSettings.debug)
            {
                return;
            }
            try
            {
                var output = "";
                output += $"clustered hits: {hitInfo.hitLocations.Length}\n";
                for (int i = 0; i < hitInfo.hitLocations.Length; i++)
                {
                    int location = hitInfo.hitLocations[i];
                    var chassisLocationFromArmorLocation =
                        MechStructureRules.GetChassisLocationFromArmorLocation((ArmorLocation)location);

                    if (location == 0 || location == 65536)
                    {
                        output += $"hitLocation {i}: NONE/INVALID\n";
                    }
                    else
                    {
                        output += $"hitLocation {i}: {chassisLocationFromArmorLocation} ({location})\n";
                    }
                }
                Logger.Debug(output);
            }
            catch (Exception e)
            {
                Logger.Error(e);
            }
        }
        public static bool ResolveSystemFailureCheck(Mech mech, int heatToCheck, int rootSequenceGUID, float heatCheck)
        {
            bool failedSystemFailureCheck = !CheckHelper.DidCheckPassThreshold(Mod.Config.Heat.SystemFailures, heatToCheck, mech, heatCheck, ModText.FT_Check_System_Failure);

            Mod.Log.Debug?.Write($"  failedSystemFailureCheck: {failedSystemFailureCheck}");
            if (failedSystemFailureCheck)
            {
                Mod.Log.Info?.Write($"-- System Failure check failed, forcing system damage on unit: {CombatantUtils.Label(mech)}");
                List <MechComponent> functionalComponents = new List <MechComponent>();
                foreach (MechComponent mc in mech.allComponents)
                {
                    bool canTarget = mc.IsFunctional;
                    if (mc.mechComponentRef.Is <Flags>(out Flags flagsCC))
                    {
                        if (flagsCC.IsSet(ModStats.ME_IgnoreDamage))
                        {
                            canTarget = false;
                            Mod.Log.Trace?.Write($"    Component: {mc.Name} / {mc.UIName} is marked ignores_damage.");
                        }
                    }
                    if (canTarget)
                    {
                        functionalComponents.Add(mc);
                    }
                }
                MechComponent componentToDamage = functionalComponents.GetRandomElement();
                Mod.Log.Info?.Write($"   Destroying component: {componentToDamage.UIName} from heat damage.");

                WeaponHitInfo fakeHit = new WeaponHitInfo(rootSequenceGUID, -1, -1, -1, string.Empty, string.Empty, -1, null, null, null, null, null, null, null,
                                                          new AttackDirection[] { AttackDirection.None }, null, null, null);
                componentToDamage.DamageComponent(fakeHit, ComponentDamageLevel.Destroyed, true);
            }

            return(failedSystemFailureCheck);
        }
Ejemplo n.º 9
0
        private static void Postfix(Mech __instance, WeaponHitInfo hitInfo, Weapon weapon, MeleeAttackType meleeAttackType)
        {
            AttackDirector.AttackSequence attackSequence = __instance.Combat.AttackDirector.GetAttackSequence(hitInfo.attackSequenceId);
            AbstractActor actor = __instance.Combat.FindActorByGUID(hitInfo.targetId);

            if (actor is Mech)
            {
                Mech target = actor as Mech;

                float stabilityDamage = hitInfo.ConsolidateInstability(weapon.Instability(), __instance.Combat.Constants.ResolutionConstants.GlancingBlowDamageMultiplier, __instance.Combat.Constants.ResolutionConstants.NormalBlowDamageMultiplier, __instance.Combat.Constants.ResolutionConstants.SolidBlowDamageMultiplier);
                stabilityDamage *= __instance.StatCollection.GetValue <float>("ReceivedInstabilityMultiplier");
                stabilityDamage *= __instance.EntrenchedMultiplier;

                if (AttackDirector.attackLogger.IsLogEnabled)
                {
                    AttackDirector.attackLogger.Log("[CBTPiloting] Checking Piloting Stability");
                    AttackDirector.attackLogger.Log(string.Format("[CBTPiloting] Is Mech: {0}", (actor is Mech)));
                    AttackDirector.attackLogger.Log(string.Format("[CBTPiloting] Weapon Stab Dmg: {0}", stabilityDamage));
                    AttackDirector.attackLogger.Log(string.Format("[CBTPiloting] Target Dead: {0}", target.IsDead));
                    AttackDirector.attackLogger.Log(string.Format("[CBTPiloting] Target Unsteady: {0}", target.IsUnsteady));
                    AttackDirector.attackLogger.Log(string.Format("[CBTPiloting] Target IsOrWillBeProne: {0}", target.IsOrWillBeProne));
                }

                if (stabilityDamage > 0 && !target.IsDead && target.IsUnsteady && !target.IsOrWillBeProne)
                {
                    float skillBonus = (float)target.SkillPiloting / __instance.Combat.Constants.PilotingConstants.PilotingDivisor;

                    float skillRoll  = __instance.Combat.NetworkRandom.Float();
                    float skillTotal = skillRoll + skillBonus;

                    if (AttackDirector.attackLogger.IsLogEnabled)
                    {
                        AttackDirector.attackLogger.Log(string.Format("[CBTPiloting] Skill Bonus: {0}", skillBonus));
                        AttackDirector.attackLogger.Log(string.Format("[CBTPiloting] Skill Roll: {0}", skillRoll));
                        AttackDirector.attackLogger.Log(string.Format("[CBTPiloting] Skill Roll Total: {0}", skillTotal));
                        AttackDirector.attackLogger.Log(string.Format("[CBTPiloting] Skill Target: {0}", CBTPiloting.Settings.PilotStabilityCheck));
                    }

                    if (skillTotal < CBTPiloting.Settings.PilotStabilityCheck)
                    {
                        if (AttackDirector.attackLogger.IsLogEnabled)
                        {
                            AttackDirector.attackLogger.Log(string.Format("[CBTPiloting] Skill Check Failed! Flagging for Knockdown"));
                        }

                        target.FlagForKnockdown();
                        target.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(new ShowActorInfoSequence(target, $"Stability Check: Failed!", FloatieMessage.MessageNature.Debuff, true)));
                    }
                    else
                    {
                        if (AttackDirector.attackLogger.IsLogEnabled)
                        {
                            AttackDirector.attackLogger.Log(string.Format("[CBTPiloting] Skill Check Succeeded!"));
                        }

                        target.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(new ShowActorInfoSequence(target, $"Stability Check: Passed!", FloatieMessage.MessageNature.Buff, true)));
                    }
                }
            }
        }
        public static bool Prefix(
            Mech __instance,
            WeaponHitInfo hitInfo,
            ArmorLocation aLoc,
            ref float directStructureDamage,
            ref bool __result)
        {
            try
            {
                if (ComponentExplosionsFeature.IsInternalExplosionContained)
                {
                    __result = false;
                    Control.Logger.Warning.Log("prevented explosion pass through (you should never see this message)");
                    return(false);
                }

                if (ComponentExplosionsFeature.IsInternalExplosion)
                {
                    var location = MechStructureRules.GetChassisLocationFromArmorLocation(aLoc);
                    UpdateStructureDamage(__instance, location, hitInfo, ref directStructureDamage);
                }
            }
            catch (Exception e)
            {
                Control.Logger.Error.Log(e);
            }

            return(true);
        }
        public static bool ResolveVolatileAmmoCheck(Mech mech, int heatToCheck, int rootSequenceGUID, float heatCheck)
        {
            bool          failedVolatileAmmoCheck = false;
            AmmunitionBox mostDamagingVolatile    = HeatHelper.FindMostDamagingAmmoBox(mech, true);

            if (mostDamagingVolatile != null)
            {
                failedVolatileAmmoCheck = !CheckHelper.DidCheckPassThreshold(Mod.Config.Heat.Explosion, heatToCheck, mech, heatCheck, ModText.FT_Check_Explosion);
                Mod.Log.Debug?.Write($"  failedVolatileAmmoCheck: {failedVolatileAmmoCheck}");
                if (failedVolatileAmmoCheck)
                {
                    Mod.Log.Info?.Write($"-- Volatile Ammo Explosion check failed on {CombatantUtils.Label(mech)}, forcing volatile ammo explosion");

                    if (mostDamagingVolatile != null)
                    {
                        Mod.Log.Info?.Write($" Exploding inferno ammo: {mostDamagingVolatile.UIName}");
                        WeaponHitInfo fakeHit = new WeaponHitInfo(rootSequenceGUID, -1, -1, -1, string.Empty, string.Empty, -1, null, null, null, null, null, null, null,
                                                                  new AttackDirection[] { AttackDirection.None }, null, null, null);
                        mostDamagingVolatile.DamageComponent(fakeHit, ComponentDamageLevel.Destroyed, true);
                    }
                    else
                    {
                        Mod.Log.Debug?.Write(" Unit has no Volatile ammo boxes, skipping.");
                    }
                }
            }

            return(failedVolatileAmmoCheck);
        }
Ejemplo n.º 12
0
 private static void AddJam(AbstractActor actor, Weapon weapon)
 {
     if (!DamagesWhenJams(weapon))
     {
         weapon.StatCollection.Set <bool>(JammedWeaponStatisticName, true);
         weapon.StatCollection.Set <bool>(TemporarilyDisabledStatisticName, true);
         actor.Combat.MessageCenter.PublishMessage(
             new AddSequenceToStackMessage(
                 new ShowActorInfoSequence(actor, $"{weapon.Name} Jammed!", FloatieMessage.MessageNature.Debuff,
                                           true)));
     }
     else
     {
         var isDestroying = weapon.DamageLevel != ComponentDamageLevel.Functional;
         var damageLevel  = isDestroying ? ComponentDamageLevel.Destroyed : ComponentDamageLevel.Penalized;
         var fakeHit      = new WeaponHitInfo(-1, -1, -1, -1, string.Empty, string.Empty, -1, null, null, null, null, null, null, null, AttackDirection.None, Vector2.zero, null);
         weapon.DamageComponent(fakeHit, damageLevel, true);
         var message = isDestroying
             ? $"{weapon.Name} misfire: Destroyed!"
             : $"{weapon.Name} misfire: Damaged!";
         actor.Combat.MessageCenter.PublishMessage(
             new AddSequenceToStackMessage(
                 new ShowActorInfoSequence(actor, message, FloatieMessage.MessageNature.Debuff, true)));
     }
 }
Ejemplo n.º 13
0
        public static void Prefix(Mech __instance, WeaponHitInfo hitInfo, ArmorLocation aLoc, Weapon weapon, float totalArmorDamage, float directStructureDamage,
                                  int hitIndex, AttackImpactQuality impactQuality, DamageType damageType)
        {
            if (aLoc == ArmorLocation.Head)
            {
                Mod.Log.Info($"Head hit from weapon:{weapon?.UIName} for {totalArmorDamage} armor damage and {directStructureDamage} structure damage. " +
                             $"Quality was:{impactQuality} with type:{damageType}");

                float currHeadArmor      = __instance.GetCurrentArmor(aLoc);
                int   damageMod          = (int)Math.Ceiling(totalArmorDamage);
                float damageThroughArmor = totalArmorDamage - currHeadArmor;
                Mod.Log.Debug($"TotalArmorDamage:{totalArmorDamage} - Head armor:{currHeadArmor} = throughArmor:{damageThroughArmor}");

                if (totalArmorDamage - currHeadArmor <= 0)
                {
                    damageMod = (int)Math.Floor(damageMod * Mod.Config.Combat.PainTolerance.HeadHitArmorOnlyMulti);
                    Mod.Log.Info($"Head hit impacted armor only, reduced damage to:{damageMod}");
                }

                if (directStructureDamage != 0)
                {
                    Mod.Log.Debug($"Attack inflicted ${directStructureDamage}, adding to total resist damage.");
                    damageMod += (int)Math.Ceiling(directStructureDamage);
                }

                ModState.InjuryResistPenalty = damageMod * Mod.Config.Combat.PainTolerance.PenaltyPerHeadDamage;
                Mod.Log.Info($"Setting resist penalty to:{damageMod} x {Mod.Config.Combat.PainTolerance.PenaltyPerHeadDamage} = {ModState.InjuryResistPenalty}");
            }
        }
Ejemplo n.º 14
0
 private void CheckWeaponDistance(NebulaObject target, WeaponHitInfo hit)
 {
     if (hit.normal && BlockedByDistance(target))
     {
         hit.Interrupt(ShotState.blockedByDistance);
     }
 }
Ejemplo n.º 15
0
 private void CheckWeaponBlocked(WeaponHitInfo hit)
 {
     if (hit.normal && blocked)
     {
         hit.Interrupt(ShotState.blockedBySkill);
     }
 }
Ejemplo n.º 16
0
        private static float GetWeaponDamage(AbstractActor target, WeaponHitInfo hitInfo, Weapon weapon)
        {
            float           damage          = weapon.parent == null ? weapon.DamagePerShot : weapon.DamagePerShotAdjusted(weapon.parent.occupiedDesignMask);
            AbstractActor   attacker        = Combat.FindActorByGUID(hitInfo.attackerId);
            LineOfFireLevel lineOfFireLevel = attacker.VisibilityCache.VisibilityToTarget(target).LineOfFireLevel;

            return(target.GetAdjustedDamage(damage, weapon.Category, target.occupiedDesignMask, lineOfFireLevel, false));
        }
Ejemplo n.º 17
0
 public static void RecordHitChance(ref WeaponHitInfo hitInfo, float toHitChance)
 {
     if (hitChance.ContainsKey(hitInfo.attackSequenceId))
     {
         return;
     }
     hitChance.Add(hitInfo.attackSequenceId, toHitChance);
 }
Ejemplo n.º 18
0
 private bool IsCritical(ref WeaponHitInfo hit)
 {
     if (RollCritical())
     {
         hit.MakeCritical();
     }
     return(hit.state == ShotState.normalCritical);
 }
Ejemplo n.º 19
0
            static void Prefix(Mech __instance, int originalHitLoc, WeaponHitInfo hitInfo, ArmorLocation aLoc, Weapon weapon,
                               float totalArmorDamage, float directStructureDamage, int hitIndex, AttackImpactQuality impactQuality, DamageType damageType)
            {
                if (aLoc == ArmorLocation.None || aLoc == ArmorLocation.Invalid)
                {
                    return;
                }


                float num          = totalArmorDamage;
                float currentArmor = __instance.GetCurrentArmor(aLoc);

                if (currentArmor > 0f)
                {
                    num = totalArmorDamage - currentArmor;
                }
                num += directStructureDamage; // account for damage split: this should get us back where we were when we both had armour spillover damage and
                // any damage done directly to the structure
                if (num <= 0f)
                {
                    return; //no need to continue if the shot doesn't do anything we care about
                }
                ChassisLocations chassisLocationFromArmorLocation = MechStructureRules.GetChassisLocationFromArmorLocation(aLoc);


                float currentStructure = __instance.GetCurrentStructure(chassisLocationFromArmorLocation);

                if (currentStructure > 0f)
                {
                    float num4         = Math.Min(num, currentStructure);
                    bool  WasDestroyed = (currentStructure - num) <= 0; //if currentstructure minus remaining damage is less or equal to 0, then the location is destroyed.

                    num -= num4;
                    if (WasDestroyed && num4 > 0.01f) //this location was destroyed, so we now check for dependents.
                    {
                        if (chassisLocationFromArmorLocation == ChassisLocations.LeftArm)
                        {
                            Holder.LeftArmSurvived = false; //invalidate if the actual arm was destroyed
                        }
                        else if (chassisLocationFromArmorLocation == ChassisLocations.RightArm)
                        {
                            Holder.RightArmSurvived = false; //invalidate if the actual arm was destroyed
                        }
                        ChassisLocations dependentLocation = MechStructureRules.GetDependentLocation(chassisLocationFromArmorLocation);
                        if (dependentLocation != ChassisLocations.None && !__instance.IsLocationDestroyed(dependentLocation))
                        {
                            if (dependentLocation == ChassisLocations.LeftArm)
                            {
                                Holder.LeftArmSurvived = true; //side torso was destroyed, no reason the arm should be totally trashed.
                            }
                            else if (dependentLocation == ChassisLocations.RightArm)
                            {
                                Holder.RightArmSurvived = true; //side torso was destroyed, no reason the arm should be totally trashed.
                            }
                        }
                    }
                }
            }
Ejemplo n.º 20
0
        public static void ApplyPilotHealthDamage(AbstractActor target, WeaponHitInfo hitInfo, int headHits, out string tooltipText)
        {
            StringBuilder pilotDamageSB = new StringBuilder();

            if (target == null || !target.IsPilotable || headHits == 0)
            {
                tooltipText = null;
                return;
            }

            int healthDamage = headHits;

            if (target.GetPilot().BonusHealth > 0)
            {
                int absorbedDamage;
                if (target.GetPilot().BonusHealth >= healthDamage)
                {
                    absorbedDamage = healthDamage;
                    healthDamage   = 0;
                }
                else
                {
                    absorbedDamage = target.GetPilot().BonusHealth;
                    healthDamage   = healthDamage - target.GetPilot().BonusHealth;
                }

                Mod.Log.Debug?.Write($"Bonus health aborbs: {absorbedDamage} leaving: {healthDamage} healthDamage.");
                target.GetPilot().StatCollection.ModifyStat <int>(hitInfo.attackerId, hitInfo.stackItemUID,
                                                                  "BonusHealth", StatCollection.StatOperation.Int_Subtract, absorbedDamage, -1, true);
                Text localText = new Text(Mod.Config.LocalizedText[ModConfig.LT_TT_PILOT_BONUS_HEALTH], new object[] { absorbedDamage });
                pilotDamageSB.Append(localText.ToString());
            }

            if (healthDamage > (target.GetPilot().Health - 1))
            {
                Mod.Log.Debug?.Write($"Health damage: {healthDamage} would kill pilot, reducing to maxHealth: {target.GetPilot().Health} - 1");
                healthDamage = target.GetPilot().Health - 1;
            }

            if (healthDamage > 0 && !Mod.Config.EnableTBAS_Injuries)
            {
                Mod.Log.Info?.Write($"Adding {healthDamage} to {CombatantUtils.Label(target)}");
                target.GetPilot().StatCollection.ModifyStat <int>(hitInfo.attackerId, hitInfo.stackItemUID,
                                                                  "Injuries", StatCollection.StatOperation.Int_Add, healthDamage, -1, true);
                Text localText = new Text(Mod.Config.LocalizedText[ModConfig.LT_TT_PILOT_HEALTH], new object[] { healthDamage });
                pilotDamageSB.Append(localText.ToString());
            }
            else if (healthDamage > 0 && Mod.Config.EnableTBAS_Injuries)
            {
                Mod.Log.Info?.Write($"Adding {healthDamage} TBAS Injuries to {CombatantUtils.Label(target)}");
                target.GetPilot().InjurePilot(hitInfo.attackerId, hitInfo.stackItemUID, healthDamage, BattleTech.DamageType.NOT_SET, null, null);
                Text localText = new Text(Mod.Config.LocalizedText[ModConfig.LT_TT_PILOT_HEALTH], new object[] { healthDamage });
                pilotDamageSB.Append(localText.ToString());
            }

            tooltipText = pilotDamageSB.ToString();
        }
Ejemplo n.º 21
0
        internal static bool ProcessWeaponHit(MechComponent mechComponent, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel, bool applyEffects)
        {
            if (mechComponent.componentDef.IsStructure())
            {
                return(false);
            }

            return(true);
        }
 private static void SetDamageLevel(MechComponent mechComponent, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel)
 {
     mechComponent.StatCollection.ModifyStat(
         hitInfo.attackerId,
         hitInfo.stackItemUID,
         "DamageLevel",
         StatCollection.StatOperation.Set,
         damageLevel);
 }
Ejemplo n.º 23
0
    private void SetHits(WeaponHitInfo hitInfo, out ComponentDamageLevel damageLevel)
    {
        if (actor == null)
        {
            throw new NullReferenceException();
        }

        int effectsMax, effectsPrev, effectsNext;

        {
            var compCritsMax  = ComponentHitMax();
            var compCritsPrev = ComponentHitCount();

            var locationDestroyed = actor.StructureForLocation(component.Location) <= 0f;
            var possibleAddedHits = locationDestroyed ? compCritsMax : 1;
            var compCritsNext     = Mathf.Min(compCritsMax, compCritsPrev + possibleAddedHits);
            var compCritsAdded    = Mathf.Max(compCritsNext - compCritsPrev, 0);

            ComponentHitCount(compCritsNext);

            // if max is reached, component is destroyed and no new effects can be applied
            // Destroyed components can still soak up crits, requires properly configured AIM from CAC
            effectsMax = Effects?.PenalizedEffectIDs.Length + 1 ?? DefaultEffectsMax();

            // move to group/component abstraction, make sure that critsAdded is clear
            if (HasLinked)
            {
                var prev = GroupHitCount();
                var next = Mathf.Min(effectsMax, prev + compCritsAdded);
                GroupHitCount(next);

                effectsPrev = prev;
                effectsNext = next;
            }
            else
            {
                effectsPrev = compCritsPrev;
                effectsNext = compCritsNext;
            }
        }

        damageLevel = effectsNext >= effectsMax ? ComponentDamageLevel.Destroyed : ComponentDamageLevel.Penalized;
        SetDamageLevel(hitInfo, damageLevel);

        if (Effects != null)
        {
            CancelEffects(effectsPrev, damageLevel);
            CreateEffects(effectsNext, damageLevel);
        }

        Control.Logger.Debug?.Log(
            $"Component hit (uid={component.uid} Id={component.Description.Id} Location={component.Location}) " +
            $"effectsMax={effectsMax} effectsPrev={effectsPrev} effectsNext={effectsNext} " +
            $"damageLevel={damageLevel} HasEffects={Effects != null} LinkedStatisticName={Effects?.LinkedStatisticName}"
            );
    }
 public static void Postfix(MechComponent __instance, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel, bool applyEffects)
 {
     try {
         Log.Debug?.TWL(0, "MechComponent.DamageComponent Postfix " + __instance.defId + " DamageLevel:" + __instance.DamageLevel + "/" + damageLevel);
         if ((__instance.DamageLevel >= ComponentDamageLevel.Destroyed) || (damageLevel >= ComponentDamageLevel.Destroyed))
         {
             Statistic isCAEDestroyed = __instance.StatCollection.GetStatistic("CAEDestroyed");
             if (isCAEDestroyed == null)
             {
                 isCAEDestroyed = __instance.StatCollection.AddStatistic("CAEDestroyed", false);
             }
             if (isCAEDestroyed.Value <bool>() == false)
             {
                 isCAEDestroyed.SetValue(true);
                 Log.Debug?.WL(1, "really destroyed at first time");
                 ActivatableComponent activatable = __instance.componentDef.GetComponent <ActivatableComponent>();
                 if (activatable == null)
                 {
                     Log.Debug?.WL(1, "not activatable");
                     return;
                 }
                 ObjectSpawnDataSelf VFX = __instance.PresitantVFX();
                 if (VFX != null)
                 {
                     VFX.CleanupSelf();
                 }
                 ;
                 VFX = __instance.ActivateVFX();
                 if (VFX != null)
                 {
                     VFX.CleanupSelf();
                 }
                 ;
                 VFX = __instance.DestroyedVFX();
                 if (VFX != null)
                 {
                     VFX.SpawnSelf(__instance.parent.Combat);
                 }
                 ;
                 if (activatable.ExplodeOnDamage)
                 {
                     __instance.AoEExplodeComponent();
                 }
                 ;
                 __instance.playDestroySound();
             }
             else
             {
                 Log.Debug?.WL(1, "not a really destroyed");
             }
             __instance.UpdateAuras();
         }
     } catch (Exception e) {
         Log.Debug?.TWL(0, e.ToString(), true);
     }
 }
Ejemplo n.º 25
0
 private static void SetDamageLevel(MechComponent mechComponent, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel)
 {
     Control.mod.Logger.LogDebug($"damageLevel={damageLevel} uid={mechComponent.uid} (Id={mechComponent.Description.Id} Location={mechComponent.Location})");
     mechComponent.StatCollection.ModifyStat(
         hitInfo.attackerId,
         hitInfo.stackItemUID,
         "DamageLevel",
         StatCollection.StatOperation.Set,
         damageLevel);
 }
Ejemplo n.º 26
0
        //private bool IsHitted(NebulaObject target, ref WeaponHitInfo hit) {
        //    if(hit.normal) {
        //        if(BlockedByDistance(target)) {
        //            hit.Interrupt(ShotState.blockedByDistance);
        //            return false;
        //        } else {
        //            float hitProb = 1.0f - ComputeMissProb(target);
        //            hitProb = Mathf.ClampLess(hitProb, 0.0f);
        //            if (Rand.Float01() <= hitProb) {
        //                hit.Interrupt(ShotState.missed);
        //                return false;
        //            } else {
        //                return true;
        //            }
        //        }

        //    }
        //    return false;
        //}

        private void CheckWeaponHit(NebulaObject target, WeaponHitInfo hit)
        {
            float hitProb = 1.0f - ComputeMissProb(target);

            hitProb = Mathf.ClampLess(hitProb, 0.0f);
            if (Rand.Float01() > hitProb)
            {
                hit.Interrupt(ShotState.missed);
            }
        }
Ejemplo n.º 27
0
            public static void Postfix(Mech __instance, WeaponHitInfo hitInfo, Weapon weapon, MeleeAttackType meleeAttackType)
            {
                Mod.Log.Trace("M:RWD entered.");

                AttackDirector.AttackSequence attackSequence = __instance.Combat.AttackDirector.GetAttackSequence(hitInfo.attackSequenceId);
                AbstractActor actor = __instance.Combat.FindActorByGUID(hitInfo.targetId);

                if (actor is Mech target)
                {
                    CombatResolutionConstantsDef crcd = __instance.Combat.Constants.ResolutionConstants;
                    float stabilityDamage             = hitInfo.ConsolidateInstability(hitInfo.targetId, weapon.Instability(),
                                                                                       crcd.GlancingBlowDamageMultiplier, crcd.NormalBlowDamageMultiplier, crcd.SolidBlowDamageMultiplier);

                    stabilityDamage *= __instance.StatCollection.GetValue <float>("ReceivedInstabilityMultiplier");
                    stabilityDamage *= __instance.EntrenchedMultiplier;

                    Mod.Log.Debug($" == Checking Piloting Stability");
                    Mod.Log.Debug($"   target:{CombatantHelper.LogLabel(target)} isMech:{(actor is Mech)} IsDead:{target.IsDead} IsUnsteady:{target.IsUnsteady} IsOrWillBeProne:{target.IsOrWillBeProne}");
                    Mod.Log.Debug($"   weapon stability damage:{stabilityDamage}");

                    if (stabilityDamage > 0 && !target.IsDead && target.IsUnsteady && !target.IsOrWillBeProne)
                    {
                        float skillBonus = (float)target.SkillPiloting / __instance.Combat.Constants.PilotingConstants.PilotingDivisor;

                        float skillRoll  = __instance.Combat.NetworkRandom.Float();
                        float skillTotal = skillRoll + skillBonus;

                        Mod.Log.Debug($" Skill check -> bonus: {skillBonus}  roll: {skillRoll}  rollTotal: {skillTotal}  target:{Mod.Config.PilotStabilityCheck}");

                        if (skillTotal < Mod.Config.PilotStabilityCheck)
                        {
                            Mod.Log.Debug(string.Format(" Skill Check Failed! Flagging for Knockdown"));
                            bool showMessage = !target.IsFlaggedForKnockdown;

                            target.FlagForKnockdown();
                            if (Mod.Config.ShowAllStabilityRolls || showMessage)
                            {
                                target.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(new ShowActorInfoSequence(target, $"Stability Check: Failed!", FloatieMessage.MessageNature.Debuff, true)));
                            }
                        }
                        else
                        {
                            Mod.Log.Debug(string.Format(" Skill Check Succeeded!"));
                            if (Mod.Config.ShowAllStabilityRolls)
                            {
                                target.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(new ShowActorInfoSequence(target, $"Stability Check: Passed!", FloatieMessage.MessageNature.Buff, true)));
                            }
                        }
                    }
                    else
                    {
                        Mod.Log.Debug($"  target has no stability damage, is not unsteady, or is dead or prone - skipping");
                    }
                }
            }
Ejemplo n.º 28
0
        public static void ArmorDamage(this AbstractActor target, List <int> locations, float amount, bool critComponents, HashSet <string> excludeTags, HashSet <string> onlyTags)
        {
            WeaponHitInfo fakeHit = new WeaponHitInfo(-1, -1, -1, -1, target.GUID, target.GUID, 1, null, null, null, null, null, null, null, null, null, null, null);

            fakeHit.toHitRolls = new float[1] {
                1.0f
            };
            fakeHit.locationRolls = new float[1] {
                1.0f
            };
            fakeHit.dodgeRolls = new float[1] {
                0.0f
            };
            fakeHit.dodgeSuccesses = new bool[1] {
                false
            };
            fakeHit.hitLocations = new int[1] {
                0
            };
            fakeHit.hitVariance = new int[1] {
                0
            };
            fakeHit.hitQualities = new AttackImpactQuality[1] {
                AttackImpactQuality.Solid
            };
            fakeHit.attackDirections = new AttackDirection[1] {
                AttackDirection.FromArtillery
            };
            fakeHit.hitPositions = new Vector3[1] {
                target.CurrentPosition
            };
            fakeHit.secondaryTargetIds = new string[1] {
                null
            };
            fakeHit.secondaryHitLocations = new int[1] {
                0
            };
            Log.Debug?.TWL(0, "StructureDamage:" + target.DisplayName + " amount:" + amount + " critComponents:" + critComponents);
            foreach (int location in locations)
            {
                Log.Debug?.WL(1, "location:" + location);
                fakeHit.hitLocations[0] = location;
                fakeHit.hitPositions[0] = target.GameRep.GetHitPosition(location);
                float prev_armor = target.ArmorForLocation(location);
                if (amount >= 1f)
                {
                    target.TakeWeaponDamage(fakeHit, location, target.ImaginaryLaserWeapon, amount, 0, 0, DamageType.ComponentExplosion);
                }
                if ((critComponents) && (prev_armor < amount))
                {
                    target.CritComponentsInLocation(location, ref fakeHit, excludeTags, onlyTags);
                }
            }
        }
Ejemplo n.º 29
0
        public static void SetImpact(MessageCenterMessage message)
        {
            if (!(message is AttackSequenceImpactMessage impactMessage))
            {
                return;
            }
            WeaponHitInfo info = impactMessage.hitInfo;

            currentImpact = info.attackSequenceId;
            currentRoll   = info.toHitRolls[impactMessage.hitIndex];
        }
 public static void Postfix(MechComponent __instance, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel, bool applyEffects)
 {
     try
     {
         ComponentExplosionsFeature.Shared.CheckForExplosion(__instance, hitInfo, damageLevel, applyEffects);
     }
     catch (Exception e)
     {
         Control.mod.Logger.LogError(e);
     }
 }