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(); }
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); }
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}"); } } }
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); }
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; } }
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); }
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(); } }
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); }
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); }
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); }
internal static bool ProcessWeaponHit(MechComponent mechComponent, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel, bool applyEffects) { return(!mechComponent.componentDef.IsStructure()); }
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); } } }
internal static bool ProcessWeaponHit(MechComponent mechComponent, WeaponHitInfo hitInfo, ComponentDamageLevel damageLevel, bool applyEffects) { if (mechComponent.componentDef.IsArmor()) { return(false); } return(true); }