Пример #1
0
        /// <summary>
        /// Replaces a prop using a building replacement.
        /// </summary>
        /// <param name="buildingElement">Building replacement element to apply</param>
        /// <param name="propReference">Individual prop reference to apply to</param>
        internal void ReplaceProp(BOBBuildingReplacement buildingElement, BuildingPropReference propReference)
        {
            // Convert offset to Vector3.
            Vector3 offset = new Vector3
            {
                x = buildingElement.offsetX,
                y = buildingElement.offsetY,
                z = buildingElement.offsetZ
            };

            // Apply replacement.
            if (buildingElement.replacementInfo is PropInfo propInfo)
            {
                propReference.building.m_props[propReference.propIndex].m_finalProp = propInfo;
            }
            else
            {
                propReference.building.m_props[propReference.propIndex].m_finalTree = (TreeInfo)buildingElement.replacementInfo;
            }

            // Angle and offset.
            propReference.building.m_props[propReference.propIndex].m_radAngle = propReference.radAngle + ((buildingElement.angle * Mathf.PI) / 180f);
            propReference.building.m_props[propReference.propIndex].m_position = propReference.postion + offset;

            // Probability.
            propReference.building.m_props[propReference.propIndex].m_probability = buildingElement.probability;

            // Add building to dirty list.
            BuildingData.DirtyList.Add(propReference.building);
        }
Пример #2
0
        /// <summary>
        /// Reverts a replacement.
        /// </summary>
        /// <param name="replacement">Replacement record to revert</param>
        /// <param name="removeEntries">True to remove the reverted entries from the list of replacements, false to leave the list unchanged</param>
        /// <returns>True if the entire building record was removed from the list (due to no remaining replacements for that prefab), false if the prefab remains in the list (has other active replacements)</returns>
        protected virtual bool Revert(BOBBuildingReplacement replacement, bool removeEntries)
        {
            // Safety check for calls without any current replacement.
            if (replacement?.targetInfo == null || replacement.references == null)
            {
                return(false);
            }

            if (replacement.references != null)
            {
                // Revert all entries in list.
                RevertReferences(replacement.targetInfo, replacement.references);

                // Remove replacement entry from list of replacements, if we're doing so.
                if (removeEntries)
                {
                    // Remove from replacement list.
                    ReplacementList(replacement.BuildingInfo).Remove(replacement);

                    // See if we've got a parent building element record, and if so, if it has any remaining replacement entries.
                    BOBBuildingElement thisElement = BuildingElement(replacement.BuildingInfo);
                    if (thisElement != null && (thisElement.replacements == null || thisElement.replacements.Count == 0))
                    {
                        // No replacement entries left - delete entire building entry and return true to indicate that we've done so.
                        BuildingElementList.Remove(thisElement);
                        return(true);
                    }
                }
            }

            // If we got here, we didn't remove any building entries from the list; return false.
            return(false);
        }
Пример #3
0
        /// <summary>
        /// Checks if there's a currently active building replacement applied to the given building prop index, and if so, returns the replacement record.
        /// </summary>
        /// <param name="buildingPrefab">Building prefab to check</param>
        /// <param name="propIndex">Prop index to check</param>
        /// <returns>Replacement record if a building replacement is currently applied, null if no building replacement is currently applied</returns>
        internal BOBBuildingReplacement ActiveReplacement(BuildingInfo buildingPrefab, int propIndex)
        {
            // Safety check.
            if (buildingPrefab != null && replacements.ContainsKey(buildingPrefab))
            {
                // Iterate through each entry in master dictionary.
                foreach (PrefabInfo target in replacements[buildingPrefab].Keys)
                {
                    BOBBuildingReplacement reference = replacements[buildingPrefab][target];
                    // Iterate through each building reference in this entry.
                    foreach (BuildingPropReference propRef in reference.references)
                    {
                        // Check for a building and prop index match.
                        if (propRef.building == buildingPrefab && propRef.propIndex == propIndex)
                        {
                            // Match!  Return the original prefab.
                            return(replacements[buildingPrefab][target]);
                        }
                    }
                }
            }

            // If we got here, no entry was found - return null to indicate no active replacement.
            return(null);
        }
