Added Undo Redo to editor

This commit is contained in:
teinarss
2019-07-13 18:21:01 +02:00
committed by abcdefg30
parent 1f78b3a425
commit 76034c198e
19 changed files with 1155 additions and 164 deletions

View File

@@ -24,6 +24,7 @@ namespace OpenRA.Mods.Common.Widgets
readonly WorldRenderer worldRenderer;
readonly World world;
readonly EditorActorLayer editorLayer;
readonly EditorActionManager editorActionManager;
readonly EditorViewportControllerWidget editorWidget;
readonly ActorPreviewWidget preview;
readonly WVec centerOffset;
@@ -38,6 +39,7 @@ namespace OpenRA.Mods.Common.Widgets
worldRenderer = wr;
world = wr.World;
editorLayer = world.WorldActor.Trait<EditorActorLayer>();
editorActionManager = world.WorldActor.Trait<EditorActionManager>();
Actor = actor;
this.owner = owner;
@@ -101,33 +103,8 @@ namespace OpenRA.Mods.Common.Widgets
return true;
// Enforce first entry of ValidOwnerNames as owner if the actor has RequiresSpecificOwners
var ownerName = owner.Name;
var specificOwnerInfo = Actor.TraitInfoOrDefault<RequiresSpecificOwnersInfo>();
if (specificOwnerInfo != null && !specificOwnerInfo.ValidOwnerNames.Contains(ownerName))
ownerName = specificOwnerInfo.ValidOwnerNames.First();
var newActorReference = new ActorReference(Actor.Name);
newActorReference.Add(new OwnerInit(ownerName));
newActorReference.Add(new LocationInit(cell));
var ios = Actor.TraitInfoOrDefault<IOccupySpaceInfo>();
if (ios != null && ios.SharesCell)
{
var subcell = editorLayer.FreeSubCellAt(cell);
if (subcell != SubCell.Invalid)
newActorReference.Add(new SubCellInit(subcell));
}
var initDict = newActorReference.InitDict;
if (Actor.HasTraitInfo<IFacingInfo>())
initDict.Add(new FacingInit(facing));
if (Actor.HasTraitInfo<TurretedInfo>())
initDict.Add(new TurretFacingInit(facing));
editorLayer.Add(newActorReference);
var action = new AddActorAction(editorLayer, Actor, cell, owner, facing);
editorActionManager.Add(action);
}
return true;
@@ -151,4 +128,69 @@ namespace OpenRA.Mods.Common.Widgets
public void Dispose() { }
}
class AddActorAction : IEditorAction
{
public string Text { get; private set; }
readonly EditorActorLayer editorLayer;
readonly ActorInfo actor;
readonly CPos cell;
readonly PlayerReference owner;
readonly int facing;
EditorActorPreview editorActorPreview;
public AddActorAction(EditorActorLayer editorLayer, ActorInfo actor, CPos cell, PlayerReference owner, int facing)
{
this.editorLayer = editorLayer;
this.actor = actor;
this.cell = cell;
this.owner = owner;
this.facing = facing;
}
public void Execute()
{
Do();
}
public void Do()
{
var ownerName = owner.Name;
var specificOwnerInfo = actor.TraitInfoOrDefault<RequiresSpecificOwnersInfo>();
if (specificOwnerInfo != null && !specificOwnerInfo.ValidOwnerNames.Contains(ownerName))
ownerName = specificOwnerInfo.ValidOwnerNames.First();
var newActorReference = new ActorReference(actor.Name);
newActorReference.Add(new OwnerInit(ownerName));
newActorReference.Add(new LocationInit(cell));
var ios = actor.TraitInfoOrDefault<IOccupySpaceInfo>();
if (ios != null && ios.SharesCell)
{
var subcell = editorLayer.FreeSubCellAt(cell);
if (subcell != SubCell.Invalid)
newActorReference.Add(new SubCellInit(subcell));
}
var initDict = newActorReference.InitDict;
if (actor.HasTraitInfo<IFacingInfo>())
initDict.Add(new FacingInit(facing));
if (actor.HasTraitInfo<TurretedInfo>())
initDict.Add(new TurretFacingInit(facing));
editorActorPreview = editorLayer.Add(newActorReference);
Text = "Added {0} ({1})".F(editorActorPreview.Info.Name, editorActorPreview.ID);
}
public void Undo()
{
editorLayer.Remove(editorActorPreview);
}
}
}

View File

@@ -36,6 +36,7 @@ namespace OpenRA.Mods.Common.Widgets
readonly EditorSelectionLayer selectionLayer;
readonly EditorActorLayer editorLayer;
readonly Func<MapCopyFilters> getCopyFilters;
readonly EditorActionManager editorActionManager;
State state;
CPos start;
@@ -46,6 +47,8 @@ namespace OpenRA.Mods.Common.Widgets
this.editorWidget = editorWidget;
worldRenderer = wr;
editorActionManager = wr.World.WorldActor.Trait<EditorActionManager>();
selectionLayer = wr.World.WorldActor.Trait<EditorSelectionLayer>();
editorLayer = wr.World.WorldActor.Trait<EditorActorLayer>();
this.getCopyFilters = getCopyFilters;
@@ -143,26 +146,8 @@ namespace OpenRA.Mods.Common.Widgets
}
}
foreach (var kv in tiles)
{
if (copyFilters.HasFlag(MapCopyFilters.Terrain))
mapTiles[kv.Key] = kv.Value.Item1;
if (copyFilters.HasFlag(MapCopyFilters.Resources))
mapResources[kv.Key] = kv.Value.Item2;
mapHeight[kv.Key] = kv.Value.Item3;
}
if (copyFilters.HasFlag(MapCopyFilters.Actors))
{
var removeActors = dest.SelectMany(editorLayer.PreviewsAt).Distinct().ToList();
foreach (var preview in removeActors)
editorLayer.Remove(preview);
}
foreach (var kv in previews)
editorLayer.Add(kv.Value);
var action = new CopyPasteEditorAction(copyFilters, worldRenderer.World.Map, tiles, previews, editorLayer, dest);
editorActionManager.Add(action);
}
public void Tick()
@@ -187,4 +172,114 @@ namespace OpenRA.Mods.Common.Widgets
selectionLayer.Clear();
}
}
class CopyPasteEditorAction : IEditorAction
{
public string Text { get; private set; }
readonly MapCopyFilters copyFilters;
readonly Dictionary<CPos, Tuple<TerrainTile, ResourceTile, byte>> tiles;
readonly Dictionary<string, ActorReference> previews;
readonly EditorActorLayer editorLayer;
readonly CellRegion dest;
readonly CellLayer<TerrainTile> mapTiles;
readonly CellLayer<byte> mapHeight;
readonly CellLayer<ResourceTile> mapResources;
readonly Queue<UndoCopyPaste> undoCopyPastes = new Queue<UndoCopyPaste>();
readonly Queue<EditorActorPreview> removedActors = new Queue<EditorActorPreview>();
readonly Queue<EditorActorPreview> addedActorPreviews = new Queue<EditorActorPreview>();
public CopyPasteEditorAction(MapCopyFilters copyFilters, Map map,
Dictionary<CPos, Tuple<TerrainTile, ResourceTile, byte>> tiles, Dictionary<string, ActorReference> previews,
EditorActorLayer editorLayer, CellRegion dest)
{
this.copyFilters = copyFilters;
this.tiles = tiles;
this.previews = previews;
this.editorLayer = editorLayer;
this.dest = dest;
mapTiles = map.Tiles;
mapHeight = map.Height;
mapResources = map.Resources;
Text = "Copied {0} tiles".F(tiles.Count);
}
public void Execute()
{
Do();
}
public void Do()
{
foreach (var kv in tiles)
{
undoCopyPastes.Enqueue(new UndoCopyPaste(kv.Key, mapTiles[kv.Key], mapResources[kv.Key], mapHeight[kv.Key]));
if (copyFilters.HasFlag(MapCopyFilters.Terrain))
mapTiles[kv.Key] = kv.Value.Item1;
if (copyFilters.HasFlag(MapCopyFilters.Resources))
mapResources[kv.Key] = kv.Value.Item2;
mapHeight[kv.Key] = kv.Value.Item3;
}
if (copyFilters.HasFlag(MapCopyFilters.Actors))
{
var removeActors = dest.SelectMany(editorLayer.PreviewsAt).Distinct().ToList();
foreach (var preview in removeActors)
{
removedActors.Enqueue(preview);
editorLayer.Remove(preview);
}
}
foreach (var kv in previews)
addedActorPreviews.Enqueue(editorLayer.Add(kv.Value));
}
public void Undo()
{
while (undoCopyPastes.Count > 0)
{
var undoCopyPaste = undoCopyPastes.Dequeue();
var cell = undoCopyPaste.Cell;
if (copyFilters.HasFlag(MapCopyFilters.Terrain))
mapTiles[cell] = undoCopyPaste.MapTile;
if (copyFilters.HasFlag(MapCopyFilters.Resources))
mapResources[cell] = undoCopyPaste.ResourceTile;
mapHeight[cell] = undoCopyPaste.Height;
}
while (addedActorPreviews.Count > 0)
editorLayer.Remove(addedActorPreviews.Dequeue());
if (copyFilters.HasFlag(MapCopyFilters.Actors))
while (removedActors.Count > 0)
editorLayer.Add(removedActors.Dequeue());
}
}
internal class UndoCopyPaste
{
public CPos Cell { get; private set; }
public TerrainTile MapTile { get; private set; }
public ResourceTile ResourceTile { get; private set; }
public byte Height { get; private set; }
public UndoCopyPaste(CPos cell, TerrainTile mapTile, ResourceTile resourceTile, byte height)
{
Cell = cell;
MapTile = mapTile;
ResourceTile = resourceTile;
Height = height;
}
}
}

View File

@@ -33,6 +33,8 @@ namespace OpenRA.Mods.Common.Widgets
readonly EditorViewportControllerWidget editorWidget;
readonly EditorActorLayer editorLayer;
readonly Dictionary<int, ResourceType> resources;
readonly EditorActionManager editorActionManager;
public EditorActorPreview SelectedActor;
int2 worldPixel;
@@ -45,6 +47,8 @@ namespace OpenRA.Mods.Common.Widgets
editorLayer = world.WorldActor.Trait<EditorActorLayer>();
resources = world.WorldActor.TraitsImplementing<ResourceType>()
.ToDictionary(r => r.Info.ResourceType, r => r);
editorActionManager = world.WorldActor.Trait<EditorActionManager>();
}
long CalculateActorSelectionPriority(EditorActorPreview actor)
@@ -74,7 +78,7 @@ namespace OpenRA.Mods.Common.Widgets
var underCursor = editorLayer.PreviewsAt(worldPixel).MinByOrDefault(CalculateActorSelectionPriority);
var mapResources = world.Map.Resources;
ResourceType type;
ResourceType type = null;
if (underCursor != null)
editorWidget.SetTooltip(underCursor.Tooltip);
else if (mapResources.Contains(cell) && resources.TryGetValue(mapResources[cell].Type, out type))
@@ -97,10 +101,10 @@ namespace OpenRA.Mods.Common.Widgets
editorWidget.SetTooltip(null);
if (underCursor != null && underCursor != SelectedActor)
editorLayer.Remove(underCursor);
editorActionManager.Add(new RemoveActorAction(editorLayer, underCursor));
if (mapResources.Contains(cell) && mapResources[cell].Type != 0)
mapResources[cell] = default(ResourceTile);
editorActionManager.Add(new RemoveResourceAction(mapResources, cell, type));
}
return true;
@@ -109,4 +113,69 @@ namespace OpenRA.Mods.Common.Widgets
public void Tick() { }
public void Dispose() { }
}
class RemoveActorAction : IEditorAction
{
public string Text { get; private set; }
readonly EditorActorLayer editorActorLayer;
readonly EditorActorPreview actor;
public RemoveActorAction(EditorActorLayer editorActorLayer, EditorActorPreview actor)
{
this.editorActorLayer = editorActorLayer;
this.actor = actor;
Text = "Removed {0} ({1})".F(actor.Info.Name, actor.ID);
}
public void Execute()
{
Do();
}
public void Do()
{
editorActorLayer.Remove(actor);
}
public void Undo()
{
editorActorLayer.Add(actor);
}
}
class RemoveResourceAction : IEditorAction
{
public string Text { get; private set; }
readonly CellLayer<ResourceTile> mapResources;
readonly CPos cell;
ResourceTile resourceTile;
public RemoveResourceAction(CellLayer<ResourceTile> mapResources, CPos cell, ResourceType type)
{
this.mapResources = mapResources;
this.cell = cell;
Text = "Removed {0}".F(type.Info.TerrainType);
}
public void Execute()
{
Do();
}
public void Do()
{
resourceTile = mapResources[cell];
mapResources[cell] = default(ResourceTile);
}
public void Undo()
{
mapResources[cell] = resourceTile;
}
}
}

View File

@@ -9,6 +9,7 @@
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
@@ -23,6 +24,10 @@ namespace OpenRA.Mods.Common.Widgets
readonly World world;
readonly EditorViewportControllerWidget editorWidget;
readonly SpriteWidget preview;
readonly EditorActionManager editorActionManager;
AddResourcesEditorAction action;
bool resourceAdded;
public EditorResourceBrush(EditorViewportControllerWidget editorWidget, ResourceTypeInfo resource, WorldRenderer wr)
{
@@ -30,6 +35,8 @@ namespace OpenRA.Mods.Common.Widgets
ResourceType = resource;
worldRenderer = wr;
world = wr.World;
editorActionManager = world.WorldActor.Trait<EditorActionManager>();
action = new AddResourcesEditorAction(world.Map, ResourceType);
preview = editorWidget.Get<SpriteWidget>("DRAG_LAYER_PREVIEW");
preview.Palette = resource.Palette;
@@ -65,11 +72,18 @@ namespace OpenRA.Mods.Common.Widgets
var cell = worldRenderer.Viewport.ViewToWorld(mi.Location);
if (mi.Button == MouseButton.Left && AllowResourceAt(cell))
if (mi.Button == MouseButton.Left && mi.Event != MouseInputEvent.Up && AllowResourceAt(cell))
{
var type = (byte)ResourceType.ResourceType;
var index = (byte)ResourceType.MaxDensity;
world.Map.Resources[cell] = new ResourceTile(type, index);
action.Add(new CellResource(cell, world.Map.Resources[cell], new ResourceTile(type, index)));
resourceAdded = true;
}
else if (resourceAdded && mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Up)
{
editorActionManager.Add(action);
action = new AddResourcesEditorAction(world.Map, ResourceType);
resourceAdded = false;
}
return true;
@@ -112,4 +126,63 @@ namespace OpenRA.Mods.Common.Widgets
public void Dispose() { }
}
struct CellResource
{
public readonly CPos Cell;
public readonly ResourceTile ResourceTile;
public readonly ResourceTile NewResourceTile;
public CellResource(CPos cell, ResourceTile resourceTile, ResourceTile newResourceTile)
{
Cell = cell;
ResourceTile = resourceTile;
NewResourceTile = newResourceTile;
}
}
class AddResourcesEditorAction : IEditorAction
{
public string Text { get; private set; }
readonly Map map;
readonly ResourceTypeInfo resourceType;
readonly List<CellResource> cellResources = new List<CellResource>();
public AddResourcesEditorAction(Map map, ResourceTypeInfo resourceType)
{
this.map = map;
this.resourceType = resourceType;
}
public void Execute()
{
}
public void Do()
{
foreach (var resourceCell in cellResources)
SetTile(resourceCell.Cell, resourceCell.NewResourceTile);
}
void SetTile(CPos cell, ResourceTile tile)
{
map.Resources[cell] = tile;
}
public void Undo()
{
foreach (var resourceCell in cellResources)
SetTile(resourceCell.Cell, resourceCell.ResourceTile);
}
public void Add(CellResource cellResource)
{
SetTile(cellResource.Cell, cellResource.NewResourceTile);
cellResources.Add(cellResource);
var cellText = cellResources.Count != 1 ? "cells" : "cell";
Text = "Added {0} {1} of {2}".F(cellResources.Count, cellText, resourceType.TerrainType);
}
}
}

View File

