Rework minefield visualisation.

This commit is contained in:
tovl
2019-07-27 14:45:55 +02:00
committed by Paul Chote
parent b7a7b7aa7e
commit 58bb7fcbc0
4 changed files with 62 additions and 39 deletions

View File

@@ -12,6 +12,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives; using OpenRA.Primitives;
using OpenRA.Traits; using OpenRA.Traits;
@@ -23,13 +24,15 @@ namespace OpenRA.Activities
{ {
public readonly Target Target; public readonly Target Target;
public readonly Color Color; public readonly Color Color;
public readonly Sprite Tile;
public TargetLineNode(Target target, Color color) public TargetLineNode(Target target, Color color, Sprite tile = null)
{ {
// Note: Not all activities are drawable. In that case, pass Target.Invalid as target, // Note: Not all activities are drawable. In that case, pass Target.Invalid as target,
// if "yield break" in TargetLineNode(Actor self) is not feasible. // if "yield break" in TargetLineNode(Actor self) is not feasible.
Target = target; Target = target;
Color = color; Color = color;
Tile = tile;
} }
} }

View File

@@ -9,6 +9,7 @@
*/ */
#endregion #endregion
using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Activities; using OpenRA.Activities;
using OpenRA.Mods.Cnc.Traits; using OpenRA.Mods.Cnc.Traits;
@@ -22,30 +23,43 @@ namespace OpenRA.Mods.Cnc.Activities
// Assumes you have Minelayer on that unit // Assumes you have Minelayer on that unit
public class LayMines : Activity public class LayMines : Activity
{ {
readonly MinelayerInfo info; readonly Minelayer minelayer;
readonly AmmoPool[] ammoPools; readonly AmmoPool[] ammoPools;
readonly IMove movement; readonly IMove movement;
readonly RearmableInfo rearmableInfo; readonly RearmableInfo rearmableInfo;
readonly CPos[] minefield;
List<CPos> minefield;
bool returnToBase;
Actor rearmTarget;
public LayMines(Actor self, CPos[] minefield) public LayMines(Actor self, CPos[] minefield)
{ {
info = self.Info.TraitInfo<MinelayerInfo>(); minelayer = self.Trait<Minelayer>();
ammoPools = self.TraitsImplementing<AmmoPool>().ToArray(); ammoPools = self.TraitsImplementing<AmmoPool>().ToArray();
movement = self.Trait<IMove>(); movement = self.Trait<IMove>();
rearmableInfo = self.Info.TraitInfoOrDefault<RearmableInfo>(); rearmableInfo = self.Info.TraitInfoOrDefault<RearmableInfo>();
this.minefield = minefield;
if (minefield != null)
this.minefield = minefield.ToList();
}
protected override void OnFirstRun(Actor self)
{
if (minefield == null)
minefield = new CPos[] { self.Location }.ToList();
} }
public override bool Tick(Actor self) public override bool Tick(Actor self)
{ {
returnToBase = false;
if (IsCanceling) if (IsCanceling)
return true; return true;
if (rearmableInfo != null && ammoPools.Any(p => p.Info.Name == info.AmmoPoolName && !p.HasAmmo())) if (rearmableInfo != null && ammoPools.Any(p => p.Info.Name == minelayer.Info.AmmoPoolName && !p.HasAmmo()))
{ {
// Rearm (and possibly repair) at rearm building, then back out here to refill the minefield some more // Rearm (and possibly repair) at rearm building, then back out here to refill the minefield some more
var rearmTarget = self.World.Actors.Where(a => self.Owner.Stances[a.Owner] == Stance.Ally rearmTarget = self.World.Actors.Where(a => self.Owner.Stances[a.Owner] == Stance.Ally
&& rearmableInfo.RearmActors.Contains(a.Info.Name)) && rearmableInfo.RearmActors.Contains(a.Info.Name))
.ClosestTo(self); .ClosestTo(self);
@@ -56,6 +70,7 @@ namespace OpenRA.Mods.Cnc.Activities
QueueChild(new MoveAdjacentTo(self, Target.FromActor(rearmTarget))); QueueChild(new MoveAdjacentTo(self, Target.FromActor(rearmTarget)));
QueueChild(movement.MoveTo(self.World.Map.CellContaining(rearmTarget.CenterPosition), rearmTarget)); QueueChild(movement.MoveTo(self.World.Map.CellContaining(rearmTarget.CenterPosition), rearmTarget));
QueueChild(new Resupply(self, rearmTarget, new WDist(512))); QueueChild(new Resupply(self, rearmTarget, new WDist(512)));
returnToBase = true;
return false; return false;
} }
@@ -63,10 +78,11 @@ namespace OpenRA.Mods.Cnc.Activities
{ {
LayMine(self); LayMine(self);
QueueChild(new Wait(20)); // A little wait after placing each mine, for show QueueChild(new Wait(20)); // A little wait after placing each mine, for show
minefield.Remove(self.Location);
return false; return false;
} }
if (minefield != null && minefield.Length > 0) if (minefield != null && minefield.Count > 0)
{ {
// Don't get stuck forever here // Don't get stuck forever here
for (var n = 0; n < 20; n++) for (var n = 0; n < 20; n++)
@@ -84,6 +100,20 @@ namespace OpenRA.Mods.Cnc.Activities
return true; return true;
} }
public override IEnumerable<TargetLineNode> TargetLineNodes(Actor self)
{
if (returnToBase)
yield return new TargetLineNode(Target.FromActor(rearmTarget), Color.Green);
if (minefield == null || minefield.Count == 0)
yield break;
yield return new TargetLineNode(Target.FromCell(self.World, minefield[0]), Color.Crimson);
foreach (var c in minefield)
yield return new TargetLineNode(Target.FromCell(self.World, c), Color.Crimson, tile: minelayer.Tile);
}
static bool ShouldLayMine(Actor self, CPos p) static bool ShouldLayMine(Actor self, CPos p)
{ {
// If there is no unit (other than me) here, we want to place a mine here // If there is no unit (other than me) here, we want to place a mine here
@@ -94,14 +124,14 @@ namespace OpenRA.Mods.Cnc.Activities
{ {
if (ammoPools != null) if (ammoPools != null)
{ {
var pool = ammoPools.FirstOrDefault(x => x.Info.Name == info.AmmoPoolName); var pool = ammoPools.FirstOrDefault(x => x.Info.Name == minelayer.Info.AmmoPoolName);
if (pool == null) if (pool == null)
return; return;
pool.TakeAmmo(self, 1); pool.TakeAmmo(self, 1);
} }
self.World.AddFrameEndTask( self.World.AddFrameEndTask(
w => w.CreateActor(info.Mine, new TypeDictionary w => w.CreateActor(minelayer.Info.Mine, new TypeDictionary
{ {
new LocationInit(self.Location), new LocationInit(self.Location),
new OwnerInit(self.Owner), new OwnerInit(self.Owner),

View File

@@ -37,27 +37,27 @@ namespace OpenRA.Mods.Cnc.Traits
public object Create(ActorInitializer init) { return new Minelayer(init.Self, this); } public object Create(ActorInitializer init) { return new Minelayer(init.Self, this); }
} }
public class Minelayer : IIssueOrder, IResolveOrder, IRenderAboveShroudWhenSelected, ISync, IIssueDeployOrder, IOrderVoice public class Minelayer : IIssueOrder, IResolveOrder, ISync, IIssueDeployOrder, IOrderVoice
{ {
readonly MinelayerInfo info; public readonly MinelayerInfo Info;
// TODO: [Sync] when sync can cope with arrays! // TODO: [Sync] when sync can cope with arrays!
public CPos[] Minefield = null; public CPos[] Minefield = null;
readonly Sprite tile; public readonly Sprite Tile;
[Sync] [Sync]
CPos minefieldStart; CPos minefieldStart;
public Minelayer(Actor self, MinelayerInfo info) public Minelayer(Actor self, MinelayerInfo info)
{ {
this.info = info; Info = info;
var tileset = self.World.Map.Tileset.ToLowerInvariant(); var tileset = self.World.Map.Tileset.ToLowerInvariant();
if (self.World.Map.Rules.Sequences.HasSequence("overlay", "build-valid-{0}".F(tileset))) if (self.World.Map.Rules.Sequences.HasSequence("overlay", "build-valid-{0}".F(tileset)))
tile = self.World.Map.Rules.Sequences.GetSequence("overlay", "build-valid-{0}".F(tileset)).GetSprite(0); Tile = self.World.Map.Rules.Sequences.GetSequence("overlay", "build-valid-{0}".F(tileset)).GetSprite(0);
else else
tile = self.World.Map.Rules.Sequences.GetSequence("overlay", "build-valid").GetSprite(0); Tile = self.World.Map.Rules.Sequences.GetSequence("overlay", "build-valid").GetSprite(0);
} }
IEnumerable<IOrderTargeter> IIssueOrder.Orders IEnumerable<IOrderTargeter> IIssueOrder.Orders
@@ -109,19 +109,18 @@ namespace OpenRA.Mods.Cnc.Traits
{ {
var movement = self.Trait<IPositionable>(); var movement = self.Trait<IPositionable>();
Minefield = GetMinefieldCells(minefieldStart, cell, info.MinefieldDepth) Minefield = GetMinefieldCells(minefieldStart, cell, Info.MinefieldDepth)
.Where(p => movement.CanEnterCell(p, null, false)).ToArray(); .Where(p => movement.CanEnterCell(p, null, false)).ToArray();
self.QueueActivity(order.Queued, new LayMines(self, Minefield)); self.QueueActivity(order.Queued, new LayMines(self, Minefield));
if (Minefield.Length == 1 && Minefield[0] != self.Location) self.ShowTargetLines();
self.ShowTargetLines();
} }
} }
string IOrderVoice.VoicePhraseForOrder(Actor self, Order order) string IOrderVoice.VoicePhraseForOrder(Actor self, Order order)
{ {
if (order.OrderString == "PlaceMine" || order.OrderString == "PlaceMinefield") if (order.OrderString == "PlaceMine" || order.OrderString == "PlaceMinefield")
return info.Voice; return Info.Voice;
return null; return null;
} }
@@ -145,23 +144,6 @@ namespace OpenRA.Mods.Cnc.Traits
yield return new CPos(i, j); yield return new CPos(i, j);
} }
IEnumerable<IRenderable> IRenderAboveShroudWhenSelected.RenderAboveShroud(Actor self, WorldRenderer wr)
{
if (self.Owner != self.World.LocalPlayer || Minefield == null)
yield break;
// Single-cell mine fields use a target line instead
if (Minefield.Length == 1)
yield break;
var pal = wr.Palette(TileSet.TerrainPaletteInternalName);
foreach (var c in Minefield)
yield return new SpriteRenderable(tile, self.World.Map.CenterOfCell(c),
WVec.Zero, -511, pal, 1f, true);
}
bool IRenderAboveShroudWhenSelected.SpatiallyPartitionable { get { return false; } }
class MinefieldOrderGenerator : OrderGenerator class MinefieldOrderGenerator : OrderGenerator
{ {
readonly List<Actor> minelayers; readonly List<Actor> minelayers;

View File

@@ -81,8 +81,16 @@ namespace OpenRA.Mods.Common.Traits
{ {
if (n.Target.Type != TargetType.Invalid) if (n.Target.Type != TargetType.Invalid)
{ {
yield return new TargetLineRenderable(new[] { prev, n.Target.CenterPosition }, n.Color, info.LineWidth, info.MarkerWidth); var pal = wr.Palette(TileSet.TerrainPaletteInternalName);
prev = n.Target.CenterPosition; var tile = n.Tile;
var pos = n.Target.CenterPosition;
if (tile == null)
yield return new TargetLineRenderable(new[] { prev, pos }, n.Color, info.LineWidth, info.MarkerWidth);
else
yield return new SpriteRenderable(tile, pos, WVec.Zero, -511, pal, 1f, true);
prev = pos;
} }
} }
} }