public static void Postfix(MechComponent __instance, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel, bool applyEffects, ref bool __state)
 {
     if (__state)
     {
         ActivatableComponent activatable = __instance.componentDef.GetComponent <ActivatableComponent>();
         if (activatable == null)
         {
             Log.Debug?.Write(" not activatable\n");
             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?.Write(" no additional processing\n");
     }
     __instance.UpdateAuras();
 }
Example #2
0
        public static ComponentDamageLevel GetDegradedComponentLevel(AIMCritInfo info, out MessageCenterMessage critMessage)
        {
            MechComponent        component            = info.component;
            ComponentDamageLevel componentDamageLevel = component.DamageLevel;

            if (component is Weapon && componentDamageLevel == ComponentDamageLevel.Functional)
            {
                componentDamageLevel = ComponentDamageLevel.Penalized;
                critMessage          = GetCritMessage(info.target, "{0} CRIT", component.UIName, FloatieMessage.MessageNature.CriticalHit);
            }
            else if (componentDamageLevel != ComponentDamageLevel.Destroyed)
            {
                componentDamageLevel = ComponentDamageLevel.Destroyed;
                critMessage          = GetCritMessage(info.target, "{0} DESTROYED", component.UIName, FloatieMessage.MessageNature.ComponentDestroyed);
            }
            else
            {
                critMessage = null;
            }
            return(componentDamageLevel);
        }
Example #3
0
        public static void Prefix(AmmunitionBox __instance, ComponentDamageLevel damageLevel, bool applyEffects)
        {
            if (__instance == null || SharedState.Combat == null)
            {
                return;                                                   // We cannot do anything
            }
            if (SharedState.Combat?.Constants?.PilotingConstants == null)
            {
                Mod.Log.Error("Piloting Constants is somehow null! This should not happen!");
                return;
            }

            if (!__instance.StatCollection.ContainsStatistic(ModStats.HBS_AmmoBox_CurrentAmmo) ||
                __instance.ammunitionBoxDef == null ||
                __instance.ammunitionBoxDef.Capacity == 0)
            {
                Mod.Log.Warn($"Invalid ammoBox '{__instance.UIName}' detected. It does not contain CurrentAmmo stat, has no ammunitionBoxDef, or has 0 capacity.");
                return;
            }

            bool explosionsCauseInjuries = SharedState.Combat?.Constants?.PilotingConstants != null ?
                                           SharedState.Combat.Constants.PilotingConstants.InjuryFromAmmoExplosion : false;

            if (applyEffects && damageLevel == ComponentDamageLevel.Destroyed && explosionsCauseInjuries)
            {
                int   value    = __instance.StatCollection.GetValue <int>("CurrentAmmo");
                int   capacity = __instance.ammunitionBoxDef.Capacity;
                float ratio    = (float)value / (float)capacity;
                Mod.Log.Debug($"Ammo explosion ratio:{ratio} = current:{value} / capacity:{capacity}");
                int resistPenalty = (int)Math.Floor(ratio * Mod.Config.Combat.PainTolerance.PenaltyPerAmmoExplosionRatio);
                Mod.Log.Debug($"Ammo explosion resist penalty:{resistPenalty} = " +
                              $"Floor( ratio:{ratio}% * penaltyPerAmmoExplosion:{Mod.Config.Combat.PainTolerance.PenaltyPerAmmoExplosionRatio} )");

                if (ratio >= 0.5f)
                {
                    ModState.InjuryResistPenalty = resistPenalty;
                    Mod.Log.Debug($"Ammo explosion will reduce resist by: {resistPenalty}");
                }
            }
        }
Example #4
0
        internal void CheckForExplosion(MechComponent component, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel, bool applyEffects)
        {
            if (!applyEffects)
            {
                return;
            }

            if (damageLevel != ComponentDamageLevel.Destroyed)
            {
                return;
            }

            if (!(component.componentDef.Is <ComponentExplosion>(out var exp)))
            {
                return;
            }

            var actor = component.parent;

            var ammoCount = 0;

            if (component is AmmunitionBox box)
            {
                var adapter = new MechComponentAdapter(box);
                ammoCount = adapter.statCollection.GetValue <int>("CurrentAmmo");
            }
            else if (component is Weapon w2)
            {
                ammoCount = Mathf.Max(w2.InternalAmmo, Mathf.Min(w2.ShotsWhenFired, w2.CurrentAmmo));
            }

            var attackSequence = actor.Combat.AttackDirector.GetAttackSequence(hitInfo.attackSequenceId);

            var heatDamage = exp.HeatDamage + ammoCount * exp.HeatDamagePerAmmo;

            //Control.mod.Logger.LogDebug($"heatDamage={heatDamage}");
            if (!Mathf.Approximately(heatDamage, 0))
            {
                actor.AddExternalHeat("AMMO EXPLOSION HEAT", (int)heatDamage);
                attackSequence?.FlagAttackDidHeatDamage(actor.GUID);
            }

            { // only applies for mechs, vehicles don't have stability
                if (actor is Mech mech)
                {
                    var stabilityDamage = exp.StabilityDamage + ammoCount * exp.StabilityDamagePerAmmo;
                    //Control.mod.Logger.LogDebug($"stabilityDamage={stabilityDamage}");
                    if (!Mathf.Approximately(stabilityDamage, 0))
                    {
                        mech.AddAbsoluteInstability(stabilityDamage, StabilityChangeSource.Effect, hitInfo.targetId);
                    }
                }
            }

            var explosionDamage = exp.ExplosionDamage + ammoCount * exp.ExplosionDamagePerAmmo;

            //Control.mod.Logger.LogDebug($"explosionDamage={explosionDamage}");

            if (Mathf.Approximately(explosionDamage, 0))
            {
                return;
            }

            IsInternalExplosion = true;
            try
            {
                attackSequence?.FlagAttackCausedAmmoExplosion(actor.GUID);

                actor.PublishFloatieMessage($"{component.Name} EXPLOSION");
                if (actor.Combat.Constants.PilotingConstants.InjuryFromAmmoExplosion)
                {
                    var pilot = actor.GetPilot();
                    pilot?.SetNeedsInjury(InjuryReason.AmmoExplosion);
                }

                if (actor is Mech mech)
                {
                    // this is very hacky as this is an invalid weapon
                    var weapon = new Weapon(mech, actor.Combat, component.mechComponentRef, component.uid);

                    mech.DamageLocation(component.Location, hitInfo, (ArmorLocation)component.Location, weapon, 0, explosionDamage, 0, AttackImpactQuality.Solid, DamageType.AmmoExplosion);
                }
                else if (actor is Vehicle vehicle)
                {
                    // this is very hacky as this is an invalid weapon
                    var weapon = new Weapon(vehicle, actor.Combat, component.vehicleComponentRef, component.uid);

                    vehicle.DamageLocation(hitInfo, component.Location, (VehicleChassisLocations)component.Location, weapon, 0, explosionDamage, AttackImpactQuality.Solid);
                }
                else if (actor is Turret turret)
                {
                    // this is very hacky as this is an invalid weapon
                    var weapon = new Weapon(turret, actor.Combat, component.turretComponentRef, component.uid);

                    turret.DamageLocation(hitInfo, (BuildingLocation)component.Location, weapon, 0, explosionDamage);
                }
            }
            finally
            {
                IsInternalExplosion = false;
            }
        }
        internal static bool ProcessWeaponHit(MechComponent mechComponent, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel, bool applyEffects, List <MessageAddition> messages)
        {
            if (!Control.settings.EngineCritsEnabled)
            {
                return(true);
            }

            if (damageLevel != ComponentDamageLevel.Destroyed) // only care about destructions, other things dont get through anyway
            {
                return(true);
            }

            if (mechComponent == null)
            {
                return(true);
            }

            if (mechComponent.DamageLevel == ComponentDamageLevel.Destroyed) // already destroyed
            {
                return(true);
            }

            if (!(mechComponent.parent is Mech))
            {
                return(true);
            }

            var componentRef = mechComponent.mechComponentRef;

            var enginePart = componentRef?.Def?.GetComponent <EnginePart>();

            if (enginePart == null)
            {
                return(true);
            }

            var mech = (Mech)mechComponent.parent;
            var mainEngineComponent = mech.allComponents.FirstOrDefault(c => c?.componentDef?.GetComponent <EngineCoreDef>() != null);

            if (mainEngineComponent == null) // no main engine left
            {
                return(true);
            }

            var location = (ChassisLocations)mechComponent.Location;

            var crits = 1;

            if (mech.IsLocationDestroyed(location))
            {
                damageLevel = ComponentDamageLevel.Destroyed;
                crits       = mechComponent.componentDef.InventorySize;

                mechComponent.StatCollection.ModifyStat(
                    hitInfo.attackerId,
                    hitInfo.stackItemUID,
                    "DamageLevel",
                    StatCollection.StatOperation.Set,
                    damageLevel);
            }

            for (var i = 0; i < crits; i++)
            {
                switch (mainEngineComponent.StatCollection.GetStatistic("DamageLevel").Value <ComponentDamageLevel>())
                {
                case ComponentDamageLevel.Functional:     // 1. CRIT
                    damageLevel = ComponentDamageLevel.Misaligned;
                    break;

                case ComponentDamageLevel.Misaligned:     // 2. CRIT
                    damageLevel = ComponentDamageLevel.Penalized;
                    break;

                case ComponentDamageLevel.Penalized:     // 3. CRIT
                    damageLevel = ComponentDamageLevel.Destroyed;
                    break;

                default:
                    continue;
                }

                mainEngineComponent.StatCollection.ModifyStat(
                    hitInfo.attackerId,
                    hitInfo.stackItemUID,
                    "DamageLevel",
                    StatCollection.StatOperation.Set,
                    damageLevel);

                var heatSink = mech.StatCollection.GetStatistic("HeatSinkCapacity");
                mech.StatCollection.Int_Add(heatSink, Control.settings.EngineHeatSinkCapacityAdjustmentPerCrit);
            }

            if (damageLevel >= ComponentDamageLevel.NonFunctional)
            {
                Control.mod.Logger.LogDebug(mainEngineComponent.Name + " " + damageLevel);
                foreach (var component in mech.allComponents.Where(c => c?.componentDef?.GetComponent <EnginePart>() != null))
                {
                    component.StatCollection.ModifyStat(
                        hitInfo.attackerId,
                        hitInfo.stackItemUID,
                        "DamageLevel",
                        StatCollection.StatOperation.Set,
                        damageLevel);
                }

                mech.FlagForDeath(
                    "Engine destroyed: " + mainEngineComponent.Description.Name,
                    DeathMethod.EngineDestroyed,
                    mainEngineComponent.Location,
                    hitInfo.stackItemUID,
                    hitInfo.attackerId,
                    false);

                // FlagForDeath already outputs a message
                //messages.Add(new MessageAddition { Nature = FloatieMessage.MessageNature.ComponentDestroyed, Text = mainEngineComponent.UIName + " DESTROYED" });
            }
            else
            {
                var text = crits == 1 ? "ENGINE CRIT" : "ENGINE CRIT X" + crits;
                messages.Add(new MessageAddition {
                    Nature = FloatieMessage.MessageNature.ComponentDestroyed, Text = text
                });
            }

            return(false);
        }
        // crit engine reduces speed
        // destroyed engine destroys CT
        public static bool Prefix(MechComponent __instance, CombatGameState ___combat, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel, bool applyEffects)
        {
            try
            {
                if (__instance.mechComponentRef.Is <Flags>(out var f) && f.IsSet("ignore_damage"))
                {
                    MechCheckForCritPatch.Message = null;
                    return(false);
                }

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

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

            return(true);
        }
 internal static MechComponentRef GetMechComponentRefForUID(this SimGameState sim, MechDef mech, string simGameUID, string componentID, ComponentType componentType, ComponentDamageLevel damageLevel, ChassisLocations desiredLocation, int hardpointSlot, ref bool itemWasFromInventory)
 {
     mechComponentRef = sim.GetMechComponentRefForUID(mech, simGameUID, componentID, componentType, damageLevel, desiredLocation, hardpointSlot, ref itemWasFromInventory);
     return(mechComponentRef);
 }
 public static void SetColor(ComponentDamageLevel damageLevel)
 {
     DamageLevel = damageLevel;
 }
 private static void SetDamageLevel(MechComponent mechComponent, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel)
 {
     mechComponent.StatCollection.ModifyStat(
         hitInfo.attackerId,
         hitInfo.stackItemUID,
         "DamageLevel",
         StatCollection.StatOperation.Set,
         damageLevel);
 }
Example #10
0
        public static void CritComponent(this MechComponent component, ref WeaponHitInfo hitInfo)
        {
            Weapon        weapon1      = component as Weapon;
            AmmunitionBox ammoBox      = component as AmmunitionBox;
            Jumpjet       jumpjet      = component as Jumpjet;
            HeatSinkDef   componentDef = component.componentDef as HeatSinkDef;
            bool          flag         = weapon1 != null;

            if (component.parent != null)
            {
                if (component.parent.GameRep != null)
                {
                    WwiseManager.SetSwitch <AudioSwitch_weapon_type>(AudioSwitch_weapon_type.laser_medium, component.parent.GameRep.audioObject);
                    WwiseManager.SetSwitch <AudioSwitch_surface_type>(AudioSwitch_surface_type.mech_critical_hit, component.parent.GameRep.audioObject);
                    int num1 = (int)WwiseManager.PostEvent <AudioEventList_impact>(AudioEventList_impact.impact_weapon, component.parent.GameRep.audioObject, (AkCallbackManager.EventCallback)null, (object)null);
                    int num2 = (int)WwiseManager.PostEvent <AudioEventList_explosion>(AudioEventList_explosion.explosion_small, component.parent.GameRep.audioObject, (AkCallbackManager.EventCallback)null, (object)null);
                    if (component.parent.team.LocalPlayerControlsTeam)
                    {
                        AudioEventManager.PlayAudioEvent("audioeventdef_musictriggers_combat", "critical_hit_friendly ", (AkGameObj)null, (AkCallbackManager.EventCallback)null);
                    }
                    else if (!component.parent.team.IsFriendly(component.parent.Combat.LocalPlayerTeam))
                    {
                        AudioEventManager.PlayAudioEvent("audioeventdef_musictriggers_combat", "critical_hit_enemy", (AkGameObj)null, (AkCallbackManager.EventCallback)null);
                    }
                    if (jumpjet == null && componentDef == null && (ammoBox == null && component.DamageLevel > ComponentDamageLevel.Functional))
                    {
                        if (component.parent is Mech mech)
                        {
                            mech.GameRep.PlayComponentCritVFX(component.Location);
                        }
                    }
                    if (ammoBox != null && component.DamageLevel > ComponentDamageLevel.Functional)
                    {
                        component.parent.GameRep.PlayVFX(component.Location, (string)component.parent.Combat.Constants.VFXNames.componentDestruction_AmmoExplosion, true, Vector3.zero, true, -1f);
                    }
                }
            }
            ComponentDamageLevel damageLevel = component.DamageLevel;

            switch (damageLevel)
            {
            case ComponentDamageLevel.Functional:
                if (flag)
                {
                    damageLevel = ComponentDamageLevel.Penalized;
                    component.parent.Combat.MessageCenter.PublishMessage((MessageCenterMessage) new AddSequenceToStackMessage((IStackSequence) new ShowActorInfoSequence((ICombatant)component.parent, new Text("{0} CRIT", new object[1]
                    {
                        (object)component.UIName
                    }), FloatieMessage.MessageNature.CriticalHit, true)));
                    goto case ComponentDamageLevel.Destroyed;
                }
                else
                {
                    damageLevel = ComponentDamageLevel.Destroyed;
                    component.parent.Combat.MessageCenter.PublishMessage((MessageCenterMessage) new AddSequenceToStackMessage((IStackSequence) new ShowActorInfoSequence((ICombatant)component.parent, new Text("{0} DESTROYED", new object[1]
                    {
                        (object)component.UIName
                    }), FloatieMessage.MessageNature.ComponentDestroyed, true)));
                    goto case ComponentDamageLevel.Destroyed;
                }

            case ComponentDamageLevel.Destroyed:
                component.DamageComponent(hitInfo, damageLevel, true);
                break;

            default:
                damageLevel = ComponentDamageLevel.Destroyed;
                component.parent.Combat.MessageCenter.PublishMessage((MessageCenterMessage) new AddSequenceToStackMessage((IStackSequence) new ShowActorInfoSequence((ICombatant)component.parent, new Text("{0} DESTROYED", new object[1]
                {
                    (object)component.UIName
                }), FloatieMessage.MessageNature.ComponentDestroyed, true)));
                goto case ComponentDamageLevel.Destroyed;
            }
        }
Example #11
0
        public static bool Prefix(MechComponent __instance, CombatGameState ___combat, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel, bool applyEffects)
        {
            try
            {
                var messages = new List <MessageAddition>();
                void ClearMessageAndPublishAdditions()
                {
                    MechCheckForCritPatch.Message = null;
                    foreach (var message in messages)
                    {
                        var actor        = __instance.parent;
                        var stackMessage = new AddSequenceToStackMessage(new ShowActorInfoSequence(actor, message.Text, message.Nature, true));
                        actor.Combat.MessageCenter.PublishMessage(stackMessage);
                    }
                }

                if (__instance.mechComponentRef.Def.IsIgnoreDamage())
                {
                    ClearMessageAndPublishAdditions();
                    return(false);
                }

                if (!CirticalHitStatesHandler.Shared.ProcessWeaponHit(__instance, ___combat, hitInfo, damageLevel, applyEffects, messages))
                {
                    ClearMessageAndPublishAdditions();
                    return(false);
                }

                if (!EngineCrits.ProcessWeaponHit(__instance, ___combat, hitInfo, damageLevel, applyEffects, messages))
                {
                    ClearMessageAndPublishAdditions();
                    return(false);
                }
            }
            catch (Exception e)
            {
                Control.mod.Logger.LogError(e);
            }

            return(true);
        }
Example #12
0
            static void SetDamageLevel(MechComponent mechComponent, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel)
            {
                Control.Logger.Debug?.Log($"damageLevel={damageLevel} uid={mechComponent.uid} (Id={mechComponent.Description.Id} Location={mechComponent.Location})");
                mechComponent.StatCollection.ModifyStat(
                    hitInfo.attackerId,
                    hitInfo.stackItemUID,
                    "DamageLevel",
                    StatCollection.StatOperation.Set,
                    damageLevel);

                if (damageLevel == ComponentDamageLevel.Destroyed)
                {
                    mechComponent.CancelCreatedEffects();
                }
            }
Example #13
0
        public bool ProcessWeaponHit(MechComponent mechComponent, CombatGameState combat, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel, bool applyEffects, List <MessageAddition> messages)
        {
            var criticalHitStates = mechComponent.componentDef?.GetComponent <CriticalHitStates>();

            if (criticalHitStates == null)
            {
                return(true);
            }

            if (criticalHitStates.CriticalHitStatesMaxCount <= 0)
            {
                return(true);
            }

            if (damageLevel != ComponentDamageLevel.Destroyed) // only care about destructions, other things dont get through anyway
            {
                return(true);
            }

            if (mechComponent.DamageLevel == ComponentDamageLevel.Destroyed) // already destroyed
            {
                return(true);
            }

            if (!(mechComponent.parent is Mech))
            {
                return(true);
            }

            var componentRef = mechComponent.mechComponentRef;
            var mech         = (Mech)mechComponent.parent;
            var location     = (ChassisLocations)mechComponent.Location;

            var crits = 1;
            var mechLocationDestroyed = mech.IsLocationDestroyed(location);

            if (mechLocationDestroyed)
            {
                crits = mechComponent.componentDef.InventorySize;
            }

            int critLevel;
            int oldCritLevel;

            {
                const string statisticName = "MechEngineer_CriticalHitState";

                var critStat = mechComponent.StatCollection.GetStatistic(statisticName) ?? mechComponent.StatCollection.AddStatistic(statisticName, 0);
                oldCritLevel = critStat.Value <int>();
                critLevel    = oldCritLevel + crits;
                critStat.SetValue(critLevel);
            }

            {
                if (mechLocationDestroyed || critLevel > criticalHitStates.CriticalHitStatesMaxCount)
                {
                    damageLevel = ComponentDamageLevel.Destroyed;
                }
                else
                {
                    damageLevel = ComponentDamageLevel.Penalized;
                }

                mechComponent.StatCollection.ModifyStat(
                    hitInfo.attackerId,
                    hitInfo.stackItemUID,
                    "DamageLevel",
                    StatCollection.StatOperation.Set,
                    damageLevel);
            }

            if (damageLevel == ComponentDamageLevel.Destroyed)
            {
                return(true); // cancel effects, etc..
            }

            if (oldCritLevel > 0)
            {
                var hitEffects = criticalHitStates
                                 .CriticalHitEffects
                                 .Where(e => e.CriticalHitState == oldCritLevel);

                foreach (var hitEffect in hitEffects)
                {
                    var statusEffects = combat.EffectManager
                                        .GetAllEffectsWithID(hitEffect.StatusEffect.Description.Id)
                                        .Where(e => e.Target == mechComponent.parent);

                    foreach (var statusEffect in statusEffects)
                    {
                        mechComponent.parent.CancelEffect(statusEffect);
                    }
                }
            }

            {
                var hitEffects = criticalHitStates
                                 .CriticalHitEffects
                                 .Where(e => e.CriticalHitState == critLevel);

                foreach (var hitEffect in hitEffects)
                {
                    var effectData = hitEffect.StatusEffect;
                    if (effectData.targetingData.effectTriggerType != EffectTriggerType.Passive) // we only support passive for now
                    {
                        continue;
                    }

                    var text = $"PassiveEffect_{mechComponent.parent.GUID}_{mechComponent.uid}";
                    combat.EffectManager.CreateEffect(effectData, text, -1, mechComponent.parent, mechComponent.parent, default(WeaponHitInfo), 0, false);
                    mechComponent.createdEffectIDs.Add(text);
                }
            }

            {
                // this will also be called on engine crit, if side torso destroyed, does not necessarly mean CT is also detroyed
                var text = componentRef.Def.Description.UIName + " " + (crits == 1 ? " CRIT" : " CRIT X" + crits);
                messages.Add(new MessageAddition {
                    Nature = FloatieMessage.MessageNature.ComponentDestroyed, Text = text
                });
            }
            return(false);
        }
        internal void CheckForExplosion(MechComponent component, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel, bool applyEffects)
        {
            if (!applyEffects)
            {
                return;
            }

            if (damageLevel != ComponentDamageLevel.Destroyed)
            {
                return;
            }

            if (!(component.componentDef.Is <ComponentExplosion>(out var exp)))
            {
                return;
            }

            if (!(component.parent is Mech mech))
            {
                return;
            }

            var ammoCount = 0;

            if (component is AmmunitionBox box)
            {
                var adapter = new MechComponentAdapter(box);
                ammoCount = adapter.statCollection.GetValue <int>("CurrentAmmo");
            }
            else if (component is Weapon w2)
            {
                ammoCount = w2.InternalAmmo;
            }


            var attackSequence = mech.Combat.AttackDirector.GetAttackSequence(hitInfo.attackSequenceId);

            var heatDamage = exp.HeatDamage + ammoCount * exp.HeatDamagePerAmmo;

            //Control.mod.Logger.LogDebug($"heatDamage={heatDamage}");
            if (!Mathf.Approximately(heatDamage, 0))
            {
                mech.AddExternalHeat("AMMO EXPLOSION HEAT", (int)heatDamage);
                attackSequence?.FlagAttackDidHeatDamage();
            }

            var stabilityDamage = exp.StabilityDamage + ammoCount * exp.StabilityDamagePerAmmo;

            //Control.mod.Logger.LogDebug($"stabilityDamage={stabilityDamage}");
            if (!Mathf.Approximately(stabilityDamage, 0))
            {
                mech.AddAbsoluteInstability(stabilityDamage, StabilityChangeSource.Effect, hitInfo.targetId);
            }

            var explosionDamage = exp.ExplosionDamage + ammoCount * exp.ExplosionDamagePerAmmo;

            //Control.mod.Logger.LogDebug($"explosionDamage={explosionDamage}");

            if (Mathf.Approximately(explosionDamage, 0))
            {
                return;
            }

            Mech_DamageLocation_Patch.IsInternalExplosion = true;
            try
            {
                attackSequence?.FlagAttackCausedAmmoExplosion();

                mech.PublishFloatieMessage($"{component.Name} EXPLOSION");
                if (mech.Combat.Constants.PilotingConstants.InjuryFromAmmoExplosion)
                {
                    var pilot = mech.GetPilot();
                    pilot?.SetNeedsInjury(InjuryReason.AmmoExplosion);
                }

                // this is very hacky as this is an invalid weapon
                var weapon = new Weapon(mech, mech.Combat, component.mechComponentRef, component.uid);

                // bool DamageLocation(int originalHitLoc, WeaponHitInfo hitInfo, ArmorLocation aLoc, Weapon weapon, float totalDamage, int hitIndex, AttackImpactQuality impactQuality)
                var args = new object[] { component.Location, hitInfo, (ArmorLocation)component.Location, weapon, explosionDamage, 0, AttackImpactQuality.Solid };
                Traverse.Create(mech).Method("DamageLocation", args).GetValue();
            }
            finally
            {
                Mech_DamageLocation_Patch.IsInternalExplosion = false;
            }
        }
        internal static bool ProcessWeaponHit(
            MechComponent mechComponent,
            CombatGameState combat,
            WeaponHitInfo hitInfo,
            ComponentDamageLevel damageLevel,
            bool applyEffects,
            List <MessageAddition> messages)
        {
            if (Control.settings.EngineCriticalHitStates == null)
            {
                return(true);
            }

            if (damageLevel != ComponentDamageLevel.Destroyed) // only care about destructions, other things dont get through anyway
            {
                return(true);
            }

            if (mechComponent == null)
            {
                return(true);
            }

            if (mechComponent.DamageLevel == ComponentDamageLevel.Destroyed) // already destroyed
            {
                return(true);
            }

            if (!(mechComponent.parent is Mech mech))
            {
                return(true);
            }

            var componentRef = mechComponent.mechComponentRef;

            if (!componentRef.Def.IsEnginePart())
            {
                return(true);
            }

            var mainEngineComponent = mech.allComponents.FirstOrDefault(c => c?.componentDef?.GetComponent <EngineCoreDef>() != null);

            if (mainEngineComponent == null) // no main engine left
            {
                return(true);
            }

            var hits     = 1;
            var location = (ChassisLocations)mechComponent.Location;
            var mechLocationDestroyed = mech.IsLocationDestroyed(location);

            if (mechLocationDestroyed)
            {
                hits = componentRef.Def.InventorySize;
            }

            if (CirticalHitStatesHandler.Shared.ProcessWeaponHit(
                    mainEngineComponent,
                    combat,
                    hitInfo,
                    damageLevel,
                    applyEffects,
                    messages,
                    Control.settings.EngineCriticalHitStates,
                    hits
                    ))
            {
                return(true);
            }

            damageLevel = mainEngineComponent.DamageLevel;

            if (mechLocationDestroyed)
            {
                mechComponent.StatCollection.ModifyStat(
                    hitInfo.attackerId,
                    hitInfo.stackItemUID,
                    "DamageLevel",
                    StatCollection.StatOperation.Set,
                    ComponentDamageLevel.Destroyed);
            }

            foreach (var component in mech.allComponents.Where(c => c.componentDef.IsEnginePart()))
            {
                if (component.DamageLevel >= damageLevel)
                {
                    continue;
                }

                component.StatCollection.ModifyStat(
                    hitInfo.attackerId,
                    hitInfo.stackItemUID,
                    "DamageLevel",
                    StatCollection.StatOperation.Set,
                    damageLevel);
            }

            return(false);
        }
Example #16
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);
 }
