From 7004b552c9cbc9ab73f1ba3d019c5eeb2e88f405 Mon Sep 17 00:00:00 2001 From: Gustas <37534529+PunkPun@users.noreply.github.com> Date: Tue, 14 Oct 2025 12:46:04 +0300 Subject: [PATCH] Add ActorMapOverlay --- .../Traits/World/ActorMapOverlay.cs | 94 +++++++++++++++++++ mods/cnc/rules/world.yaml | 1 + mods/common/fluent/common.ftl | 3 + mods/d2k/rules/world.yaml | 1 + mods/ra/rules/world.yaml | 1 + mods/ts/rules/world.yaml | 1 + 6 files changed, 101 insertions(+) create mode 100644 OpenRA.Mods.Common/Traits/World/ActorMapOverlay.cs diff --git a/OpenRA.Mods.Common/Traits/World/ActorMapOverlay.cs b/OpenRA.Mods.Common/Traits/World/ActorMapOverlay.cs new file mode 100644 index 0000000000..cedfb7cc55 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/World/ActorMapOverlay.cs @@ -0,0 +1,94 @@ +#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.Commands; +using OpenRA.Mods.Common.Graphics; +using OpenRA.Primitives; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [TraitLocation(SystemActors.World)] + [IncludeStaticFluentReferences(typeof(ActorMapOverlay))] + [Desc("Renders a debug overlay showing the actor influence map. Attach this to the world actor.")] + public class ActorMapOverlayInfo : TraitInfo, Requires { } + + public class ActorMapOverlay : IRenderAnnotations, IWorldLoaded, IChatCommand + { + const string CommandName = "actor-map"; + + [FluentReference] + const string CommandDescription = "description-actor-map-overlay"; + + public bool Enabled; + + void IWorldLoaded.WorldLoaded(World w, WorldRenderer wr) + { + var console = w.WorldActor.TraitOrDefault(); + var help = w.WorldActor.TraitOrDefault(); + + if (console == null || help == null) + return; + + console.RegisterCommand(CommandName, this); + help.RegisterHelp(CommandName, CommandDescription); + } + + void IChatCommand.InvokeCommand(string name, string arg) + { + if (name == CommandName) + Enabled = !Enabled; + } + + IEnumerable IRenderAnnotations.RenderAnnotations(Actor self, WorldRenderer wr) + { + if (!Enabled) + yield break; + + var map = wr.World.Map; + + // Only get actors in actor map. + // Cull to visible cells while we are at it. + var actorsInBox = new HashSet(100); + foreach (var mpos in wr.Viewport.AllVisibleCells.CandidateMapCoords) + actorsInBox.UnionWith(wr.World.ActorMap.GetActorsAt(mpos.ToCPos(map))); + + foreach (var actor in actorsInBox) + { + if (actor.OccupiesSpace is not IOccupySpace space) + continue; + + foreach (var (cell, subCell) in space.OccupiedCells()) + { + var pos = map.CenterOfSubCell(cell, subCell); + var fullCell = subCell == SubCell.FullCell; + + Color color; + color = fullCell ? Color.Red : wr.World.ActorMap.HasFreeSubCell(cell, false) ? Color.Yellow : Color.Orange; + + var ramp = map.Ramp[cell]; + var corners = map.Grid.Ramps[ramp].Corners; + for (var i = 0; i < 4; i++) + { + var j = (i + 1) % 4; + var start = pos + corners[i] / (fullCell ? 1 : 2); + var end = pos + corners[j] / (fullCell ? 1 : 2); + yield return new LineAnnotationRenderable(start, end, 2, color); + } + } + } + } + + bool IRenderAnnotations.SpatiallyPartitionable => false; + } +} diff --git a/mods/cnc/rules/world.yaml b/mods/cnc/rules/world.yaml index b112a34d25..029972ec97 100644 --- a/mods/cnc/rules/world.yaml +++ b/mods/cnc/rules/world.yaml @@ -156,6 +156,7 @@ World: PathFinderOverlay: AutoSave: HierarchicalPathFinderOverlay: + ActorMapOverlay: PlayerCommands: HelpCommand: ScreenShaker: diff --git a/mods/common/fluent/common.ftl b/mods/common/fluent/common.ftl index 013c86f360..e584a5e962 100644 --- a/mods/common/fluent/common.ftl +++ b/mods/common/fluent/common.ftl @@ -758,6 +758,9 @@ description-path-debug-overlay = toggles a visualization of path searching. ## TerrainGeometryOverlay description-terrain-geometry-overlay = toggles the terrain geometry overlay. +## ActorMapOverlay +description-actor-map-overlay = toggles the actor map overlay. + ## MapOptions, MissionBrowserLogic options-game-speed = .slowest = Slowest diff --git a/mods/d2k/rules/world.yaml b/mods/d2k/rules/world.yaml index 006d767eb6..1ac69705d1 100644 --- a/mods/d2k/rules/world.yaml +++ b/mods/d2k/rules/world.yaml @@ -123,6 +123,7 @@ World: DebugVisualizationCommands: PathFinderOverlay: HierarchicalPathFinderOverlay: + ActorMapOverlay: PlayerCommands: HelpCommand: AutoSave: diff --git a/mods/ra/rules/world.yaml b/mods/ra/rules/world.yaml index 1a2deaf017..080d05296d 100644 --- a/mods/ra/rules/world.yaml +++ b/mods/ra/rules/world.yaml @@ -164,6 +164,7 @@ World: DebugVisualizationCommands: PathFinderOverlay: HierarchicalPathFinderOverlay: + ActorMapOverlay: PlayerCommands: HelpCommand: ScreenShaker: diff --git a/mods/ts/rules/world.yaml b/mods/ts/rules/world.yaml index 07a439ae78..2d6f09983c 100644 --- a/mods/ts/rules/world.yaml +++ b/mods/ts/rules/world.yaml @@ -239,6 +239,7 @@ World: DebugVisualizationCommands: PathFinderOverlay: HierarchicalPathFinderOverlay: + ActorMapOverlay: PlayerCommands: HelpCommand: BuildingInfluence: