Track per-player frozen actors.
This commit is contained in:
@@ -234,6 +234,7 @@
|
|||||||
<Compile Include="Graphics\BeamRenderable.cs" />
|
<Compile Include="Graphics\BeamRenderable.cs" />
|
||||||
<Compile Include="Graphics\ContrailRenderable.cs" />
|
<Compile Include="Graphics\ContrailRenderable.cs" />
|
||||||
<Compile Include="Widgets\ViewportControllerWidget.cs" />
|
<Compile Include="Widgets\ViewportControllerWidget.cs" />
|
||||||
|
<Compile Include="Traits\Player\FrozenActorLayer.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
|
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
|
||||||
|
|||||||
@@ -120,6 +120,24 @@ namespace OpenRA.Traits
|
|||||||
line.SetTarget(self, target, color, display);
|
line.SetTarget(self, target, color, display);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void SetTargetLine(this Actor self, FrozenActor target, Color color, bool display)
|
||||||
|
{
|
||||||
|
if (self.Owner != self.World.LocalPlayer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
self.World.AddFrameEndTask(w =>
|
||||||
|
{
|
||||||
|
if (self.Destroyed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
target.Flash();
|
||||||
|
|
||||||
|
var line = self.TraitOrDefault<DrawLineToTarget>();
|
||||||
|
if (line != null)
|
||||||
|
line.SetTarget(self, Target.FromPos(target.CenterPosition), color, display);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
173
OpenRA.Game/Traits/Player/FrozenActorLayer.cs
Executable file
173
OpenRA.Game/Traits/Player/FrozenActorLayer.cs
Executable file
@@ -0,0 +1,173 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
|
||||||
|
* 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. For more information,
|
||||||
|
* see COPYING.
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using OpenRA.FileFormats;
|
||||||
|
using OpenRA.Graphics;
|
||||||
|
using OpenRA.Traits;
|
||||||
|
|
||||||
|
namespace OpenRA.Traits
|
||||||
|
{
|
||||||
|
public class FrozenActorLayerInfo : ITraitInfo
|
||||||
|
{
|
||||||
|
[Desc("Size of partition bins (screen pixels)")]
|
||||||
|
public readonly int BinSize = 250;
|
||||||
|
|
||||||
|
public object Create(ActorInitializer init) { return new FrozenActorLayer(init.world, this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FrozenActor
|
||||||
|
{
|
||||||
|
public readonly IEnumerable<CPos> Footprint;
|
||||||
|
public readonly WPos CenterPosition;
|
||||||
|
public readonly Rectangle Bounds;
|
||||||
|
readonly Actor actor;
|
||||||
|
|
||||||
|
public IRenderable[] Renderables { set; private get; }
|
||||||
|
public Player Owner;
|
||||||
|
|
||||||
|
public string TooltipName;
|
||||||
|
public Player TooltipOwner;
|
||||||
|
|
||||||
|
public int HP;
|
||||||
|
public DamageState DamageState;
|
||||||
|
|
||||||
|
public bool Visible;
|
||||||
|
|
||||||
|
public FrozenActor(Actor self, IEnumerable<CPos> footprint)
|
||||||
|
{
|
||||||
|
actor = self;
|
||||||
|
Footprint = footprint;
|
||||||
|
CenterPosition = self.CenterPosition;
|
||||||
|
Bounds = self.Bounds.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint ID { get { return actor.ActorID; } }
|
||||||
|
public bool IsValid { get { return Owner != null; } }
|
||||||
|
public ActorInfo Info { get { return actor.Info; } }
|
||||||
|
public Actor Actor { get { return !actor.IsDead() ? actor : null; } }
|
||||||
|
|
||||||
|
int flashTicks;
|
||||||
|
public void Tick(World world, Shroud shroud)
|
||||||
|
{
|
||||||
|
Visible = !Footprint.Any(c => shroud.IsVisible(c));
|
||||||
|
|
||||||
|
if (flashTicks > 0)
|
||||||
|
flashTicks--;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Flash()
|
||||||
|
{
|
||||||
|
flashTicks = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<IRenderable> Render(WorldRenderer wr)
|
||||||
|
{
|
||||||
|
if (Renderables == null)
|
||||||
|
return SpriteRenderable.None;
|
||||||
|
|
||||||
|
if (flashTicks > 0 && flashTicks % 2 == 0)
|
||||||
|
{
|
||||||
|
var highlight = wr.Palette("highlight");
|
||||||
|
return Renderables.Concat(Renderables.Where(r => !r.IsDecoration)
|
||||||
|
.Select(r => r.WithPalette(highlight)));
|
||||||
|
}
|
||||||
|
return Renderables;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FrozenActorLayer : IRender, ITick, ISync
|
||||||
|
{
|
||||||
|
[Sync] public int VisibilityHash;
|
||||||
|
[Sync] public int FrozenHash;
|
||||||
|
|
||||||
|
readonly FrozenActorLayerInfo info;
|
||||||
|
Dictionary<uint, FrozenActor> frozen;
|
||||||
|
List<FrozenActor>[,] bins;
|
||||||
|
|
||||||
|
public FrozenActorLayer(World world, FrozenActorLayerInfo info)
|
||||||
|
{
|
||||||
|
this.info = info;
|
||||||
|
frozen = new Dictionary<uint, FrozenActor>();
|
||||||
|
bins = new List<FrozenActor>[
|
||||||
|
world.Map.MapSize.X * Game.CellSize / info.BinSize,
|
||||||
|
world.Map.MapSize.Y * Game.CellSize / info.BinSize];
|
||||||
|
|
||||||
|
for (var j = 0; j <= bins.GetUpperBound(1); j++)
|
||||||
|
for (var i = 0; i <= bins.GetUpperBound(0); i++)
|
||||||
|
bins[i, j] = new List<FrozenActor>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(FrozenActor fa)
|
||||||
|
{
|
||||||
|
frozen.Add(fa.ID, fa);
|
||||||
|
|
||||||
|
var top = (int)Math.Max(0, fa.Bounds.Top / info.BinSize);
|
||||||
|
var left = (int)Math.Max(0, fa.Bounds.Left / info.BinSize);
|
||||||
|
var bottom = (int)Math.Min(bins.GetUpperBound(1), fa.Bounds.Bottom / info.BinSize);
|
||||||
|
var right = (int)Math.Min(bins.GetUpperBound(0), fa.Bounds.Right / info.BinSize);
|
||||||
|
for (var j = top; j <= bottom; j++)
|
||||||
|
for (var i = left; i <= right; i++)
|
||||||
|
bins[i, j].Add(fa);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Tick(Actor self)
|
||||||
|
{
|
||||||
|
var remove = new List<uint>();
|
||||||
|
VisibilityHash = 0;
|
||||||
|
FrozenHash = 0;
|
||||||
|
|
||||||
|
foreach (var kv in frozen)
|
||||||
|
{
|
||||||
|
FrozenHash += (int)kv.Key;
|
||||||
|
|
||||||
|
kv.Value.Tick(self.World, self.Owner.Shroud);
|
||||||
|
if (kv.Value.Visible)
|
||||||
|
VisibilityHash += (int)kv.Key;
|
||||||
|
|
||||||
|
if (!kv.Value.Visible && kv.Value.Actor == null)
|
||||||
|
remove.Add(kv.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var r in remove)
|
||||||
|
{
|
||||||
|
foreach (var bin in bins)
|
||||||
|
bin.Remove(frozen[r]);
|
||||||
|
frozen.Remove(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr)
|
||||||
|
{
|
||||||
|
return frozen.Values
|
||||||
|
.Where(f => f.Visible)
|
||||||
|
.SelectMany(ff => ff.Render(wr));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<FrozenActor> FrozenActorsAt(int2 pxPos)
|
||||||
|
{
|
||||||
|
var x = (pxPos.X / info.BinSize).Clamp(0, bins.GetUpperBound(0));
|
||||||
|
var y = (pxPos.Y / info.BinSize).Clamp(0, bins.GetUpperBound(1));
|
||||||
|
return bins[x, y].Where(p => p.Bounds.Contains(pxPos) && p.IsValid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrozenActor FromID(uint id)
|
||||||
|
{
|
||||||
|
FrozenActor ret;
|
||||||
|
if (!frozen.TryGetValue(id, out ret))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,6 +27,20 @@ namespace OpenRA
|
|||||||
return FindActorsInBox(world, loc, loc).Where(a => !world.FogObscures(a));
|
return FindActorsInBox(world, loc, loc).Where(a => !world.FogObscures(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static readonly IEnumerable<FrozenActor> NoFrozenActors = new FrozenActor[0].AsEnumerable();
|
||||||
|
public static IEnumerable<FrozenActor> FindFrozenActorsAtMouse(this World world, int2 mouseLocation)
|
||||||
|
{
|
||||||
|
if (world.RenderPlayer == null)
|
||||||
|
return NoFrozenActors;
|
||||||
|
|
||||||
|
var frozenLayer = world.RenderPlayer.PlayerActor.TraitOrDefault<FrozenActorLayer>();
|
||||||
|
if (frozenLayer == null)
|
||||||
|
return NoFrozenActors;
|
||||||
|
|
||||||
|
var loc = Game.viewport.ViewToWorldPx(mouseLocation).ToInt2();
|
||||||
|
return frozenLayer.FrozenActorsAt(loc);
|
||||||
|
}
|
||||||
|
|
||||||
public static IEnumerable<Actor> FindActorsInBox(this World world, CPos tl, CPos br)
|
public static IEnumerable<Actor> FindActorsInBox(this World world, CPos tl, CPos br)
|
||||||
{
|
{
|
||||||
return world.FindActorsInBox(tl.TopLeft, br.BottomRight);
|
return world.FindActorsInBox(tl.TopLeft, br.BottomRight);
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
#region Copyright & License Information
|
|
||||||
/*
|
|
||||||
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
|
|
||||||
* 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. For more information,
|
|
||||||
* see COPYING.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using OpenRA.Effects;
|
|
||||||
using OpenRA.Graphics;
|
|
||||||
using OpenRA.Traits;
|
|
||||||
|
|
||||||
namespace OpenRA.Mods.RA.Effects
|
|
||||||
{
|
|
||||||
public class FrozenActorProxy : IEffect
|
|
||||||
{
|
|
||||||
readonly Actor self;
|
|
||||||
readonly IEnumerable<CPos> footprint;
|
|
||||||
IRenderable[] renderables;
|
|
||||||
|
|
||||||
public FrozenActorProxy(Actor self, IEnumerable<CPos> footprint)
|
|
||||||
{
|
|
||||||
this.self = self;
|
|
||||||
this.footprint = footprint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Tick(World world) { }
|
|
||||||
public void SetRenderables(IEnumerable<IRenderable> r)
|
|
||||||
{
|
|
||||||
renderables = r.Select(rr => rr).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<IRenderable> Render(WorldRenderer wr)
|
|
||||||
{
|
|
||||||
if (renderables == null)
|
|
||||||
return SpriteRenderable.None;
|
|
||||||
|
|
||||||
if (footprint.Any(c => !wr.world.FogObscures(c)))
|
|
||||||
{
|
|
||||||
if (self.Destroyed)
|
|
||||||
self.World.AddFrameEndTask(w => w.Remove(this));
|
|
||||||
|
|
||||||
return SpriteRenderable.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
return renderables;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,9 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using OpenRA.FileFormats;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
using OpenRA.Mods.RA.Buildings;
|
using OpenRA.Mods.RA.Buildings;
|
||||||
using OpenRA.Mods.RA.Effects;
|
using OpenRA.Mods.RA.Effects;
|
||||||
@@ -24,50 +26,97 @@ namespace OpenRA.Mods.RA
|
|||||||
public object Create(ActorInitializer init) { return new FrozenUnderFog(init, this); }
|
public object Create(ActorInitializer init) { return new FrozenUnderFog(init, this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FrozenUnderFog : IRenderModifier, IVisibilityModifier, ITickRender
|
public class FrozenUnderFog : IRenderModifier, IVisibilityModifier, ITick, ITickRender, ISync
|
||||||
{
|
{
|
||||||
FrozenActorProxy proxy;
|
[Sync] public int VisibilityHash;
|
||||||
|
|
||||||
|
bool initialized, startsRevealed;
|
||||||
IEnumerable<CPos> footprint;
|
IEnumerable<CPos> footprint;
|
||||||
bool visible, cacheFirstFrame;
|
Lazy<IToolTip> tooltip;
|
||||||
|
Lazy<Health> health;
|
||||||
|
|
||||||
|
Dictionary<Player, bool> visible;
|
||||||
|
Dictionary<Player, FrozenActor> frozen;
|
||||||
|
|
||||||
public FrozenUnderFog(ActorInitializer init, FrozenUnderFogInfo info)
|
public FrozenUnderFog(ActorInitializer init, FrozenUnderFogInfo info)
|
||||||
{
|
{
|
||||||
footprint = FootprintUtils.Tiles(init.self);
|
|
||||||
proxy = new FrozenActorProxy(init.self, footprint);
|
|
||||||
init.world.AddFrameEndTask(w => w.Add(proxy));
|
|
||||||
|
|
||||||
// Spawned actors (e.g. building husks) shouldn't be revealed
|
// Spawned actors (e.g. building husks) shouldn't be revealed
|
||||||
cacheFirstFrame = info.StartsRevealed && !init.Contains<ParentActorInit>();
|
startsRevealed = info.StartsRevealed && !init.Contains<ParentActorInit>();
|
||||||
|
footprint = FootprintUtils.Tiles(init.self);
|
||||||
|
tooltip = Lazy.New(() => init.self.TraitsImplementing<IToolTip>().FirstOrDefault());
|
||||||
|
tooltip = Lazy.New(() => init.self.TraitsImplementing<IToolTip>().FirstOrDefault());
|
||||||
|
health = Lazy.New(() => init.self.TraitOrDefault<Health>());
|
||||||
|
|
||||||
|
frozen = new Dictionary<Player, FrozenActor>();
|
||||||
|
visible = init.world.Players.ToDictionary(p => p, p => false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsVisible(Actor self, Player byPlayer)
|
public bool IsVisible(Actor self, Player byPlayer)
|
||||||
{
|
{
|
||||||
return byPlayer == null || footprint.Any(c => byPlayer.Shroud.IsVisible(c));
|
return byPlayer == null || visible[byPlayer];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TickRender(WorldRenderer wr, Actor self)
|
public void Tick(Actor self)
|
||||||
{
|
{
|
||||||
if (self.Destroyed)
|
if (self.Destroyed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
visible = IsVisible(self, self.World.RenderPlayer);
|
VisibilityHash = 0;
|
||||||
|
foreach (var p in self.World.Players)
|
||||||
if (cacheFirstFrame)
|
|
||||||
{
|
{
|
||||||
visible = true;
|
visible[p] = footprint.Any(c => p.Shroud.IsVisible(c));
|
||||||
cacheFirstFrame = false;
|
if (visible[p])
|
||||||
|
VisibilityHash += p.ClientIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (visible)
|
if (!initialized)
|
||||||
{
|
{
|
||||||
var comparer = new RenderableComparer(wr);
|
foreach (var p in self.World.Players)
|
||||||
proxy.SetRenderables(self.Render(wr).OrderBy(r => r, comparer));
|
{
|
||||||
|
visible[p] |= startsRevealed;
|
||||||
|
frozen[p] = new FrozenActor(self, footprint);
|
||||||
|
p.PlayerActor.Trait<FrozenActorLayer>().Add(frozen[p]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var player in self.World.Players)
|
||||||
|
{
|
||||||
|
if (!visible[player])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
frozen[player].Owner = self.Owner;
|
||||||
|
|
||||||
|
if (health.Value != null)
|
||||||
|
{
|
||||||
|
frozen[player].HP = health.Value.HP;
|
||||||
|
frozen[player].DamageState = health.Value.DamageState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tooltip.Value != null)
|
||||||
|
{
|
||||||
|
frozen[player].TooltipName = tooltip.Value.Name();
|
||||||
|
frozen[player].TooltipOwner = tooltip.Value.Owner();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TickRender(WorldRenderer wr, Actor self)
|
||||||
|
{
|
||||||
|
if (self.Destroyed || !initialized || !visible.Any(v => v.Value))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Force a copy of the underlying data
|
||||||
|
var renderables = self.Render(wr).Select(rr => rr).ToArray();
|
||||||
|
foreach (var player in self.World.Players)
|
||||||
|
if (visible[player])
|
||||||
|
frozen[player].Renderables = renderables;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<IRenderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r)
|
public IEnumerable<IRenderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r)
|
||||||
{
|
{
|
||||||
return visible ? r : SpriteRenderable.None;
|
return IsVisible(self, self.World.RenderPlayer) ? r : SpriteRenderable.None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -462,7 +462,6 @@
|
|||||||
<Compile Include="World\DomainIndex.cs" />
|
<Compile Include="World\DomainIndex.cs" />
|
||||||
<Compile Include="MPStartUnits.cs" />
|
<Compile Include="MPStartUnits.cs" />
|
||||||
<Compile Include="Orders\SetChronoTankDestination.cs" />
|
<Compile Include="Orders\SetChronoTankDestination.cs" />
|
||||||
<Compile Include="Effects\FrozenActorProxy.cs" />
|
|
||||||
<Compile Include="Widgets\Logic\WorldTooltipLogic.cs" />
|
<Compile Include="Widgets\Logic\WorldTooltipLogic.cs" />
|
||||||
<Compile Include="TeslaZapRenderable.cs" />
|
<Compile Include="TeslaZapRenderable.cs" />
|
||||||
<Compile Include="Buildings\Bib.cs" />
|
<Compile Include="Buildings\Bib.cs" />
|
||||||
|
|||||||
@@ -203,6 +203,7 @@ Player:
|
|||||||
BaseAttackNotifier:
|
BaseAttackNotifier:
|
||||||
Shroud:
|
Shroud:
|
||||||
PlayerStatistics:
|
PlayerStatistics:
|
||||||
|
FrozenActorLayer:
|
||||||
|
|
||||||
World:
|
World:
|
||||||
LoadWidgetAtGameStart:
|
LoadWidgetAtGameStart:
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ Player:
|
|||||||
RemapIndex: 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240
|
RemapIndex: 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240
|
||||||
BaseAttackNotifier:
|
BaseAttackNotifier:
|
||||||
Shroud:
|
Shroud:
|
||||||
Shroud: false
|
FrozenActorLayer:
|
||||||
HarvesterAttackNotifier:
|
HarvesterAttackNotifier:
|
||||||
PlayerStatistics:
|
PlayerStatistics:
|
||||||
|
|
||||||
|
|||||||
@@ -524,6 +524,7 @@ Player:
|
|||||||
RemapIndex: 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95
|
RemapIndex: 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95
|
||||||
GpsWatcher:
|
GpsWatcher:
|
||||||
Shroud:
|
Shroud:
|
||||||
|
FrozenActorLayer:
|
||||||
BaseAttackNotifier:
|
BaseAttackNotifier:
|
||||||
PlayerStatistics:
|
PlayerStatistics:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user