Example #17
0
        internal static bool ProcessWeaponHit(MechComponent mechComponent, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel, bool applyEffects)
        {
            if (!Control.settings.EngineCritsEnabled)
            {
                return(true);
            }

            if (damageLevel != ComponentDamageLevel.Destroyed) // only care about destructions, other things dont get through anyway
            {
                return(true);
            }

            if (mechComponent == null)
            {
                return(true);
            }

            if (mechComponent.DamageLevel == ComponentDamageLevel.Destroyed) // already destroyed
            {
                return(true);
            }

            if (!(mechComponent.parent is Mech))
            {
                return(true);
            }

            var componentRef = mechComponent.mechComponentRef;

            if (componentRef == null || componentRef.Def == null)
            {
                return(true);
            }

            if (!componentRef.Def.IsEnginePart())
            {
                return(true);
            }

            var mech = (Mech)mechComponent.parent;
            var mainEngineComponent = mech.allComponents.FirstOrDefault(c => c != null && c.componentDef != null && c.componentDef.IsMainEngine());

            if (mainEngineComponent == null) // no main engine left
            {
                return(true);
            }

            var location = (ChassisLocations)mechComponent.Location;

            if (mech.IsLocationDestroyed(location))
            {
                damageLevel = ComponentDamageLevel.Destroyed;
            }
            else
            {
                switch (mainEngineComponent.StatCollection.GetStatistic("DamageLevel").Value <ComponentDamageLevel>())
                {
                case ComponentDamageLevel.Functional:     // 1. CRIT
                    damageLevel = ComponentDamageLevel.Misaligned;
                    break;

                case ComponentDamageLevel.Misaligned:     // 2. CRIT
                    damageLevel = ComponentDamageLevel.Penalized;
                    break;

                case ComponentDamageLevel.Penalized:     // 3. CRIT
                    damageLevel = ComponentDamageLevel.Destroyed;
                    break;
                }
            }

            // sync damage level to all engine parts
            foreach (var component in mech.allComponents.Where(c => c != null && c.componentDef != null && c.componentDef.IsEnginePart()))
            {
                component.StatCollection.ModifyStat(
                    hitInfo.attackerId,
                    hitInfo.stackItemUID,
                    "DamageLevel",
                    StatCollection.StatOperation.Set,
                    damageLevel);
            }

            // do on CRIT
            if (damageLevel < ComponentDamageLevel.NonFunctional)
            {
                Control.mod.Logger.LogDebug(mainEngineComponent.Name + " " + damageLevel);

                var walkSpeed = mech.StatCollection.GetStatistic("WalkSpeed");
                var runSpeed  = mech.StatCollection.GetStatistic("RunSpeed");
                mech.StatCollection.Float_Multiply(walkSpeed, Control.settings.SpeedMultiplierPerDamagedEnginePart);
                mech.StatCollection.Float_Multiply(runSpeed, Control.settings.SpeedMultiplierPerDamagedEnginePart);

                var heatSink = mech.StatCollection.GetStatistic("HeatSinkCapacity");
                mech.StatCollection.Int_Add(heatSink, Control.settings.HeatSinkCapacityAdjustmentPerCrit);
            }
            else
            {
                Control.mod.Logger.LogDebug(mainEngineComponent.Name + " " + damageLevel);

                mech.FlagForDeath(
                    "Engine destroyed: " + mainEngineComponent.Description.Name,
                    DeathMethod.EngineDestroyed,
                    mainEngineComponent.Location,
                    hitInfo.stackItemUID,
                    hitInfo.attackerId,
                    false);
            }

            return(false);
        }