Пример #4
0
        /// <summary>
        /// Applies a replacement.
        /// </summary>
        /// <param name="replacement">Replacement record to apply</param>
        protected override void ApplyReplacement(BOBBuildingReplacement replacement)
        {
            // Don't do anything if prefabs can't be found, or if building prefab has no prop array.
            if (replacement?.targetInfo == null || replacement.replacementInfo == null || replacement.BuildingInfo?.m_props == null)
            {
                return;
            }

            // (Re)set replacement list.
            replacement.references = new List <BuildingPropReference>();

            // Iterate through each prop in building.
            for (int propIndex = 0; propIndex < replacement.BuildingInfo.m_props.Length; ++propIndex)
            {
                // Check for any currently active individual building prop replacement.
                if (IndividualBuildingReplacement.Instance.ActiveReplacement(replacement.BuildingInfo, propIndex) != null)
                {
                    // Active individual building prop replacement; skip this one.
                    continue;
                }

                // Local reference.
                BuildingInfo.Prop thisBuildingProp = replacement.BuildingInfo.m_props[propIndex];

                // Check for any existing all-building replacement.
                PrefabInfo thisProp = AllBuildingReplacement.Instance.ActiveReplacement(replacement.BuildingInfo, propIndex)?.targetInfo;
                if (thisProp == null)
                {
                    // No active replacement; use current PropInfo.
                    if (replacement.targetInfo is PropInfo)
                    {
                        thisProp = thisBuildingProp.m_finalProp;
                    }
                    else
                    {
                        thisProp = thisBuildingProp.m_finalTree;
                    }
                }

                // See if this prop matches our replacement.
                if (thisProp != null && thisProp == replacement.targetInfo)
                {
                    // Match!  Add reference data to the list.
                    replacement.references.Add(CreateReference(replacement.BuildingInfo, thisProp, propIndex, replacement.isTree));
                }
            }

            // Now, iterate through each entry found.
            foreach (BuildingPropReference propReference in replacement.references)
            {
                // Reset any all-building replacements first.
                AllBuildingReplacement.Instance.RemoveEntry(propReference.buildingInfo, replacement.targetInfo, propReference.propIndex);

                // Apply the replacement.
                ReplaceProp(replacement, propReference);
            }
        }
Пример #5
0
        /// <summary>
        /// Applies a replacement.
        /// </summary>
        /// <param name="replacement">Replacement record to apply</param>
        protected override void ApplyReplacement(BOBBuildingReplacement replacement)
        {
            // Don't do anything if prefabs can't be found.
            if (replacement?.targetInfo == null || replacement.replacementInfo == null)
            {
                return;
            }

            // Create new reference list.
            List <BuildingPropReference> referenceList = new List <BuildingPropReference>();

            // Iterate through each loaded building and record props to be replaced.
            for (int i = 0; i < PrefabCollection <BuildingInfo> .LoadedCount(); ++i)
            {
                // Get local reference.
                BuildingInfo buildingInfo = PrefabCollection <BuildingInfo> .GetLoaded((uint)i);

                // If no props in this building, skip it and go to the next one.
                if (buildingInfo?.m_props == null)
                {
                    continue;
                }

                // Iterate through each prop in building.
                for (int propIndex = 0; propIndex < buildingInfo.m_props.Length; ++propIndex)
                {
                    // Check for any currently active building or individual building prop replacement.
                    if (BuildingReplacement.Instance.ActiveReplacement(buildingInfo, propIndex) != null || IndividualBuildingReplacement.Instance.ActiveReplacement(buildingInfo, propIndex) != null)
                    {
                        // Active building replacement; skip this one.
                        continue;
                    }

                    // Get this prop from building.
                    PrefabInfo thisProp = replacement.isTree ? (PrefabInfo)buildingInfo.m_props[propIndex].m_finalTree : (PrefabInfo)buildingInfo.m_props[propIndex].m_finalProp;

                    // See if this prop matches our replacement.
                    if (thisProp != null && thisProp == replacement.targetInfo)
                    {
                        // Match!  Add reference data to the list.
                        referenceList.Add(CreateReference(buildingInfo, thisProp, propIndex, replacement.isTree));
                    }
                }
            }

            // Now, iterate through each entry found (if any) and apply the replacement to each one.
            foreach (BuildingPropReference propReference in referenceList)
            {
                // Add entry to dictionary.
                AddReference(replacement, propReference);

                // Apply the replacement.
                ReplaceProp(replacement, propReference);
            }
        }
Пример #6
0
        /// <summary>
        /// Reverts a replacement.
        /// </summary>
        /// <param name="replacement">Replacement record to revert</param>
        /// <param name="removeEntries">True to remove the reverted entries from the list of replacements, false to leave the list unchanged</param>
        /// <returns>Always false (all-building entries never remove parent network elements)</returns>
        protected override bool Revert(BOBBuildingReplacement replacement, bool removeEntries)
        {
            // Safety check for calls without any current replacement.
            if (replacement?.targetInfo == null)
            {
                return(false);
            }

            // List of prefabs where references need to be removed.
            List <BuildingInfo> removeList = new List <BuildingInfo>();

            // Iterate through each entry in prop reference dictionary.
            foreach (KeyValuePair <BuildingInfo, Dictionary <PrefabInfo, List <BuildingPropReference> > > keyPair in propReferences)
            {
                // Attempt to get a replacement list for this entry.
                if (keyPair.Value.TryGetValue(replacement.targetInfo, out List <BuildingPropReference> referenceList))
                {
                    // Got a list - revert all entries.
                    RevertReferences(replacement.targetInfo, referenceList);

                    // Add dictionary to list to be cleared.
                    removeList.Add(keyPair.Key);
                }
            }

            // Remove references from dictionary.
            foreach (BuildingInfo removeEntry in removeList)
            {
                if (propReferences.ContainsKey(removeEntry))
                {
                    if (propReferences[removeEntry].ContainsKey(replacement.targetInfo))
                    {
                        // Remove target info entries from this building entry.
                        propReferences[removeEntry].Remove(replacement.targetInfo);
                    }

                    // If no entries left for this building, remove entire building entry.
                    if (propReferences[removeEntry].Count == 0)
                    {
                        propReferences.Remove(removeEntry);
                    }
                }
            }

            // Remove replacement entry from list of replacements, if we're doing so.
            if (removeEntries)
            {
                // Remove from replacement list.
                ReplacementList(replacement.BuildingInfo).Remove(replacement);
            }

            // If we got here, we didn't remove any network entries from the list; return false.
            return(false);
        }
Пример #7
0
        /// <summary>
        /// Updates the target item record for changes in replacement status (e.g. after applying or reverting changes).
        /// </summary>
        /// <param name="propListItem">Target item</param>
        protected override void UpdateTargetItem(PropListItem propListItem)
        {
            // Determine index to test - if no individaul index, just grab first one from list.
            int propIndex = propListItem.index;

            if (propIndex < 0)
            {
                propIndex = propListItem.indexes[0];
            }

            // All-building replacement and original probability (if any).
            BOBBuildingReplacement allBuildingReplacement = AllBuildingReplacement.instance.ActiveReplacement(currentBuilding, propIndex);

            if (allBuildingReplacement != null)
            {
                propListItem.allPrefab = allBuildingReplacement.replacementInfo;
                propListItem.allProb   = allBuildingReplacement.probability;
            }
            else
            {
                // If no active current record, ensure that it's reset to null.
                propListItem.allPrefab = null;
            }

            // Building replacement and original probability (if any).
            BOBBuildingReplacement buildingReplacement = BuildingReplacement.instance.ActiveReplacement(currentBuilding, propIndex);

            if (buildingReplacement != null)
            {
                propListItem.replacementPrefab = buildingReplacement.replacementInfo;
                propListItem.replacementProb   = buildingReplacement.probability;
            }
            else
            {
                // If no active current record, ensure that it's reset to null.
                propListItem.replacementPrefab = null;
            }

            // Individual replacement and original probability (if any).
            BOBBuildingReplacement individualReplacement = IndividualReplacement.instance.ActiveReplacement(currentBuilding, propIndex);

            if (individualReplacement != null)
            {
                propListItem.individualPrefab = individualReplacement.replacementInfo;
                propListItem.individualProb   = individualReplacement.probability;
            }
            else
            {
                // If no active current record, ensure that it's reset to null.
                propListItem.individualPrefab = null;
            }
        }
Пример #8
0
        /// <summary>
        /// Adds the given prop reference to the record for the given replacement.
        /// </summary>
        /// <param name="replacement">Replacement reference</param>
        /// <param name="propReference">Pop reference to store</param>
        protected override void AddReference(BOBBuildingReplacement replacement, BuildingPropReference propReference)
        {
            // Check to see if we've got an entry for this target prefab in our dictionary, and if not, create one.
            if (!propReferences.ContainsKey(propReference.buildingInfo))
            {
                propReferences.Add(propReference.buildingInfo, new Dictionary <PrefabInfo, List <BuildingPropReference> >());
            }

            // Check to see if we've got an entry for this network prefab in our dictionary entry for this target prefab, and if not, create one.
            if (!propReferences[propReference.buildingInfo].ContainsKey(replacement.targetInfo))
            {
                propReferences[propReference.buildingInfo].Add(replacement.targetInfo, new List <BuildingPropReference>());
            }

            // Add this prop reference to the dictioanry.
            propReferences[propReference.buildingInfo][replacement.targetInfo].Add(propReference);
        }
Пример #9
0
        /// <summary>
        /// Applies a new (or updated) replacement.
        /// </summary>
        /// <param name="buildingInfo">Targeted building prefab</param>
        /// <param name="targetInfo">Targeted (original) prop prefab</param>
        /// <param name="replacementInfo">Replacment prop prefab</param>
        /// <param name="propIndex">Prop index to apply replacement to (ignored)</param>
        /// <param name="angle">Replacment prop angle adjustment</param>
        /// <param name="offsetX">Replacment X position offset</param>
        /// <param name="offsetY">Replacment Y position offset</param>
        /// <param name="offsetZ">Replacment Z position offset</param>
        /// <param name="probability">Replacement probability</param>
        internal void Replace(BuildingInfo buildingInfo, PrefabInfo targetInfo, PrefabInfo replacementInfo, int propIndex, float angle, float offsetX, float offsetY, float offsetZ, int probability)
        {
            // Make sure that target and replacement are the same type before doing anything.
            if (targetInfo?.name == null || replacementInfo?.name == null || (targetInfo is TreeInfo && !(replacementInfo is TreeInfo)) || (targetInfo is PropInfo) && !(replacementInfo is PropInfo))
            {
                return;
            }

            // Revert any current replacement entry for this prop.
            Revert(buildingInfo, targetInfo, propIndex, true);

            // Get configuration file building list entry.
            List <BOBBuildingReplacement> replacementsList = ReplacementEntry(buildingInfo);

            // Get current replacement after reversion above.
            BOBBuildingReplacement thisReplacement = EligibileReplacement(buildingInfo, targetInfo, propIndex);

            // Create new replacement list entry if none already exists.
            if (thisReplacement == null)
            {
                thisReplacement = new BOBBuildingReplacement
                {
                    parentInfo = buildingInfo,
                    target     = targetInfo.name,
                    targetInfo = targetInfo,
                    propIndex  = propIndex
                };
                replacementsList.Add(thisReplacement);
            }

            // Add/replace replacement data.
            thisReplacement.isTree      = targetInfo is TreeInfo;
            thisReplacement.angle       = angle;
            thisReplacement.offsetX     = offsetX;
            thisReplacement.offsetY     = offsetY;
            thisReplacement.offsetZ     = offsetZ;
            thisReplacement.probability = probability;

            // Record replacement prop.
            thisReplacement.replacementInfo = replacementInfo;
            thisReplacement.Replacement     = replacementInfo.name;

            // Apply replacement.
            ApplyReplacement(thisReplacement);
        }
