示例#1
0
        public MechComponentRef GetReplaceFor(MechDef mech, string categoryId, ChassisLocations location, SimGameState state)
        {
            bool check_def(IDefault def)
            {
                return(def.CategoryID == categoryId && (def.AnyLocation || location == def.Location));
            }

            foreach (var def in mech.Chassis.GetComponents <ChassisDefaults>())
            {
                if (check_def(def))
                {
                    return(def.GetReplace(mech, state));
                }
            }

            if (TaggedDefaults != null)
            {
                foreach (var def in TaggedDefaults.Where(check_def))
                {
                    if (mech.MechTags.Contains(def.Tag) || mech.Chassis.ChassisTags.Contains(def.Tag))
                    {
                        return(def.GetReplace(mech, state));
                    }
                }
            }

            return(Defaults != null?Defaults.Where(check_def).Select(def => def.GetReplace(mech, state)).FirstOrDefault() : null);
        }
示例#2
0
        public object GetDefId(MechDef mech, string categoryId, ChassisLocations location)
        {
            bool check_def(IDefault def)
            {
                return(def.CategoryID == categoryId && (def.AnyLocation || location == def.Location));
            }

            foreach (var def in mech.Chassis.GetComponents <ChassisDefaults>())
            {
                if (check_def(def))
                {
                    return(def.DefID);
                }
            }

            foreach (var def in TaggedDefaults.Where(check_def))
            {
                if (mech.MechTags.Contains(def.Tag) || mech.Chassis.ChassisTags.Contains(def.Tag))
                {
                    return(def.DefID);
                }
            }

            return(Defaults.Where(check_def).Select(def => def.DefID).FirstOrDefault());
        }
        internal static void OverrideApplyStructureStatDamage(
            this Mech mech,
            ChassisLocations location,
            float damage,
            WeaponHitInfo hitInfo
            )
        {
            try
            {
                if (IsInternalExplosion)
                {
                    var properties = ComponentExplosionHandler.Shared.GetCASEProperties(currentMech, (int)location);
                    if (properties.MaximumDamage.HasValue)
                    {
                        var directDamage = Mathf.Min(damage, properties.MaximumDamage.Value);
                        var backDamage   = damage - directDamage;
                        //Control.mod.Logger.LogDebug($"reducing structure damage from {damage} to {directDamage} in {Mech.GetAbbreviatedChassisLocation(location)}");
                        damage = directDamage;

                        if (backDamage > 0)
                        {
                            currentMech.PublishFloatieMessage("EXPLOSION REDIRECTED");

                            if ((location & ChassisLocations.Torso) > 0)
                            {
                                ArmorLocation armorLocation;
                                switch (location)
                                {
                                case ChassisLocations.LeftTorso:
                                    armorLocation = ArmorLocation.LeftTorsoRear;
                                    break;

                                case ChassisLocations.RightTorso:
                                    armorLocation = ArmorLocation.RightTorsoRear;
                                    break;

                                default:
                                    armorLocation = ArmorLocation.CenterTorsoRear;
                                    break;
                                }

                                var armor = mech.GetCurrentArmor(armorLocation);
                                if (armor > 0)
                                {
                                    var armorDamage = Mathf.Min(backDamage, armor);
                                    //Control.mod.Logger.LogDebug($"added blowout armor damage {armorDamage} to {Mech.GetLongArmorLocation(armorLocation)}");

                                    mech.ApplyArmorStatDamage(armorLocation, armorDamage, hitInfo);
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Control.mod.Logger.LogError(e);
            }
            mech.ApplyStructureStatDamage(location, damage, hitInfo);
        }
示例#4
0
        private static string ValidateECM(MechLabItemSlotElement item, ChassisLocations locations)
        {
            var def = item.ComponentRef.Def;

            if (def.ComponentSubType < MechComponentType.Prototype_Generic &&
                def.ComponentSubType != MechComponentType.ElectronicWarfare)
            {
                return(string.Empty);
            }

            int count = MechLabHelper.CurrentMechLab.ActiveMech.Inventory.Count(cref => cref.Def.ComponentSubType == def.ComponentSubType);

            if (count > 0)
            {
                if (def.ComponentSubType == MechComponentType.ElectronicWarfare || def.ComponentSubType == MechComponentType.Prototype_ElectronicWarfare)
                {
                    return
                        ("ELECTRONIC WARFARE COMPONENT LIMIT: You can only equip one Electronic Warfare component on this 'Mech.");
                }
                else
                {
                    return
                        ($"PROTOTYPE COMPONENT LIMIT: You can only equip one {def.ComponentSubType} component on this 'Mech");
                }
            }

            return(string.Empty);
        }
示例#5
0
        public static string GetDefaultActuator(MechDef mech, ChassisLocations location, ArmActuatorSlot slot)
        {
            if (location != ChassisLocations.RightArm && location != ChassisLocations.LeftArm)
            {
                return(null);
            }

            if (mech == null || !mech.Chassis.Is <ArmActuatorSupport>(out var support))
            {
                return(GetComponentIdForSlot(slot));
            }

            switch (slot)
            {
            case ArmActuatorSlot.PartShoulder:
                return(support.GetShoulder(location));

            case ArmActuatorSlot.PartUpper:
                return(support.GetUpper(location));

            case ArmActuatorSlot.PartLower:
                return(support.GetLower(location));

            case ArmActuatorSlot.PartHand:
                return(support.GetHand(location));

            default:
                return(null);
            }
        }
 public Change_Add(MechLabItemSlotElement slot, ChassisLocations location)
 {
     this.slot = slot;
     ItemID    = slot.ComponentRef.ComponentDefID;
     Type      = slot.ComponentRef.ComponentDefType;
     Location  = location;
 }
        public void ModifyMech(MechDef mDef, SimGameState s, UpgradeList ulist, ref float canFreeTonns, List <string[]> changedAmmoTypes, MechDef fromData)
        {
            BTRandomMechComponentUpgrader_Init.Log.Log("checking addition sublists");
            List <MechComponentRef> inv = mDef.Inventory.ToList();

            foreach (UpgradeList.UpgradeEntry[] l in ulist.Additions)
            {
                if (s.NetworkRandom.Float(0f, 1f) < ulist.UpgradePerComponentChance)
                {
                    string log = "";
                    UpgradeList.UpgradeEntry ue = ulist.RollEntryFromSubList(l, s.NetworkRandom, -1, s.CurrentDate, ref log, ulist.UpgradePerComponentChance);
                    if (ue != null && !ue.ID.Equals(""))
                    {
                        MechComponentDef d   = s.GetComponentDefFromID(ue.ID);
                        ChassisLocations loc = mDef.SearchLocationToAddComponent(d, canFreeTonns, inv, null, ChassisLocations.None);
                        if (loc == ChassisLocations.None)
                        {
                            BTRandomMechComponentUpgrader_Init.Log.Log("cannot add " + log);
                            continue;
                        }
                        BTRandomMechComponentUpgrader_Init.Log.Log($"adding {log} into {loc}");
                        MechComponentRef r = new MechComponentRef(ue.ID, null, d.ComponentType, loc, -1, ComponentDamageLevel.Functional, false);
                        r.SetComponentDef(d);
                        inv.Add(r);
                        canFreeTonns -= d.Tonnage;
                    }
                    else
                    {
                        BTRandomMechComponentUpgrader_Init.Log.Log("cannot add, nothing rolled " + log);
                    }
                }
            }
            mDef.SetInventory(inv.ToArray());
        }
示例#8
0
        private HardpointDataDef._WeaponHardpointData GetWeaponData(ChassisLocations location)
        {
            var locationString = location.ToString().ToLower();
            var weaponsData    = chassisDef.HardpointDataDef.HardpointData.FirstOrDefault(x => x.location == locationString);

            return(weaponsData);
        }
示例#9
0
        internal LocationHelper GetLocationHelper(ChassisLocations location)
        {
            switch (location)
            {
            case ChassisLocations.Head:
                return(LHelper_HD);

            case ChassisLocations.LeftArm:
                return(LHelper_LA);

            case ChassisLocations.LeftTorso:
                return(LHelper_LT);

            case ChassisLocations.CenterTorso:
                return(LHelper_CT);

            case ChassisLocations.RightTorso:
                return(LHelper_RT);

            case ChassisLocations.RightArm:
                return(LHelper_RA);

            case ChassisLocations.LeftLeg:
                return(LHelper_LL);

            case ChassisLocations.RightLeg:
                return(LHelper_RL);
            }

            return(null);
        }
示例#10
0
        internal HardpointDataDef._WeaponHardpointData GetWeaponData(ChassisLocations location)
        {
            var locationString = VHLUtils.GetStringFromLocation(location);
            var weaponsData    = chassisDef.HardpointDataDef.HardpointData.FirstOrDefault(x => x.location == locationString);

            return(weaponsData);
        }
示例#11
0
        private void CalculateBlanksForLocation(ChassisLocations location)
        {
            var weaponData = GetWeaponData(location);

            if (weaponData.weapons == null || weaponData.blanks == null)
            {
                return;
            }

            var usedMappings = weaponMappings
                               .Where(x => x.Key.MountedLocation == location)
                               .Select(x => x.Value)
                               .Distinct()
                               .ToList();

            var usedSlots = weaponData.weapons
                            .Where(x => x.Any(y => usedMappings.Contains(y))) // find all mappings in the same groups
                            .SelectMany(x => x)
                            .Select(PrefabHardpoint)                          // we only care about the last part of the id
                            .Distinct()
                            .ToList();

            var requiredBlanks = weaponData.blanks
                                 .Where(x => !usedSlots.Contains(PrefabHardpoint(x)))
                                 .ToList();

            Control.Logger.Debug?.Log($"Blank mappings for chassis {chassisDef.Description.Id} at {location} [{requiredBlanks.JoinAsString()}]");

            blanks[location] = requiredBlanks;
        }
示例#12
0
        public MechLabLocationWidget GetLocationWidget(ChassisLocations location)
        {
            switch (location)
            {
            case ChassisLocations.Head:
                return(MechLab.headWidget);

            case ChassisLocations.LeftArm:
                return(MechLab.leftArmWidget);

            case ChassisLocations.LeftTorso:
                return(MechLab.leftTorsoWidget);

            case ChassisLocations.CenterTorso:
                return(MechLab.centerTorsoWidget);

            case ChassisLocations.RightTorso:
                return(MechLab.rightTorsoWidget);

            case ChassisLocations.RightArm:
                return(MechLab.rightArmWidget);

            case ChassisLocations.LeftLeg:
                return(MechLab.leftLegWidget);

            case ChassisLocations.RightLeg:
                return(MechLab.rightLegWidget);
            }

            return(null);
        }
        public void ComponentInstallWorkOrder(MechComponentRef mechComponent, ChassisLocations newLocation, WorkOrderEntry_InstallComponent result)
        {
            var workOrderCosts = mechComponent.Def.GetComponent <WorkOrderCosts>();

            if (workOrderCosts == null)
            {
                return;
            }

            if (newLocation == ChassisLocations.None) // remove
            {
                if (mechComponent.DamageLevel == ComponentDamageLevel.Destroyed)
                {
                    ApplyCosts(result, workOrderCosts.RemoveDestroyed);
                }
                else
                {
                    ApplyCosts(result, workOrderCosts.Remove);
                }
            }
            else // install
            {
                ApplyCosts(result, workOrderCosts.Install);
            }
        }
示例#14
0
        internal bool Add(MechComponentDef def, ChassisLocations location = ChassisLocations.None, bool force = false)
        {
            // find location
            if (location == ChassisLocations.None || LocationCount(location) > 1)
            {
                location = FindSpaceAtLocations(def.InventorySize, def.AllowedLocations);
                if (location == ChassisLocations.None)
                {
                    return(false);
                }
            }

            var newLocationUsage = GetUsedSlots(location) + def.InventorySize;

            if (!force && newLocationUsage > GetMaxSlots(location))
            {
                return(false);
            }

            TotalUsage += def.InventorySize;
            LocationUsage[location] = newLocationUsage;

            if (def.Is <DynamicSlots>(out var ds))
            {
                DynamicSlots.Add(ds);
            }

            var componentRef = new MechComponentRef(def.Description.Id, null, def.ComponentType, location);

            componentRef.DataManager = DataManager;
            componentRef.RefreshComponentDef();
            Inventory.Add(componentRef);
            return(true);
        }
        internal static float AccuracyForLocation(StatCollection statCollection, ChassisLocations location)
        {
            var naming = new MechLocationNaming(location);
            var key    = naming.LocationalStatisticName("Accuracy");

            return(AccuracyForKey(statCollection, key));
        }
 public void OnRemove(ChassisLocations location, InventoryOperationState state)
 {
     if (!Def.IsDefault() && state.Mech.HasWeaponDefaults(location))
     {
         state.AddChange(new Change_WeaponAdjust(location));
     }
 }
示例#17
0
        public static AddFromInventoryChange FoundInInventory(ChassisLocations location, MechLabHelper mechlab, Predicate <MechComponentDef> SearchTerms)
        {
            Control.LogDebug(DType.ComponentInstall, $"AddFromInventoryChange.Create() one search");
            var item = search_item(mechlab, SearchTerms);

            return(item == null ? null : new AddFromInventoryChange(location, item));
        }
示例#18
0
        public static void AddInventory(string defaultID, MechDef mech, ChassisLocations location, ComponentType type, SimGameState state)
        {
            var r = CreateRef(defaultID, type, mech.DataManager, state);

            if (r != null)
            {
                r.SetData(location, -1, ComponentDamageLevel.Functional, true);
                var inv = mech.Inventory.ToList();
                inv.Add(r);
                mech.SetInventory(inv.ToArray());

#if CCDEBUG
                if (Control.Settings.DebugInfo.HasFlag(DType.FixedCheck))
                {
                    var flag = r.GetComponent <Flags>();
                    Control.LogDebug(DType.FixedCheck,
                                     $"AddInventory: {r.Def.Description.Id} isdefult:{r.Def.IsDefault()} isfixed:{r.IsFixed} isFlag:{flag == null}");
                    if (flag == null)
                    {
                        Control.LogDebug(DType.FixedCheck, $"-- NO FLAGS!");
                    }
                    else
                    {
                        Control.LogDebug(DType.FixedCheck,
                                         $"-- default: {flag.IsSet("default")} isdefault:{flag.Default}");
                    }

                    foreach (var simpleCustomComponent in r.GetComponents <SimpleCustomComponent>())
                    {
                        Control.LogDebug(DType.FixedCheck, $"-- {simpleCustomComponent}");
                    }
                }
#endif
            }
        }
 public static void LogOverheat(Mech __instance, ChassisLocations location, float damageAmount)
 {
     try {
         string line = thisSequence;
         if (DebugLog)
         {
             Verbo("Overheat damage {1} to {0}", location, damageAmount);
         }
         if (LogLocation)
         {
             line += FillBlanks(11) + Separator + "--";
             if (LogDamage)
             {
                 line += Separator + damageAmount
                         + Separator + location // stops at
                         + FillBlanks(2)        // armours
                         + Separator + beforeStruct + Separator + __instance.GetCurrentStructure(location);
                 if (LogCritical)
                 {
                     line += CritDummy;
                 }
             }
         }
         log.Add(line);
     }                 catch (Exception ex) { Error(ex); }
 }
示例#20
0
        private static float WeakestArmour(Mech mech, ChassisLocations location)
        {
            // For side torsos, report 0 if rear armour is breached.
            if (location == ChassisLocations.LeftTorso)
            {
                if (mech.GetCurrentArmor(LeftTorsoRear) <= 0)
                {
                    return(0);
                }
            }
            else if (location == ChassisLocations.RightTorso)
            {
                if (mech.GetCurrentArmor(RightTorsoRear) <= 0)
                {
                    return(0);
                }
            }
            float armour = mech.GetCurrentArmor((ArmorLocation)location);

            // Arms are at most as strong as the side torsos
            if (location == ChassisLocations.LeftArm)
            {
                return(Math.Min(armour, WeakestArmour(mech, ChassisLocations.LeftTorso)));
            }
            else if (location == ChassisLocations.RightArm)
            {
                return(Math.Min(armour, WeakestArmour(mech, ChassisLocations.RightTorso)));
            }
            return(armour);
        }
 public static void LogBaseCritChance(float __result, Mech target, ChassisLocations hitLocation)
 {
     try {
         thisBaseCritChance = __result;
         thisLocationMaxHP  = target.GetMaxStructure(hitLocation);
     }                 catch (Exception ex) { Error(ex); }
 }
        public string PreValidateDrop(MechLabItemSlotElement item, ChassisLocations location)
        {
            if (!Valid)
            {
                return(string.Empty);
            }

            var hardpoints = MechLabHelper.CurrentMechLab.ActiveMech.GetAllHardpoints(location);

            int n = 0;

            foreach (var hardpoint in hardpoints)
            {
                if (hardpoint.hpInfo.Visible)
                {
                    n += 1;
                    if (hardpoint.hpInfo.WeaponCategory.ID == WeaponCategory.ID)
                    {
                        return(string.Empty);
                    }
                }
            }

            if (n >= 4)
            {
                return(Control.Settings.Message.Hardpoints_TooManyHardpoints);
            }

            return(string.Empty);
        }
示例#23
0
        private PrefabSets GetAvailablePrefabSetsForLocation(ChassisLocations location)
        {
            var locationString = VHLUtils.GetStringFromLocation(location);
            var weaponsData    = chassisDef.HardpointDataDef.HardpointData.FirstOrDefault(x => x.location == locationString);
            var sets           = new PrefabSets();

            if (weaponsData.weapons == null)
            {
                //Control.mod.Logger.LogDebug($"no hardpoint data found for {chassisDef.Description.Id} at {location}");
            }
            else
            {
                foreach (var weapons in weaponsData.weapons)
                {
                    var index = sets.Count;
                    try
                    {
                        var set = new PrefabSet(index, weapons);
                        sets.Add(set);
                    }
                    catch (Exception e)
                    {
                        Control.mod.Logger.LogDebug($"error processing hardpoint data for {chassisDef.Description.Id} at {location}: index={index} weapons=[{weapons?.JoinAsString()}]", e);
                        throw;
                    }
                }
            }
            return(sets);
        }
示例#24
0
    private static void ModifyInventorySlots(ref LocationDef locationDef, ChassisLocations location, ValueChange <int> change)
    {
        if (locationDef.Location != location)
        {
            return;
        }

        var newValue = change.Change(locationDef.InventorySlots);

        if (!newValue.HasValue)
        {
            return;
        }

        if (location == ChassisLocations.CenterTorso)
        {
            newValue += MechLabSlotsFeature.settings.TopLeftWidget.Slots +
                        MechLabSlotsFeature.settings.TopRightWidget.Slots;
        }

        var info  = typeof(LocationDef).GetField("InventorySlots");
        var value = Convert.ChangeType(newValue, info.FieldType);
        var box   = (object)locationDef;

        info.SetValue(box, value);
        locationDef = (LocationDef)box;

        Control.Logger.Debug?.Log($"set InventorySlots={locationDef.InventorySlots} on location={location}");
    }
示例#25
0
        // Evaluates whether a given mech needs any structure repaired
        public static bool CheckStructureDamage(MechDef mech)
        {
            // Default to not requesting any structure repair
            bool mechNeedsRepair = false;

            // Using the repair priority for loop here as it is faster and simpler than foreach'ing over ChassisLocations and filtering out ones that don't have armor
            for (int index = 0; index < Globals.repairPriorities.Count; index++)
            {
                // Set current ChassisLocation
                ChassisLocations thisLoc = Globals.repairPriorities.ElementAt(index).Value;
                // Get current mech location loadout from the looped chassis definitions
                LocationLoadoutDef thisLocLoadout = mech.GetLocationLoadoutDef(thisLoc);
                // Friendly name for this location
                string thisLocName = thisLoc.ToString();

                // Work out difference of armor lost for each location - default to 0
                int   structureDifference = 0;
                float currentStructure    = thisLocLoadout.CurrentInternalStructure;
                float definedStructure    = mech.GetChassisLocationDef(thisLoc).InternalStructure;
                structureDifference = (int)Mathf.Abs(currentStructure - definedStructure);

                // If any difference betwen the location's current and assigned armor is detected, flag this mech for armor repair
                if (structureDifference > 0)
                {
                    Logger.LogDebug(mech.Name + " requires structure repair based on damage to: " + thisLocName);
                    mechNeedsRepair = true;
                    break; // Stop evaluating other locations once a repair requirement is determined
                }
            }

            return(mechNeedsRepair);
        }
        public static void FixCost(
            SimGameState __instance,
            MechComponentRef mechComponent,
            ChassisLocations newLocation,
            ChassisLocations previousLocation,
            string mechSimGameUID,
            WorkOrderEntry_InstallComponent __result)
        {
            try
            {
                Control.LogDebug(DType.InstallCost, $"SimGameState_CreateComponentInstallWorkOrder: for {mechComponent.ComponentDefID}");
                Control.LogDebug(DType.InstallCost, $"-- from {previousLocation} to {newLocation}");
                Control.LogDebug(DType.InstallCost, $"-- order {__result?.GetType().ToString() ?? "null"}");

                if (__result == null)
                {
                    Control.LogDebug(DType.InstallCost, "-- No order");
                    return;
                }


                if (__result.DesiredLocation == ChassisLocations.None)
                {
                    var tr = Traverse.Create(__result);
                    tr.Field <int>("Cost").Value = 0;
                }
                else
                {
                    var tr = Traverse.Create(__result).Field <int>("Cost");

                    if (tr == null)
                    {
                        Control.LogDebug(DType.InstallCost, "SimGameState_CreateComponentInstallWorkOrder: traverce not created!");
                        return;
                    }
                    MechDef mechByID = __instance.GetMechByID(mechSimGameUID);
#if CCDEBUG
                    if (mechByID == null)
                    {
                        Control.LogDebug(DType.InstallCost, "-- no mech found!");
                    }
#endif
                    if (mechByID != null && mechByID.Chassis.ChassisTags.Contains(Control.Settings.OmniTechFlag))
                    {
                        Control.LogDebug(DType.InstallCost, "-- mech is omni!");
                        tr.Value = (Control.Settings.OmniTechCostBySize ? mechComponent.Def.InventorySize / 2 : 1) * Control.Settings.OmniTechInstallCost;
                    }


                    if (tr.Value == 0)
                    {
                        tr.Value = 1;
                    }
                }
            }
            catch (Exception e)
            {
                Control.LogError(e);
            }
        }
示例#27
0
        private List <PrefabSet> GetAvailablePrefabSetsForLocation(ChassisLocations location)
        {
            var weaponsData = GetWeaponData(location);
            var sets        = new List <PrefabSet>();

            if (weaponsData.weapons == null)
            {
                Control.Logger.Debug?.Log($"no hardpoint data found for {chassisDef.Description.Id} at {location}");
            }
            else
            {
                foreach (var weapons in weaponsData.weapons)
                {
                    var index = sets.Count;
                    try
                    {
                        var set = new PrefabSet(index, weapons.Where(x => !preMappedPrefabNames.Contains(x)));
                        sets.Add(set);
                    }
                    catch (Exception e)
                    {
                        Control.Logger.Warning?.Log($"error processing hardpoint data for {chassisDef.Description.Id} at {location}: index={index} weapons=[{weapons?.JoinAsString()}]", e);
                        //throw;
                    }
                }
            }
            return(sets);
        }
        private string[] RemoveUnwantedHardpoints(ChassisLocations location, string[] hardpointSet)
        {
            var counter = new HardpointCounter(chassisDef, location);

            IEnumerable <string> hardpoints = hardpointSet;

            if (counter.numBallistic == 0)
            {
                hardpoints = hardpoints.Where(hp => !hp.Contains("_bh"));
            }

            if (counter.numEnergy == 0 && counter.numSmall == 0)
            {
                hardpoints = hardpoints.Where(hp => !hp.Contains("_eh"));
            }

            if (counter.numMissile == 0)
            {
                hardpoints = hardpoints.Where(hp => !hp.Contains("_mh"));
            }

            if (counter.numSmall == 0)
            {
                hardpoints = hardpoints.Where(hp => !hp.Contains("_ah"));
            }

            return(hardpoints.ToArray());
        }
 private inv_change(bool add, string id, ChassisLocations location, ComponentType type = ComponentType.NotSet)
 {
     this.add      = add;
     this.Location = location;
     this.Id       = id;
     this.Type     = type;
 }
        private void CalculateMappingForLocation(ChassisLocations location, List <MechComponentRef> sortedComponentRefs)
        {
            var bestSelection     = new PrefabSelectionCandidate(GetAvailablePrefabSetsForLocation(location), new List <PrefabMapping>());
            var currentCandidates = new List <PrefabSelectionCandidate> {
                bestSelection
            };

            foreach (var componentRef in sortedComponentRefs)
            {
                var newCandidates = new List <PrefabSelectionCandidate>();
                foreach (var candidate in currentCandidates)
                {
                    var hasNew = false;
                    foreach (var set in candidate.Sets)
                    {
                        var prefabName = set.GetCompatiblePrefab(componentRef.Def.PrefabIdentifier);
                        if (prefabName == null)
                        {
                            //Control.mod.Logger.LogDebug("could not find prefabName for " + componentRef?.Def?.PrefabIdentifier);
                            continue;
                        }

                        var newMapping   = new PrefabMapping(prefabName, componentRef);
                        var newCandidate = candidate.CreateWithoutSet(set, newMapping);
                        newCandidates.Add(newCandidate);
                        hasNew = true;
                    }

                    if (!hasNew) // we didn't find anything better, so re-add the old one
                    {
                        newCandidates.Add(candidate);
                    }
                }

                currentCandidates = newCandidates;
            }

            foreach (var candidate in currentCandidates)
            {
                if (candidate.CompareTo(bestSelection) > 0)
                {
                    bestSelection = candidate;
                }
            }

            if (bestSelection.Mappings.Count < 1)
            {
                return;
            }

            var text = $"Mappings for chassis {chassisDef.Description.Id} at {location}";

            foreach (var mapping in bestSelection.Mappings)
            {
                text += $"\n{mapping.MechComponentRef.Def.Description.Id} {mapping.PrefabName}";
                cacheMappings[mapping.MechComponentRef] = mapping.PrefabName;
            }
            Control.mod.Logger.LogDebug(text);
        }