Make brush rendering self-contained

This commit is contained in:
Gustas
2024-08-16 17:53:49 +03:00
committed by Paul Chote
parent b073155018
commit 87850378c7
19 changed files with 259 additions and 314 deletions

View File

@@ -9,30 +9,65 @@
*/ */
#endregion #endregion
using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Widgets namespace OpenRA.Mods.Common.Widgets
{ {
public sealed class EditorActorBrush : IEditorBrush public sealed class EditorActorBrush : IEditorBrush
{ {
public EditorActorPreview Preview;
readonly World world; readonly World world;
readonly EditorActorLayer editorLayer; readonly EditorActorLayer editorLayer;
readonly EditorCursorLayer editorCursor;
readonly EditorActionManager editorActionManager; readonly EditorActionManager editorActionManager;
readonly EditorViewportControllerWidget editorWidget; 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) public EditorActorBrush(EditorViewportControllerWidget editorWidget, ActorInfo actor, PlayerReference owner, WorldRenderer wr)
{ {
this.editorWidget = editorWidget; this.editorWidget = editorWidget;
world = wr.World; world = wr.World;
editorLayer = world.WorldActor.Trait<EditorActorLayer>(); editorLayer = world.WorldActor.Trait<EditorActorLayer>();
editorCursor = world.WorldActor.Trait<EditorCursorLayer>();
editorActionManager = world.WorldActor.Trait<EditorActionManager>(); 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) public bool HandleMouseInput(MouseInput mi)
@@ -52,29 +87,56 @@ namespace OpenRA.Mods.Common.Widgets
return false; return false;
} }
if (editorCursor.CurrentToken != cursorToken)
return false;
if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Down) if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Down)
{ {
// Check the actor is inside the map // Check the actor is inside the map
var actor = editorCursor.Actor; if (!Preview.Footprint.All(c => world.Map.Tiles.Contains(c.Key)))
if (!actor.Footprint.All(c => world.Map.Tiles.Contains(c.Key)))
return true; return true;
var action = new AddActorAction(editorLayer, actor.Export()); var action = new AddActorAction(editorLayer, Preview.Export());
editorActionManager.Add(action); editorActionManager.Add(action);
} }
return true; 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 Tick() { }
public void Dispose() public void Dispose() { }
{
editorCursor.Clear(cursorToken);
}
} }
sealed class AddActorAction : IEditorAction sealed class AddActorAction : IEditorAction

View File

@@ -14,6 +14,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common.EditorBrushes; using OpenRA.Mods.Common.EditorBrushes;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
namespace OpenRA.Mods.Common.Widgets namespace OpenRA.Mods.Common.Widgets
@@ -94,14 +95,23 @@ namespace OpenRA.Mods.Common.Widgets
return false; 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() public void Tick()
{ {
PastePreviewPosition = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos); PastePreviewPosition = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos);
} }
public void Dispose() public void Dispose() { }
{
}
} }
sealed class CopyPasteEditorAction : IEditorAction sealed class CopyPasteEditorAction : IEditorAction

View File

