Example #1
0
        public static void ProcessDamage(AbstractActor actor, float damage, float directStructureDamage, int heatdamage)
        {
            if (ShouldSkipProcessing(actor))
            {
                return;
            }

            AbstractActor attacker = TurnDamageTracker.attackActor();//just for logging

            //LogReport(new string('═', 46));
            LogReport($"Damage to {actor.DisplayName}/{actor.Nickname}/{actor.GUID}");
            if (attacker != null)
            {
                LogReport($"Damage by {attacker.DisplayName}/{attacker.Nickname}/{attacker.GUID}");
            }

            AbstractActor defender = null;

            switch (actor)
            {
            case Vehicle _:
                defender = (Vehicle)actor;
                break;

            case Mech _:
                defender = (Mech)actor;
                break;
            }

            // a building or turret?
            if (defender == null)
            {
                LogDebug("Not a mech or vehicle");
                return;
            }

            if (defender.IsDead || defender.IsFlaggedForDeath)
            {
                LogDebug("He's dead Jim.....");
                return;
            }
            LogReport($"Damage >>> D: {damage:F3} DS: {directStructureDamage:F3} H: {heatdamage}");
            TurnDamageTracker.batchDamageDuringActivation(actor, damage, directStructureDamage, heatdamage);
        }
Example #2
0
        public static void ProcessBatchedTurnDamage(AbstractActor actor)
        {
            int heatdamage = 0;

            if (ShouldSkipProcessing(actor))
            {
                return;
            }

            AbstractActor attacker = TurnDamageTracker.attackActor();

            LogReport($"\n{new string('═', 46)}");
            LogReport($"Damage to {actor.DisplayName}/{actor.Nickname}/{actor.GUID}");
            LogReport($"Damage by {attacker.DisplayName}/{attacker.Nickname}/{attacker.GUID}");

            // get the attacker in case they have mech quirks
            AbstractActor defender = null;

            switch (actor)
            {
            case Vehicle _:
                defender = (Vehicle)actor;
                break;

            case Mech _:
                defender = (Mech)actor;
                break;
            }

            // a building or turret?
            if (defender == null)
            {
                LogDebug("Not a mech or vehicle");
                return;
            }

            if (defender.IsDead || defender.IsFlaggedForDeath)
            {
                LogDebug("He's dead Jim.....");
                return;
            }

            var index = GetActorIndex(defender);

            if (modSettings.OneChangePerTurn &&
                TrackedActors[index].PanicWorsenedRecently)
            {
                LogDebug($"OneChangePerTurn {defender.Nickname} - abort");
                return;
            }

            float damageIncludingHeatDamage = 0;

            if (!modSettings.AlwaysPanic &&
                !ShouldPanic(defender, attacker, out heatdamage, out damageIncludingHeatDamage))
            {
                return;
            }

            // automatically eject a klutzy pilot on knockdown with an additional roll failing on 13
            if (defender.IsFlaggedForKnockdown)
            {
                var defendingMech = (Mech)defender;
                if (defendingMech.pilot.pilotDef.PilotTags.Contains("pilot_klutz"))
                {
                    if (Random.Range(1, 100) == 13)
                    {
                        defender.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage
                                                                         (new ShowActorInfoSequence(defender, "WOOPS!", FloatieMessage.MessageNature.Debuff, false)));
                        LogReport("Very klutzy!");
                        return;
                    }
                }
            }

            // store saving throw
            // check it against panic
            // check it again ejection
            var savingThrow = SavingThrows.GetSavingThrow(defender, attacker, heatdamage, damageIncludingHeatDamage);

            // panic saving throw
            if (SavingThrows.SavedVsPanic(defender, savingThrow))
            {
                return;
            }

            if (!modSettings.OneChangePerTurn)
            {
                TurnDamageTracker.resetDamageTrackerFor(defender);
            }

            // stop if pilot isn't Panicked
            if (TrackedActors[index].PanicStatus != PanicStatus.Panicked)
            {
                return;
            }

            // eject saving throw
            if (!modSettings.AlwaysPanic &&
                SavingThrows.SavedVsEject(defender, savingThrow))
            {
                return;
            }

            // ejecting
            // random phrase
            if (modSettings.EnableEjectPhrases &&
                defender is Mech &&
                Random.Range(1, 100) <= modSettings.EjectPhraseChance)
            {
                var ejectMessage = ejectPhraseList[Random.Range(0, ejectPhraseList.Count)];
                var castDef      = Coordinator.CreateCast(defender);
                var content      = Coordinator.BuildDialogueContent(castDef, ejectMessage, Color.white);
                defender.Combat.MessageCenter.PublishMessage(new PanicSystemDialogMessage(defender, content, 6));
            }

            // remove effects, to prevent exceptions that occur for unknown reasons
            var combat           = UnityGameInstance.BattleTechGame.Combat;
            var effectsTargeting = combat.EffectManager.GetAllEffectsTargeting(defender);

            foreach (var effect in effectsTargeting)
            {
                // some effects removal throw, so silently drop them
                try
                {
                    defender.CancelEffect(effect);
                }
                catch
                {
                    // ignored
                }
            }

            if (modSettings.VehiclesCanPanic &&
                defender is Vehicle v)
            {
                // make the regular Pilot Ejected floatie not appear, for this ejection
                Patches.VehicleRepresentation.supressDeathFloatieOnce();
                defender.EjectPilot(defender.GUID, -1, DeathMethod.PilotEjection, true);
                CastDef castDef = Coordinator.CreateCast(defender);
                var     content = Coordinator.BuildDialogueContent(castDef, "Destroy the tech, let's get outta here!", Color.white);
                defender.Combat.MessageCenter.PublishMessage(new PanicSystemDialogMessage(defender, content, 5));
            }
            else
            {
                defender.EjectPilot(defender.GUID, -1, DeathMethod.PilotEjection, false);
            }

            LogReport("Ejected");
            //LogDebug($"Runtime {stopwatch.Elapsed}");

            if (!modSettings.CountAsKills)
            {
                return;
            }

            //handle weird cases due to damage from all sources
            if (attacker.GUID == defender.GUID)
            {
                //killed himself - possibly mines or made a building land on his own head ;)
                LogReport("Self Kill not counting");
                return;
            }

            if (attacker.team.GUID == defender.team.GUID)
            {
                //killed a friendly
                LogReport("Friendly Fire, Same Team Kill, not counting");
                return;
            }

            if (TurnDamageTracker.EjectionAlreadyCounted(defender))
            {
                return;
            }

            try
            {
                // this seems pretty convoluted
                var attackerPilot = combat.AllMechs.Where(mech => mech.pilot.Team.IsLocalPlayer)
                                    .Where(x => x.PilotableActorDef == attacker.PilotableActorDef).Select(y => y.pilot).FirstOrDefault();

                var statCollection = attackerPilot?.StatCollection;
                if (statCollection == null)
                {
                    return;
                }

                if (defender is Mech)
                {
                    // add UI icons.. and pilot history?   ... MechsKilled already incremented??
                    // TODO count kills recorded on pilot history so it's not applied twice -added a check above should work unless other mods are directly modifying stats
                    statCollection.Set("MechsKilled", attackerPilot.MechsKilled + 1);
                    var stat = statCollection.GetStatistic("MechsEjected");
                    if (stat == null)
                    {
                        statCollection.AddStatistic("MechsEjected", 1);
                    }
                    else
                    {
                        var value = stat.Value <int>();
                        statCollection.Set("MechsEjected", value + 1);
                    }
                }
                else if (modSettings.VehiclesCanPanic &&
                         defender is Vehicle)
                {
                    statCollection.Set("OthersKilled", attackerPilot.OthersKilled + 1);
                    var stat = statCollection.GetStatistic("VehiclesEjected");
                    if (stat == null)
                    {
                        statCollection.AddStatistic("VehiclesEjected", 1);
                        //return;
                    }
                    else
                    {
                        var value = stat.Value <int>();
                        statCollection.Set("VehiclesEjected", value + 1);
                    }
                }

                try
                {
                    object PlayerMechStats = playerMechStatsField.GetValue(CombatProcessor);
                    bool   result          = (bool)tryGetValue.Invoke(PlayerMechStats, new[] { attackerPilot.GUID, mechCombatStats });

                    if (result)
                    {
                        incrementKillCount.Invoke(mechCombatStats, null);
                    }
                }
                catch (Exception e)
                {
                    LogError(e);
                    // add achievement kill (more complicated)
                    var combatProcessors = Traverse.Create(UnityGameInstance.BattleTechGame.Achievements).Field("combatProcessors").GetValue <AchievementProcessor[]>();
                    var combatProcessor  = combatProcessors.FirstOrDefault(x => x.GetType() == AccessTools.TypeByName("BattleTech.Achievements.CombatProcessor"));

                    // field is of type Dictionary<string, CombatProcessor.MechCombatStats>
                    var playerMechStats = Traverse.Create(combatProcessor).Field("playerMechStats").GetValue <IDictionary>();
                    if (playerMechStats != null)
                    {
                        foreach (DictionaryEntry kvp in playerMechStats)
                        {
                            if ((string)kvp.Key == attackerPilot.GUID)
                            {
                                Traverse.Create(kvp.Value).Method("IncrementKillCount").GetValue();
                            }
                        }
                    }
                }


                var r = attackerPilot.StatCollection.GetStatistic("MechsEjected") == null
                        ? 0
                        : attackerPilot.StatCollection.GetStatistic("MechsEjected").Value <int>();
                LogDebug($"{attackerPilot.Callsign} SetMechEjectionCount {r}");

                r = attackerPilot.StatCollection.GetStatistic("VehiclesEjected") == null
                    ? 0
                    : attackerPilot.StatCollection.GetStatistic("VehiclesEjected").Value <int>();
                LogDebug($"{attackerPilot.Callsign} SetVehicleEjectionCount {r}");
            }
            catch (Exception ex)
            {
                LogDebug(ex);
            }
        }