Example #18
0
 internal static bool ProcessWeaponHit(MechComponent mechComponent, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel, bool applyEffects)
 {
     return(!mechComponent.componentDef.IsStructure());
 }
Example #19
0
 public static void AddMechComponentToSalvage(this Contract c, List <SalvageDef> l, MechComponentDef d, ComponentDamageLevel dl, bool b, SimGameConstants co, NetworkRandom r, bool b2)
 {
     throw new NotImplementedException();
 }
        public bool ProcessWeaponHit(
            MechComponent mechComponent,
            CombatGameState combat,
            WeaponHitInfo hitInfo,
            ComponentDamageLevel damageLevel,
            bool applyEffects,
            List <MessageAddition> messages,
            CriticalHitStates criticalHitStates = null,
            int?hits = null)
        {
            if (criticalHitStates == null)
            {
                criticalHitStates = mechComponent.componentDef?.GetComponent <CriticalHitStates>();
            }

            if (criticalHitStates == null)
            {
                return(true);
            }

            if (criticalHitStates.MaxStates <= 0)
            {
                return(true);
            }

            if (damageLevel != ComponentDamageLevel.Destroyed) // only care about destructions, other things dont get through anyway
            {
                return(true);
            }

            if (mechComponent.DamageLevel == ComponentDamageLevel.Destroyed) // already destroyed
            {
                return(true);
            }

            if (!(mechComponent.parent is Mech mech))
            {
                return(true);
            }

            var componentRef          = mechComponent.mechComponentRef;
            var location              = (ChassisLocations)mechComponent.Location;
            var mechLocationDestroyed = mech.IsLocationDestroyed(location);

            var crits = 1;

            if (hits.HasValue)
            {
                crits = hits.Value;
            }
            else if (mechLocationDestroyed)
            {
                crits = mechComponent.componentDef.InventorySize;
            }

            int critLevel;
            int oldCritLevel;

            {
                const string statisticName = "MechEngineer_CriticalHitState";

                var critStat = mechComponent.StatCollection.GetStatistic(statisticName) ?? mechComponent.StatCollection.AddStatistic(statisticName, 0);
                oldCritLevel = critStat.Value <int>();
                critLevel    = oldCritLevel + crits;
                critStat.SetValue(critLevel);
            }

            {
                if (mechLocationDestroyed || critLevel > criticalHitStates.MaxStates)
                {
                    damageLevel = ComponentDamageLevel.Destroyed;
                }
                else
                {
                    damageLevel = ComponentDamageLevel.Penalized;
                }

                mechComponent.StatCollection.ModifyStat(
                    hitInfo.attackerId,
                    hitInfo.stackItemUID,
                    "DamageLevel",
                    StatCollection.StatOperation.Set,
                    damageLevel);
            }

            //Control.mod.Logger.LogDebug($"oldCritLevel={oldCritLevel} critLevel={critLevel} damageLevel={damageLevel}");

            if (oldCritLevel > 0)
            {
                var hitEffects = criticalHitStates
                                 .HitEffects
                                 .Where(e => e.State == oldCritLevel);

                foreach (var hitEffect in hitEffects)
                {
                    var effectId = hitEffect.StatusEffect.IdForComponent(mechComponent);

                    var statusEffects = combat.EffectManager
                                        .GetAllEffectsWithID(effectId)
                                        .Where(e => e.Target == mechComponent.parent);

                    foreach (var statusEffect in statusEffects)
                    {
                        mechComponent.parent.CancelEffect(statusEffect);
                        mechComponent.createdEffectIDs.Remove(effectId);
                    }
                }
            }

            if (damageLevel < ComponentDamageLevel.Destroyed)
            {
                var hitEffects = criticalHitStates
                                 .HitEffects
                                 .Where(e => e.State == critLevel);

                foreach (var hitEffect in hitEffects)
                {
                    var effectData = hitEffect.StatusEffect;
                    if (effectData.targetingData.effectTriggerType != EffectTriggerType.Passive) // we only support passive for now
                    {
                        continue;
                    }

                    var effectId = hitEffect.StatusEffect.IdForComponent(mechComponent);
                    combat.EffectManager.CreateEffect(effectData, effectId, -1, mechComponent.parent, mechComponent.parent, default(WeaponHitInfo), 0, false);
                    mechComponent.createdEffectIDs.Add(effectId);
                }
            }

            if (damageLevel == ComponentDamageLevel.Destroyed)
            {
                if (criticalHitStates.DeathMethod != DeathMethod.NOT_SET)
                {
                    mech.FlagForDeath(
                        mechComponent.Description.Name + " DESTROYED",
                        criticalHitStates.DeathMethod,
                        DamageType.Combat,
                        mechComponent.Location,
                        hitInfo.stackItemUID,
                        hitInfo.attackerId,
                        false);
                }
                else
                {
                    var text = componentRef.Def.Description.UIName + " DESTROYED";
                    messages.Add(new MessageAddition {
                        Nature = FloatieMessage.MessageNature.ComponentDestroyed, Text = text
                    });
                }
            }
            else
            {
                var text = componentRef.Def.Description.UIName + " " + (crits == 1 ? " CRIT" : " CRIT X" + crits);
                messages.Add(new MessageAddition {
                    Nature = FloatieMessage.MessageNature.ComponentDestroyed, Text = text
                });
            }

            return(false);
        }
        public void ProcessWeaponHit(MechComponent mechComponent, WeaponHitInfo hitInfo, ref ComponentDamageLevel damageLevel)
        {
            if (mechComponent.parent == null)
            {
                return;
            }

            if (mechComponent.DamageLevel == ComponentDamageLevel.Destroyed) // already destroyed
            {
                return;
            }

            var criticalEffects = mechComponent.GetCriticalEffects();

            if (criticalEffects == null)
            {
                return;
            }

            var actor = mechComponent.parent;

            damageLevel = ComponentDamageLevel.Penalized;

            int critsPrev;
            int critsNext;
            int critsAdded;

            {
                critsPrev = criticalEffects.HasLinked ? mechComponent.CriticalSlotsHitLinked() : mechComponent.CriticalSlotsHit();
                var critsMax = criticalEffects.PenalizedEffectIDs.Length + 1; // max = how many crits can be absorbed, last one destroys component

                var slots = mechComponent.CriticalSlots();                    // critical slots left

                var locationDestroyed = actor.StructureForLocation(mechComponent.Location) <= 0f;
                var critsHit          = locationDestroyed ? slots : Mathf.Min(1, slots);

                critsNext = Mathf.Min(critsMax, critsPrev + critsHit);
                if (critsNext >= critsMax)
                {
                    damageLevel = ComponentDamageLevel.Destroyed;
                }
                if (criticalEffects.HasLinked)
                {
                    mechComponent.CriticalSlotsHitLinked(critsNext);
                }

                critsAdded = Mathf.Max(critsNext - critsPrev, 0);

                var slotsHitPrev = mechComponent.CriticalSlotsHit();
                mechComponent.CriticalSlotsHit(slotsHitPrev + critsAdded);

                Control.mod.Logger.LogDebug(
                    $"{criticalEffects.Def.Description.Id} on {mechComponent.Location} " +
                    $"critsAdded={critsAdded} critsMax={critsMax} " +
                    $"critsPrev={critsPrev} critsNext={critsNext} " +
                    $"critsHit={critsHit} " +
                    $"slots={slots} slotsHitPrev={slotsHitPrev} " +
                    $"damageLevel={damageLevel} " +
                    $"HasLinked={criticalEffects.HasLinked}"
                    );
            }

            {
                SetDamageLevel(mechComponent, hitInfo, damageLevel);

                if (criticalEffects.HasLinked)
                {
                    var scopedId = mechComponent.ScopedId(criticalEffects.LinkedStatisticName, true);

                    foreach (var mc in actor.allComponents)
                    {
                        if (mc.DamageLevel == ComponentDamageLevel.Destroyed)
                        {
                            continue;
                        }

                        var ce = mc.GetCriticalEffects();
                        if (ce == null)
                        {
                            continue;
                        }

                        if (!ce.HasLinked)
                        {
                            continue;
                        }

                        var otherScopedId = mc.ScopedId(ce.LinkedStatisticName, true);
                        if (scopedId == otherScopedId)
                        {
                            SetDamageLevel(mc, hitInfo, damageLevel);
                        }
                    }
                }
            }

            {
                // cancel effects
                var effectIds = new string[0];

                if (critsPrev > 0 && critsPrev <= criticalEffects.PenalizedEffectIDs.Length)
                {
                    effectIds = effectIds.AddRange(criticalEffects.PenalizedEffectIDs[critsPrev - 1]);
                }

                if (damageLevel == ComponentDamageLevel.Destroyed)
                {
                    effectIds = effectIds.AddRange(criticalEffects.OnDestroyedDisableEffectIds);
                }

                foreach (var effectId in effectIds)
                {
                    var util = new EffectIdUtil(effectId, mechComponent, criticalEffects);
                    util.CancelCriticalEffect();
                }
            }

            {
                // create effects
                var effectIds = new string[0];

                if (critsNext > 0 && critsNext <= criticalEffects.PenalizedEffectIDs.Length)
                {
                    effectIds = criticalEffects.PenalizedEffectIDs[critsNext - 1];
                }

                if (damageLevel == ComponentDamageLevel.Destroyed)
                {
                    effectIds = criticalEffects.OnDestroyedEffectIDs;
                }

                // collect disabled effects, probably easier to cache these in a mech statistic
                var disabledEffectIds = DisabledScopedIdsOnActor(actor);
                //Control.mod.Logger.LogDebug($"disabledEffectIds={string.Join(",", disabledEffectIds.ToArray())}");
                foreach (var effectId in effectIds)
                {
                    var scopedId = mechComponent.ScopedId(effectId, criticalEffects.HasLinked);
                    if (disabledEffectIds.Contains(scopedId))
                    {
                        continue;
                    }

                    var util = new EffectIdUtil(effectId, mechComponent, criticalEffects);
                    util.CreateCriticalEffect(damageLevel < ComponentDamageLevel.Destroyed);
                }
            }

            if (damageLevel == ComponentDamageLevel.Destroyed)
            {
                if (criticalEffects.DeathMethod != DeathMethod.NOT_SET)
                {
                    actor.FlagForDeath(
                        $"{mechComponent.UIName} DESTROYED",
                        criticalEffects.DeathMethod,
                        DamageType.Combat,
                        mechComponent.Location,
                        hitInfo.stackItemUID,
                        hitInfo.attackerId,
                        false);
                    actor.HandleDeath(hitInfo.attackerId);
                }

                if (!string.IsNullOrEmpty(criticalEffects.OnDestroyedVFXName))
                {
                    actor.GameRep.PlayVFX(mechComponent.Location, criticalEffects.OnDestroyedVFXName, true, Vector3.zero, true, -1f);
                }

                if (!string.IsNullOrEmpty(criticalEffects.OnDestroyedAudioEventName))
                {
                    WwiseManager.PostEvent(criticalEffects.OnDestroyedAudioEventName, actor.GameRep.audioObject);
                }
            }
        }
Example #22
0
        internal static bool ProcessWeaponHit(MechComponent mechComponent, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel, bool applyEffects)
        {
            if (mechComponent.componentDef.IsArmor())
            {
                return(false);
            }

            return(true);
        }