@@ -10,7 +10,9 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Widgets; using OpenRA.Widgets;
@@ -20,6 +22,10 @@ namespace OpenRA.Mods.Common.Widgets
{ {
bool HandleMouseInput(MouseInput mi); bool HandleMouseInput(MouseInput mi);
void Tick(); 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 public class EditorSelection
@@ -255,6 +261,17 @@ namespace OpenRA.Mods.Common.Widgets
return true; 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 Tick() { }
public void Dispose() { } public void Dispose() { }

View File

@@ -75,6 +75,10 @@ namespace OpenRA.Mods.Common.Widgets
return true; 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 Tick() { }
public void Dispose() { } public void Dispose() { }

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
@@ -23,13 +24,15 @@ namespace OpenRA.Mods.Common.Widgets
readonly World world; readonly World world;
readonly EditorViewportControllerWidget editorWidget; readonly EditorViewportControllerWidget editorWidget;
readonly EditorActionManager editorActionManager; readonly EditorActionManager editorActionManager;
readonly EditorCursorLayer editorCursor;
readonly IResourceLayer resourceLayer; readonly IResourceLayer resourceLayer;
readonly int cursorToken;
AddResourcesEditorAction action; AddResourcesEditorAction action;
bool resourceAdded; bool resourceAdded;
CPos cell;
readonly List<IRenderable> preview = new();
readonly IResourceRenderer[] resourceRenderers;
public EditorResourceBrush(EditorViewportControllerWidget editorWidget, string resourceType, WorldRenderer wr) public EditorResourceBrush(EditorViewportControllerWidget editorWidget, string resourceType, WorldRenderer wr)
{ {
this.editorWidget = editorWidget; this.editorWidget = editorWidget;
@@ -37,11 +40,13 @@ namespace OpenRA.Mods.Common.Widgets
worldRenderer = wr; worldRenderer = wr;
world = wr.World; world = wr.World;
editorActionManager = world.WorldActor.Trait<EditorActionManager>(); editorActionManager = world.WorldActor.Trait<EditorActionManager>();
editorCursor = world.WorldActor.Trait<EditorCursorLayer>();
resourceLayer = world.WorldActor.Trait<IResourceLayer>(); 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) public bool HandleMouseInput(MouseInput mi)
@@ -61,9 +66,6 @@ namespace OpenRA.Mods.Common.Widgets
return false; return false;
} }
if (editorCursor.CurrentToken != cursorToken)
return false;
var cell = worldRenderer.Viewport.ViewToWorld(mi.Location); var cell = worldRenderer.Viewport.ViewToWorld(mi.Location);
if (mi.Button == MouseButton.Left && mi.Event != MouseInputEvent.Up && resourceLayer.CanAddResource(ResourceType, cell)) if (mi.Button == MouseButton.Left && mi.Event != MouseInputEvent.Up && resourceLayer.CanAddResource(ResourceType, cell))
@@ -81,12 +83,30 @@ namespace OpenRA.Mods.Common.Widgets
return true; 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 Tick() { }
public void Dispose() public void Dispose() { }
{
editorCursor.Clear(cursorToken);
}
} }
readonly struct CellResource readonly struct CellResource

View File

