Refactor editor clipboard logic as blitting logic
- Refactors internal editor clipboard logic into reusable map contents "Blitting" functionality. - Fix actor processing being unnecessarily (cell) looped within CopySelectionContents (now CopyRegionContents). - Deduplicates largely repeated code. - Minor code simplifications and renames.
This commit is contained in:
215
OpenRA.Mods.Common/EditorBrushes/EditorBlit.cs
Normal file
215
OpenRA.Mods.Common/EditorBrushes/EditorBlit.cs
Normal file
@@ -0,0 +1,215 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.EditorBrushes
|
||||
{
|
||||
public readonly struct BlitTile
|
||||
{
|
||||
public readonly TerrainTile TerrainTile;
|
||||
public readonly ResourceTile ResourceTile;
|
||||
public readonly ResourceLayerContents? ResourceLayerContents;
|
||||
public readonly byte Height;
|
||||
|
||||
public BlitTile(TerrainTile terrainTile, ResourceTile resourceTile, ResourceLayerContents? resourceLayerContents, byte height)
|
||||
{
|
||||
TerrainTile = terrainTile;
|
||||
ResourceTile = resourceTile;
|
||||
ResourceLayerContents = resourceLayerContents;
|
||||
Height = height;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct EditorBlitSource
|
||||
{
|
||||
public readonly CellRegion CellRegion;
|
||||
public readonly Dictionary<string, EditorActorPreview> Actors;
|
||||
public readonly Dictionary<CPos, BlitTile> Tiles;
|
||||
|
||||
public EditorBlitSource(CellRegion cellRegion, Dictionary<string, EditorActorPreview> actors, Dictionary<CPos, BlitTile> tiles)
|
||||
{
|
||||
CellRegion = cellRegion;
|
||||
Actors = actors;
|
||||
Tiles = tiles;
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum MapBlitFilters
|
||||
{
|
||||
None = 0,
|
||||
Terrain = 1,
|
||||
Resources = 2,
|
||||
Actors = 4,
|
||||
All = Terrain | Resources | Actors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Core implementation for EditorActions which overwrite a region of the map (such as
|
||||
/// copy-paste).
|
||||
/// </summary>
|
||||
public sealed class EditorBlit
|
||||
{
|
||||
readonly MapBlitFilters blitFilters;
|
||||
readonly IResourceLayer resourceLayer;
|
||||
readonly EditorActorLayer editorActorLayer;
|
||||
readonly EditorBlitSource blitSource;
|
||||
readonly EditorBlitSource revertBlitSource;
|
||||
readonly CPos blitPosition;
|
||||
readonly Map map;
|
||||
readonly bool respectBounds;
|
||||
|
||||
public EditorBlit(
|
||||
MapBlitFilters blitFilters,
|
||||
IResourceLayer resourceLayer,
|
||||
CPos blitPosition,
|
||||
Map map,
|
||||
EditorBlitSource blitSource,
|
||||
EditorActorLayer editorActorLayer,
|
||||
bool respectBounds)
|
||||
{
|
||||
this.blitFilters = blitFilters;
|
||||
this.resourceLayer = resourceLayer;
|
||||
this.blitSource = blitSource;
|
||||
this.blitPosition = blitPosition;
|
||||
this.editorActorLayer = editorActorLayer;
|
||||
this.map = map;
|
||||
this.respectBounds = respectBounds;
|
||||
|
||||
var blitSize = blitSource.CellRegion.BottomRight - blitSource.CellRegion.TopLeft;
|
||||
revertBlitSource = CopyRegionContents(
|
||||
map,
|
||||
editorActorLayer,
|
||||
resourceLayer,
|
||||
new CellRegion(map.Grid.Type, blitPosition, blitPosition + blitSize),
|
||||
blitFilters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an EditorBlitSource containing the map contents for a given region.
|
||||
/// </summary>
|
||||
public static EditorBlitSource CopyRegionContents(
|
||||
Map map,
|
||||
EditorActorLayer editorActorLayer,
|
||||
IResourceLayer resourceLayer,
|
||||
CellRegion region,
|
||||
MapBlitFilters blitFilters)
|
||||
{
|
||||
var mapTiles = map.Tiles;
|
||||
var mapHeight = map.Height;
|
||||
var mapResources = map.Resources;
|
||||
|
||||
var previews = new Dictionary<string, EditorActorPreview>();
|
||||
var tiles = new Dictionary<CPos, BlitTile>();
|
||||
|
||||
foreach (var cell in region.CellCoords)
|
||||
{
|
||||
if (!mapTiles.Contains(cell))
|
||||
continue;
|
||||
|
||||
tiles.Add(
|
||||
cell,
|
||||
new BlitTile(mapTiles[cell],
|
||||
mapResources[cell],
|
||||
resourceLayer?.GetResource(cell),
|
||||
mapHeight[cell]));
|
||||
}
|
||||
|
||||
if (blitFilters.HasFlag(MapBlitFilters.Actors))
|
||||
foreach (var preview in editorActorLayer.PreviewsInCellRegion(region.CellCoords))
|
||||
previews.TryAdd(preview.ID, preview);
|
||||
|
||||
return new EditorBlitSource(region, previews, tiles);
|
||||
}
|
||||
|
||||
void Blit(EditorBlitSource source, bool isRevert)
|
||||
{
|
||||
var blitPos = isRevert ? source.CellRegion.TopLeft : blitPosition;
|
||||
var blitVec = blitPos - source.CellRegion.TopLeft;
|
||||
var blitSize = source.CellRegion.BottomRight - source.CellRegion.TopLeft;
|
||||
var blitRegion = new CellRegion(map.Grid.Type, blitPos, blitPos + blitSize);
|
||||
|
||||
if (blitFilters.HasFlag(MapBlitFilters.Actors))
|
||||
{
|
||||
// Clear any existing actors in the paste cells.
|
||||
foreach (var regionActor in editorActorLayer.PreviewsInCellRegion(blitRegion.CellCoords).ToList())
|
||||
editorActorLayer.Remove(regionActor);
|
||||
}
|
||||
|
||||
foreach (var tileKeyValuePair in source.Tiles)
|
||||
{
|
||||
var position = tileKeyValuePair.Key + blitVec;
|
||||
if (!map.Tiles.Contains(position) || (respectBounds && !map.Contains(position)))
|
||||
continue;
|
||||
|
||||
// Clear any existing resources.
|
||||
if (resourceLayer != null && blitFilters.HasFlag(MapBlitFilters.Resources))
|
||||
resourceLayer.ClearResources(position);
|
||||
|
||||
var tile = tileKeyValuePair.Value;
|
||||
var resourceLayerContents = tile.ResourceLayerContents;
|
||||
|
||||
if (blitFilters.HasFlag(MapBlitFilters.Terrain))
|
||||
{
|
||||
map.Tiles[position] = tile.TerrainTile;
|
||||
map.Height[position] = tile.Height;
|
||||
}
|
||||
|
||||
if (blitFilters.HasFlag(MapBlitFilters.Resources) &&
|
||||
resourceLayerContents.HasValue &&
|
||||
!string.IsNullOrWhiteSpace(resourceLayerContents.Value.Type))
|
||||
resourceLayer.AddResource(resourceLayerContents.Value.Type, position, resourceLayerContents.Value.Density);
|
||||
}
|
||||
|
||||
if (blitFilters.HasFlag(MapBlitFilters.Actors))
|
||||
{
|
||||
if (isRevert)
|
||||
{
|
||||
// For reverts, just place the original actors back exactly how they were.
|
||||
foreach (var actor in source.Actors.Values)
|
||||
editorActorLayer.Add(actor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create copies of the original actors, update their locations, and place.
|
||||
foreach (var actorKeyValuePair in source.Actors)
|
||||
{
|
||||
var copy = actorKeyValuePair.Value.Export();
|
||||
var locationInit = copy.GetOrDefault<LocationInit>();
|
||||
if (locationInit != null)
|
||||
{
|
||||
var actorPosition = locationInit.Value + blitVec;
|
||||
if (respectBounds && !map.Contains(actorPosition))
|
||||
continue;
|
||||
|
||||
copy.RemoveAll<LocationInit>();
|
||||
copy.Add(new LocationInit(actorPosition));
|
||||
}
|
||||
|
||||
editorActorLayer.Add(copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Commit() => Blit(blitSource, false);
|
||||
public void Revert() => Blit(revertBlitSource, true);
|
||||
|
||||
public int TileCount()
|
||||
{
|
||||
return blitSource.Tiles.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user