/// <summary> /// Initializes the list of discovered resources, accounting for both DLC and Vanilla. /// </summary> private void InitDiscovered() { Type baseType; object instance; // TODO Vanilla/DLC code if ((baseType = PPatchTools.GetTypeSafe("DiscoveredResources")) == null) { // Vanilla baseType = PPatchTools.GetTypeSafe(nameof(WorldInventory)); instance = baseType?.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null, null); } else { // DLC instance = baseType.GetFieldSafe("Instance", true)?.GetValue(null); } #if DEBUG PUtil.LogDebug("Using inventory type: " + (baseType?.Name ?? "None")); #endif if (instance != null) { discovered = baseType.CreateDelegate <ResourceCheckDelegate>( "TryGetDiscoveredResourcesFromTag", instance, typeof(Tag), typeof( HashSet <Tag>).MakeByRefType()); } else { PUtil.LogWarning("Unable to find discovered resource list, no resources will be available to sweep"); } }
private static void RoomsExpandedCompat() { var db = Db.Get(); var RoomsExpanded = PPatchTools.GetTypeSafe("RoomsExpanded.RoomTypes_AllModded", "RoomsExpandedMerged"); if (RoomsExpanded != null) { PUtil.LogDebug("RoomsExpanded found. Attempt to add compatibility."); try { AquariumRoom = (RoomType)RoomsExpanded.GetPropertySafe <RoomType>("Aquarium", true)?.GetValue(null, null); if (AquariumRoom != null) { var upgrade_paths = db.RoomTypes.CreaturePen.upgrade_paths.AddToArray(AquariumRoom); Traverse.Create(db.RoomTypes.CreaturePen).Property(nameof(RoomType.upgrade_paths)).SetValue(upgrade_paths); Traverse.Create(AquariumRoom).Property(nameof(RoomType.priority)).SetValue(db.RoomTypes.CreaturePen.priority); RoomsExpandedFound = true; } } catch (System.Exception e) { PUtil.LogExcWarn(e); } } if (RoomsExpandedFound) { harmony.PatchTranspile(typeof(RanchStation.Instance), nameof(RanchStation.Instance.FindRanchable), new HarmonyMethod(typeof(RanchStation_Instance_FindRanchable), nameof(RanchStation_Instance_FindRanchable.TranspilerCompat))); } }
internal BetterInfoCardsCompat() { RegisterMethodFunc addConv = null; exportMethod = null; try { addConv = PPatchTools.GetTypeSafe(BIC_NAMESPACE + "ConverterManager", BIC_ASSEMBLY)?.Detour <RegisterMethodFunc>("AddConverterReflect"); var patchType = PPatchTools.GetTypeSafe(BIC_NAMESPACE + "CollectHoverInfo", BIC_ASSEMBLY)?.GetNestedType("GetSelectInfo_Patch", BindingFlags.Static | BindingFlags.Instance | PPatchTools.BASE_FLAGS); if (patchType != null) { exportMethod = patchType.Detour <ExportMethodFunc>("Export"); } } catch (AmbiguousMatchException e) { PUtil.LogWarning("Exception when loading Better Info Cards compatibility:"); PUtil.LogExcWarn(e); } catch (DetourException e) { PUtil.LogWarning("Exception when loading Better Info Cards compatibility:"); PUtil.LogExcWarn(e); } if (addConv != null && exportMethod != null) { try { Register(addConv, EXPORT_THERMAL_MASS, ObjectToFloat, SumThermalMass); Register(addConv, EXPORT_HEAT_ENERGY, ObjectToFloat, SumHeatEnergy); PUtil.LogDebug("Registered Better Info Cards status data handlers"); } catch (Exception e) { PUtil.LogWarning("Exception when registering Better Info Cards compatibility:"); PUtil.LogExcWarn(e.GetBaseException()); } } }
internal static void AfterDbInit() { // Assets are now loaded, so create pip icon var pip = Assets.GetAnim("squirrel_kanim"); Sprite sprite = null; if (pip != null) { sprite = Def.GetUISpriteFromMultiObjectAnim(pip); } if (sprite == null) { // Pip anim is somehow missing? sprite = Assets.GetSprite("overlay_farming"); } Assets.Sprites.Add(PipPlantOverlayStrings.OVERLAY_ICON, sprite); // SPPR fixes the symmetry rule bool ruleFix = PPatchTools.GetTypeSafe("MightyVincent.Patches", "SimplerPipPlantRule") != null; if (ruleFix) { PUtil.LogDebug("Detected Simpler Pip Plant Overlay, adjusting radius"); } PipPlantOverlayTests.SymmetricalRadius = ruleFix; }
internal static MethodBase TargetMethod() { // Target HoverTextHelper in DLC, and WorldInspector in vanilla // TODO Vanilla/DLC code var type = PPatchTools.GetTypeSafe("HoverTextHelper"); if (type == null) { type = PPatchTools.GetTypeSafe("WorldInspector"); } return(type?.GetMethodSafe(METHOD, true, typeof(int))); }
private static void LagooPatch() { var BaseLagooConfig = PPatchTools.GetTypeSafe("Lagoo.BaseLagooConfig", "LagooMerged"); if (BaseLagooConfig != null) { PUtil.LogDebug("'Lagoo' found, trying to apply a compatibility patch."); var postfix = new HarmonyMethod(typeof(BaseSquirrelConfig_BaseSquirrel), nameof(BaseSquirrelConfig_BaseSquirrel.Postfix)); harmonyInstance.Patch(BaseLagooConfig, "BaseLagoo", null, postfix); var transpiler = new HarmonyMethod(typeof(BaseSquirrelConfig_BaseSquirrel), nameof(BaseSquirrelConfig_BaseSquirrel.Transpiler)); harmonyInstance.PatchTranspile(BaseLagooConfig, "BaseLagoo", transpiler); } }
public static void PostPatch(HarmonyInstance instance) { var steamMod = PPatchTools.GetTypeSafe("KMod.Steam"); if (steamMod != null) { #if DEBUG PUtil.LogDebug("Transpiling Steam.UpdateMods()"); #endif // Transpile UpdateMods only for Steam versions (not EGS) instance.Patch(steamMod.GetMethodSafe("UpdateMods", false, PPatchTools. AnyArguments), transpiler: new HarmonyMethod(typeof(StockBugsPatches), nameof(TranspileUpdateMods))); instance.Patch(typeof(MainMenu).GetMethodSafe("Update", false), postfix: new HarmonyMethod(typeof(StockBugsPatches), nameof(PostfixMenuUpdate))); } }
public override BuildingDef CreateBuildingDef() { var def = ThermalInterfacePlate.CreateDef(); // Is "Drywall Hides Pipes" installed? If so, hide pipes with this too if (PPatchTools.GetTypeSafe("DrywallHidesPipes.DrywallPatch") != null) { def.SceneLayer = Grid.SceneLayer.LogicGatesFront; } // Is "Faster Drywall & Plate Construction" installed? If so, reduce // construction time by 5x if (PPatchTools.GetTypeSafe("Patches.ExteriorWallAdjust") != null) { def.ConstructionTime = 6.0f; } return(def); }
internal static void SetupTooltips() { var options = POptions.ReadSettings <ThermalTooltipsOptions>() ?? new ThermalTooltipsOptions(); // Check for DisplayAllTemps if (PPatchTools.GetTypeSafe("DisplayAllTemps.State", "DisplayAllTemps") != null) { // Let Display All Temps take over display (ironically setting AllUnits // to FALSE) since it patches GetFormattedTemperature PUtil.LogDebug("DisplayAllTemps compatibility activated"); options.AllUnits = false; } TooltipInstance = new ExtendedThermalTooltip(options, bicCompat); PUtil.LogDebug("Created ExtendedThermalTooltip"); }
/// <summary> /// Patches Text Mesh Pro input fields to fix a variety of bugs. Should be used before /// any Text Mesh Pro objects are created. /// </summary> public static void Patch() { lock (patchLock) { if (!patchChecked) { var tmpType = PPatchTools.GetTypeSafe("TMPro.TMP_InputField"); if (tmpType != null) { try { InputFieldPatches(tmpType); } catch (Exception) { PUtil.LogWarning("Unable to patch TextMeshPro bug, text fields may display improperly inside scroll areas"); } } patchChecked = true; } } }
/// <summary> /// Fixes the race condition in Steam.UpdateMods. /// </summary> /// <param name="instance">The Harmony instance to use for patching.</param> private void FixModUpdateRace(Harmony instance) { var steamMod = PPatchTools.GetTypeSafe("KMod.Steam"); const string BUG_KEY = "Bugs.ModUpdateRace"; if (steamMod != null && !PRegistry.GetData <bool>(BUG_KEY)) { // Transpile UpdateMods only for Steam versions (not EGS) #if DEBUG PUtil.LogDebug("Transpiling Steam.UpdateMods()"); #endif PRegistry.PutData(BUG_KEY, true); instance.Patch(steamMod.GetMethodSafe("UpdateMods", false, PPatchTools. AnyArguments), transpiler: new HarmonyMethod(typeof(StockBugsPatches), nameof(TranspileUpdateMods))); instance.Patch(typeof(MainMenu).GetMethodSafe("OnSpawn", false), postfix: new HarmonyMethod(typeof(StockBugsPatches), nameof(PostfixMenuSpawn))); } }
internal BetterInfoCardsCompat() { MethodInfo addConv = null; exportMethod = null; try { addConv = PPatchTools.GetTypeSafe(BIC_NAMESPACE + "ConverterManager", BIC_ASSEMBLY)?.GetMethodSafe("AddConverter", true, PPatchTools. AnyArguments); var patchType = PPatchTools.GetTypeSafe(BIC_NAMESPACE + "CollectHoverInfo", BIC_ASSEMBLY)?.GetNestedType("GetSelectInfo_Patch", BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); if (patchType != null) { exportMethod = patchType.CreateStaticDelegate <ExportMethodFunc>("Export", typeof(string), typeof(object)); } } catch (TargetInvocationException e) { PUtil.LogWarning("Exception when loading Better Info Cards compatibility:"); PUtil.LogExcWarn(e?.GetBaseException() ?? e); } catch (AmbiguousMatchException e) { PUtil.LogWarning("Exception when loading Better Info Cards compatibility:"); PUtil.LogExcWarn(e); } if (addConv != null && exportMethod != null) { // Delegates are much faster at runtime registerMethod = addConv; try { Register(EXPORT_THERMAL_MASS, ObjectToFloat, SumThermalMass); Register(EXPORT_HEAT_ENERGY, ObjectToFloat, SumHeatEnergy); PUtil.LogDebug("Registered Better Info Cards status data handlers"); } catch (TargetInvocationException e) { PUtil.LogWarning("Exception when registering Better Info Cards compatibility:"); PUtil.LogExcWarn(e?.GetBaseException() ?? e); } } else { registerMethod = null; } }
/// <summary> /// Checks for compatibility and applies fast fetch manager updates only if Efficient /// Supply is not enabled. /// </summary> /// <param name="harmony">The Harmony instance to use for patching.</param> private static void CheckFetchCompat(Harmony harmony) { if (PPatchTools.GetTypeSafe("PeterHan.EfficientFetch.EfficientFetchManager") == null) { PathPatches.AsyncBrainGroupUpdater.AllowFastListSwap = true; harmony.Patch(typeof(FetchManager.FetchablesByPrefabId), nameof(FetchManager.FetchablesByPrefabId.UpdatePickups), prefix: new HarmonyMethod(typeof(GamePatches.FetchManagerFastUpdate), nameof(GamePatches.FetchManagerFastUpdate.BeforeUpdatePickups))); #if DEBUG PUtil.LogDebug("Patched FetchManager for fast pickup updates"); #endif } else { PUtil.LogWarning("Disabling fast pickup updates: Efficient Supply active"); PathPatches.AsyncBrainGroupUpdater.AllowFastListSwap = false; } }
/// <summary> /// Attempts to also patch the Decor Reimagined implementation of DecorProvider. /// Refresh. /// </summary> /// <param name="harmony">The Harmony instance to use for patching.</param> internal static void ApplyPatch(Harmony harmony) { var patchMethod = new HarmonyMethod(typeof(DecorProviderRefreshFix), nameof( TranspileRefresh)); var targetMethod = PPatchTools.GetTypeSafe( "ReimaginationTeam.DecorRework.DecorSplatNew", "DecorReimagined")?. GetMethodSafe("RefreshDecor", false, PPatchTools.AnyArguments); if (targetMethod != null) { PUtil.LogDebug("Patching Decor Reimagined for DecorProvider.RefreshDecor"); harmony.Patch(targetMethod, transpiler: patchMethod); } PUtil.LogDebug("Patching DecorProvider.Refresh"); harmony.Patch(typeof(DecorProvider).GetMethodSafe(nameof(DecorProvider.Refresh), false, PPatchTools.AnyArguments), transpiler: patchMethod); harmony.Patch(typeof(RoomProber), nameof(RoomProber.Sim1000ms), prefix: new HarmonyMethod(typeof(DecorProviderRefreshFix), nameof(PrefixRoomProbe))); ROOMS_PENDING.Clear(); }
public override void OnLoad(Harmony harmony) { IsMono = PPatchTools.GetTypeSafe("Mono.Runtime") != null; base.OnLoad(harmony); PUtil.InitLibrary(); #if DEBUG SpamObjectsHandler.PrepareSpamHandler(new PLib.PatchManager.PPatchManager(harmony)); #endif var inst = ExtendedTagBits.Instance; // Force these tags into the efficient lower bits foreach (var tag in FORCE_LOWER_BITS) { inst.ManifestFlagIndex(tag); } FetchManager.disallowedTagMask = TagBitOps.Not(FetchManager.disallowedTagBits); PDetours.DetourField <FetchAreaChore.StatesInstance, TagBits>( "s_transientDeliveryMask").Set(null, TagBitOps.Not(new TagBits(new Tag[] { GameTags.Garbage, GameTags.Creatures.Deliverable }))); new PVersionCheck().Register(this, new SteamVersionChecker()); }
public override void OnAllModsLoaded(Harmony harmony, IReadOnlyList <Mod> mods) { base.OnAllModsLoaded(harmony, mods); // Manual patch in the rewritten FetchManager.UpdatePickups only if Efficient // Supply is not enabled if (FastTrackOptions.Instance.FastUpdatePickups) { if (PPatchTools.GetTypeSafe("PeterHan.EfficientFetch.EfficientFetchManager") == null) { harmony.Patch(typeof(FetchManager.FetchablesByPrefabId), nameof(FetchManager.FetchablesByPrefabId.UpdatePickups), prefix: new HarmonyMethod(typeof(FetchManagerFastUpdate), nameof(FetchManagerFastUpdate.Prefix))); PUtil.LogDebug("Patched FetchManager for fast pickup updates"); } else { PUtil.LogWarning("Disabling fast pickup updates: Efficient Supply active"); } } }
/// <summary> /// Checks to see if the conditions for a method running are met. /// </summary> /// <param name="assemblyName">The assembly name that must be present, or null if none is required.</param> /// <param name="typeName">The type full name that must be present, or null if none is required.</param> /// <param name="requiredType">The type that was required, if typeName was not null or empty.</param> /// <returns>true if the requirements are met, or false otherwise.</returns> internal static bool CheckConditions(string assemblyName, string typeName, out Type requiredType) { bool ok = false, emptyType = string.IsNullOrEmpty(typeName); if (string.IsNullOrEmpty(assemblyName)) { if (emptyType) { requiredType = null; ok = true; } else { requiredType = PPatchTools.GetTypeSafe(typeName); ok = requiredType != null; } } else if (emptyType) { requiredType = null; // Search for assembly only, by name foreach (var candidate in AppDomain.CurrentDomain.GetAssemblies()) { if (candidate.GetName().Name == assemblyName) { ok = true; break; } } } else { requiredType = PPatchTools.GetTypeSafe(typeName, assemblyName); ok = requiredType != null; } return(ok); }
/// <summary> /// Finds the type for the given name using the method that Unity/Mono uses to report /// types internally. /// </summary> /// <param name="typeName">The type name.</param> /// <returns>The types for each type name, or null if no type could be resolved.</returns> internal static Type GetTypeByUnityName(this string typeName) { Type type = null; if (!string.IsNullOrEmpty(typeName) && !SHORTHAND_TYPES.TryGetValue(typeName, out type)) { // Generic type? var match = GENERIC_TYPE.Match(typeName); if (match.Success) { var parameters = match.Groups[2].Value.Split(','); int nParams = parameters.Length; // Convert parameters to types var paramTypes = new Type[nParams]; for (int i = 0; i < nParams; i++) { paramTypes[i] = parameters[i].GetTypeByUnityName(); } // Genericize it var baseType = PPatchTools.GetTypeSafe(RemoveBacktick(match.Groups[1]. Value)); if (baseType != null && baseType.IsGenericTypeDefinition) { type = baseType.MakeGenericType(paramTypes); } else { type = baseType; } } else { type = PPatchTools.GetTypeSafe(RemoveBacktick(typeName)); } } return(type); }
/// <summary> /// Enumerates a list of methods and resolves their types. This method handles /// constructors as well. /// /// This method is very slow. Only execute when necessary. /// </summary> /// <param name="specs">The methods to look up.</param> /// <returns>The resolved methods or constructors.</returns> private static IEnumerable <MethodBase> CreateMethodList(ICollection <string> specs) { var methods = new List <MethodBase>(specs.Count); foreach (string mSpec in specs) { int index = mSpec.IndexOf(':'); if (index > 0) { string[] types = mSpec.Substring(0, index).Split('+'); int n = types.Length; MethodBase result = null; // Resolve the type, descending by '+' if needed var type = PPatchTools.GetTypeSafe(types[0]); for (int i = 1; i < n && type != null; i++) { type = type.GetNestedType(types[i], BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); } // Access constructor or method if (type == null) { PUtil.LogWarning("Unable to find type: " + mSpec); } else { result = LookupMethod(type, mSpec.Substring(index + 1)); } if (result != null) { methods.Add(result); } } } return(methods); }
internal static void TestAdvancedFilter() { AdvancedFilterEnabled = PPatchTools.GetTypeSafe( "AdvancedFilterMenu.AdvancedFiltrationAssets", "AdvancedFilterMenu") != null; }
public static void PrePatch(HarmonyInstance _) { IsMono = PPatchTools.GetTypeSafe("Mono.Runtime") != null; }
internal static void Init() { var db = Db.Get(); // тюнингуем и актифируем комнату // подхватывать максимальный размер комнаты из тюнинга int maxRoomSize = TuningData <RoomProber.Tuning> .Get().maxRoomSize; var MAXIMUM_SIZE_MAX = new RoomConstraints.Constraint( building_criteria: null, room_criteria: (Room room) => room.cavity.numCells <= maxRoomSize, times_required: 1, name: string.Format(ROOMS.CRITERIA.MAXIMUM_SIZE.NAME, maxRoomSize), description: string.Format(ROOMS.CRITERIA.MAXIMUM_SIZE.DESCRIPTION, maxRoomSize), stomp_in_conflict: null); var additional_constraints = db.RoomTypes.MachineShop.additional_constraints; for (int i = 0; i < additional_constraints.Length; i++) { if (additional_constraints[i] == RoomConstraints.MAXIMUM_SIZE_96) { additional_constraints[i] = MAXIMUM_SIZE_MAX; break; } } db.RoomTypes.Add(db.RoomTypes.MachineShop); // детектим "Rooms Expanded". модифицируем "мастерскую" чтобы она могла быть обгрейднутна до "кухни" var RoomsExpanded = PPatchTools.GetTypeSafe("RoomsExpanded.RoomTypes_AllModded", "RoomsExpandedMerged"); if (RoomsExpanded != null) { PUtil.LogDebug("RoomsExpanded found. Attempt to add compatibility."); try { KitchenRoom = (RoomType)RoomsExpanded.GetPropertySafe <RoomType>("KitchenRoom", true)?.GetValue(null, null); if (KitchenRoom != null) { var upgrade_paths = db.RoomTypes.MachineShop.upgrade_paths.AddToArray(KitchenRoom); Traverse.Create(db.RoomTypes.MachineShop).Property(nameof(RoomType.upgrade_paths)).SetValue(upgrade_paths); Traverse.Create(db.RoomTypes.MachineShop).Property(nameof(RoomType.priority)).SetValue(KitchenRoom.priority); KitchenBuildingTag = "KitchenBuildingTag".ToTag(); RoomsExpandedFound = true; } } catch (System.Exception e) { PUtil.LogExcWarn(e); } } // добавляем перк для работы на станции CanMachineTinker = db.SkillPerks.Add(new SimpleSkillPerk( id: REQUIRED_ROLE_PERK, description: STRINGS.PERK_CAN_MACHINE_TINKER.DESCRIPTION)); db.Skills.Technicals1.perks.Add(CanMachineTinker); // добавляем модификаторы и эффекты string text = DUPLICANTS.MODIFIERS.MACHINETINKER.NAME; string description = STRINGS.DUPLICANTS.MODIFIERS.MACHINETINKER.TOOLTIP; CraftingSpeed = db.Attributes.Add(new Attribute( id: CRAFTING_SPEED_MODIFIER_NAME, is_trainable: false, show_in_ui: Attribute.Display.General, is_profession: false, base_value: BASE_SPEED_VALUE)); CraftingSpeed.SetFormatter(new PercentAttributeFormatter()); MachinerySpeedModifier = new AttributeModifier( attribute_id: MACHINERY_SPEED_MODIFIER_NAME, value: MACHINERY_SPEED_MODIFIER, description: text, is_readonly: false); CraftingSpeedModifier = new AttributeModifier( attribute_id: CRAFTING_SPEED_MODIFIER_NAME, value: CRAFTING_SPEED_MODIFIER, description: text, is_readonly: false); MachineTinkerEffect = db.effects.Add(new Effect( id: MACHINE_TINKER_EFFECT_NAME, name: text, description: description, duration: MACHINE_TINKER_EFFECT_DURATION * Constants.SECONDS_PER_CYCLE, show_in_ui: true, trigger_floating_text: true, is_bad: false)); MachineTinkerEffect.Add(MachinerySpeedModifier); MachineTinkerEffect.Add(CraftingSpeedModifier); MachineTinkerEffectDuration = db.AttributeConverters.Create( id: "MachineTinkerEffectDuration", name: "Engie's Jerry Rig Effect Duration", description: STRINGS.DUPLICANTS.ATTRIBUTES.MACHINERY.MACHINE_TINKER_EFFECT_MODIFIER, attribute: db.Attributes.Machinery, multiplier: MACHINE_TINKER_EFFECT_DURATION_PER_SKILL, base_value: 0, formatter: new ToPercentAttributeFormatter(1f, GameUtil.TimeSlice.None), available_dlcs: DlcManager.AVAILABLE_ALL_VERSIONS); }
/// <summary> /// Finds the private class and method to patch. /// </summary> internal static MethodBase TargetMethod() { return(PPatchTools.GetTypeSafe("ClearableManager")?.GetMethodSafe( nameof(ChoreProvider.CollectChores), false, PPatchTools.AnyArguments)); }
/// <summary> /// Adds more items to the spawner list, including geysers, artifacts, and POI items. /// </summary> /// <param name="instance">The sandbox tool menu to modify.</param> private static void AddToSpawnerMenu(SandboxToolParameterMenu instance) { // Transpiling it is possible (and a bit faster) but way more brittle var selector = instance.entitySelector; var filters = ListPool <SearchFilter, SandboxToolParameterMenu> .Allocate(); filters.AddRange(selector.filters); // POI Props filters.Add(new SearchFilter(SandboxToolsStrings.FILTER_POIPROPS, (entity) => { var prefab = entity as KPrefabID; bool ok = prefab != null; if (ok) { string name = prefab.PrefabTag.Name; // Include anti-entropy thermo nullifier and neural vacillator // Vacillator's ID is private, we have to make do ok = (name.StartsWith("Prop") && name.Length > 4 && char.IsUpper( name, 4)) || name == MassiveHeatSinkConfig.ID || name == "GeneShuffler"; } return(ok); }, null, Def.GetUISprite(Assets.GetPrefab("PropLadder")))); // Artifacts filters.Add(new SearchFilter(SandboxToolsStrings.FILTER_ARTIFACTS, (entity) => { var prefab = entity as KPrefabID; bool ok = prefab != null; if (ok) { ok = prefab.PrefabTag.Name.StartsWith("artifact_"); } return(ok); }, null, Def.GetUISprite(Assets.GetPrefab("artifact_eggrock")))); // Geysers filters.Add(new SearchFilter(SandboxToolsStrings.FILTER_GEYSERS, (entity) => { var prefab = entity as KPrefabID; return(prefab != null && (prefab.GetComponent <Geyser>() != null || prefab. PrefabTag.Name == "OilWell")); }, null, Def.GetUISprite(Assets.GetPrefab("GeyserGeneric_slush_water")))); // TODO Vanilla/DLC code if (PPatchTools.GetTypeSafe("FullereneCometConfig") == null) { // Update the special filter to add other comet types foreach (var filter in filters) { if (filter.Name == STRINGS.UI.SANDBOXTOOLS.FILTERS.ENTITIES.SPECIAL) { var oldCondition = filter.condition; filter.condition = (entity) => { var prefab = entity as KPrefabID; return((prefab != null && prefab.GetComponent <Comet>() != null) || oldCondition.Invoke(entity)); }; } } } // Add matching assets var options = ListPool <object, SandboxToolParameterMenu> .Allocate(); foreach (var prefab in Assets.Prefabs) { foreach (var filter in filters) { if (filter.condition(prefab)) { options.Add(prefab); break; } } } #if DEBUG PUtil.LogDebug("Added {0:D} options to spawn menu".F(options.Count)); #endif selector.options = options.ToArray(); selector.filters = filters.ToArray(); options.Recycle(); filters.Recycle(); }