@@ -20,6 +20,7 @@ namespace OpenRA.Mods.Common.Widgets
{ {
public sealed class EditorTileBrush : IEditorBrush public sealed class EditorTileBrush : IEditorBrush
{ {
public readonly TerrainTemplateInfo TerrainTemplate;
public readonly ushort Template; public readonly ushort Template;
readonly WorldRenderer worldRenderer; readonly WorldRenderer worldRenderer;
@@ -27,11 +28,14 @@ namespace OpenRA.Mods.Common.Widgets
readonly ITemplatedTerrainInfo terrainInfo; readonly ITemplatedTerrainInfo terrainInfo;
readonly EditorViewportControllerWidget editorWidget; readonly EditorViewportControllerWidget editorWidget;
readonly EditorActionManager editorActionManager; readonly EditorActionManager editorActionManager;
readonly EditorCursorLayer editorCursor;
readonly int cursorToken;
bool painting; bool painting;
readonly ITiledTerrainRenderer terrainRenderer;
CPos cell;
readonly List<IRenderable> preview = new();
public EditorTileBrush(EditorViewportControllerWidget editorWidget, ushort id, WorldRenderer wr) public EditorTileBrush(EditorViewportControllerWidget editorWidget, ushort id, WorldRenderer wr)
{ {
this.editorWidget = editorWidget; this.editorWidget = editorWidget;
@@ -42,12 +46,12 @@ namespace OpenRA.Mods.Common.Widgets
throw new InvalidDataException("EditorTileBrush can only be used with template-based tilesets"); throw new InvalidDataException("EditorTileBrush can only be used with template-based tilesets");
editorActionManager = world.WorldActor.Trait<EditorActionManager>(); editorActionManager = world.WorldActor.Trait<EditorActionManager>();
editorCursor = world.WorldActor.Trait<EditorCursorLayer>(); terrainRenderer = world.WorldActor.Trait<ITiledTerrainRenderer>();
Template = id; Template = id;
TerrainTemplate = terrainInfo.Templates.First(t => t.Value.Id == id).Value;
var template = terrainInfo.Templates.First(t => t.Value.Id == id).Value; cell = wr.Viewport.ViewToWorld(wr.Viewport.WorldToViewPx(Viewport.LastMousePos));
cursorToken = editorCursor.SetTerrainTemplate(wr, template); UpdatePreview();
} }
public bool HandleMouseInput(MouseInput mi) public bool HandleMouseInput(MouseInput mi)
@@ -81,9 +85,6 @@ namespace OpenRA.Mods.Common.Widgets
if (mi.Event != MouseInputEvent.Down && mi.Event != MouseInputEvent.Move) if (mi.Event != MouseInputEvent.Down && mi.Event != MouseInputEvent.Move)
return true; return true;
if (editorCursor.CurrentToken != cursorToken)
return false;
var cell = worldRenderer.Viewport.ViewToWorld(mi.Location); var cell = worldRenderer.Viewport.ViewToWorld(mi.Location);
var isMoving = mi.Event == MouseInputEvent.Move; var isMoving = mi.Event == MouseInputEvent.Move;
@@ -143,12 +144,30 @@ namespace OpenRA.Mods.Common.Widgets
return false; 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 Tick() { }
public void Dispose() public void Dispose() { }
{
editorCursor.Clear(cursorToken);
}
} }
sealed class PaintTileEditorAction : IEditorAction sealed class PaintTileEditorAction : IEditorAction

View File

@@ -28,6 +28,9 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Size of partition bins (world pixels).")] [Desc("Size of partition bins (world pixels).")]
public readonly int BinSize = 250; 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) 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."); throw new NotImplementedException("EditorActorLayer must not be defined on the world actor.");

View File

@@ -12,57 +12,24 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common.Terrain; using OpenRA.Mods.Common.Widgets;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
public enum EditorCursorType { None, Actor, TerrainTemplate, Resource }
[TraitLocation(SystemActors.EditorWorld)] [TraitLocation(SystemActors.EditorWorld)]
[Desc("Required for the map editor to work. Attach this to the world actor.")] [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 public void SetBrush(IEditorBrush brush)
{
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)
{ {
this.info = info; this.brush = brush;
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();
} }
void ITickRender.TickRender(WorldRenderer wr, Actor self) void ITickRender.TickRender(WorldRenderer wr, Actor self)
@@ -70,69 +37,15 @@ namespace OpenRA.Mods.Common.Traits
if (wr.World.Type != WorldType.Editor) if (wr.World.Type != WorldType.Editor)
return; return;
if (Type == EditorCursorType.TerrainTemplate || Type == EditorCursorType.Resource) brush?.TickRender(wr, self);
{
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);
}
} }
static readonly IEnumerable<IRenderable> NoRenderables = Enumerable.Empty<IRenderable>();
IEnumerable<IRenderable> IRenderAboveShroud.RenderAboveShroud(Actor self, WorldRenderer wr) IEnumerable<IRenderable> IRenderAboveShroud.RenderAboveShroud(Actor self, WorldRenderer wr)
{ {
if (wr.World.Type != WorldType.Editor) if (wr.World.Type != WorldType.Editor)
return NoRenderables; return NoRenderables;
if (Type == EditorCursorType.TerrainTemplate || Type == EditorCursorType.Resource) return brush?.RenderAboveShroud(self, wr) ?? NoRenderables;
return terrainOrResourcePreview;
if (Type == EditorCursorType.Actor)
return Actor.Render().OrderBy(WorldRenderer.RenderableZPositionComparisonKey);
return NoRenderables;
} }
bool IRenderAboveShroud.SpatiallyPartitionable => false; bool IRenderAboveShroud.SpatiallyPartitionable => false;
@@ -142,88 +55,9 @@ namespace OpenRA.Mods.Common.Traits
if (wr.World.Type != WorldType.Editor) if (wr.World.Type != WorldType.Editor)
return NoRenderables; return NoRenderables;
return Type == EditorCursorType.Actor ? Actor.RenderAnnotations() : NoRenderables; return brush?.RenderAnnotations(self, wr) ?? NoRenderables;
} }
bool IRenderAnnotations.SpatiallyPartitionable => false; 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;
}
} }
} }

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -88,6 +88,7 @@ namespace OpenRA.Mods.Common.UpdateRules
new RemoveEditorSelectionLayerProperties(), new RemoveEditorSelectionLayerProperties(),
new AddMarkerLayerOverlay(), new AddMarkerLayerOverlay(),
new AddSupportPowerBlockedCursor(), new AddSupportPowerBlockedCursor(),
new MovePreviewFacing(),
// Execute these rules last to avoid premature yaml merge crashes. // Execute these rules last to avoid premature yaml merge crashes.
new ReplaceCloakPalette(), new ReplaceCloakPalette(),

View File

