public static Func <AttackModifier> GetMeleeModifierFactor(string factorId)
        {
            switch (factorId)
            {
            case "armmounted":
                return(() => { AttackModifier result = new AttackModifier("PUNCHING ARM");
                               if (AttackType == MeleeAttackType.DFA || Target is Vehicle || Target.IsProne || !(Attacker is Mech mech))
                               {
                                   return result;
                               }
                               if (mech.MechDef.Chassis.PunchesWithLeftArm)
                               {
                                   if (mech.IsLocationDestroyed(ChassisLocations.LeftArm))
                                   {
                                       return result;
                                   }
                               }
                               else if (mech.IsLocationDestroyed(ChassisLocations.RightArm))
                               {
                                   return result;
                               }
                               return result.SetValue(CombatConstants.ToHit.ToHitSelfArmMountedWeapon); });

            case "dfa":
                return(() => new AttackModifier("DEATH FROM ABOVE", Hit.GetDFAModifier(AttackType)));

            case "height":
                return(() => { AttackModifier result = new AttackModifier("HEIGHT DIFF");
                               if (AttackType == MeleeAttackType.DFA)
                               {
                                   return result.SetValue(Hit.GetHeightModifier(Attacker.CurrentPosition.y, Target.CurrentPosition.y));
                               }
                               float diff = AttackPos.y - Target.CurrentPosition.y;
                               if (Math.Abs(diff) < HalfMaxMeleeVerticalOffset || (diff < 0 && !CombatConstants.ToHit.ToHitElevationApplyPenalties))
                               {
                                   return result;
                               }
                               float mod = CombatConstants.ToHit.ToHitElevationModifierPerLevel;
                               return result.SetValue(diff <= 0 ? mod : -mod); });

            case "obstruction":
                return(() => new AttackModifier("OBSTRUCTED", Hit.GetCoverModifier(Attacker, Target, Combat.LOS.GetLineOfFire(Attacker, AttackPos, Target, TargetPos, Target.CurrentRotation, out Vector3 collision))));

            case "refire":
                return(() => new AttackModifier("RE-ATTACK", Hit.GetRefireModifier(AttackWeapon)));

            case "selfchassis":
                return(() => new AttackModifier(Hit.GetMeleeChassisToHitModifier(Attacker, AttackType)).SetName("CHASSIS PENALTY", "CHASSIS BONUS"));

            case "targetevasion":
                return(() => { AttackModifier result = new AttackModifier("TARGET MOVED");
                               if (!(Target is AbstractActor actor))
                               {
                                   return result;
                               }
                               return result.SetValue(Hit.GetEvasivePipsModifier(actor.EvasivePipsCurrent, AttackWeapon)); });

            case "targetprone":
                return(() => new AttackModifier("TARGET PRONE", Hit.GetTargetProneModifier(Target, true)));

            case "targetshutdown":
                return(() => new AttackModifier("TARGET SHUTDOWN", Hit.GetTargetShutdownModifier(Target, true)));
            }
            return(null);
        }
        public static Func <AttackModifier> GetRangedModifierFactor(string factorId)
        {
            switch (factorId)
            {
            case "armmounted":
                return(() => new AttackModifier("ARM MOUNTED", Hit.GetSelfArmMountedModifier(AttackWeapon)));

            case "range": // Depended by ShowNeutralRangeInBreakdown
                return(() => {
                    Weapon w = AttackWeapon;
                    float range = Vector3.Distance(AttackPos, TargetPos), modifier = Hit.GetRangeModifierForDist(w, range);
                    AttackModifier result = new AttackModifier(modifier);
                    if (range < w.MinRange)
                    {
                        return result.SetName($"MIN RANGE (<{(int)w.MinRange}m)");
                    }
                    if (range < w.ShortRange)
                    {
                        return result.SetName("SHORT RANGE" + SmartRange(w.MinRange, range, w.ShortRange));
                    }
                    if (range < w.MediumRange)
                    {
                        return result.SetName("MED RANGE" + SmartRange(w.ShortRange, range, w.MediumRange));
                    }
                    if (range < w.LongRange)
                    {
                        return result.SetName("LONG RANGE" + SmartRange(w.MediumRange, range, w.LongRange));
                    }
                    if (range < w.MaxRange)
                    {
                        return result.SetName("MAX RANGE" + SmartRange(w.LongRange, range, w.MaxRange));
                    }
                    return result.SetName($"OUT OF RANGE (>{(int)w.MaxRange}m)");
                });

            case "height":
                return(() => new AttackModifier("HEIGHT", Hit.GetHeightModifier(AttackPos.y, TargetPos.y)));

            case "indirect":
                return(() => new AttackModifier("INDIRECT FIRE", Hit.GetIndirectModifier(Attacker, LineOfFire < LineOfFireLevel.LOFObstructed && AttackWeapon.IndirectFireCapable)));

            case "locationdamage":
                return(() => {
                    if (Attacker is Mech mech)
                    {
                        Text location = Mech.GetAbbreviatedChassisLocation((ChassisLocations)AttackWeapon.Location);
                        return new AttackModifier($"{location} DAMAGED", MechStructureRules.GetToHitModifierLocationDamage(mech, AttackWeapon));
                    }
                    else
                    {
                        return new AttackModifier("CHASSIS DAMAGED", Hit.GetSelfDamageModifier(Attacker, AttackWeapon));
                    }
                });

            case "obstruction":
                return(() => new AttackModifier("OBSTRUCTED", Hit.GetCoverModifier(Attacker, Target, LineOfFire)));

            case "precision":
                return(() => new AttackModifier(CombatConstants.CombatUIConstants.MoraleAttackDescription.Name, Hit.GetMoraleAttackModifier(Target, IsMoraleAttack)));

            case "refire":
                return(() => new AttackModifier("RECOIL", Hit.GetRefireModifier(AttackWeapon)));

            case "targetevasion":
                return(() => new AttackModifier("TARGET MOVED", Hit.GetTargetSpeedModifier(Target, AttackWeapon)));

            case "targetprone":
                return(() => new AttackModifier("TARGET PRONE", Hit.GetTargetProneModifier(Target, false)));

            case "targetshutdown":
                return(() => new AttackModifier("TARGET SHUTDOWN", Hit.GetTargetShutdownModifier(Target, false)));

            case "sensorlock":
                return(() => new AttackModifier("SENSOR LOCK", Hit.GetTargetDirectFireModifier(Target, LineOfFire < LineOfFireLevel.LOFObstructed && AttackWeapon.IndirectFireCapable)));

            case "weapondamage":
                return(() => {
                    AttackModifier result = new AttackModifier("WEAPON DAMAGED");
                    if (!(Attacker is Mech mech))
                    {
                        return result;
                    }
                    return result.SetValue(MechStructureRules.GetToHitModifierWeaponDamage(mech, AttackWeapon));
                });
            }
            return(null);
        }