Files
OpenRA/OpenRA.Mods.Common/EditorBrushes/EditorMarkerLayerBrush.cs
let5sne.win10 9cf6ebb986
Some checks failed
Continuous Integration / Linux (.NET 8.0) (push) Has been cancelled
Continuous Integration / Windows (.NET 8.0) (push) Has been cancelled
Initial commit: OpenRA game engine
Fork from OpenRA/OpenRA with one-click launch script (start-ra.cmd)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 21:46:54 +08:00

266 lines
6.6 KiB
C#

#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.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
namespace OpenRA.Mods.Common.Widgets
{
public sealed class EditorMarkerLayerBrush : IEditorBrush
{
public int? Template;
readonly WorldRenderer worldRenderer;
readonly World world;
readonly EditorActionManager editorActionManager;
readonly MarkerLayerOverlay markerLayerOverlay;
readonly EditorViewportControllerWidget editorWidget;
readonly List<PaintMarkerTile> paintTiles = [];
bool painting;
CPos cell;
public EditorMarkerLayerBrush(EditorViewportControllerWidget editorWidget, int? id, WorldRenderer wr)
{
this.editorWidget = editorWidget;
worldRenderer = wr;
world = wr.World;
editorActionManager = world.WorldActor.Trait<EditorActionManager>();
markerLayerOverlay = world.WorldActor.Trait<MarkerLayerOverlay>();
Template = id;
}
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)
return true;
if (mi.Event == MouseInputEvent.Up)
{
UpdatePreview();
if (paintTiles.Count != 0)
{
editorActionManager.Add(new PaintMarkerTileEditorAction(Template, paintTiles.ToImmutableArray(), markerLayerOverlay));
paintTiles.Clear();
UpdatePreview(true);
}
painting = false;
}
else
{
painting = true;
UpdatePreview();
}
return true;
}
void UpdatePreview(bool forceRefresh = false)
{
var currentCell = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos);
if (!forceRefresh && cell == currentCell)
return;
cell = currentCell;
if (!painting)
{
foreach (var paintTile in paintTiles)
markerLayerOverlay.SetTile(paintTile.Cell, paintTile.Previous);
paintTiles.Clear();
}
foreach (var cell in markerLayerOverlay.CalculateMirrorPositions(cell))
{
if (paintTiles.Any(t => t.Cell == cell))
continue;
var existing = markerLayerOverlay.CellLayer[cell];
if (existing == Template)
continue;
paintTiles.Add(new PaintMarkerTile(cell, existing));
markerLayerOverlay.SetTile(cell, Template);
}
}
void IEditorBrush.TickRender(WorldRenderer wr, Actor self) { UpdatePreview(); }
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()
{
foreach (var paintTile in paintTiles)
markerLayerOverlay.SetTile(paintTile.Cell, paintTile.Previous);
}
}
readonly struct PaintMarkerTile
{
public readonly CPos Cell;
public readonly int? Previous;
public PaintMarkerTile(CPos cell, int? previous)
{
Cell = cell;
Previous = previous;
}
}
sealed class PaintMarkerTileEditorAction : IEditorAction
{
[FluentReference("count", "type")]
const string AddedMarkerTiles = "notification-added-marker-tiles";
[FluentReference("count")]
const string RemovedMarkerTiles = "notification-removed-marker-tiles";
public string Text { get; }
readonly int? type;
readonly MarkerLayerOverlay markerLayerOverlay;
readonly ImmutableArray<PaintMarkerTile> paintTiles = [];
public PaintMarkerTileEditorAction(
int? type,
ImmutableArray<PaintMarkerTile> paintTiles,
MarkerLayerOverlay markerLayerOverlay)
{
this.type = type;
this.paintTiles = paintTiles;
this.markerLayerOverlay = markerLayerOverlay;
if (type != null)
{
var typeLabel = FluentProvider.GetMessage(markerLayerOverlay.Info.Colors.ElementAt(type.Value).Key);
Text = FluentProvider.GetMessage(AddedMarkerTiles, "count", paintTiles.Length, "type", typeLabel);
}
else
Text = FluentProvider.GetMessage(RemovedMarkerTiles, "count", paintTiles.Length);
}
public void Execute() { }
public void Do()
{
foreach (var paintTile in paintTiles)
markerLayerOverlay.SetTile(paintTile.Cell, type);
}
public void Undo()
{
foreach (var paintTile in paintTiles)
markerLayerOverlay.SetTile(paintTile.Cell, paintTile.Previous);
}
}
sealed class ClearSelectedMarkerTilesEditorAction : IEditorAction
{
[FluentReference("count", "type")]
const string ClearedSelectedMarkerTiles = "notification-cleared-selected-marker-tiles";
public string Text { get; }
readonly MarkerLayerOverlay markerLayerOverlay;
readonly ImmutableArray<CPos> tiles;
readonly int tile;
public ClearSelectedMarkerTilesEditorAction(
int tile,
MarkerLayerOverlay markerLayerOverlay)
{
this.tile = tile;
this.markerLayerOverlay = markerLayerOverlay;
tiles = markerLayerOverlay.Tiles[tile].ToImmutableArray();
var typeLabel = FluentProvider.GetMessage(markerLayerOverlay.Info.Colors.ElementAt(tile).Key);
Text = FluentProvider.GetMessage(ClearedSelectedMarkerTiles, "count", tiles.Length, "type", typeLabel);
}
public void Execute()
{
Do();
}
public void Do()
{
markerLayerOverlay.ClearSelected(tile);
}
public void Undo()
{
markerLayerOverlay.SetSelected(tile, tiles.AsSpan());
}
}
sealed class ClearAllMarkerTilesEditorAction : IEditorAction
{
[FluentReference("count")]
const string ClearedAllMarkerTiles = "notification-cleared-all-marker-tiles";
public string Text { get; }
readonly MarkerLayerOverlay markerLayerOverlay;
readonly FrozenDictionary<int, ImmutableArray<CPos>> tiles;
public ClearAllMarkerTilesEditorAction(
MarkerLayerOverlay markerLayerOverlay)
{
this.markerLayerOverlay = markerLayerOverlay;
tiles = markerLayerOverlay.Tiles.ToFrozenDictionary(t => t.Key, t => t.Value.ToImmutableArray());
var allTilesCount = tiles.Values.Sum(x => x.Length);
Text = FluentProvider.GetMessage(ClearedAllMarkerTiles, "count", allTilesCount);
}
public void Execute()
{
Do();
}
public void Do()
{
markerLayerOverlay.ClearAll();
}
public void Undo()
{
markerLayerOverlay.SetAll(tiles);
}
}
}