@@ -11,12 +11,23 @@
using System; using System;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
using OpenRA.Widgets; using OpenRA.Widgets;
using Color = OpenRA.Primitives.Color;
namespace OpenRA.Mods.Common.Widgets namespace OpenRA.Mods.Common.Widgets
{ {
public class EditorViewportControllerWidget : Widget 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 IEditorBrush CurrentBrush { get; private set; }
public readonly string TooltipContainer; public readonly string TooltipContainer;
@@ -27,6 +38,8 @@ namespace OpenRA.Mods.Common.Widgets
readonly Lazy<TooltipContainerWidget> tooltipContainer; readonly Lazy<TooltipContainerWidget> tooltipContainer;
readonly WorldRenderer worldRenderer; readonly WorldRenderer worldRenderer;
readonly EditorCursorLayer editorCursor;
public int2 SelectionAltOffset { get; }
bool enableTooltips; bool enableTooltips;
@@ -37,8 +50,15 @@ namespace OpenRA.Mods.Common.Widgets
tooltipContainer = Exts.Lazy(() => Ui.Root.Get<TooltipContainerWidget>(TooltipContainer)); tooltipContainer = Exts.Lazy(() => Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
CurrentBrush = DefaultBrush = new EditorDefaultBrush(this, worldRenderer); CurrentBrush = DefaultBrush = new EditorDefaultBrush(this, worldRenderer);
editorCursor = worldRenderer.World.WorldActor.Trait<EditorCursorLayer>();
editorCursor.SetBrush(CurrentBrush);
// Allow zooming out to full map size // Allow zooming out to full map size
worldRenderer.Viewport.UnlockMinimumZoom(0.25f); 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); } public void ClearBrush() { SetBrush(null); }
@@ -50,6 +70,7 @@ namespace OpenRA.Mods.Common.Widgets
CurrentBrush = brush ?? DefaultBrush; CurrentBrush = brush ?? DefaultBrush;
BrushChanged?.Invoke(); BrushChanged?.Invoke();
editorCursor.SetBrush(CurrentBrush);
} }
public override void MouseEntered() public override void MouseEntered()

View File

@@ -43,7 +43,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly DropDownButtonWidget ownersDropDown; readonly DropDownButtonWidget ownersDropDown;
readonly Ruleset mapRules; readonly Ruleset mapRules;
readonly ActorSelectorActor[] allActors; readonly ActorSelectorActor[] allActors;
readonly EditorCursorLayer editorCursor; readonly EditorViewportControllerWidget editor;
PlayerReference selectedOwner; PlayerReference selectedOwner;
@@ -53,7 +53,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
mapRules = world.Map.Rules; mapRules = world.Map.Rules;
ownersDropDown = widget.Get<DropDownButtonWidget>("OWNERS_DROPDOWN"); 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>(); var editorLayer = world.WorldActor.Trait<EditorActorLayer>();
selectedOwner = editorLayer.Players.Players.Values.First(); selectedOwner = editorLayer.Players.Players.Values.First();
@@ -167,9 +167,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
ownersDropDown.TextColor = option.Color; ownersDropDown.TextColor = option.Color;
InitializePreviews(); InitializePreviews();
var actor = editorCursor.Actor; if (editor.CurrentBrush is EditorActorBrush brush)
if (actor != null)
{ {
var actor = brush.Preview;
actor.Owner = option; actor.Owner = option;
actor.ReplaceInit(new OwnerInit(option.Name)); actor.ReplaceInit(new OwnerInit(option.Name));
actor.ReplaceInit(new FactionInit(option.Faction)); actor.ReplaceInit(new FactionInit(option.Faction));
@@ -204,7 +204,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
try try
{ {
var item = ScrollItemWidget.Setup(ItemTemplate, 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))); () => Editor.SetBrush(new EditorActorBrush(Editor, actor, selectedOwner, WorldRenderer)));
var preview = item.Get<ActorPreviewWidget>("ACTOR_PREVIEW"); var preview = item.Get<ActorPreviewWidget>("ACTOR_PREVIEW");

View File

@@ -19,7 +19,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
readonly EditorViewportControllerWidget editor; readonly EditorViewportControllerWidget editor;
readonly WorldRenderer worldRenderer; readonly WorldRenderer worldRenderer;
readonly EditorCursorLayer editorCursor;
readonly ScrollPanelWidget layerTemplateList; readonly ScrollPanelWidget layerTemplateList;
readonly ScrollItemWidget layerPreviewTemplate; readonly ScrollItemWidget layerPreviewTemplate;
@@ -29,8 +28,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
this.worldRenderer = worldRenderer; this.worldRenderer = worldRenderer;
editor = widget.Parent.Parent.Get<EditorViewportControllerWidget>("MAP_EDITOR"); editor = widget.Parent.Parent.Get<EditorViewportControllerWidget>("MAP_EDITOR");
editorCursor = worldRenderer.World.WorldActor.Trait<EditorCursorLayer>();
layerTemplateList = widget.Get<ScrollPanelWidget>("LAYERTEMPLATE_LIST"); layerTemplateList = widget.Get<ScrollPanelWidget>("LAYERTEMPLATE_LIST");
layerTemplateList.Layout = new GridLayout(layerTemplateList); layerTemplateList.Layout = new GridLayout(layerTemplateList);
layerPreviewTemplate = layerTemplateList.Get<ScrollItemWidget>("LAYERPREVIEW_TEMPLATE"); layerPreviewTemplate = layerTemplateList.Get<ScrollItemWidget>("LAYERPREVIEW_TEMPLATE");
@@ -46,7 +43,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
foreach (var resourceType in resourceRenderer.ResourceTypes) foreach (var resourceType in resourceRenderer.ResourceTypes)
{ {
var newResourcePreviewTemplate = ScrollItemWidget.Setup(layerPreviewTemplate, 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))); () => editor.SetBrush(new EditorResourceBrush(editor, resourceType, worldRenderer)));
newResourcePreviewTemplate.Bounds.X = 0; newResourcePreviewTemplate.Bounds.X = 0;

