Make brush rendering self-contained
This commit is contained in:
@@ -9,30 +9,65 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public sealed class EditorActorBrush : IEditorBrush
|
||||
{
|
||||
public EditorActorPreview Preview;
|
||||
|
||||
readonly World world;
|
||||
readonly EditorActorLayer editorLayer;
|
||||
readonly EditorCursorLayer editorCursor;
|
||||
readonly EditorActionManager editorActionManager;
|
||||
readonly EditorViewportControllerWidget editorWidget;
|
||||
readonly int cursorToken;
|
||||
readonly WVec centerOffset;
|
||||
readonly bool sharesCell;
|
||||
|
||||
CPos cell;
|
||||
SubCell subcell = SubCell.Invalid;
|
||||
|
||||
public EditorActorBrush(EditorViewportControllerWidget editorWidget, ActorInfo actor, PlayerReference owner, WorldRenderer wr)
|
||||
{
|
||||
this.editorWidget = editorWidget;
|
||||
world = wr.World;
|
||||
editorLayer = world.WorldActor.Trait<EditorActorLayer>();
|
||||
editorCursor = world.WorldActor.Trait<EditorCursorLayer>();
|
||||
editorActionManager = world.WorldActor.Trait<EditorActionManager>();
|
||||
|
||||
cursorToken = editorCursor.SetActor(wr, actor, owner);
|
||||
var ios = actor.TraitInfoOrDefault<IOccupySpaceInfo>();
|
||||
centerOffset = (ios as BuildingInfo)?.CenterOffset(world) ?? WVec.Zero;
|
||||
sharesCell = ios != null && ios.SharesCell;
|
||||
|
||||
// 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 reference = new ActorReference(actor.Name)
|
||||
{
|
||||
new OwnerInit(ownerName),
|
||||
new FactionInit(owner.Faction)
|
||||
};
|
||||
|
||||
var worldPx = wr.Viewport.ViewToWorldPx(Viewport.LastMousePos) - wr.ScreenPxOffset(centerOffset);
|
||||
cell = wr.Viewport.ViewToWorld(wr.Viewport.WorldToViewPx(worldPx));
|
||||
reference.Add(new LocationInit(cell));
|
||||
if (sharesCell)
|
||||
{
|
||||
subcell = editorLayer.FreeSubCellAt(cell);
|
||||
if (subcell != SubCell.Invalid)
|
||||
reference.Add(new SubCellInit(subcell));
|
||||
}
|
||||
|
||||
if (actor.HasTraitInfo<IFacingInfo>())
|
||||
reference.Add(new FacingInit(editorLayer.Info.DefaultActorFacing));
|
||||
|
||||
Preview = new EditorActorPreview(wr, null, reference, owner);
|
||||
}
|
||||
|
||||
public bool HandleMouseInput(MouseInput mi)
|
||||
@@ -52,29 +87,56 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
return false;
|
||||
}
|
||||
|
||||
if (editorCursor.CurrentToken != cursorToken)
|
||||
return false;
|
||||
|
||||
if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Down)
|
||||
{
|
||||
// Check the actor is inside the map
|
||||
var actor = editorCursor.Actor;
|
||||
if (!actor.Footprint.All(c => world.Map.Tiles.Contains(c.Key)))
|
||||
if (!Preview.Footprint.All(c => world.Map.Tiles.Contains(c.Key)))
|
||||
return true;
|
||||
|
||||
var action = new AddActorAction(editorLayer, actor.Export());
|
||||
var action = new AddActorAction(editorLayer, Preview.Export());
|
||||
editorActionManager.Add(action);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void IEditorBrush.TickRender(WorldRenderer wr, Actor self)
|
||||
{
|
||||
// Offset mouse position by the center offset (in world pixels)
|
||||
var worldPx = wr.Viewport.ViewToWorldPx(Viewport.LastMousePos) - wr.ScreenPxOffset(centerOffset);
|
||||
var currentCell = wr.Viewport.ViewToWorld(wr.Viewport.WorldToViewPx(worldPx));
|
||||
var currentSubcell = sharesCell ? editorLayer.FreeSubCellAt(currentCell) : SubCell.Invalid;
|
||||
if (cell != currentCell || subcell != currentSubcell)
|
||||
{
|
||||
cell = currentCell;
|
||||
Preview.ReplaceInit(new LocationInit(cell));
|
||||
|
||||
if (sharesCell)
|
||||
{
|
||||
subcell = editorLayer.FreeSubCellAt(cell);
|
||||
if (subcell == SubCell.Invalid)
|
||||
Preview.RemoveInit<SubCellInit>();
|
||||
else
|
||||
Preview.ReplaceInit(new SubCellInit(subcell));
|
||||
}
|
||||
|
||||
Preview.UpdateFromMove();
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<IRenderable> IEditorBrush.RenderAboveShroud(Actor self, WorldRenderer wr)
|
||||
{
|
||||
return Preview.Render().OrderBy(WorldRenderer.RenderableZPositionComparisonKey);
|
||||
}
|
||||
|
||||
IEnumerable<IRenderable> IEditorBrush.RenderAnnotations(Actor self, WorldRenderer wr)
|
||||
{
|
||||
return Preview.RenderAnnotations();
|
||||
}
|
||||
|
||||
public void Tick() { }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
editorCursor.Clear(cursorToken);
|
||||
}
|
||||
public void Dispose() { }
|
||||
}
|
||||
|
||||
sealed class AddActorAction : IEditorAction
|
||||
|
||||
@@ -14,6 +14,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.EditorBrushes;
|
||||
using OpenRA.Mods.Common.Graphics;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
@@ -94,14 +95,23 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
return false;
|
||||
}
|
||||
|
||||
void IEditorBrush.TickRender(WorldRenderer wr, Actor self) { }
|
||||
IEnumerable<IRenderable> IEditorBrush.RenderAboveShroud(Actor self, WorldRenderer wr) { yield break; }
|
||||
IEnumerable<IRenderable> IEditorBrush.RenderAnnotations(Actor self, WorldRenderer wr)
|
||||
{
|
||||
if (PastePreviewPosition != null)
|
||||
{
|
||||
yield return new EditorSelectionAnnotationRenderable(Region, editorWidget.SelectionAltColor, editorWidget.SelectionAltOffset, PastePreviewPosition);
|
||||
yield return new EditorSelectionAnnotationRenderable(Region, editorWidget.PasteColor, int2.Zero, PastePreviewPosition);
|
||||
}
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
PastePreviewPosition = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
public void Dispose() { }
|
||||
}
|
||||
|
||||
sealed class CopyPasteEditorAction : IEditorAction
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Graphics;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
@@ -20,6 +22,10 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
bool HandleMouseInput(MouseInput mi);
|
||||
void Tick();
|
||||
|
||||
void TickRender(WorldRenderer wr, Actor self);
|
||||
IEnumerable<IRenderable> RenderAboveShroud(Actor self, WorldRenderer wr);
|
||||
IEnumerable<IRenderable> RenderAnnotations(Actor self, WorldRenderer wr);
|
||||
}
|
||||
|
||||
public class EditorSelection
|
||||
@@ -255,6 +261,17 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
return true;
|
||||
}
|
||||
|
||||
void IEditorBrush.TickRender(WorldRenderer wr, Actor self) { }
|
||||
IEnumerable<IRenderable> IEditorBrush.RenderAboveShroud(Actor self, WorldRenderer wr) { yield break; }
|
||||
IEnumerable<IRenderable> IEditorBrush.RenderAnnotations(Actor self, WorldRenderer wr)
|
||||
{
|
||||
if (CurrentDragBounds != null)
|
||||
{
|
||||
yield return new EditorSelectionAnnotationRenderable(CurrentDragBounds, editorWidget.SelectionAltColor, editorWidget.SelectionAltOffset, null);
|
||||
yield return new EditorSelectionAnnotationRenderable(CurrentDragBounds, editorWidget.SelectionMainColor, int2.Zero, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void Tick() { }
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
@@ -75,6 +75,10 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
return true;
|
||||
}
|
||||
|
||||
void IEditorBrush.TickRender(WorldRenderer wr, Actor self) { }
|
||||
IEnumerable<IRenderable> IEditorBrush.RenderAboveShroud(Actor self, WorldRenderer wr) { yield break; }
|
||||
IEnumerable<IRenderable> IEditorBrush.RenderAnnotations(Actor self, WorldRenderer wr) { yield break; }
|
||||
|
||||
public void Tick() { }
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
|
||||
@@ -23,13 +24,15 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
readonly World world;
|
||||
readonly EditorViewportControllerWidget editorWidget;
|
||||
readonly EditorActionManager editorActionManager;
|
||||
readonly EditorCursorLayer editorCursor;
|
||||
readonly IResourceLayer resourceLayer;
|
||||
readonly int cursorToken;
|
||||
|
||||
AddResourcesEditorAction action;
|
||||
bool resourceAdded;
|
||||
|
||||
CPos cell;
|
||||
readonly List<IRenderable> preview = new();
|
||||
readonly IResourceRenderer[] resourceRenderers;
|
||||
|
||||
public EditorResourceBrush(EditorViewportControllerWidget editorWidget, string resourceType, WorldRenderer wr)
|
||||
{
|
||||
this.editorWidget = editorWidget;
|
||||
@@ -37,11 +40,13 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
worldRenderer = wr;
|
||||
world = wr.World;
|
||||
editorActionManager = world.WorldActor.Trait<EditorActionManager>();
|
||||
editorCursor = world.WorldActor.Trait<EditorCursorLayer>();
|
||||
resourceLayer = world.WorldActor.Trait<IResourceLayer>();
|
||||
action = new AddResourcesEditorAction(resourceType, resourceLayer);
|
||||
|
||||
cursorToken = editorCursor.SetResource(wr, resourceType);
|
||||
resourceRenderers = world.WorldActor.TraitsImplementing<IResourceRenderer>().ToArray();
|
||||
cell = wr.Viewport.ViewToWorld(wr.Viewport.WorldToViewPx(Viewport.LastMousePos));
|
||||
UpdatePreview();
|
||||
|
||||
action = new AddResourcesEditorAction(resourceType, resourceLayer);
|
||||
}
|
||||
|
||||
public bool HandleMouseInput(MouseInput mi)
|
||||
@@ -61,9 +66,6 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
return false;
|
||||
}
|
||||
|
||||
if (editorCursor.CurrentToken != cursorToken)
|
||||
return false;
|
||||
|
||||
var cell = worldRenderer.Viewport.ViewToWorld(mi.Location);
|
||||
|
||||
if (mi.Button == MouseButton.Left && mi.Event != MouseInputEvent.Up && resourceLayer.CanAddResource(ResourceType, cell))
|
||||
@@ -81,12 +83,30 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdatePreview()
|
||||
{
|
||||
var pos = world.Map.CenterOfCell(cell);
|
||||
|
||||
preview.Clear();
|
||||
preview.AddRange(resourceRenderers.SelectMany(r => r.RenderPreview(worldRenderer, ResourceType, pos)));
|
||||
}
|
||||
|
||||
void IEditorBrush.TickRender(WorldRenderer wr, Actor self)
|
||||
{
|
||||
var currentCell = wr.Viewport.ViewToWorld(Viewport.LastMousePos);
|
||||
if (cell != currentCell)
|
||||
{
|
||||
cell = currentCell;
|
||||
UpdatePreview();
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<IRenderable> IEditorBrush.RenderAboveShroud(Actor self, WorldRenderer wr) { return preview; }
|
||||
IEnumerable<IRenderable> IEditorBrush.RenderAnnotations(Actor self, WorldRenderer wr) { yield break; }
|
||||
|
||||
public void Tick() { }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
editorCursor.Clear(cursorToken);
|
||||
}
|
||||
public void Dispose() { }
|
||||
}
|
||||
|
||||
readonly struct CellResource
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public sealed class EditorTileBrush : IEditorBrush
|
||||
{
|
||||
public readonly TerrainTemplateInfo TerrainTemplate;
|
||||
public readonly ushort Template;
|
||||
|
||||
readonly WorldRenderer worldRenderer;
|
||||
@@ -27,11 +28,14 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
readonly ITemplatedTerrainInfo terrainInfo;
|
||||
readonly EditorViewportControllerWidget editorWidget;
|
||||
readonly EditorActionManager editorActionManager;
|
||||
readonly EditorCursorLayer editorCursor;
|
||||
readonly int cursorToken;
|
||||
|
||||
bool painting;
|
||||
|
||||
readonly ITiledTerrainRenderer terrainRenderer;
|
||||
|
||||
CPos cell;
|
||||
readonly List<IRenderable> preview = new();
|
||||
|
||||
public EditorTileBrush(EditorViewportControllerWidget editorWidget, ushort id, WorldRenderer wr)
|
||||
{
|
||||
this.editorWidget = editorWidget;
|
||||
@@ -42,12 +46,12 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
throw new InvalidDataException("EditorTileBrush can only be used with template-based tilesets");
|
||||
|
||||
editorActionManager = world.WorldActor.Trait<EditorActionManager>();
|
||||
editorCursor = world.WorldActor.Trait<EditorCursorLayer>();
|
||||
terrainRenderer = world.WorldActor.Trait<ITiledTerrainRenderer>();
|
||||
|
||||
Template = id;
|
||||
|
||||
var template = terrainInfo.Templates.First(t => t.Value.Id == id).Value;
|
||||
cursorToken = editorCursor.SetTerrainTemplate(wr, template);
|
||||
TerrainTemplate = terrainInfo.Templates.First(t => t.Value.Id == id).Value;
|
||||
cell = wr.Viewport.ViewToWorld(wr.Viewport.WorldToViewPx(Viewport.LastMousePos));
|
||||
UpdatePreview();
|
||||
}
|
||||
|
||||
public bool HandleMouseInput(MouseInput mi)
|
||||
@@ -81,9 +85,6 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
if (mi.Event != MouseInputEvent.Down && mi.Event != MouseInputEvent.Move)
|
||||
return true;
|
||||
|
||||
if (editorCursor.CurrentToken != cursorToken)
|
||||
return false;
|
||||
|
||||
var cell = worldRenderer.Viewport.ViewToWorld(mi.Location);
|
||||
var isMoving = mi.Event == MouseInputEvent.Move;
|
||||
|
||||
@@ -143,12 +144,30 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
return false;
|
||||
}
|
||||
|
||||
void UpdatePreview()
|
||||
{
|
||||
var pos = world.Map.CenterOfCell(cell);
|
||||
|
||||
preview.Clear();
|
||||
preview.AddRange(terrainRenderer.RenderPreview(worldRenderer, TerrainTemplate, pos));
|
||||
}
|
||||
|
||||
void IEditorBrush.TickRender(WorldRenderer wr, Actor self)
|
||||
{
|
||||
var currentCell = wr.Viewport.ViewToWorld(Viewport.LastMousePos);
|
||||
if (cell != currentCell)
|
||||
{
|
||||
cell = currentCell;
|
||||
UpdatePreview();
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<IRenderable> IEditorBrush.RenderAboveShroud(Actor self, WorldRenderer wr) { return preview; }
|
||||
IEnumerable<IRenderable> IEditorBrush.RenderAnnotations(Actor self, WorldRenderer wr) { yield break; }
|
||||
|
||||
public void Tick() { }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
editorCursor.Clear(cursorToken);
|
||||
}
|
||||
public void Dispose() { }
|
||||
}
|
||||
|
||||
sealed class PaintTileEditorAction : IEditorAction
|
||||
|
||||
@@ -28,6 +28,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Size of partition bins (world pixels).")]
|
||||
public readonly int BinSize = 250;
|
||||
|
||||
[Desc("Facing of new actors.")]
|
||||
public readonly WAngle DefaultActorFacing = new(384);
|
||||
|
||||
void ICreatePlayersInfo.CreateServerPlayers(MapPreview map, Session lobbyInfo, List<GameInformation.Player> players, MersenneTwister playerRandom)
|
||||
{
|
||||
throw new NotImplementedException("EditorActorLayer must not be defined on the world actor.");
|
||||
|
||||
@@ -12,57 +12,24 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Terrain;
|
||||
using OpenRA.Mods.Common.Widgets;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
public enum EditorCursorType { None, Actor, TerrainTemplate, Resource }
|
||||
|
||||
[TraitLocation(SystemActors.EditorWorld)]
|
||||
[Desc("Required for the map editor to work. Attach this to the world actor.")]
|
||||
public class EditorCursorLayerInfo : TraitInfo, Requires<EditorActorLayerInfo>, Requires<ITiledTerrainRendererInfo>
|
||||
public class EditorCursorLayerInfo : TraitInfo<EditorCursorLayer>, Requires<EditorActorLayerInfo>, Requires<ITiledTerrainRendererInfo> { }
|
||||
|
||||
public class EditorCursorLayer : ITickRender, IRenderAboveShroud, IRenderAnnotations
|
||||
{
|
||||
public readonly WAngle PreviewFacing = new(384);
|
||||
IEditorBrush brush;
|
||||
|
||||
public override object Create(ActorInitializer init) { return new EditorCursorLayer(init.Self, this); }
|
||||
}
|
||||
static readonly IEnumerable<IRenderable> NoRenderables = Enumerable.Empty<IRenderable>();
|
||||
|
||||
public class EditorCursorLayer : IWorldLoaded, ITickRender, IRenderAboveShroud, IRenderAnnotations
|
||||
{
|
||||
readonly EditorCursorLayerInfo info;
|
||||
readonly EditorActorLayer editorLayer;
|
||||
readonly ITiledTerrainRenderer terrainRenderer;
|
||||
readonly World world;
|
||||
IResourceRenderer[] resourceRenderers;
|
||||
|
||||
public int CurrentToken { get; private set; }
|
||||
public EditorCursorType Type { get; private set; }
|
||||
public EditorActorPreview Actor { get; private set; }
|
||||
CPos actorLocation;
|
||||
SubCell actorSubCell;
|
||||
WVec actorCenterOffset;
|
||||
bool actorSharesCell;
|
||||
|
||||
public TerrainTemplateInfo TerrainTemplate { get; private set; }
|
||||
public string ResourceType { get; private set; }
|
||||
CPos terrainOrResourceCell;
|
||||
bool terrainOrResourceDirty;
|
||||
readonly List<IRenderable> terrainOrResourcePreview = new();
|
||||
|
||||
public EditorCursorLayer(Actor self, EditorCursorLayerInfo info)
|
||||
public void SetBrush(IEditorBrush brush)
|
||||
{
|
||||
this.info = info;
|
||||
world = self.World;
|
||||
editorLayer = self.Trait<EditorActorLayer>();
|
||||
terrainRenderer = self.Trait<ITiledTerrainRenderer>();
|
||||
|
||||
Type = EditorCursorType.None;
|
||||
}
|
||||
|
||||
void IWorldLoaded.WorldLoaded(World w, WorldRenderer wr)
|
||||
{
|
||||
resourceRenderers = w.WorldActor.TraitsImplementing<IResourceRenderer>().ToArray();
|
||||
this.brush = brush;
|
||||
}
|
||||
|
||||
void ITickRender.TickRender(WorldRenderer wr, Actor self)
|
||||
@@ -70,69 +37,15 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (wr.World.Type != WorldType.Editor)
|
||||
return;
|
||||
|
||||
if (Type == EditorCursorType.TerrainTemplate || Type == EditorCursorType.Resource)
|
||||
{
|
||||
var cell = wr.Viewport.ViewToWorld(Viewport.LastMousePos);
|
||||
if (terrainOrResourceCell != cell || terrainOrResourceDirty)
|
||||
{
|
||||
terrainOrResourceCell = cell;
|
||||
terrainOrResourceDirty = false;
|
||||
terrainOrResourcePreview.Clear();
|
||||
|
||||
var pos = world.Map.CenterOfCell(cell);
|
||||
if (Type == EditorCursorType.TerrainTemplate)
|
||||
terrainOrResourcePreview.AddRange(terrainRenderer.RenderPreview(wr, TerrainTemplate, pos));
|
||||
else
|
||||
terrainOrResourcePreview.AddRange(resourceRenderers.SelectMany(r => r.RenderPreview(wr, ResourceType, pos)));
|
||||
}
|
||||
}
|
||||
else if (Type == EditorCursorType.Actor)
|
||||
{
|
||||
// Offset mouse position by the center offset (in world pixels)
|
||||
var worldPx = wr.Viewport.ViewToWorldPx(Viewport.LastMousePos) - wr.ScreenPxOffset(actorCenterOffset);
|
||||
var cell = wr.Viewport.ViewToWorld(wr.Viewport.WorldToViewPx(worldPx));
|
||||
var subCell = actorSharesCell ? editorLayer.FreeSubCellAt(cell) : SubCell.Invalid;
|
||||
var updated = false;
|
||||
if (actorLocation != cell)
|
||||
{
|
||||
actorLocation = cell;
|
||||
Actor.ReplaceInit(new LocationInit(cell));
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (actorSubCell != subCell)
|
||||
{
|
||||
actorSubCell = subCell;
|
||||
|
||||
if (Actor.RemoveInits<SubCellInit>() > 0)
|
||||
updated = true;
|
||||
|
||||
var subcell = world.Map.Tiles.Contains(cell) ? editorLayer.FreeSubCellAt(cell) : SubCell.Invalid;
|
||||
if (subcell != SubCell.Invalid)
|
||||
{
|
||||
Actor.AddInit(new SubCellInit(subcell));
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (updated)
|
||||
Actor = new EditorActorPreview(wr, null, Actor.Export(), Actor.Owner);
|
||||
}
|
||||
brush?.TickRender(wr, self);
|
||||
}
|
||||
|
||||
static readonly IEnumerable<IRenderable> NoRenderables = Enumerable.Empty<IRenderable>();
|
||||
IEnumerable<IRenderable> IRenderAboveShroud.RenderAboveShroud(Actor self, WorldRenderer wr)
|
||||
{
|
||||
if (wr.World.Type != WorldType.Editor)
|
||||
return NoRenderables;
|
||||
|
||||
if (Type == EditorCursorType.TerrainTemplate || Type == EditorCursorType.Resource)
|
||||
return terrainOrResourcePreview;
|
||||
|
||||
if (Type == EditorCursorType.Actor)
|
||||
return Actor.Render().OrderBy(WorldRenderer.RenderableZPositionComparisonKey);
|
||||
|
||||
return NoRenderables;
|
||||
return brush?.RenderAboveShroud(self, wr) ?? NoRenderables;
|
||||
}
|
||||
|
||||
bool IRenderAboveShroud.SpatiallyPartitionable => false;
|
||||
@@ -142,88 +55,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (wr.World.Type != WorldType.Editor)
|
||||
return NoRenderables;
|
||||
|
||||
return Type == EditorCursorType.Actor ? Actor.RenderAnnotations() : NoRenderables;
|
||||
return brush?.RenderAnnotations(self, wr) ?? NoRenderables;
|
||||
}
|
||||
|
||||
bool IRenderAnnotations.SpatiallyPartitionable => false;
|
||||
|
||||
public int SetActor(WorldRenderer wr, ActorInfo actor, PlayerReference owner)
|
||||
{
|
||||
var ios = actor.TraitInfoOrDefault<IOccupySpaceInfo>();
|
||||
var buildingInfo = ios as BuildingInfo;
|
||||
actorCenterOffset = buildingInfo?.CenterOffset(world) ?? WVec.Zero;
|
||||
actorSharesCell = ios != null && ios.SharesCell;
|
||||
actorSubCell = SubCell.Invalid;
|
||||
|
||||
// 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 reference = new ActorReference(actor.Name)
|
||||
{
|
||||
new OwnerInit(ownerName),
|
||||
new FactionInit(owner.Faction)
|
||||
};
|
||||
|
||||
var worldPx = wr.Viewport.ViewToWorldPx(Viewport.LastMousePos) - wr.ScreenPxOffset(actorCenterOffset);
|
||||
var cell = wr.Viewport.ViewToWorld(wr.Viewport.WorldToViewPx(worldPx));
|
||||
|
||||
reference.Add(new LocationInit(cell));
|
||||
if (ios != null && ios.SharesCell)
|
||||
{
|
||||
actorSubCell = editorLayer.FreeSubCellAt(cell);
|
||||
if (actorSubCell != SubCell.Invalid)
|
||||
reference.Add(new SubCellInit(actorSubCell));
|
||||
}
|
||||
|
||||
if (actor.HasTraitInfo<IFacingInfo>())
|
||||
reference.Add(new FacingInit(info.PreviewFacing));
|
||||
|
||||
Type = EditorCursorType.Actor;
|
||||
Actor = new EditorActorPreview(wr, null, reference, owner);
|
||||
TerrainTemplate = null;
|
||||
ResourceType = null;
|
||||
|
||||
return ++CurrentToken;
|
||||
}
|
||||
|
||||
public int SetTerrainTemplate(WorldRenderer wr, TerrainTemplateInfo template)
|
||||
{
|
||||
terrainOrResourceCell = wr.Viewport.ViewToWorld(wr.Viewport.WorldToViewPx(Viewport.LastMousePos));
|
||||
|
||||
Type = EditorCursorType.TerrainTemplate;
|
||||
TerrainTemplate = template;
|
||||
Actor = null;
|
||||
ResourceType = null;
|
||||
terrainOrResourceDirty = true;
|
||||
|
||||
return ++CurrentToken;
|
||||
}
|
||||
|
||||
public int SetResource(WorldRenderer wr, string resourceType)
|
||||
{
|
||||
terrainOrResourceCell = wr.Viewport.ViewToWorld(wr.Viewport.WorldToViewPx(Viewport.LastMousePos));
|
||||
|
||||
Type = EditorCursorType.Resource;
|
||||
ResourceType = resourceType;
|
||||
Actor = null;
|
||||
TerrainTemplate = null;
|
||||
terrainOrResourceDirty = true;
|
||||
|
||||
return ++CurrentToken;
|
||||
}
|
||||
|
||||
public void Clear(int token)
|
||||
{
|
||||
if (token != CurrentToken)
|
||||
return;
|
||||
|
||||
Type = EditorCursorType.None;
|
||||
Actor = null;
|
||||
TerrainTemplate = null;
|
||||
ResourceType = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
#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.Collections.Generic;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Graphics;
|
||||
using OpenRA.Mods.Common.Widgets;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Widgets;
|
||||
using Color = OpenRA.Primitives.Color;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[TraitLocation(SystemActors.EditorWorld)]
|
||||
[Desc("Renders the selection grid in the editor.")]
|
||||
public class EditorSelectionLayerInfo : TraitInfo, Requires<LoadWidgetAtGameStartInfo>, IEditorSelectionLayer
|
||||
{
|
||||
[Desc("Main color of the selection grid.")]
|
||||
public readonly Color MainColor = Color.White;
|
||||
|
||||
[Desc("Alternate color of the selection grid.")]
|
||||
public readonly Color AltColor = Color.Black;
|
||||
|
||||
[Desc("Main color of the paste grid.")]
|
||||
public readonly Color PasteColor = Color.FromArgb(0xFF4CFF00);
|
||||
|
||||
[Desc("Thickness of the selection grid lines.")]
|
||||
public readonly int LineThickness = 1;
|
||||
|
||||
[Desc("Render offset of the secondary grid lines.")]
|
||||
public readonly int2 AltPixelOffset = new(1, 1);
|
||||
|
||||
public override object Create(ActorInitializer init) { return new EditorSelectionLayer(this); }
|
||||
}
|
||||
|
||||
public class EditorSelectionLayer : IRenderAnnotations, IWorldLoaded
|
||||
{
|
||||
readonly EditorSelectionLayerInfo info;
|
||||
EditorViewportControllerWidget editor;
|
||||
|
||||
public EditorSelectionLayer(EditorSelectionLayerInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
void IWorldLoaded.WorldLoaded(World w, WorldRenderer wr)
|
||||
{
|
||||
var worldRoot = Ui.Root.Get<ContainerWidget>("EDITOR_WORLD_ROOT");
|
||||
editor = worldRoot.Get<EditorViewportControllerWidget>("MAP_EDITOR");
|
||||
}
|
||||
|
||||
IEnumerable<IRenderable> IRenderAnnotations.RenderAnnotations(Actor self, WorldRenderer wr)
|
||||
{
|
||||
if (editor.DefaultBrush.CurrentDragBounds != null)
|
||||
{
|
||||
yield return new EditorSelectionAnnotationRenderable(editor.DefaultBrush.CurrentDragBounds, info.AltColor, info.AltPixelOffset, null);
|
||||
yield return new EditorSelectionAnnotationRenderable(editor.DefaultBrush.CurrentDragBounds, info.MainColor, int2.Zero, null);
|
||||
}
|
||||
|
||||
if (editor.CurrentBrush is EditorCopyPasteBrush pasteBrush && pasteBrush.PastePreviewPosition != null)
|
||||
{
|
||||
yield return new EditorSelectionAnnotationRenderable(pasteBrush.Region, info.AltColor, info.AltPixelOffset, pasteBrush.PastePreviewPosition);
|
||||
yield return new EditorSelectionAnnotationRenderable(pasteBrush.Region, info.PasteColor, int2.Zero, pasteBrush.PastePreviewPosition);
|
||||
}
|
||||
}
|
||||
|
||||
bool IRenderAnnotations.SpatiallyPartitionable => false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#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.Collections.Generic;
|
||||
|
||||
namespace OpenRA.Mods.Common.UpdateRules.Rules
|
||||
{
|
||||
public class MovePreviewFacing : UpdateRule
|
||||
{
|
||||
public override string Name => "Move map editor preview facing to EditorActorLayer";
|
||||
|
||||
public override string Description =>
|
||||
"PreviewFacing property was moved from the EditorCursorLayer to the EditorActorLayer.";
|
||||
|
||||
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNodeBuilder actorNode)
|
||||
{
|
||||
var cursorLayer = actorNode.LastChildMatching("EditorCursorLayer");
|
||||
if (cursorLayer == null || cursorLayer.IsRemoval())
|
||||
yield break;
|
||||
|
||||
var node = cursorLayer.LastChildMatching("PreviewFacing");
|
||||
cursorLayer.RemoveNodes("PreviewFacing");
|
||||
if (node == null || node.IsRemoval())
|
||||
yield break;
|
||||
|
||||
var actorLayer = actorNode.LastChildMatching("EditorActorLayer");
|
||||
if (actorLayer != null && !actorLayer.IsRemoval())
|
||||
{
|
||||
node.RenameKey("DefaultActorFacing");
|
||||
actorLayer.AddNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,7 @@ namespace OpenRA.Mods.Common.UpdateRules
|
||||
new RemoveEditorSelectionLayerProperties(),
|
||||
new AddMarkerLayerOverlay(),
|
||||
new AddSupportPowerBlockedCursor(),
|
||||
new MovePreviewFacing(),
|
||||
|
||||
// Execute these rules last to avoid premature yaml merge crashes.
|
||||
new ReplaceCloakPalette(),
|
||||
|
||||
@@ -11,12 +11,23 @@
|
||||
|
||||
using System;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Widgets;
|
||||
using Color = OpenRA.Primitives.Color;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
{
|
||||
public class EditorViewportControllerWidget : Widget
|
||||
{
|
||||
[Desc("Main color of the selection grid.")]
|
||||
public readonly Color SelectionMainColor = Color.White;
|
||||
|
||||
[Desc("Alternate color of the selection grid.")]
|
||||
public readonly Color SelectionAltColor = Color.Black;
|
||||
|
||||
[Desc("Main color of the copy / paste grid.")]
|
||||
public readonly Color PasteColor = Color.FromArgb(0xFF4CFF00);
|
||||
|
||||
public IEditorBrush CurrentBrush { get; private set; }
|
||||
|
||||
public readonly string TooltipContainer;
|
||||
@@ -27,6 +38,8 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
|
||||
readonly Lazy<TooltipContainerWidget> tooltipContainer;
|
||||
readonly WorldRenderer worldRenderer;
|
||||
readonly EditorCursorLayer editorCursor;
|
||||
public int2 SelectionAltOffset { get; }
|
||||
|
||||
bool enableTooltips;
|
||||
|
||||
@@ -37,8 +50,15 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
tooltipContainer = Exts.Lazy(() => Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
|
||||
CurrentBrush = DefaultBrush = new EditorDefaultBrush(this, worldRenderer);
|
||||
|
||||
editorCursor = worldRenderer.World.WorldActor.Trait<EditorCursorLayer>();
|
||||
editorCursor.SetBrush(CurrentBrush);
|
||||
|
||||
// Allow zooming out to full map size
|
||||
worldRenderer.Viewport.UnlockMinimumZoom(0.25f);
|
||||
|
||||
SelectionAltOffset = worldRenderer.World.Map.Grid.Type == MapGridType.Rectangular
|
||||
? new int2(1, 1)
|
||||
: new int2(0, 1);
|
||||
}
|
||||
|
||||
public void ClearBrush() { SetBrush(null); }
|
||||
@@ -50,6 +70,7 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
CurrentBrush = brush ?? DefaultBrush;
|
||||
|
||||
BrushChanged?.Invoke();
|
||||
editorCursor.SetBrush(CurrentBrush);
|
||||
}
|
||||
|
||||
public override void MouseEntered()
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
readonly DropDownButtonWidget ownersDropDown;
|
||||
readonly Ruleset mapRules;
|
||||
readonly ActorSelectorActor[] allActors;
|
||||
readonly EditorCursorLayer editorCursor;
|
||||
readonly EditorViewportControllerWidget editor;
|
||||
|
||||
PlayerReference selectedOwner;
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
mapRules = world.Map.Rules;
|
||||
ownersDropDown = widget.Get<DropDownButtonWidget>("OWNERS_DROPDOWN");
|
||||
editorCursor = world.WorldActor.Trait<EditorCursorLayer>();
|
||||
editor = widget.Parent.Parent.Get<EditorViewportControllerWidget>("MAP_EDITOR");
|
||||
var editorLayer = world.WorldActor.Trait<EditorActorLayer>();
|
||||
|
||||
selectedOwner = editorLayer.Players.Players.Values.First();
|
||||
@@ -167,9 +167,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
ownersDropDown.TextColor = option.Color;
|
||||
InitializePreviews();
|
||||
|
||||
var actor = editorCursor.Actor;
|
||||
if (actor != null)
|
||||
if (editor.CurrentBrush is EditorActorBrush brush)
|
||||
{
|
||||
var actor = brush.Preview;
|
||||
actor.Owner = option;
|
||||
actor.ReplaceInit(new OwnerInit(option.Name));
|
||||
actor.ReplaceInit(new FactionInit(option.Faction));
|
||||
@@ -204,7 +204,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
try
|
||||
{
|
||||
var item = ScrollItemWidget.Setup(ItemTemplate,
|
||||
() => editorCursor.Type == EditorCursorType.Actor && editorCursor.Actor.Info == actor,
|
||||
() => Editor.CurrentBrush is EditorActorBrush eab && eab.Preview.Info == actor,
|
||||
() => Editor.SetBrush(new EditorActorBrush(Editor, actor, selectedOwner, WorldRenderer)));
|
||||
|
||||
var preview = item.Get<ActorPreviewWidget>("ACTOR_PREVIEW");
|
||||
|
||||
@@ -19,7 +19,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
readonly EditorViewportControllerWidget editor;
|
||||
readonly WorldRenderer worldRenderer;
|
||||
readonly EditorCursorLayer editorCursor;
|
||||
|
||||
readonly ScrollPanelWidget layerTemplateList;
|
||||
readonly ScrollItemWidget layerPreviewTemplate;
|
||||
@@ -29,8 +28,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
this.worldRenderer = worldRenderer;
|
||||
editor = widget.Parent.Parent.Get<EditorViewportControllerWidget>("MAP_EDITOR");
|
||||
editorCursor = worldRenderer.World.WorldActor.Trait<EditorCursorLayer>();
|
||||
|
||||
layerTemplateList = widget.Get<ScrollPanelWidget>("LAYERTEMPLATE_LIST");
|
||||
layerTemplateList.Layout = new GridLayout(layerTemplateList);
|
||||
layerPreviewTemplate = layerTemplateList.Get<ScrollItemWidget>("LAYERPREVIEW_TEMPLATE");
|
||||
@@ -46,7 +43,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
foreach (var resourceType in resourceRenderer.ResourceTypes)
|
||||
{
|
||||
var newResourcePreviewTemplate = ScrollItemWidget.Setup(layerPreviewTemplate,
|
||||
() => editorCursor.Type == EditorCursorType.Resource && editorCursor.ResourceType == resourceType,
|
||||
() => editor.CurrentBrush is EditorResourceBrush brush && brush.ResourceType == resourceType,
|
||||
() => editor.SetBrush(new EditorResourceBrush(editor, resourceType, worldRenderer)));
|
||||
|
||||
newResourcePreviewTemplate.Bounds.X = 0;
|
||||
|
||||
@@ -15,7 +15,6 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Terrain;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
@@ -40,7 +39,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
|
||||
readonly ITemplatedTerrainInfo terrainInfo;
|
||||
readonly TileSelectorTemplate[] allTemplates;
|
||||
readonly EditorCursorLayer editorCursor;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public TileSelectorLogic(Widget widget, ModData modData, World world, WorldRenderer worldRenderer)
|
||||
@@ -51,7 +49,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
throw new InvalidDataException("TileSelectorLogic requires a template-based tileset.");
|
||||
|
||||
allTemplates = terrainInfo.Templates.Values.Select(t => new TileSelectorTemplate(t)).ToArray();
|
||||
editorCursor = world.WorldActor.Trait<EditorCursorLayer>();
|
||||
|
||||
allCategories = allTemplates.SelectMany(t => t.Categories)
|
||||
.Distinct()
|
||||
@@ -108,7 +105,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
|
||||
var tileId = t.Template.Id;
|
||||
var item = ScrollItemWidget.Setup(ItemTemplate,
|
||||
() => editorCursor.Type == EditorCursorType.TerrainTemplate && editorCursor.TerrainTemplate.Id == tileId,
|
||||
() => Editor.CurrentBrush is EditorTileBrush editorCursor && editorCursor.TerrainTemplate.Id == tileId,
|
||||
() => Editor.SetBrush(new EditorTileBrush(Editor, tileId, WorldRenderer)));
|
||||
|
||||
var preview = item.Get<TerrainTemplatePreviewWidget>("TILE_PREVIEW");
|
||||
|
||||
@@ -294,7 +294,6 @@ EditorWorld:
|
||||
TerrainType: BlueTiberium
|
||||
AllowedTerrainTypes: Clear, Road
|
||||
MaxDensity: 12
|
||||
EditorSelectionLayer:
|
||||
LoadWidgetAtGameStart:
|
||||
EditorActionManager:
|
||||
BuildableTerrainOverlay:
|
||||
|
||||
@@ -260,7 +260,6 @@ EditorWorld:
|
||||
TerrainType: Spice
|
||||
AllowedTerrainTypes: SpiceSand
|
||||
MaxDensity: 20
|
||||
EditorSelectionLayer:
|
||||
LoadWidgetAtGameStart:
|
||||
EditorActionManager:
|
||||
BuildableTerrainOverlay:
|
||||
|
||||
@@ -316,7 +316,6 @@ EditorWorld:
|
||||
TerrainType: Gems
|
||||
AllowedTerrainTypes: Clear, Road
|
||||
MaxDensity: 3
|
||||
EditorSelectionLayer:
|
||||
LoadWidgetAtGameStart:
|
||||
EditorActionManager:
|
||||
BuildableTerrainOverlay:
|
||||
|
||||
@@ -422,8 +422,6 @@ EditorWorld:
|
||||
AllowedTerrainTypes: Clear, Rough, DirtRoad
|
||||
MaxDensity: 2
|
||||
VeinholeActors: veinhole
|
||||
EditorSelectionLayer:
|
||||
AltPixelOffset: 0,1
|
||||
LoadWidgetAtGameStart:
|
||||
EditorActionManager:
|
||||
BuildableTerrainOverlay:
|
||||
|
||||
Reference in New Issue
Block a user