Пример #10
0
        /// <summary>
        /// Restores a replacement, if any (e.g. after a higher-priority replacement has been reverted).
        /// </summary>
        /// <param name="buildingInfo">Building prefab</param>
        /// <param name="targetInfo">Target prop info</param>
        /// <param name="propIndex">Prop index</param>
        /// <returns>True if a restoration was made, false otherwise</returns>
        internal bool Restore(BuildingInfo buildingInfo, PrefabInfo targetInfo, int propIndex)
        {
            // See if we have a relevant replacement record.
            BOBBuildingReplacement thisReplacement = EligibileReplacement(buildingInfo, targetInfo, propIndex);

            if (thisReplacement != null)
            {
                // Yes - add reference data to the list.
                BuildingPropReference newReference = CreateReference(buildingInfo, targetInfo, propIndex, thisReplacement.isTree);
                AddReference(thisReplacement, newReference);

                // Apply replacement and return true to indicate restoration.
                ReplaceProp(thisReplacement, newReference);

                return(true);
            }

            // If we got here, no restoration was made.
            return(false);
        }
Пример #11
0
        /// <summary>
        /// Checks if there's a currently active all-building replacement applied to the given building prop index, and if so, returns the *original* prefab..
        /// </summary>
        /// <param name="buildingPrefab">Building prefab to check</param>
        /// <param name="propIndex">Prop index to check</param>
        /// <returns>Replacement record if an all-building replacement is currently applied, null if no all-building replacement is currently applied</returns>
        internal PrefabInfo GetOriginal(BuildingInfo buildingPrefab, int propIndex)
        {
            // Iterate through each entry in master dictionary.
            foreach (PrefabInfo target in replacements.Keys)
            {
                BOBBuildingReplacement reference = replacements[target];
                // Iterate through each reference in this entry.
                foreach (BuildingPropReference propRef in reference.references)
                {
                    // Check for a building and prop index match.
                    if (propRef.building == buildingPrefab && propRef.propIndex == propIndex)
                    {
                        // Match!  Return the original prefab.
                        return(target);
                    }
                }
            }

            // If we got here, no entry was found - return null to indicate no active replacement.
            return(null);
        }
Пример #12
0
        /// <summary>
        /// Replaces a prop, using a building replacement.
        /// </summary>
        /// <param name="replacement">Building replacement element to apply</param>
        /// <param name="propReference">Individual prop reference to apply to</param>
        protected void ReplaceProp(BOBBuildingReplacement replacement, BuildingPropReference propReference)
        {
            // Convert offset to Vector3.
            Vector3 offset = new Vector3
            {
                x = replacement.offsetX,
                y = replacement.offsetY,
                z = replacement.offsetZ
            };

            // Apply replacement.
            if (replacement.replacementInfo is PropInfo propInfo)
            {
                propReference.buildingInfo.m_props[propReference.propIndex].m_prop        = propInfo;
                propReference.buildingInfo.m_props[propReference.propIndex].m_fixedHeight = !(propInfo.m_requireHeightMap || propInfo.m_requireWaterMap);
            }
            else if (replacement.replacementInfo is TreeInfo treeInfo)
            {
                propReference.buildingInfo.m_props[propReference.propIndex].m_tree        = treeInfo;
                propReference.buildingInfo.m_props[propReference.propIndex].m_fixedHeight = true;
            }
            else
            {
                Logging.Error("invalid replacement ", replacement.replacementInfo?.name ?? "null", " passed to BuildingInfo.ReplaceProp");
                return;
            }

            // Angle and offset.
            propReference.buildingInfo.m_props[propReference.propIndex].m_radAngle = propReference.radAngle + ((replacement.angle * Mathf.PI) / 180f);
            propReference.buildingInfo.m_props[propReference.propIndex].m_position = propReference.position + offset;

            // Probability.
            propReference.buildingInfo.m_props[propReference.propIndex].m_probability = replacement.probability;

            // Update building prop references.
            propReference.buildingInfo.CheckReferences();

            // Add building to dirty list.
            BuildingData.DirtyList.Add(propReference.buildingInfo);
        }