View File

@@ -15,7 +15,6 @@ using System.IO;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common.Terrain; using OpenRA.Mods.Common.Terrain;
using OpenRA.Mods.Common.Traits;
using OpenRA.Widgets; using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic namespace OpenRA.Mods.Common.Widgets.Logic
@@ -40,7 +39,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly ITemplatedTerrainInfo terrainInfo; readonly ITemplatedTerrainInfo terrainInfo;
readonly TileSelectorTemplate[] allTemplates; readonly TileSelectorTemplate[] allTemplates;
readonly EditorCursorLayer editorCursor;
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
public TileSelectorLogic(Widget widget, ModData modData, World world, WorldRenderer worldRenderer) 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."); throw new InvalidDataException("TileSelectorLogic requires a template-based tileset.");
allTemplates = terrainInfo.Templates.Values.Select(t => new TileSelectorTemplate(t)).ToArray(); allTemplates = terrainInfo.Templates.Values.Select(t => new TileSelectorTemplate(t)).ToArray();
editorCursor = world.WorldActor.Trait<EditorCursorLayer>();
allCategories = allTemplates.SelectMany(t => t.Categories) allCategories = allTemplates.SelectMany(t => t.Categories)
.Distinct() .Distinct()
@@ -108,7 +105,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var tileId = t.Template.Id; var tileId = t.Template.Id;
var item = ScrollItemWidget.Setup(ItemTemplate, 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))); () => Editor.SetBrush(new EditorTileBrush(Editor, tileId, WorldRenderer)));
var preview = item.Get<TerrainTemplatePreviewWidget>("TILE_PREVIEW"); var preview = item.Get<TerrainTemplatePreviewWidget>("TILE_PREVIEW");

View File

@@ -294,7 +294,6 @@ EditorWorld:
TerrainType: BlueTiberium TerrainType: BlueTiberium
AllowedTerrainTypes: Clear, Road AllowedTerrainTypes: Clear, Road
MaxDensity: 12 MaxDensity: 12
EditorSelectionLayer:
LoadWidgetAtGameStart: LoadWidgetAtGameStart:
EditorActionManager: EditorActionManager:
BuildableTerrainOverlay: BuildableTerrainOverlay:

View File

@@ -260,7 +260,6 @@ EditorWorld:
TerrainType: Spice TerrainType: Spice
AllowedTerrainTypes: SpiceSand AllowedTerrainTypes: SpiceSand
MaxDensity: 20 MaxDensity: 20
EditorSelectionLayer:
LoadWidgetAtGameStart: LoadWidgetAtGameStart:
EditorActionManager: EditorActionManager:
BuildableTerrainOverlay: BuildableTerrainOverlay:

View File

@@ -316,7 +316,6 @@ EditorWorld:
TerrainType: Gems TerrainType: Gems
AllowedTerrainTypes: Clear, Road AllowedTerrainTypes: Clear, Road
MaxDensity: 3 MaxDensity: 3
EditorSelectionLayer:
LoadWidgetAtGameStart: LoadWidgetAtGameStart:
EditorActionManager: EditorActionManager:
BuildableTerrainOverlay: BuildableTerrainOverlay:

View File

@@ -422,8 +422,6 @@ EditorWorld:
AllowedTerrainTypes: Clear, Rough, DirtRoad AllowedTerrainTypes: Clear, Rough, DirtRoad
MaxDensity: 2 MaxDensity: 2
VeinholeActors: veinhole VeinholeActors: veinhole
EditorSelectionLayer:
AltPixelOffset: 0,1
LoadWidgetAtGameStart: LoadWidgetAtGameStart:
EditorActionManager: EditorActionManager:
BuildableTerrainOverlay: BuildableTerrainOverlay: