#region Copyright & License Information /* * Copyright 2007-2019 The OpenRA Developers (see AUTHORS) * 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.Graphics; using OpenRA.Mods.Common.Traits; namespace OpenRA.Mods.Common.Widgets { [Flags] public enum MapCopyFilters { None = 0, Terrain = 1, Resources = 2, Actors = 4, All = Terrain | Resources | Actors } public sealed class EditorCopyPasteBrush : IEditorBrush { enum State { SelectFirst, SelectSecond, Paste } readonly WorldRenderer worldRenderer; readonly EditorViewportControllerWidget editorWidget; readonly EditorSelectionLayer selectionLayer; readonly EditorActorLayer editorLayer; readonly Func getCopyFilters; State state; CPos start; CPos end; public EditorCopyPasteBrush(EditorViewportControllerWidget editorWidget, WorldRenderer wr, Func getCopyFilters) { this.editorWidget = editorWidget; worldRenderer = wr; selectionLayer = wr.World.WorldActor.Trait(); editorLayer = wr.World.WorldActor.Trait(); this.getCopyFilters = getCopyFilters; } public bool HandleMouseInput(MouseInput mi) { // Exclusively uses left and right mouse buttons, but nothing else if (mi.Button != MouseButton.Left && mi.Button != MouseButton.Right) return false; if (mi.Button == MouseButton.Right) { if (mi.Event == MouseInputEvent.Up) { editorWidget.ClearBrush(); return true; } return false; } if (mi.Button == MouseButton.Left && (mi.Event == MouseInputEvent.Up || mi.Event == MouseInputEvent.Down)) { var cell = worldRenderer.Viewport.ViewToWorld(mi.Location); switch (state) { case State.SelectFirst: if (mi.Event != MouseInputEvent.Down) break; start = cell; selectionLayer.SetCopyRegion(start, end); state = State.SelectSecond; break; case State.SelectSecond: if (mi.Event != MouseInputEvent.Up) break; end = cell; selectionLayer.SetCopyRegion(start, end); state = State.Paste; break; case State.Paste: { var gridType = worldRenderer.World.Map.Grid.Type; var source = CellRegion.BoundingRegion(gridType, new[] { start, end }); Copy(source, cell - end); editorWidget.ClearBrush(); break; } } return true; } return false; } void Copy(CellRegion source, CVec offset) { var gridType = worldRenderer.World.Map.Grid.Type; var mapTiles = worldRenderer.World.Map.Tiles; var mapHeight = worldRenderer.World.Map.Height; var mapResources = worldRenderer.World.Map.Resources; var dest = new CellRegion(gridType, source.TopLeft + offset, source.BottomRight + offset); var previews = new Dictionary(); var tiles = new Dictionary>(); var copyFilters = getCopyFilters(); foreach (var cell in source) { if (!mapTiles.Contains(cell) || !mapTiles.Contains(cell + offset)) continue; tiles.Add(cell + offset, Tuple.Create(mapTiles[cell], mapResources[cell], mapHeight[cell])); if (copyFilters.HasFlag(MapCopyFilters.Actors)) { foreach (var preview in editorLayer.PreviewsAt(cell)) { if (previews.ContainsKey(preview.ID)) continue; var copy = preview.Export(); if (copy.InitDict.Contains()) { var location = copy.InitDict.Get(); copy.InitDict.Remove(location); copy.InitDict.Add(new LocationInit(location.Value(worldRenderer.World) + offset)); } previews.Add(preview.ID, copy); } } } 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); } public void Tick() { var cell = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos); if (state == State.Paste) { selectionLayer.SetPasteRegion(cell + (start - end), cell); return; } if (state == State.SelectFirst) start = end = cell; else if (state == State.SelectSecond) end = cell; selectionLayer.SetCopyRegion(start, end); } public void Dispose() { selectionLayer.Clear(); } } }