public static void Prefix(AttackStackSequence __instance, MessageCenterMessage message) { AttackCompleteMessage attackCompleteMessage = message as AttackCompleteMessage; bool ShouldPanic = false; bool IsEarlyPanic = false; Mech mech = null; if (attackCompleteMessage == null || attackCompleteMessage.stackItemUID != __instance.SequenceGUID) { return; } if (__instance.directorSequences[0].target is Mech) { mech = __instance.directorSequences[0].target as Mech; ShouldPanic = RollHelpers.ShouldPanic(mech, attackCompleteMessage.attackSequence); } if (mech == null || mech.GUID == null || attackCompleteMessage == null) { return; } Holder.SerializeActiveJson(); if (PanicHelpers.IsPanicking(mech, ref IsEarlyPanic) && BasicPanic.RollForEjectionResult(mech, attackCompleteMessage.attackSequence, IsEarlyPanic)) { mech.EjectPilot(mech.GUID, attackCompleteMessage.stackItemUID, DeathMethod.PilotEjection, false); } }
static void Postfix(Mech __instance) { if (__instance == null || __instance.IsDead || (__instance.IsFlaggedForDeath && __instance.HasHandledDeath)) { return; } int index = PanicHelpers.GetTrackedPilotIndex(__instance); if (BasicPanic.Settings.LosingLimbAlwaysPanics) { if (index < 0) { Holder.TrackedPilots.Add(new PanicTracker(__instance)); //add a new tracker to tracked pilot, then we run it all over again; index = PanicHelpers.GetTrackedPilotIndex(__instance); if (index < 0) { return; } } if (Holder.TrackedPilots[index].trackedMech != __instance.GUID) { return; } if (Holder.TrackedPilots[index].trackedMech == __instance.GUID && Holder.TrackedPilots[index].ChangedRecently && BasicPanic.Settings.AlwaysGatedChanges) { return; } RollHelpers.ApplyPanicDebuff(__instance, index); } }
public static void Prefix(AbstractActor __instance) { if (!(__instance is Mech mech) || mech.IsDead || (mech.IsFlaggedForDeath && mech.HasHandledDeath)) { return; } bool FoundPilot = false; Pilot pilot = mech.GetPilot(); int index = -1; if (pilot == null) { return; } index = PanicHelpers.GetTrackedPilotIndex(mech); if (index > -1) { FoundPilot = true; } if (!FoundPilot) { PanicTracker panicTracker = new PanicTracker(mech); Holder.TrackedPilots.Add(panicTracker); //add a new tracker to tracked pilot, then we run it all over again;; index = PanicHelpers.GetTrackedPilotIndex(mech); if (index > -1) { FoundPilot = true; } else { return; } } PanicStatus originalStatus = Holder.TrackedPilots[index].pilotStatus; if (FoundPilot && !Holder.TrackedPilots[index].ChangedRecently) { if (Holder.TrackedPilots[index].pilotStatus == PanicStatus.Fatigued) { Holder.TrackedPilots[index].pilotStatus = PanicStatus.Normal; } if (Holder.TrackedPilots[index].pilotStatus == PanicStatus.Stressed) { Holder.TrackedPilots[index].pilotStatus = PanicStatus.Fatigued; } if (Holder.TrackedPilots[index].pilotStatus == PanicStatus.Panicked) { Holder.TrackedPilots[index].pilotStatus = PanicStatus.Stressed; } } //reset panic values to account for panic level changes if we get this far, and we recovered. if (Holder.TrackedPilots[index].ChangedRecently) { Holder.TrackedPilots[index].ChangedRecently = false; } else if (Holder.TrackedPilots[index].pilotStatus != originalStatus) { __instance.StatCollection.ModifyStat <float>("Panic Turn Reset: Accuracy", -1, "AccuracyModifier", StatCollection.StatOperation.Set, 0f, -1, true); __instance.StatCollection.ModifyStat <float>("Panic Turn Reset: Mech To Hit", -1, "ToHitThisActor", StatCollection.StatOperation.Set, 0f, -1, true); if (Holder.TrackedPilots[index].pilotStatus == PanicStatus.Fatigued) { __instance.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(new ShowActorInfoSequence(mech, $"Recovered To Fatigued!", FloatieMessage.MessageNature.Buff, true))); __instance.StatCollection.ModifyStat <float>("Panic Turn: Fatigued Aim", -1, "AccuracyModifier", StatCollection.StatOperation.Float_Add, BasicPanic.Settings.FatiguedAimModifier, -1, true); } else if (Holder.TrackedPilots[index].pilotStatus == PanicStatus.Stressed) { __instance.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(new ShowActorInfoSequence(mech, $"Recovered To Stressed!", FloatieMessage.MessageNature.Buff, true))); __instance.StatCollection.ModifyStat <float>("Panic Turn: Stressed Aim", -1, "AccuracyModifier", StatCollection.StatOperation.Float_Add, BasicPanic.Settings.StressedAimModifier, -1, true); __instance.StatCollection.ModifyStat <float>("Panic Turn: Stressed Defence", -1, "ToHitThisActor", StatCollection.StatOperation.Float_Add, BasicPanic.Settings.StressedToHitModifier, -1, true); } else //now normal { __instance.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(new ShowActorInfoSequence(mech, $"Recovered To Normal!", FloatieMessage.MessageNature.Buff, true))); } } Holder.SerializeActiveJson(); }
public static bool ShouldPanic(Mech mech, AttackDirector.AttackSequence attackSequence) { if (mech == null || mech.IsDead || (mech.IsFlaggedForDeath && mech.HasHandledDeath)) { return(false); } if (attackSequence == null) { return(false); } if (!attackSequence.attackDidDamage) //no point in panicking over nothing { return(false); } if (!attackSequence.attackDamagedStructure && !attackSequence.lowArmorStruck) // no structure damage and didn't strike low armour (unclear what lowArmorStruck is) { Logger.Logline($"attackDamagedStructure {attackSequence.attackDamagedStructure}, lowArmorStruck {attackSequence.lowArmorStruck}"); float totalArmor = 0, maxArmor = 0; maxArmor = GetTotalMechArmour(mech, maxArmor); totalArmor = GetCurrentMechArmour(mech, totalArmor); float currentArmorPercent = totalArmor / maxArmor * 100; Logger.Logline($"maxArmor {maxArmor}, totalArmor {totalArmor}, currentArmorPercent { currentArmorPercent}"); var settings = BasicPanic.Settings; var percentOfCurrentArmorDamaged = attackSequence.attackArmorDamage / currentArmorPercent; float mininumDamagePerecentRequired = settings.MinimumArmourDamagePercentageRequired; if (percentOfCurrentArmorDamaged <= 10) // (deprecated) basically if this equals to 100%, mech didn't lose enough armour { return(false); } } if (mech.team == mech.Combat.LocalPlayerTeam && !BasicPanic.Settings.PlayerTeamCanPanic) { return(false); } else if (mech.team != mech.Combat.LocalPlayerTeam && !BasicPanic.Settings.EnemiesCanPanic) { return(false); } int PanicRoll = 0; Pilot pilot = mech.GetPilot(); var weapons = mech.Weapons; var guts = mech.SkillGuts; var tactics = mech.SkillTactics; var total = guts + tactics; int index = -1; index = PanicHelpers.GetTrackedPilotIndex(mech); float lowestRemaining = mech.CenterTorsoStructure + mech.CenterTorsoFrontArmor; float panicModifiers = 0; if (index < 0) { Holder.TrackedPilots.Add(new PanicTracker(mech)); //add a new tracker to tracked pilot, then we run it all over again; index = PanicHelpers.GetTrackedPilotIndex(mech); if (index < 0) { return(false); } } if (Holder.TrackedPilots[index].trackedMech != mech.GUID) { return(false); } if (Holder.TrackedPilots[index].trackedMech == mech.GUID && Holder.TrackedPilots[index].ChangedRecently && BasicPanic.Settings.AlwaysGatedChanges) { return(false); } // pilot health if (pilot != null) { float pilotHealthPercent = 1 - ((float)pilot.Injuries / pilot.Health); if (pilotHealthPercent < 1) { panicModifiers += BasicPanic.Settings.PilotHealthMaxModifier * (1 - pilotHealthPercent); } } if (mech.IsUnsteady) { panicModifiers += BasicPanic.Settings.UnsteadyModifier; } // Head var headHealthPercent = (mech.HeadArmor + mech.HeadStructure) / (mech.GetMaxArmor(ArmorLocation.Head) + mech.GetMaxStructure(ChassisLocations.Head)); if (headHealthPercent < 1) { panicModifiers += BasicPanic.Settings.HeadDamageMaxModifier * (1 - headHealthPercent); } // CT var ctPercent = (mech.CenterTorsoFrontArmor + mech.CenterTorsoStructure) / (mech.GetMaxArmor(ArmorLocation.CenterTorso) + mech.GetMaxStructure(ChassisLocations.CenterTorso)); if (ctPercent < 1) { panicModifiers += BasicPanic.Settings.CTDamageMaxModifier * (1 - ctPercent); lowestRemaining = Math.Min(mech.CenterTorsoStructure, lowestRemaining); } // side torsos var ltStructurePercent = mech.LeftTorsoStructure / mech.GetMaxStructure(ChassisLocations.LeftTorso); if (ltStructurePercent < 1) { panicModifiers += BasicPanic.Settings.SideTorsoInternalDamageMaxModifier * (1 - ltStructurePercent); } var rtStructurePercent = mech.RightTorsoStructure / mech.GetMaxStructure(ChassisLocations.RightTorso); if (rtStructurePercent < 1) { panicModifiers += BasicPanic.Settings.SideTorsoInternalDamageMaxModifier * (1 - rtStructurePercent); } // legs if (mech.RightLegDamageLevel == LocationDamageLevel.Destroyed || mech.LeftLegDamageLevel == LocationDamageLevel.Destroyed) { float legPercent; if (mech.LeftLegDamageLevel == LocationDamageLevel.Destroyed) { legPercent = (mech.RightLegStructure + mech.RightLegArmor) / (mech.GetMaxStructure(ChassisLocations.RightLeg) + mech.GetMaxArmor(ArmorLocation.RightLeg)); } else { legPercent = (mech.LeftLegStructure + mech.LeftLegArmor) / (mech.GetMaxStructure(ChassisLocations.LeftLeg) + mech.GetMaxArmor(ArmorLocation.LeftLeg)); } if (legPercent < 1) { lowestRemaining = Math.Min(legPercent * (mech.GetMaxStructure(ChassisLocations.LeftLeg) + mech.GetMaxArmor(ArmorLocation.LeftLeg)), lowestRemaining); panicModifiers += BasicPanic.Settings.LeggedMaxModifier * (1 - legPercent); } } // next shot could kill if (lowestRemaining <= attackSequence.cumulativeDamage) { panicModifiers += BasicPanic.Settings.NextShotLikeThatCouldKill; } // weaponless if (weapons.TrueForAll(w => w.DamageLevel == ComponentDamageLevel.Destroyed || w.DamageLevel == ComponentDamageLevel.NonFunctional)) { panicModifiers += BasicPanic.Settings.WeaponlessModifier; } // alone if (mech.Combat.GetAllAlliesOf(mech).TrueForAll(m => m.IsDead || m == mech as AbstractActor)) { panicModifiers += BasicPanic.Settings.AloneModifier; } //straight up add guts, tactics, and morale to this as negative values panicModifiers -= total; if (mech.team == mech.Combat.LocalPlayerTeam) { MoraleConstantsDef moraleDef = mech.Combat.Constants.GetActiveMoraleDef(mech.Combat); panicModifiers -= Math.Max(mech.Combat.LocalPlayerTeam.Morale - moraleDef.CanUseInspireLevel, 0) / (float)2; } //reduce modifiers by 5 to account change to D20 roll instead of D100 roll, then min it t0 20 or modified floor panicModifiers /= 5; PanicRoll = PanicRoll + (int)panicModifiers; if ((total >= 20 || PanicRoll <= 0) && !BasicPanic.Settings.AtLeastOneChanceToPanic) { return(false); } PanicRoll = Math.Min(PanicRoll, 20); if (PanicRoll < 0) { PanicRoll = 0; //make this have some kind of chance to happen } PanicRoll = UnityEngine.Random.Range(PanicRoll, 20); // actual roll //we get this far, we reduce total to under the max panic chance total = Math.Min(total, (int)BasicPanic.Settings.MaxPanicResistTotal); int rngRoll = UnityEngine.Random.Range(total, 20); if (rngRoll <= PanicRoll) { ApplyPanicDebuff(mech, index); return(true); } mech.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(new ShowActorInfoSequence(mech, $"Resisted Morale Check!", FloatieMessage.MessageNature.Buff, true))); return(false); }