Пример #13
0
        /// <summary>
        /// Applies a replacement.
        /// </summary>
        /// <param name="replacement">Replacement record to apply</param>
        protected override void ApplyReplacement(BOBBuildingReplacement replacement)
        {
            // Don't do anything if prefabs can't be found.
            if (replacement?.targetInfo == null || replacement.replacementInfo == null || replacement.BuildingInfo == null)
            {
                return;
            }

            // Check index bounds.
            if (replacement.BuildingInfo.m_props == null || replacement.propIndex >= replacement.BuildingInfo.m_props.Length)
            {
                Logging.Message("ignoring invalid individual building replacement index ", replacement.propIndex, " for building ", replacement.BuildingInfo.name);
                return;
            }

            // Check prop index.
            BuildingInfo.Prop thisProp = replacement.BuildingInfo.m_props[replacement.propIndex];
            if (thisProp == null)
            {
                return;
            }

            // Reset any building or all-building replacements first.
            BuildingReplacement.Instance.RemoveEntry(replacement.BuildingInfo, replacement.targetInfo, replacement.propIndex);
            AllBuildingReplacement.Instance.RemoveEntry(replacement.BuildingInfo, replacement.targetInfo, replacement.propIndex);

            // Create replacment entry.
            BuildingPropReference newPropReference = CreateReference(replacement.BuildingInfo, replacement.targetInfo, replacement.propIndex, replacement.isTree);

            // Reset replacement list to be only our new replacement entry.
            replacement.references = new List <BuildingPropReference> {
                newPropReference
            };

            // Apply the replacement.
            ReplaceProp(replacement, newPropReference);
        }
Пример #14
0
 /// <summary>
 /// Adds the given prop reference to the record for the given replacement.
 /// </summary>
 /// <param name="replacement">Replacement reference</param>
 /// <param name="propReference">Pop reference to store</param>
 protected virtual void AddReference(BOBBuildingReplacement replacement, BuildingPropReference propReference) => replacement.references.Add(propReference);
Пример #15
0
 /// <summary>
 /// Applies a replacement.
 /// </summary>
 /// <param name="replacement">Replacement record to apply</param>
 protected abstract void ApplyReplacement(BOBBuildingReplacement replacement);
Пример #16
0
        /// <summary>
        /// Populates the target fastlist with a list of target-specific trees or props.
        /// </summary>
        protected override void TargetList()
        {
            System.Diagnostics.Stopwatch targetStopwatch = new System.Diagnostics.Stopwatch();
            targetStopwatch.Start();

            // Clear current selection.
            targetList.selectedIndex = -1;

            // List of prefabs that have passed filtering.
            List <PropListItem> propList = new List <PropListItem>();

            // Check to see if this building contains any props.
            if (currentBuilding.m_props == null || currentBuilding.m_props.Length == 0)
            {
                // No props - show 'no props' label and return an empty list.
                noPropsLabel.Show();
                targetList.m_rowsData = new FastList <object>();
                return;
            }


            // Iterate through each prop in building.
            for (int propIndex = 0; propIndex < currentBuilding.m_props.Length; ++propIndex)
            {
                // Create new list item.
                PropListItem propListItem = new PropListItem();

                // Try to get relevant prefab (prop/tree), using finalProp.
                PrefabInfo finalInfo = IsTree ? (PrefabInfo)currentBuilding.m_props[propIndex]?.m_finalTree : (PrefabInfo)currentBuilding.m_props[propIndex]?.m_finalProp;

                // Check to see if we were succesful - if not (e.g. we only want trees and this is a prop), continue on to next building prop.
                if (finalInfo?.name == null)
                {
                    continue;
                }

                // Grouped or individual?
                if (indCheck.isChecked)
                {
                    // Individual - set index to the current building prop indexes.
                    propListItem.index = propIndex;
                }
                else
                {
                    // Grouped - set index to -1 and add to our list of indexes.
                    propListItem.index = -1;
                    propListItem.indexes.Add(propIndex);
                }

                // Get original (pre-replacement) tree/prop prefab and current probability (as default original probability).
                propListItem.originalPrefab = BuildingReplacement.instance.GetOriginal(currentBuilding, propIndex) ?? AllBuildingReplacement.instance.GetOriginal(currentBuilding, propIndex) ?? finalInfo;
                propListItem.originalProb   = currentBuilding.m_props[propIndex].m_probability;
                propListItem.originalAngle  = (currentBuilding.m_props[propIndex].m_radAngle * 180f) / Mathf.PI;

                // All-building replacement and original probability (if any).
                BOBBuildingReplacement allBuildingReplacement = AllBuildingReplacement.instance.ActiveReplacement(currentBuilding, propIndex);
                if (allBuildingReplacement != null)
                {
                    propListItem.allPrefab = allBuildingReplacement.replacementInfo;
                    propListItem.allProb   = allBuildingReplacement.probability;
                }

                // Building replacement and original probability (if any).
                BOBBuildingReplacement buildingReplacement = BuildingReplacement.instance.ActiveReplacement(currentBuilding, propIndex);
                if (buildingReplacement != null)
                {
                    propListItem.replacementPrefab = buildingReplacement.replacementInfo;
                    propListItem.replacementProb   = buildingReplacement.probability;
                }

                // Individual replacement and original probability (if any).
                BOBBuildingReplacement individualReplacement = IndividualReplacement.instance.ActiveReplacement(currentBuilding, propIndex);
                if (individualReplacement != null)
                {
                    propListItem.individualPrefab = individualReplacement.replacementInfo;
                    propListItem.individualProb   = individualReplacement.probability;
                }

                // Are we grouping?
                if (propListItem.index == -1)
                {
                    // Yes, grouping - initialise a flag to show if we've matched.
                    bool matched = false;

                    // Iterate through each item in our existing list of props.
                    foreach (PropListItem item in propList)
                    {
                        // Check to see if we already have this in the list - matching original prefab, building replacement prefab, all-building replacement prefab, and probability.
                        if (item.originalPrefab == propListItem.originalPrefab && item.individualPrefab == propListItem.individualPrefab && item.replacementPrefab == propListItem.replacementPrefab && propListItem.allPrefab == item.allPrefab)
                        {
                            // We've already got an identical grouped instance of this item - add this index and lane to the lists of indexes and lanes under that item and set the flag to indicate that we've done so.
                            item.indexes.Add(propIndex);
                            matched = true;

                            // No point going any further through the list, since we've already found our match.
                            break;
                        }
                    }

                    // Did we get a match?
                    if (matched)
                    {
                        // Yes - continue on to next building prop (without adding this item separately to the list).
                        continue;
                    }
                }

                // Add this item to our list.
                propList.Add(propListItem);
            }

            Logging.Message("basic target list setup time ", targetStopwatch.ElapsedMilliseconds.ToString());

            // Create return fastlist from our filtered list, ordering by name.
            targetList.m_rowsData = new FastList <object>
            {
                m_buffer = targetSearchStatus == (int)OrderBy.NameDescending ? propList.OrderByDescending(item => item.DisplayName).ToArray() : propList.OrderBy(item => item.DisplayName).ToArray(),
                m_size   = propList.Count
            };
            targetList.Refresh();

            // If the list is empty, show the 'no props' label; otherwise, hide it.
            if (targetList.m_rowsData.m_size == 0)
            {
                noPropsLabel.Show();
            }
            else
            {
                noPropsLabel.Hide();
            }

            targetStopwatch.Stop();
            Logging.Message("target list sort time ", targetStopwatch.ElapsedMilliseconds.ToString());
        }