Example #1
0
        public ActorInitiative(AbstractActor actor)
        {
            //SkillBasedInit.Logger.Log($"Initializing ActorInitiative for {actor.DisplayName} with GUID {actor.GUID}.");

            if (actor.GetType() == typeof(Mech))
            {
                this.type = ActorType.Mech;
            }
            else if (actor.GetType() == typeof(Vehicle))
            {
                this.type = ActorType.Vehicle;
            }
            else
            {
                this.type = ActorType.Turret;
            }

            // --- UNIT IMPACTS ---
            // Static initiative from tonnage
            float tonnage    = UnitHelper.GetUnitTonnage(actor);
            int   tonnageMod = UnitHelper.GetTonnageModifier(tonnage);

            // Any special modifiers by type
            int typeMod = UnitHelper.GetTypeModifier(actor);

            // Any modifiers that come from the chassis/mech/vehicle defs
            int componentsMod = UnitHelper.GetNormalizedComponentModifier(actor);

            // Modifier from the engine
            int engineMod = UnitHelper.GetEngineModifier(actor);

            // --- PILOT IMPACTS ---
            Pilot pilot = actor.GetPilot();

            PilotHelper.LogPilotStats(pilot);

            // Normalize skills so that values above 10 don't screw the system
            this.gunneryEffectMod = PilotHelper.GetGunneryModifier(pilot);

            this.gutsEffectMod = PilotHelper.GetGutsModifier(pilot);
            this.injuryBounds  = PilotHelper.GetInjuryBounds(pilot);

            this.pilotingEffectMod = PilotHelper.GetPilotingModifier(pilot);
            this.randomnessBounds  = PilotHelper.GetRandomnessBounds(pilot);

            this.tacticsEffectMod = PilotHelper.GetTacticsModifier(pilot);

            int pilotTagsMod = PilotHelper.GetTagsModifier(pilot);

            this.calledShotMod = PilotHelper.GetCalledShotModifier(pilot);
            this.vigilianceMod = PilotHelper.GetVigilanceModifier(pilot);

            // --- COMBO IMPACTS --
            // Determine the melee modifier
            int[] meleeMods = PilotHelper.GetMeleeModifiers(pilot, tonnage);
            this.meleeAttackMod  = meleeMods[0];
            this.meleeDefenseMod = meleeMods[1];
            Mod.Log.Debug($"Actor:{actor.DisplayName}_{pilot.Name} has meleeAttackMod:{meleeAttackMod} meleeDefenseMod:{meleeDefenseMod}");

            // Log the full view for testing
            roundInitBase  = tonnageMod;
            roundInitBase += typeMod;
            roundInitBase += componentsMod;
            roundInitBase += engineMod;
            roundInitBase += tacticsEffectMod;
            roundInitBase += pilotTagsMod;

            Mod.Log.Info($"Actor:{actor.DisplayName}_{pilot.Name} has " +
                         $"roundInitBase:{roundInitBase} = (tonnage:{tonnageMod} + typeMod:{typeMod} + components:{componentsMod} + engine:{engineMod} " +
                         $"tactics:{tacticsEffectMod} + pilotTags:{pilotTagsMod}) " +
                         $"randomness:({randomnessBounds[0]}-{randomnessBounds[1]}) " +
                         $"injuryBounds:({injuryBounds[0]}-{injuryBounds[1]}) " +
                         $"gutsMod:{gutsEffectMod} pilotingMod:{pilotingEffectMod} tacticsMod:{tacticsEffectMod}");
        }
        private static string BuildTooltipText(AbstractActor actor)
        {
            ActorInitiative actorInit = ActorInitiativeHolder.GetOrCreate(actor);

            // -- Mech
            List <string> mechDetails = new List <string> {
            };
            Mech  actorMech           = actor as Mech;
            float tonnage             = actorMech.MechDef.Chassis.Tonnage;
            int   tonnageMod          = UnitHelper.GetTonnageModifier(tonnage);

            mechDetails.Add($"MECH => tonnage:{tonnageMod}");
            int expectedInitMax = tonnageMod;

            // Any modifiers that come from the chassis/mech/vehicle defs
            int componentsMod = UnitHelper.GetNormalizedComponentModifier(actor);

            if (componentsMod > 0)
            {
                mechDetails.Add($"<color=#00FF00>{componentsMod:+0} components</color>");
            }
            else if (componentsMod < 0)
            {
                mechDetails.Add($"<color=#FF0000>{componentsMod:0} components</color>");
            }
            expectedInitMax += componentsMod;

            // Modifier from the engine
            int engineMod = UnitHelper.GetEngineModifier(actor);

            if (engineMod > 0)
            {
                mechDetails.Add($"<color=#00FF00>{engineMod:+0} engine</color>");
            }
            else if (engineMod < 0)
            {
                mechDetails.Add($"<color=#FF0000>{engineMod:0} engine</color>");
            }
            expectedInitMax += engineMod;

            // Check for leg / side loss
            Mech mech = (Mech)actor;

            if (mech.IsLocationDestroyed(ChassisLocations.LeftLeg) || mech.IsLocationDestroyed(ChassisLocations.RightLeg))
            {
                int rawMod  = Mod.Config.CrippledMovementModifier + actorInit.pilotingEffectMod;
                int penalty = Math.Min(-1, rawMod);
                mechDetails.Add($"<color=#FF0000>{penalty} Leg Destroyed</color>");
                expectedInitMax += penalty;
            }

            // Check for prone
            if (actor.IsProne)
            {
                int rawMod  = Mod.Config.ProneModifier + actorInit.pilotingEffectMod;
                int penalty = Math.Min(-1, rawMod);
                mechDetails.Add($"<color=#FF0000>{penalty} Prone</color>");
                expectedInitMax += penalty;
            }

            // Check for shutdown
            if (actor.IsShutDown)
            {
                int rawMod  = Mod.Config.ShutdownModifier + actorInit.pilotingEffectMod;
                int penalty = Math.Min(-1, rawMod);
                mechDetails.Add($"<color=#FF0000>{penalty} Shutdown</color>");
                expectedInitMax += penalty;
            }

            // Check for melee impacts
            if (actorInit.lastRoundMeleeMod > 0)
            {
                expectedInitMax -= actorInit.lastRoundMeleeMod;
                mechDetails.Add($"<color=#FF0000>-{actorInit.lastRoundMeleeMod} Melee Impact</color>");
            }

            // --- PILOT ---
            List <string> pilotDetails = new List <string> {
            };

            Pilot selectedPilot = actor.GetPilot();
            int   tacticsMod    = PilotHelper.GetTacticsModifier(selectedPilot);

            pilotDetails.Add($"PILOT => Tactics: <color=#00FF00>{tacticsMod:+0}</color>");
            expectedInitMax += tacticsMod;

            int pilotTagsMod = PilotHelper.GetTagsModifier(selectedPilot);

            pilotDetails.AddRange(PilotHelper.GetTagsModifierDetails(selectedPilot));
            expectedInitMax += pilotTagsMod;

            int[] randomnessBounds    = PilotHelper.GetRandomnessBounds(selectedPilot);
            int   expectedInitRandMin = randomnessBounds[0];
            int   expectedInitRandMax = randomnessBounds[1];

            // Check for inspired status
            if (actor.IsMoraleInspired || actor.IsFuryInspired)
            {
                pilotDetails.Add($"<color=#00FF00>+1 to +3 Inspired</color>");
                expectedInitRandMin += 1;
                expectedInitRandMin += 3;
            }

            // Check for injuries. If there injuries on the previous round, apply them in full force. Otherwise, reduce them.
            if (actorInit.lastRoundInjuryMod != 0)
            {
                pilotDetails.Add($"<color=#FF0000>-{actorInit.lastRoundInjuryMod} Fresh Injury</color>");
                expectedInitMax -= actorInit.lastRoundInjuryMod;
            }
            else if (actor.GetPilot().Injuries != 0)
            {
                // TODO: fold this in
                int rawPenalty = actorInit.CalculateInjuryPenalty(0, actor.GetPilot().Injuries);
                int penalty    = (int)Math.Ceiling(rawPenalty / 2.0);
                pilotDetails.Add($"<color=#FF0000>-{penalty} Painful Injury</color>");
                expectedInitMax -= penalty;
            }

            // Check for an overly cautious player
            if (actorInit.lastRoundHesitationPenalty > 0)
            {
                expectedInitMax -= actorInit.lastRoundHesitationPenalty;
                pilotDetails.Add($"<color=#FF0000>-{actorInit.lastRoundHesitationPenalty} Hesitation</color>");
            }

            // Check for called shot
            if (actorInit.lastRoundCalledShotMod > 0)
            {
                expectedInitMax -= actorInit.lastRoundCalledShotMod;
                pilotDetails.Add($"<color=#FF0000>-{actorInit.lastRoundCalledShotMod} Called Shot Target</color>");
            }

            // Check for vigilance
            if (actorInit.lastRoundVigilanceMod > 0)
            {
                expectedInitMax -= actorInit.lastRoundVigilanceMod;
                pilotDetails.Add($"<color=#00FF00>-{actorInit.lastRoundVigilanceMod} Vigilance</color>");
            }

            // Finally, randomness bounds
            pilotDetails.Add($"\nRandom (tactics): <color=#FF0000>-{actorInit.randomnessBounds[0]} to -{actorInit.randomnessBounds[1]}</color>");

            int maxInit = Math.Max(expectedInitMax - expectedInitRandMin, Mod.MinPhase);
            int minInit = Math.Max(expectedInitMax - expectedInitRandMax, Mod.MinPhase);

            List <string> toolTipDetails = new List <string> {
            };

            toolTipDetails.Add(String.Join(", ", mechDetails.ToArray()));
            toolTipDetails.Add(String.Join(", ", pilotDetails.ToArray()));
            toolTipDetails.Add($"Expected Phase: <b>{maxInit} to {minInit}</b>");
            toolTipDetails.Add($"Hover over init in Mechlab/Deploy for details.");

            string tooltipText = String.Join("\n", toolTipDetails.ToArray());

            return(tooltipText);
        }
        public static void Postfix(LanceLoadoutSlot __instance, GameObject ___initiativeObj, TextMeshProUGUI ___initiativeText,
                                   UIColorRefTracker ___initiativeColor, HBSTooltip ___initiativeTooltip, LanceConfiguratorPanel ___LC)
        {
            if (___initiativeObj == null || ___initiativeText == null || ___initiativeColor == null || ___initiativeTooltip == null)
            {
                return;
            }

            //SkillBasedInit.Logger.Log($"LanceLoadoutSlot::RefreshInitiativeData::post - disabling text");
            bool bothSelected = __instance.SelectedMech != null && __instance.SelectedPilot != null;

            if (!bothSelected)
            {
                ___initiativeText.SetText("-");
                ___initiativeColor.SetUIColor(UIColor.MedGray);
            }
            else
            {
                int initValue = 0;

                // --- MECH ---
                MechDef       selectedMechDef = __instance.SelectedMech.MechDef;
                List <string> details         = new List <string>();

                // Static initiative from tonnage
                float tonnage    = __instance.SelectedMech.MechDef.Chassis.Tonnage;
                int   tonnageMod = UnitHelper.GetTonnageModifier(tonnage);
                initValue += tonnageMod;
                details.Add($"Tonnage Base: {tonnageMod}");

                // Any special modifiers by type - NA, Mech is the only type

                // Any modifiers that come from the chassis/mech/vehicle defs
                int componentsMod = UnitHelper.GetNormalizedComponentModifier(selectedMechDef);
                initValue += componentsMod;
                if (componentsMod > 0)
                {
                    details.Add($"<space=2em><color=#00FF00>{componentsMod:+0} components</color>");
                }
                else if (componentsMod < 0)
                {
                    details.Add($"<space=2em><color=#FF0000>{componentsMod:0} components</color>");
                }

                // Modifier from the engine
                int engineMod = UnitHelper.GetEngineModifier(selectedMechDef);
                initValue += engineMod;
                if (engineMod > 0)
                {
                    details.Add($"<space=2em><color=#00FF00>{engineMod:+0} engine</color>");
                }
                else if (engineMod < 0)
                {
                    details.Add($"<space=2em><color=#FF0000>{engineMod:0} engine</color>");
                }

                // --- PILOT ---
                Pilot selectedPilot = __instance.SelectedPilot.Pilot;

                int tacticsMod = PilotHelper.GetTacticsModifier(selectedPilot);
                details.Add($"<space=2em>{tacticsMod:+0} tactics");
                initValue += tacticsMod;

                int pilotTagsMod = PilotHelper.GetTagsModifier(selectedPilot);
                details.AddRange(PilotHelper.GetTagsModifierDetails(selectedPilot));
                initValue += pilotTagsMod;

                int[] randomnessBounds = PilotHelper.GetRandomnessBounds(selectedPilot);

                // --- LANCE ---
                if (___LC != null)
                {
                    initValue += ___LC.lanceInitiativeModifier;
                    if (___LC.lanceInitiativeModifier > 0)
                    {
                        details.Add($"<space=2em><color=#00FF00>{___LC.lanceInitiativeModifier:+0} lance</color>");
                    }
                    else if (___LC.lanceInitiativeModifier < 0)
                    {
                        details.Add($"<space=2em><color=#FF0000>{___LC.lanceInitiativeModifier:0} lance</color>");
                    }
                }

                // --- Badge ---
                ___initiativeText.SetText($"{initValue}");
                ___initiativeText.color = Color.black;
                ___initiativeColor.SetUIColor(UIColor.White);

                // --- Tooltip ---
                int maxInit = Math.Max(initValue - randomnessBounds[0], Mod.MinPhase);
                int minInit = Math.Max(initValue - randomnessBounds[1], Mod.MinPhase);
                details.Add($"Total:{initValue}");
                details.Add($"<space=2em><color=#FF0000>-{randomnessBounds[0]} to -{randomnessBounds[1]} randomness</color> (piloting)");
                details.Add($"<b>Expected Phase<b>: {maxInit} to {minInit}");

                string             tooltipTitle   = $"{selectedMechDef.Name}: {selectedPilot.Name}";
                string             tooltipText    = String.Join("\n", details.ToArray());
                BaseDescriptionDef initiativeData = new BaseDescriptionDef("LLS_MECH_TT", tooltipTitle, tooltipText, null);
                ___initiativeTooltip.enabled = true;
                ___initiativeTooltip.SetDefaultStateData(TooltipUtilities.GetStateDataFromObject(initiativeData));
            }
        }