@@ -13,6 +13,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
namespace OpenRA.Mods.Common.Widgets
@@ -26,6 +27,7 @@ namespace OpenRA.Mods.Common.Widgets
readonly EditorViewportControllerWidget editorWidget;
readonly TerrainTemplatePreviewWidget preview;
readonly Rectangle bounds;
readonly EditorActionManager editorActionManager;
bool painting;
@@ -36,6 +38,8 @@ namespace OpenRA.Mods.Common.Widgets
worldRenderer = wr;
world = wr.World;
editorActionManager = world.WorldActor.Trait<EditorActionManager>();
preview = editorWidget.Get<TerrainTemplatePreviewWidget>("DRAG_TILE_PREVIEW");
preview.GetScale = () => worldRenderer.Viewport.Zoom;
preview.IsVisible = () => editorWidget.CurrentBrush == this;
@@ -97,33 +101,13 @@ namespace OpenRA.Mods.Common.Widgets
void PaintCell(CPos cell, bool isMoving)
{
var map = world.Map;
var mapTiles = map.Tiles;
var mapHeight = map.Height;
var tileset = map.Rules.TileSet;
var template = tileset.Templates[Template];
var baseHeight = mapHeight.Contains(cell) ? mapHeight[cell] : (byte)0;
if (isMoving && PlacementOverlapsSameTemplate(template, cell))
return;
var i = 0;
for (var y = 0; y < template.Size.Y; y++)
{
for (var x = 0; x < template.Size.X; x++, i++)
{
if (template.Contains(i) && template[i] != null)
{
var index = template.PickAny ? (byte)Game.CosmeticRandom.Next(0, template.TilesCount) : (byte)i;
var c = cell + new CVec(x, y);
if (!mapTiles.Contains(c))
continue;
mapTiles[c] = new TerrainTile(Template, index);
mapHeight[c] = (byte)(baseHeight + template[index].Height).Clamp(0, map.Grid.MaximumTerrainHeight);
}
}
}
editorActionManager.Add(new PaintTileEditorAction(Template, map, cell));
}
void FloodFillWithBrush(CPos cell, bool isMoving)
@@ -239,4 +223,87 @@ namespace OpenRA.Mods.Common.Widgets
public void Dispose() { }
}
class PaintTileEditorAction : IEditorAction
{
public string Text { get; private set; }
readonly ushort template;
readonly Map map;
readonly CPos cell;
readonly Queue<UndoTile> undoTiles = new Queue<UndoTile>();
readonly TerrainTemplateInfo terrainTemplate;
public PaintTileEditorAction(ushort template, Map map, CPos cell)
{
this.template = template;
this.map = map;
this.cell = cell;
var tileset = map.Rules.TileSet;
terrainTemplate = tileset.Templates[template];
Text = "Added tile {0}".F(terrainTemplate.Id);
}
public void Execute()
{
Do();
}
public void Do()
{
var mapTiles = map.Tiles;
var mapHeight = map.Height;
var baseHeight = mapHeight.Contains(cell) ? mapHeight[cell] : (byte)0;
var i = 0;
for (var y = 0; y < terrainTemplate.Size.Y; y++)
{
for (var x = 0; x < terrainTemplate.Size.X; x++, i++)
{
if (terrainTemplate.Contains(i) && terrainTemplate[i] != null)
{
var index = terrainTemplate.PickAny ? (byte)Game.CosmeticRandom.Next(0, terrainTemplate.TilesCount) : (byte)i;
var c = cell + new CVec(x, y);
if (!mapTiles.Contains(c))
continue;
undoTiles.Enqueue(new UndoTile(c, mapTiles[c], mapHeight[c]));
mapTiles[c] = new TerrainTile(template, index);
mapHeight[c] = (byte)(baseHeight + terrainTemplate[index].Height).Clamp(0, map.Grid.MaximumTerrainHeight);
}
}
}
}
public void Undo()
{
var mapTiles = map.Tiles;
var mapHeight = map.Height;
while (undoTiles.Count > 0)
{
var undoTile = undoTiles.Dequeue();
mapTiles[undoTile.Cell] = undoTile.MapTile;
mapHeight[undoTile.Cell] = undoTile.Height;
}
}
}
class UndoTile
{
public CPos Cell { get; private set; }
public TerrainTile MapTile { get; private set; }
public byte Height { get; private set; }
public UndoTile(CPos cell, TerrainTile mapTile, byte height)
{
Cell = cell;
MapTile = mapTile;
Height = height;
}
}
}