/// <summary> /// Creates the preview for a given light. /// /// RadiationGridManager.CreatePreview has no references so no sense in patching that /// yet. /// </summary> /// <param name="origin">The starting cell.</param> /// <param name="radius">The light radius.</param> /// <param name="shape">The light shape.</param> /// <param name="lux">The base brightness in lux.</param> /// <returns>true if the lighting was handled, or false otherwise.</returns> internal bool PreviewLight(int origin, float radius, LightShape shape, int lux) { bool handled = false; if (shape != LightShape.Circle && shape != LightShape.Cone) { var cells = DictionaryPool <int, float, PLightManager> .Allocate(); // Replicate the logic of the original one... int index = shape - LightShape.Cone - 1; if (index < shapes.Count) { // Found handler! shapes[index].FillLight(CallingObject, origin, (int)radius, cells); foreach (var pair in cells) { int cell = pair.Key; if (Grid.IsValidCell(cell)) { // Allow any fraction, not just linear falloff int lightValue = (int)Math.Round(lux * pair.Value); LightGridManager.previewLightCells.Add(new Tuple <int, int>(cell, lightValue)); LightGridManager.previewLux[cell] = lightValue; } } CallingObject = null; handled = true; } cells.Recycle(); } return(handled); }
/// <summary> /// Handles a lighting system call. Not intended to be used - exists as a fallback. /// </summary> /// <param name="cell">The origin cell.</param> /// <param name="visiblePoints">The location where lit points will be stored.</param> /// <param name="range">The light radius.</param> /// <param name="shape">The light shape.</param> /// <returns>true if the lighting was handled, or false otherwise.</returns> internal bool GetVisibleCells(int cell, IList <int> visiblePoints, int range, LightShape shape) { int index = shape - LightShape.Cone - 1; bool handled = false; if (index >= 0 && index < shapes.Count) { var ps = shapes[index]; // Do what we can, this only is reachable through methods we have patched var lux = DictionaryPool <int, float, PLightManager> .Allocate(); #if DEBUG PUtil.LogWarning("Unpatched call to GetVisibleCells; use LightGridEmitter." + "UpdateLitCells instead."); #endif ps.FillLight(CallingObject, cell, range, lux); // Intensity does not matter foreach (var point in lux) { visiblePoints.Add(point.Key); } lux.Recycle(); handled = true; } return(handled); }
/// <summary> /// Updates the critter counts of visible pinned rows. /// </summary> internal void UpdateContents() { var ci = ClusterManager.Instance.activeWorld.GetComponent <CritterInventory>(); if (ci != null) { var allCounts = DictionaryPool <Tag, CritterTotals, PinnedCritterManager> . Allocate(); foreach (var pair in pinnedObjects) { allCounts.Clear(); ci.PopulateTotals(pair.Key, allCounts); foreach (var speciesPair in pair.Value) { // Only refresh active rows var entry = speciesPair.Value; if (entry.gameObject.activeSelf) { int available = 0; if (allCounts.TryGetValue(speciesPair.Key, out CritterTotals totals)) { available = totals.Available; } RefreshLine(entry, available); } } } allCounts.Recycle(); } }
/// <summary> /// Sorts the table's rows. /// </summary> /// <param name="instance">The table screen to sort.</param> private static void SortRows(TableScreen instance) { var rows = instance.all_sortable_rows; bool reversed = instance.sort_is_reversed; var comparison = new TableSortComparison(instance.active_sort_method, reversed); var dividerIndices = DictionaryPool<int, int, TableScreen>.Allocate(); var dupesByWorld = DictionaryPool<int, TableRowList.PooledList, TableScreen>. Allocate(); int index = 0; var entryList = instance.scroll_content_transform; UpdateHeaders(instance, reversed); GroupDupesByWorld(rows, dupesByWorld); rows.Clear(); foreach (var pair in dupesByWorld) { var list = pair.Value; // 1 offset for the item added plus the number of items in this list dividerIndices.Add(pair.Key, index); index++; if (comparison.IsSortable) list.Sort(comparison); index += list.Count; rows.AddRange(list); list.Recycle(); } dupesByWorld.Recycle(); MoveRows(instance, rows, dividerIndices); dividerIndices.Recycle(); // Schedule a freeze if (entryList != null && instance.isActiveAndEnabled) instance.StartCoroutine(FreezeLayouts(rows)); }
/// <summary> /// Adds dividers for each planetoid in the Spaced Out DLC. /// </summary> /// <param name="instance">The table screen to update.</param> private static void AddDividers(TableScreen instance) { GameObject target; int id; var occupiedWorlds = DictionaryPool<int, WorldContainer, TableScreen>.Allocate(); foreach (int worldId in ClusterManager.Instance.GetWorldIDsSorted()) instance.AddWorldDivider(worldId); // List occupied planets foreach (object obj in Components.MinionAssignablesProxy) if (obj is MinionAssignablesProxy proxy && proxy != null && (target = proxy. GetTargetGameObject()) != null) { var world = target.GetMyWorld(); if (world != null && !occupiedWorlds.ContainsKey(id = world.id)) occupiedWorlds.Add(id, world); } foreach (var pair in instance.worldDividers) if (pair.Value.TryGetComponent(out HierarchyReferences hr)) { var dividerRow = hr.GetReference("NobodyRow"); id = pair.Key; if (occupiedWorlds.TryGetValue(id, out WorldContainer world)) { dividerRow.gameObject.SetActive(false); pair.Value.SetActive(world.IsDiscovered); } else { dividerRow.gameObject.SetActive(true); pair.Value.SetActive(ClusterManager.Instance.GetWorld(id).IsDiscovered); } } occupiedWorlds.Recycle(); }
/// <summary> /// Adds the correct descriptors to a critter info screen. /// </summary> /// <param name="critter">The critter to query.</param> /// <param name="descriptors">The location where the descriptors should be placed.</param> internal static void AddCritterDescriptors(GameObject critter, IList <Descriptor> descriptors) { var drops = critter.GetComponentSafe <Butcherable>()?.Drops; // Check the meat it drops if (critter != null && drops != null && drops.Length > 0) { var dict = DictionaryPool <string, int, FoodRecipeCache> .Allocate(); GetEggsPerCycle(critter, out float replacement, out float noReplacement); // Find out what it drops when it dies - critters always die so the // no-replacement rate must be positive CollectDrops(drops, dict); foreach (var pair in dict) { CreateDescriptors(TagManager.Create(pair.Key), descriptors, pair.Value * noReplacement, FoodDescriptorTexts.CRITTERS); } dict.Recycle(); // How much omelette can the egg be made into? Babies are excluded here var fertDef = critter.GetDef <FertilityMonitor.Def>(); if (fertDef != null) { CreateDescriptors(fertDef.eggPrefab, descriptors, replacement, FoodDescriptorTexts.CRITTERS); } } }
/// <summary> /// Initializes the available mass by element display. /// </summary> /// <param name="panel">The parent info panel.</param> /// <param name="harvestable">The POI to be harvested.</param> /// <param name="details">The panel to refresh.</param> private void InitElements(SpacePOISimpleInfoPanel panel, HarvestablePOIStates. Instance harvestable, CollapsibleDetailContentPanel details) { var elementRows = panel.elementRows; var existingRows = DictionaryPool <Tag, GameObject, SpacePOISimpleInfoPanel> . Allocate(); foreach (var pair in panel.elementRows) { existingRows[pair.Key] = pair.Value; } if (harvestable != null) { // Shared dictionary, does not allocate var sortedElements = ListPool <ElementWeight, SpacePOISimpleInfoPanel> . Allocate(); sortedElements.AddRange(harvestable.configuration.GetElementsWithWeights()); sortedElements.Sort(SortElementsByMassComparer.Instance); int n = sortedElements.Count; for (int i = 0; i < n; i++) { var pair = sortedElements[i]; SimHashes element = pair.Key; Tag elementTag = new Tag(element.ToString()); if (!elementRows.TryGetValue(elementTag, out GameObject row)) { row = Util.KInstantiateUI(panel.simpleInfoRoot.iconLabelRow, details. Content.gameObject, true); elementRows[elementTag] = row; } else { row.SetActive(true); } if (row.TryGetComponent(out HierarchyReferences hr)) { var uiSprite = Def.GetUISprite(elementTag, "ui", false); var icon = hr.GetReference <Image>("Icon"); if (uiSprite != null) { icon.sprite = uiSprite.first; icon.color = uiSprite.second; } hr.GetReference <LocText>("NameLabel").SetText(ElementLoader. FindElementByHash(element).name); hr.GetReference <LocText>("ValueLabel").alignment = TMPro. TextAlignmentOptions.MidlineRight; } existingRows.Remove(elementTag); } sortedElements.Recycle(); } // Turn off all rows that were not turned on foreach (var pair in existingRows) { pair.Value.SetActive(false); } existingRows.Recycle(); }
/// <summary> /// Updates a critter resource category header. /// </summary> /// <param name="header">The category header to update.</param> /// <param name="type">The critter type it contains (can be pulled from the CritterResourceInfo component).</param> public static void Update(this ResourceCategoryHeader header, CritterType type) { var totals = DictionaryPool <Tag, CritterTotals, ResourceCategoryHeader> .Allocate(); var all = CritterInventoryUtils.FindCreatures(totals, type); var discovered = header.ResourcesDiscovered; var trCategory = Traverse.Create(header); // Previously discovered but now extinct critters need an empty entry foreach (var pair in discovered) { var species = pair.Key; if (!totals.ContainsKey(species)) { totals.Add(species, new CritterTotals()); } } // Go through resource entries for each species and update them foreach (var pair in totals) { var quantity = pair.Value; var species = pair.Key; // Look up the species to see if we have found it already if (!discovered.TryGetValue(species, out ResourceEntry entry)) { entry = trCategory.CallMethod <ResourceEntry>("NewResourceEntry", species, GameUtil.MeasureUnit.quantity); entry.SetName(species.ProperName()); // Add component to tag it as wild/tame entry.gameObject.AddComponent <CritterResourceInfo>().CritterType = type; discovered.Add(species, entry); } UpdateEntry(entry, quantity); } bool anyDiscovered = discovered.Count > 0; // Enable display and open/close based on critter presence header.elements.QuantityText.SetText(all.Available.ToString()); trCategory.CallMethod("SetActiveColor", all.HasAny); trCategory.CallMethod("SetInteractable", anyDiscovered); // Still need to set this for expand/contract to work trCategory.SetField("anyDiscovered", anyDiscovered); // Update category tooltip var tooltip = trCategory.GetField <ToolTip>("tooltip"); if (tooltip != null) { tooltip.OnToolTip = null; tooltip.toolTip = CritterInventoryUtils.FormatTooltip(header.elements. LabelText.text, all); } // Disabled until coolazura's tags are up to date #if false FavoritesCategoryCompat(totals, type); #endif totals.Recycle(); }
/// <summary> /// Initializes and computes horizontal sizes for the components in this relative /// layout. /// </summary> /// <param name="children">The location to store information about these components.</param> /// <param name="all">The components to lay out.</param> /// <param name="constraints">The constraints defined for these components.</param> internal static void CalcX(this ICollection <RelativeLayoutResults> children, RectTransform all, IDictionary <GameObject, RelativeLayoutParams> constraints) { var comps = ListPool <Component, RelativeLayoutGroup> .Allocate(); var paramMap = DictionaryPool <GameObject, RelativeLayoutResults, RelativeLayoutGroup> .Allocate(); int n = all.childCount; children.Clear(); for (int i = 0; i < n; i++) { var child = all.GetChild(i)?.gameObject; if (child != null) { comps.Clear(); // Calculate the preferred size using all layout components child.GetComponents(comps); var horiz = PUIUtils.CalcSizes(child, PanelDirection.Horizontal, comps); if (!horiz.ignore) { RelativeLayoutResults ip; float w = horiz.preferred; if (constraints.TryGetValue(child, out RelativeLayoutParams cons)) { ip = new RelativeLayoutResults(child.rectTransform(), cons); // Set override size by axis if necessary var overrideSize = ip.OverrideSize; if (overrideSize.x > 0.0f) { w = overrideSize.x; } paramMap[child] = ip; } else { // Default its layout to fill all ip = new RelativeLayoutResults(child.rectTransform(), null); } ip.PreferredWidth = w; children.Add(ip); } } } // Resolve object references to other children foreach (var ip in children) { ip.TopParams = InitResolve(ip.TopEdge, paramMap); ip.BottomParams = InitResolve(ip.BottomEdge, paramMap); ip.LeftParams = InitResolve(ip.LeftEdge, paramMap); ip.RightParams = InitResolve(ip.RightEdge, paramMap); // All of these will die simultaneously when the list is recycled } paramMap.Recycle(); comps.Recycle(); }
/// <summary> /// Finds selectable objects for creating info cards. /// </summary> /// <param name="cell">The cell to search.</param> /// <param name="coords">The raw mouse coordinates.</param> /// <param name="hits">The location where all hits will be stored.</param> /// <param name="compareSet">The set of objects to compare.</param> private static void FindSelectables(int cell, Vector3 coords, List <KSelectable> hits, ISet <Component> compareSet) { var gsp = GameScenePartitioner.Instance; var seen = DictionaryPool <KSelectable, float, SelectTool> .Allocate(); var xy = new Vector2(coords.x, coords.y); // The override might already be there foreach (var obj in hits) { seen[obj] = -100.0f; } AddStatusIntersections(seen, xy); var entries = ListPool <ScenePartitionerEntry, SelectTool> .Allocate(); Grid.CellToXY(cell, out int x, out int y); gsp.GatherEntries(x, y, 1, 1, gsp.collisionLayer, entries); foreach (var entry in entries) { if (entry.obj is KCollider2D collider && collider.Intersects(xy)) { if (!collider.TryGetComponent(out KSelectable selectable)) { selectable = collider.GetComponentInParent <KSelectable>(); } if (selectable != null && selectable.IsSelectable) { float distance = selectable.transform.GetPosition().z - coords.z; if (seen.TryGetValue(selectable, out float oldDistance)) { seen[selectable] = Mathf.Min(oldDistance, distance); } else { seen.Add(selectable, distance); } } } } entries.Recycle(); hits.Clear(); foreach (var pair in seen) { var selectable = pair.Key; compareSet.Add(selectable); hits.Add(selectable); } seen.Recycle(); // Sort the hits; compares fewer objects at the expense of comparing a slightly // different behaviour (the original compares the collider) hits.Sort(COMPARE_SELECTABLES); }
/// <summary> /// Updates the critter resource category header. /// </summary> /// <param name="anyDiscovered">A reference to the anyDiscovered field in header.</param> internal void UpdateHeader(ref bool anyDiscovered) { var ci = ClusterManager.Instance.activeWorld.GetComponent <CritterInventory>(); if (ci != null) { var totals = DictionaryPool <Tag, CritterTotals, ResourceCategoryHeader> . Allocate(); var all = ci.PopulateTotals(CritterType, totals); var discovered = header.ResourcesDiscovered; // Previously discovered but now extinct critters need an empty entry foreach (var pair in discovered) { var species = pair.Key; if (!totals.ContainsKey(species)) { totals.Add(species, new CritterTotals()); } } // Go through resource entries for each species and update them foreach (var pair in totals) { var quantity = pair.Value; var species = pair.Key; // Look up the species to see if we have found it already if (!discovered.TryGetValue(species, out ResourceEntry entry)) { discovered.Add(species, entry = NewResourceEntry(header, species, CritterType)); } var cre = entry.GetComponent <CritterResourceEntry>(); if (cre != null) { cre.UpdateEntry(quantity); } } // Still need to set this for expand/contract to work anyDiscovered = discovered.Count > 0; // Enable display and open/close based on critter presence header.elements.QuantityText.SetText(all.Available.ToString()); SET_ACTIVE_COLOR.Invoke(header, all.HasAny); SET_INTERACTABLE.Invoke(header, anyDiscovered); // Update category tooltip var tooltip = header.GetComponent <ToolTip>(); if (tooltip != null) { tooltip.OnToolTip = OnAllTooltip; } totals.Recycle(); } }
/// <summary> /// Adds ElementConsumer range previews to the specified building def. /// </summary> /// <param name="def">The preview to add.</param> private static void AddConsumerPreview(BuildingDef def) { GameObject complete = def.BuildingComplete, preview = def.BuildingPreview, inBuild = def.BuildingUnderConstruction; var consumers = complete.GetComponents <ElementConsumer>(); int n = consumers.Length; var existing = DictionaryPool <CellOffset, int, ElementConsumer> .Allocate(); foreach (var consumer in consumers) { // Avoid stomping the range preview of Wall Vents and Pumps if (consumer.GetType().FullName != IGNORE_WALLPUMPS) { int radius = consumer.consumptionRadius & 0xFF; var sco = consumer.sampleCellOffset; var color = Color.white; var offset = new CellOffset(Mathf.RoundToInt(sco.x), Mathf.RoundToInt( sco.y)); // Make secondary consumers a color related to their element if (n > 1 && consumer.configuration == ElementConsumer.Configuration. Element) { var target = ElementLoader.FindElementByHash(consumer. elementToConsume); if (target != null) { color = target.substance.conduitColour; } } if (!existing.TryGetValue(offset, out int oldRad) || radius != oldRad) { PUtil.LogDebug("Visualizer added to {0}, range {1:D}".F(def.PrefabID, radius)); ElementConsumerVisualizer.Create(complete, offset, radius, color); // Consumer found, update the preview and under construction versions if (preview != null) { // Previews should always be white as other colors are hard to see // on overlays ElementConsumerVisualizer.Create(preview, offset, radius); } if (inBuild != null) { ElementConsumerVisualizer.Create(inBuild, offset, radius, color); } existing[offset] = radius; } } } existing.Recycle(); }
/// <summary> /// Applied before AddTechToQueue runs. /// </summary> internal static bool Prefix(List <TechInstance> ___queuedTech, Tech tech) { var dict = DictionaryPool <string, TechInstance, Research> .Allocate(); var techList = ListPool <TechInstance, Research> .Allocate(); AddTechToQueue(___queuedTech, dict, tech); // Sort by tech level and add at end techList.AddRange(dict.Values); techList.Sort(TechTierSorter.Instance); ___queuedTech.AddRange(techList); // Update display UpdateResearchOrder(___queuedTech); dict.Recycle(); techList.Recycle(); return(false); }
public override GameObject Build() { var panel = PUIElements.CreateUI(null, Name); var mapping = DictionaryPool <IUIComponent, GameObject, PRelativePanel> .Allocate(); SetImage(panel); // Realize each component and add them to the panel foreach (var pair in constraints) { var component = pair.Key; var realized = component.Build(); realized.SetParent(panel); // We were already guaranteed that there were no duplicate keys mapping[component] = realized; } // Add layout component var layout = panel.AddComponent <RelativeLayoutGroup>(); layout.Margin = Margin; foreach (var pair in constraints) { var realized = mapping[pair.Key]; var rawParams = pair.Value; var newParams = new RelativeLayoutParams(); // Copy all of the settings Resolve(newParams.TopEdge, rawParams.TopEdge, mapping); Resolve(newParams.BottomEdge, rawParams.BottomEdge, mapping); Resolve(newParams.LeftEdge, rawParams.LeftEdge, mapping); Resolve(newParams.RightEdge, rawParams.RightEdge, mapping); newParams.OverrideSize = rawParams.OverrideSize; newParams.Insets = rawParams.Insets; layout.SetRaw(realized, newParams); } if (!DynamicSize) { layout.LockLayout(); } mapping.Recycle(); // Set flex size layout.flexibleWidth = FlexSize.x; layout.flexibleHeight = FlexSize.y; InvokeRealize(panel); return(panel); }
/// <summary> /// Goes through the research screen and updates titles for techs in the queue. /// </summary> /// <param name="queuedTech">The current research queue.</param> private static void UpdateResearchOrder(IList <TechInstance> queuedTech) { var inst = ManagementMenu.Instance; var screen = (inst == null) ? null : RESEARCH_SCREEN.Get(inst); if (queuedTech == null) { throw new ArgumentNullException("queuedTech"); } int n = queuedTech.Count; if (screen != null && RESEARCH_NAME != null) { // O(N^2) sucks var techIndex = DictionaryPool <string, int, ResearchScreen> .Allocate(); for (int i = 0; i < n; i++) { techIndex.Add(queuedTech[i].tech.Id, i + 1); } LocText lt; foreach (var tech in Db.Get().Techs.resources) { var entry = screen.GetEntry(tech); // Update all techs with the order count if (entry != null && (lt = RESEARCH_NAME.Get(entry)) != null) { if (techIndex.TryGetValue(tech.Id, out int order)) { lt.SetText(string.Format(ResearchQueueStrings.QueueFormat, tech. Name, order)); } else { lt.SetText(tech.Name); } } } techIndex.Recycle(); } }
/// <summary> /// Goes through the research screen and updates titles for techs in the queue. /// </summary> /// <param name="queuedTech">The current research queue.</param> private static void UpdateResearchOrder(IList <TechInstance> queuedTech) { var screen = ManagementMenu.Instance?.researchScreen as ResearchScreen; if (queuedTech == null) { throw new ArgumentNullException("queuedTech"); } int n = queuedTech.Count; if (screen != null) { // O(N^2) sucks var techIndex = DictionaryPool <string, int, ResearchScreen> .Allocate(); for (int i = 0; i < n; i++) { techIndex.Add(queuedTech[i].tech.Id, i + 1); } foreach (var tech in Db.Get().Techs.resources) { var entry = screen.GetEntry(tech); // Update all techs with the order count if (entry != null) { var lt = Traverse.Create(entry).GetField <LocText>("researchName"); if (techIndex.TryGetValue(tech.Id, out int order)) { lt?.SetText(string.Format(ResearchQueueStrings.QueueFormat, tech. Name, order)); } else { lt?.SetText(tech.Name); } } } techIndex.Recycle(); } }
/// <summary> /// Updates the headings for the entire category. /// </summary> internal void UpdateContents() { var ci = ClusterManager.Instance.activeWorld.GetComponent <CritterInventory>(); if (ci != null) { var allTotals = DictionaryPool <Tag, CritterTotals, CritterResourceRowGroup> . Allocate(); var totals = ci.PopulateTotals(CritterType, allTotals); refs.GetReference <LocText>("AvailableLabel").SetText(GameUtil. GetFormattedSimple(totals.Available)); refs.GetReference <LocText>("TotalLabel").SetText(GameUtil. GetFormattedSimple(totals.Total)); refs.GetReference <LocText>("ReservedLabel").SetText(GameUtil. GetFormattedSimple(totals.Reserved)); foreach (var resource in resources) { resource.Value.UpdateContents(allTotals, ci); } allTotals.Recycle(); } }
/// <summary> /// Creates a ZIP file of the old mod contents merged with the saved configs at /// tempFilePath. /// </summary> /// <param name="copied">The number of configuration files backed up.</param> /// <returns>true if successful, or false if an I/O error occurred.</returns> public bool CreateMergedPackage(out int copied) { bool ok = false; var toCopy = HashSetPool <string, ConfigBackupUtility> .Allocate(); var toAdd = DictionaryPool <string, string, ConfigBackupUtility> .Allocate(); // Open old ZIP file copied = 0; try { using (var src = new ZipFile(oldFilePath)) { foreach (var entry in src) { // Klei normalizes the file names to forward slashes toCopy.Add(FileSystem.Normalize(entry.FileName)); } FindFilesToCopy(toCopy, toAdd); using (var dst = new ZipFile(tempFilePath)) { CopyFiles(src, dst, toCopy, toAdd); dst.Save(); copied = toAdd.Count; PUtil.LogDebug("Config backup for {0} copied {1:D} files".F(modFolder, copied)); } } ok = true; } catch (IOException e) { PUtil.LogWarning("Unable to backup mod configs:"); PUtil.LogExcWarn(e); } catch (UnauthorizedAccessException e) { PUtil.LogWarning("Unable to backup mod configs:"); PUtil.LogExcWarn(e); } toAdd.Recycle(); toCopy.Recycle(); return(ok); }
/// <summary> /// Creates new rows if necessary for each critter species, and sorts them by name. /// </summary> /// <param name="allResources">The parent window for the rows.</param> internal void SpawnRows(AllResourcesScreen allResources) { var ci = ClusterManager.Instance.activeWorld.GetComponent <CritterInventory>(); if (ci != null) { var allCritters = DictionaryPool <Tag, CritterTotals, CritterResourceRowGroup> . Allocate(); ci.PopulateTotals(CritterType, allCritters); bool dirty = false; // Insert new rows where necessary foreach (var pair in allCritters) { var species = pair.Key; if (!resources.ContainsKey(species)) { resources.Add(species, Create(allResources, species)); dirty = true; } } // Iterate and place in SORTED order in the UI if (dirty) { foreach (var resource in resources) { resource.Value.gameObject.transform.SetAsLastSibling(); } } allCritters.Recycle(); if (dirty) { UpdateContents(); } } }
private void UpdateOpenOrders() { ComplexRecipe[] recipes = GetRecipes(); if (recipes.Length != openOrderCounts.Count) { Debug.LogErrorFormat(base.gameObject, "Recipe count {0} doesn't match open order count {1}", recipes.Length, openOrderCounts.Count); } bool flag = false; hasOpenOrders = false; for (int i = 0; i < recipes.Length; i++) { ComplexRecipe recipe = recipes[i]; int recipePrefetchCount = GetRecipePrefetchCount(recipe); if (recipePrefetchCount > 0) { hasOpenOrders = true; } int num = openOrderCounts[i]; if (num != recipePrefetchCount) { if (recipePrefetchCount < num) { flag = true; } openOrderCounts[i] = recipePrefetchCount; } } DictionaryPool <Tag, float, ComplexFabricator> .PooledDictionary pooledDictionary = DictionaryPool <Tag, float, ComplexFabricator> .Allocate(); DictionaryPool <Tag, float, ComplexFabricator> .PooledDictionary pooledDictionary2 = DictionaryPool <Tag, float, ComplexFabricator> .Allocate(); for (int j = 0; j < openOrderCounts.Count; j++) { int num2 = openOrderCounts[j]; if (num2 > 0) { ComplexRecipe complexRecipe = recipe_list[j]; ComplexRecipe.RecipeElement[] ingredients = complexRecipe.ingredients; ComplexRecipe.RecipeElement[] array = ingredients; foreach (ComplexRecipe.RecipeElement recipeElement in array) { pooledDictionary[recipeElement.material] = inStorage.GetAmountAvailable(recipeElement.material); } } } for (int l = 0; l < recipe_list.Length; l++) { int num3 = openOrderCounts[l]; if (num3 > 0) { ComplexRecipe complexRecipe2 = recipe_list[l]; ComplexRecipe.RecipeElement[] ingredients2 = complexRecipe2.ingredients; ComplexRecipe.RecipeElement[] array2 = ingredients2; foreach (ComplexRecipe.RecipeElement recipeElement2 in array2) { float num4 = recipeElement2.amount * (float)num3; float num5 = num4 - pooledDictionary[recipeElement2.material]; if (num5 > 0f) { pooledDictionary2.TryGetValue(recipeElement2.material, out float value); pooledDictionary2[recipeElement2.material] = value + num5; pooledDictionary[recipeElement2.material] = 0f; } else { DictionaryPool <Tag, float, ComplexFabricator> .PooledDictionary pooledDictionary3; Tag material; (pooledDictionary3 = pooledDictionary)[material = recipeElement2.material] = pooledDictionary3[material] - num4; } } } } if (flag) { CancelFetches(); } if (pooledDictionary2.Count > 0) { UpdateFetches(pooledDictionary2); } UpdateMaterialNeeds(pooledDictionary2); pooledDictionary2.Recycle(); pooledDictionary.Recycle(); }
/// <summary> /// The better and more optimized UpdatePickups. Aggregate runtime on a test world /// dropped from ~60 ms/1000 ms to ~45 ms/1000 ms. /// </summary> private static void UpdatePickups(FetchManager.FetchablesByPrefabId targets, Navigator navigator, GameObject worker) { var canBePickedUp = DictionaryPool <PickupTagKey, FetchManager.Pickup, FetchManager> .Allocate(); var pathCosts = DictionaryPool <int, int, FetchManager> .Allocate(); var finalPickups = targets.finalPickups; // Will reflect the changes from Waste Not, Want Not and No Manual Delivery var comparer = PICKUP_COMPARER.Get(null); foreach (var fetchable in targets.fetchables.GetDataList()) { var target = fetchable.pickupable; int cell = target.cachedCell; if (target.CouldBePickedUpByMinion(worker)) { // Look for cell cost, share costs across multiple queries to a cell if (!pathCosts.TryGetValue(cell, out int cost)) { pathCosts.Add(cell, cost = target.GetNavigationCost(navigator, cell)); } // Exclude unreachable items if (cost >= 0) { int hash = fetchable.tagBitsHash; var key = new PickupTagKey(hash, target.KPrefabID); var candidate = new FetchManager.Pickup { pickupable = target, tagBitsHash = hash, PathCost = (ushort)cost, masterPriority = fetchable.masterPriority, freshness = fetchable. freshness, foodQuality = fetchable.foodQuality }; if (canBePickedUp.TryGetValue(key, out FetchManager.Pickup current)) { // Is the new one better? int result = comparer.Compare(current, candidate); if (result > 0 || (result == 0 && candidate.pickupable. UnreservedAmount > current.pickupable.UnreservedAmount)) { canBePickedUp[key] = candidate; } } else { canBePickedUp.Add(key, candidate); } } } } // Copy the remaining pickups to the list, there are now way fewer because only // one was kept per possible tag bits (with the highest priority, best path cost, // etc) finalPickups.Clear(); foreach (var pair in canBePickedUp) { finalPickups.Add(pair.Value); } pathCosts.Recycle(); canBePickedUp.Recycle(); }
public void SendMetricsEvent() { ListPool <string, Manager> .PooledList pooledList = ListPool <string, Manager> .Allocate(); foreach (Mod mod in mods) { if (mod.enabled) { pooledList.Add(mod.title); } } DictionaryPool <string, object, Manager> .PooledDictionary pooledDictionary = DictionaryPool <string, object, Manager> .Allocate(); pooledDictionary["ModCount"] = pooledList.Count; pooledDictionary["Mods"] = pooledList; ThreadedHttps <KleiMetrics> .Instance.SendEvent(pooledDictionary); pooledDictionary.Recycle(); pooledList.Recycle(); KCrashReporter.haveActiveMods = (pooledList.Count > 0); }
public void Render1000ms(float dt) { DictionaryPool <int, ListPool <FetchList2, FetchListStatusItemUpdater> .PooledList, FetchListStatusItemUpdater> .PooledDictionary pooledDictionary = DictionaryPool <int, ListPool <FetchList2, FetchListStatusItemUpdater> .PooledList, FetchListStatusItemUpdater> .Allocate(); foreach (FetchList2 fetchList in fetchLists) { if (!((Object)fetchList.Destination == (Object)null)) { ListPool <FetchList2, FetchListStatusItemUpdater> .PooledList value = null; int instanceID = fetchList.Destination.GetInstanceID(); if (!pooledDictionary.TryGetValue(instanceID, out value)) { value = (pooledDictionary[instanceID] = ListPool <FetchList2, FetchListStatusItemUpdater> .Allocate()); } value.Add(fetchList); } } DictionaryPool <Tag, float, FetchListStatusItemUpdater> .PooledDictionary pooledDictionary2 = DictionaryPool <Tag, float, FetchListStatusItemUpdater> .Allocate(); DictionaryPool <Tag, float, FetchListStatusItemUpdater> .PooledDictionary pooledDictionary3 = DictionaryPool <Tag, float, FetchListStatusItemUpdater> .Allocate(); foreach (KeyValuePair <int, ListPool <FetchList2, FetchListStatusItemUpdater> .PooledList> item in pooledDictionary) { ListPool <Tag, FetchListStatusItemUpdater> .PooledList pooledList2 = ListPool <Tag, FetchListStatusItemUpdater> .Allocate(); Storage destination = item.Value[0].Destination; foreach (FetchList2 item2 in item.Value) { item2.UpdateRemaining(); Dictionary <Tag, float> remaining = item2.GetRemaining(); foreach (KeyValuePair <Tag, float> item3 in remaining) { if (!pooledList2.Contains(item3.Key)) { pooledList2.Add(item3.Key); } } } ListPool <Pickupable, FetchListStatusItemUpdater> .PooledList pooledList3 = ListPool <Pickupable, FetchListStatusItemUpdater> .Allocate(); foreach (GameObject item4 in destination.items) { if (!((Object)item4 == (Object)null)) { Pickupable component = item4.GetComponent <Pickupable>(); if (!((Object)component == (Object)null)) { pooledList3.Add(component); } } } DictionaryPool <Tag, float, FetchListStatusItemUpdater> .PooledDictionary pooledDictionary4 = DictionaryPool <Tag, float, FetchListStatusItemUpdater> .Allocate(); foreach (Tag item5 in pooledList2) { float num = 0f; foreach (Pickupable item6 in pooledList3) { if (item6.KPrefabID.HasTag(item5)) { num += item6.TotalAmount; } } pooledDictionary4[item5] = num; } foreach (Tag item7 in pooledList2) { if (!pooledDictionary2.ContainsKey(item7)) { pooledDictionary2[item7] = WorldInventory.Instance.GetTotalAmount(item7); } if (!pooledDictionary3.ContainsKey(item7)) { pooledDictionary3[item7] = WorldInventory.Instance.GetAmount(item7); } } foreach (FetchList2 item8 in item.Value) { bool should_add = false; bool should_add2 = true; bool should_add3 = false; Dictionary <Tag, float> remaining2 = item8.GetRemaining(); foreach (KeyValuePair <Tag, float> item9 in remaining2) { Tag key = item9.Key; float value2 = item9.Value; float num2 = pooledDictionary4[key]; float b = pooledDictionary2[key]; float num3 = pooledDictionary3[key]; float num4 = Mathf.Min(value2, b); float num5 = num3 + num4; float minimumAmount = item8.GetMinimumAmount(key); if (num2 + num5 < minimumAmount) { should_add = true; } if (num5 < value2) { should_add2 = false; } if (num2 + num5 > value2 && value2 > num5) { should_add3 = true; } } item8.UpdateStatusItem(Db.Get().BuildingStatusItems.WaitingForMaterials, ref item8.waitingForMaterialsHandle, should_add2); item8.UpdateStatusItem(Db.Get().BuildingStatusItems.MaterialsUnavailable, ref item8.materialsUnavailableHandle, should_add); item8.UpdateStatusItem(Db.Get().BuildingStatusItems.MaterialsUnavailableForRefill, ref item8.materialsUnavailableForRefillHandle, should_add3); } pooledDictionary4.Recycle(); pooledList3.Recycle(); pooledList2.Recycle(); item.Value.Recycle(); } pooledDictionary3.Recycle(); pooledDictionary2.Recycle(); pooledDictionary.Recycle(); }
/// <summary> /// Applies decor values from the database. /// </summary> internal static void ApplyDatabase(DecorReimaginedOptions options) { DecorDbEntry[] entries = null; try { // Read in database from the embedded config json using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream( "PeterHan.DecorRework.buildings.json")) { var jr = new JsonTextReader(new StreamReader(stream)); entries = new JsonSerializer { MaxDepth = 2 }.Deserialize <DecorDbEntry[]>(jr); jr.Close(); } } catch (JsonException e) { // Error when loading decor PUtil.LogExcWarn(e); } catch (IOException e) { // Error when loading decor PUtil.LogExcWarn(e); } if (entries != null) { var editDecor = DictionaryPool <string, DecorDbEntry, DecorDbEntry> .Allocate(); string id; // Add to dictionary, way faster foreach (var entry in entries) { if (!string.IsNullOrEmpty(id = entry.id) && !editDecor.ContainsKey(id)) { editDecor.Add(id, entry); } } foreach (var def in Assets.BuildingDefs) { if (editDecor.TryGetValue(id = def.PrefabID, out DecorDbEntry entry)) { float decor = entry.decor; int radius = entry.radius; var provider = def.BuildingComplete.GetComponent <DecorProvider>(); // For reference, these do not alter the BuildingComplete def.BaseDecor = decor; def.BaseDecorRadius = radius; // Actual decor provider if (provider != null) { PUtil.LogDebug("Patched: {0} Decor: {1:F1} Radius: {2:D}".F(id, decor, radius)); provider.baseDecor = decor; provider.baseRadius = radius; } } } editDecor.Recycle(); } // Patch in the debris decor var baseOreTemplate = Traverse.Create(typeof(EntityTemplates)).GetField < GameObject>("baseOreTemplate"); DecorProvider component; if (baseOreTemplate != null && (component = baseOreTemplate. GetComponent <DecorProvider>()) != null) { component.baseDecor = options.DebrisDecor; component.baseRadius = Math.Max(1, options.DebrisRadius); } // Patch the suits PUtil.LogDebug("Snazzy Suit: {0:D} Warm/Cool Vest: {1:D}".F(options. SnazzySuitDecor, options.VestDecor)); ClothingWearer.ClothingInfo.FANCY_CLOTHING.decorMod = options.SnazzySuitDecor; ClothingWearer.ClothingInfo.COOL_CLOTHING.decorMod = options.VestDecor; ClothingWearer.ClothingInfo.WARM_CLOTHING.decorMod = options.VestDecor; }
/// <summary> /// Applied before UpdatePickups runs. A more optimized UpdatePickups whose aggregate /// runtime on a test world dropped from ~60 ms/1000 ms to ~45 ms/1000 ms. /// </summary> internal static bool BeforeUpdatePickups(FetchManager.FetchablesByPrefabId __instance, Navigator worker_navigator, GameObject worker_go) { var canBePickedUp = Allocate(); var pathCosts = DictionaryPool <int, int, FetchManager> .Allocate(); var finalPickups = __instance.finalPickups; // Will reflect the changes from Waste Not, Want Not and No Manual Delivery var comparer = FetchManager.ComparerIncludingPriority; bool needThreadSafe = FastTrackOptions.Instance.PickupOpts; var fetchables = __instance.fetchables.GetDataList(); int n = fetchables.Count; for (int i = 0; i < n; i++) { var fetchable = fetchables[i]; var target = fetchable.pickupable; int cell = target.cachedCell; if (target.CouldBePickedUpByMinion(worker_go)) { // Look for cell cost, share costs across multiple queries to a cell // If this is being run synchronous, no issue, otherwise the GSP patch will // avoid races on the scene partitioner if (!pathCosts.TryGetValue(cell, out int cost)) { if (needThreadSafe) { worker_navigator.GetNavigationCostNU(target, cell, out cost); } else { cost = worker_navigator.GetNavigationCost(target); } pathCosts.Add(cell, cost); } // Exclude unreachable items if (cost >= 0) { int hash = fetchable.tagBitsHash; var key = new PickupTagKey(hash, target.KPrefabID); var candidate = new FetchManager.Pickup { pickupable = target, tagBitsHash = hash, PathCost = (ushort)cost, masterPriority = fetchable.masterPriority, freshness = fetchable. freshness, foodQuality = fetchable.foodQuality }; if (canBePickedUp.TryGetValue(key, out FetchManager.Pickup current)) { // Is the new one better? int result = comparer.Compare(candidate, current); if (result < 0 || (result == 0 && candidate.pickupable. UnreservedAmount > current.pickupable.UnreservedAmount)) { canBePickedUp[key] = candidate; } } else { canBePickedUp.Add(key, candidate); } } } } // Copy the remaining pickups to the list, there are now way fewer because only // one was kept per possible tag bits (with the highest priority, best path cost, // etc) finalPickups.Clear(); foreach (var pickup in canBePickedUp.Values) { finalPickups.Add(pickup); } pathCosts.Recycle(); Recycle(canBePickedUp); // Prevent the original method from running return(false); }