Exemplo n.º 1
0
        public static int GetResolveValue(this AbstractActor actor)
        {
            WeightClass        weightClass     = actor.GetWeightClass();
            MoraleConstantsDef activeMoraleDef = actor.Combat.Constants.GetActiveMoraleDef(actor.Combat);

            // Special case for turrets as in related code (AttackDirector.ResolveSequenceMorale)
            if (actor.UnitType == UnitType.Turret)
            {
                return(activeMoraleDef.ChangeEnemyDestroyedLight);
            }

            switch (weightClass)
            {
            case WeightClass.LIGHT:
                return(activeMoraleDef.ChangeEnemyDestroyedLight);

            case WeightClass.MEDIUM:
                return(activeMoraleDef.ChangeEnemyDestroyedMedium);

            case WeightClass.HEAVY:
                return(activeMoraleDef.ChangeEnemyDestroyedHeavy);

            case WeightClass.ASSAULT:
                return(activeMoraleDef.ChangeEnemyDestroyedAssault);

            default:
                return(0);
            }
        }
Exemplo n.º 2
0
        public static bool RollForEjectionResult(Mech mech, AttackDirector.AttackSequence attackSequence, bool IsEarlyPanic)
        {
            if (mech == null || mech.IsDead || (mech.IsFlaggedForDeath && !mech.HasHandledDeath))
            {
                return(false);
            }

            // knocked down mechs cannot eject
            if (mech.IsProne && Settings.KnockedDownCannotEject)
            {
                return(false);
            }

            // have to do damage
            if (!attackSequence.attackDidDamage)
            {
                return(false);
            }

            Pilot pilot   = mech.GetPilot();
            var   weapons = mech.Weapons;
            var   guts    = mech.SkillGuts;
            var   tactics = mech.SkillTactics;
            var   total   = guts + tactics;

            float lowestRemaining = mech.CenterTorsoStructure + mech.CenterTorsoFrontArmor;
            float ejectModifiers  = 0;

            // guts 10 makes you immune, player character cannot be forced to eject
            if ((guts >= 10 && Settings.GutsTenAlwaysResists) || (pilot != null && pilot.IsPlayerCharacter && Settings.PlayerCharacterAlwaysResists))
            {
                return(false);
            }

            // tactics 10 makes you immune, or combination of guts and tactics makes you immune.
            if ((tactics >= 10 && Settings.TacticsTenAlwaysResists) || (total >= 10 && Settings.ComboTenAlwaysResists))
            {
                return(false);
            }

            // pilots that cannot eject or be headshot shouldn't eject
            if (!mech.CanBeHeadShot || (pilot != null && !pilot.CanEject))
            {
                return(false);
            }

            // pilot health
            if (pilot != null)
            {
                float pilotHealthPercent = 1 - ((float)pilot.Injuries / pilot.Health);

                if (pilotHealthPercent < 1)
                {
                    ejectModifiers += Settings.PilotHealthMaxModifier * (1 - pilotHealthPercent);
                }
            }

            if (mech.IsUnsteady)
            {
                ejectModifiers += Settings.UnsteadyModifier;
            }

            // Head
            var headHealthPercent = (mech.HeadArmor + mech.HeadStructure) / (mech.GetMaxArmor(ArmorLocation.Head) + mech.GetMaxStructure(ChassisLocations.Head));

            if (headHealthPercent < 1)
            {
                ejectModifiers += Settings.HeadDamageMaxModifier * (1 - headHealthPercent);
            }

            // CT
            var ctPercent = (mech.CenterTorsoFrontArmor + mech.CenterTorsoStructure) / (mech.GetMaxArmor(ArmorLocation.CenterTorso) + mech.GetMaxStructure(ChassisLocations.CenterTorso));

            if (ctPercent < 1)
            {
                ejectModifiers += Settings.CTDamageMaxModifier * (1 - ctPercent);
                lowestRemaining = Math.Min(mech.CenterTorsoStructure, lowestRemaining);
            }

            // side torsos
            var ltStructurePercent = mech.LeftTorsoStructure / mech.GetMaxStructure(ChassisLocations.LeftTorso);

            if (ltStructurePercent < 1)
            {
                ejectModifiers += Settings.SideTorsoInternalDamageMaxModifier * (1 - ltStructurePercent);
            }

            var rtStructurePercent = mech.RightTorsoStructure / mech.GetMaxStructure(ChassisLocations.RightTorso);

            if (rtStructurePercent < 1)
            {
                ejectModifiers += 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);
                    ejectModifiers += Settings.LeggedMaxModifier * (1 - legPercent);
                }
            }

            // next shot could kill
            if (lowestRemaining <= attackSequence.cumulativeDamage)
            {
                ejectModifiers += Settings.NextShotLikeThatCouldKill;
            }

            // weaponless
            if (weapons.TrueForAll(w =>
                                   w.DamageLevel == ComponentDamageLevel.Destroyed || w.DamageLevel == ComponentDamageLevel.NonFunctional))
            {
                ejectModifiers += Settings.WeaponlessModifier;
            }

            // alone
            if (mech.Combat.GetAllAlliesOf(mech).TrueForAll(m => m.IsDead || m == mech as AbstractActor))
            {
                ejectModifiers += Settings.AloneModifier;
            }

            var modifiers = (ejectModifiers - Settings.BaseEjectionResist - (Settings.GutsEjectionResistPerPoint * guts) - (Settings.TacticsEjectionResistPerPoint * tactics)) * 5;

            if (mech.team == mech.Combat.LocalPlayerTeam)
            {
                MoraleConstantsDef moraleDef = mech.Combat.Constants.GetActiveMoraleDef(mech.Combat);
                modifiers -= Math.Max(mech.Combat.LocalPlayerTeam.Morale - moraleDef.CanUseInspireLevel, 0);
            }

            if (modifiers < 0)
            {
                return(false);
            }

            var   rng = (new System.Random()).Next(100);
            float rollToBeat;

            if (!IsEarlyPanic)
            {
                rollToBeat = Math.Min(modifiers, Settings.MaxEjectChance);
            }
            else
            {
                rollToBeat = Math.Min(modifiers, Settings.MaxEjectChanceWhenEarly);
            }

            mech.Combat.MessageCenter.PublishMessage(!(rng < rollToBeat)
                ? new AddSequenceToStackMessage(new ShowActorInfoSequence(mech, $"Guts/Tactics Check Passed {Math.Floor(rollToBeat)}%", FloatieMessage.MessageNature.Buff, true))
                : new AddSequenceToStackMessage(new ShowActorInfoSequence(mech, $"Punchin' Out! {Math.Floor(rollToBeat)}%", FloatieMessage.MessageNature.Debuff, true)));

            return(rng < rollToBeat);
        }
Exemplo n.º 3
0
        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);
        }