/// <summary>
    /// Upgrade tile system from v1.0.0-v1.0.8 to v2.0.0.
    /// </summary>
    /// <remarks>
    /// <para>Replicates upgrade process that was included in v1.0.9+ but converts straight
    /// to v2.0.0 instead of v1.0.9.</para>
    /// </remarks>
    /// <param name="v1">Old tile system.</param>
    public static void UpgradeTileSystemA(MonoBehaviour v1)
    {
        RtsUpgradedBrushMap map = RtsBrushUpgradeUtility.BrushMappings;

        EditorUtility.DisplayProgressBar("Upgrade Tile System", "Initializing new data structure...", 0.0f);
        try {
            PropertyInfo piTileData_hasGameObject = typeof(TileData).GetProperty("HasGameObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

            Vector3 tileSize = (Vector3)_fiTileSystem_tileSize.GetValue(v1);
            int     rows     = (int)_fiTileSystem_rows.GetValue(v1);
            int     columns  = (int)_fiTileSystem_columns.GetValue(v1);

            // Create v2.x tile system.
            TileSystem v2 = v1.gameObject.AddComponent <TileSystem>();
            v2.CreateSystem(tileSize.x, tileSize.y, tileSize.z, rows, columns, 30, 30);
            CopyProperties(v1, v2);

            // Assume value that was consistent with original default settings
            v2.applyRuntimeStripping = true;
            v2.StrippingPreset       = StrippingPreset.NoStripping;
            v2.BeginBulkEdit();

            Component[] instanceComponents = v1.GetComponentsInChildren(_tyTileInstance, true);

            float task      = 0.0f;
            float taskCount = instanceComponents.Length;
            float taskRatio = 1.0f / taskCount;

            TileData tile = new TileData();

            // Retrieve all tile instance components
            foreach (MonoBehaviour instance in instanceComponents)
            {
                EditorUtility.DisplayProgressBar("Upgrade Tile System", "Processing tile data...", (task++) * taskRatio);

                int row    = (int)_fiTileInstance_row.GetValue(instance);
                int column = (int)_fiTileInstance_column.GetValue(instance);

                tile.Clear();

                // Create and assign tile data
                tile.brush           = map.Lookup((Object)_piTileInstance_brush.GetValue(instance, null));
                tile.orientationMask = (byte)OrientationUtility.MaskFromName((string)_fiTileInstance_orientationName.GetValue(instance));
                tile.variationIndex  = (byte)(int)_fiTileInstance_variationIndex.GetValue(instance);
                tile.Empty           = false;
                tile.gameObject      = instance.gameObject;
                piTileData_hasGameObject.SetValue(tile, true, null);

                v2.SetTileFrom(row, column, tile);

                Chunk chunk = v2.GetChunkFromTileIndex(row, column);
                ForceRepaintForAtlasTiles(v2.GetTile(row, column), chunk);
                if (instance == null)
                {
                    continue;
                }

                // Cleanup original tile instance?
                if (!StrippingUtility.StripEmptyGameObject(instance.transform))
                {
                    // Reparent game object to its shiny new chunk!
                    instance.gameObject.transform.parent = chunk.transform;
                }

                // Destroy unwanted tile instance component
                Object.DestroyImmediate(instance);
            }

            int count = v2.EndBulkEdit();
            RemoveTileSystem(v1);

            if (count > 0)
            {
                Debug.Log(string.Format("Upgrade of tile system '{0}' completed and {1} tile(s) were force refreshed.", v2.name, count));
            }
            else
            {
                Debug.Log(string.Format("Upgrade of tile system '{0}' completed.", v2.name));
            }
        }
        finally {
            EditorUtility.ClearProgressBar();
        }
    }
        public void Build(TileSystem system)
        {
            // Notes for multi-threading in the future:
            //   1. Prepare map using multiple processors.
            //   2. Wait until map is prepared.
            //   3. Perform snap and smooth using multiple processors by dividing map
            //      into buckets.
            //   4. Wait until buckets have been processed.
            //   5. Perform snap and smooth between edges of buckets (stitching).
            //   6. Merge meshes using multiple processors.
            //   7. Wait until finished.

            this.progressTitle = string.Format(
                /* 0: name of tile system */
                TileLang.Text("Building tile system '{0}'..."),
                system.name
                );
            this.SetContext(system);

            // Strip build paths from optimized version of tile system.
            system.LastBuildPrefabPath = "";
            system.LastBuildDataPath   = "";

            this.GeneratedMeshes.Clear();

            // Prepare chunk data and count tasks
            this.currentTaskNumber            = 0;
            this.taskCount                    = this.CountTasks(system);
            this.taskNumberToPercentageFactor = 1f / (float)this.taskCount;

            if (system.ReduceColliders.Active)
            {
                this.ReportProgress(TileLang.Text("Optimizing tile colliders..."));
                this.ReduceTileColliders();
            }

            if (system.combineMethod != BuildCombineMethod.None)
            {
                // Prepare mesh data from tile meshes
                this.ReportProgress(TileLang.Text("Preparing tile meshes..."));
                this.PrepareMap();

                // Perform vertex snapping and smoothing
                this.ReportProgress(TileLang.Text("Snapping vertices..."));
                this.PerformSnapAndSmooth();

                // Merge meshes into chunks
                this.ReportProgress(TileLang.Text("Finalizing chunks..."));
                this.PerformMergeIntoChunks();
            }

            this.OptimizeProceduralMeshes(system);

            // Apply stripping to tile system!
            this.ReportProgress(TileLang.Text("Applying stripping rules..."));
            StrippingUtility.ApplyStripping(system);
            this.ReportProgress();

            // Indicates that tile system has been built
            system.isBuilt = true;
        }