Files
OpenRA/OpenRA.Mods.RA/Effects/GpsDot.cs
RoosterDragon f96d02a794 Fix GPS dot relying on frozen actor render state.
The GPS dot was not displayed if a frozen actor for it was already visible. However, this was mistakenly affecting visibility checks in the world. Now, we only avoid rendering it when the frozen actor is present, but the indicator is still regarded as visible for visilbity checks in the world.
2015-12-12 19:57:07 +00:00

145 lines
3.8 KiB
C#

#region Copyright & License Information
/*
* Copyright 2007-2015 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.Linq;
using OpenRA.Effects;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
using OpenRA.Mods.RA.Traits;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.Effects
{
class GpsDotInfo : ITraitInfo
{
public readonly string String = "Infantry";
public readonly string IndicatorPalettePrefix = "player";
public object Create(ActorInitializer init)
{
return new GpsDot(init.Self, this);
}
}
class GpsDot : IEffect
{
readonly Actor self;
readonly GpsDotInfo info;
readonly Animation anim;
readonly Dictionary<Player, DotState> stateByPlayer = new Dictionary<Player, DotState>();
readonly Lazy<HiddenUnderFog> huf;
readonly Lazy<FrozenUnderFog> fuf;
readonly Lazy<Disguise> disguise;
readonly Lazy<Cloak> cloak;
readonly Cache<Player, FrozenActorLayer> frozen;
class DotState
{
public readonly GpsWatcher Gps;
public bool IsTargetable;
public bool ShouldRender;
public DotState(GpsWatcher gps)
{
Gps = gps;
}
}
public GpsDot(Actor self, GpsDotInfo info)
{
this.self = self;
this.info = info;
anim = new Animation(self.World, "gpsdot");
anim.PlayRepeating(info.String);
self.World.AddFrameEndTask(w => w.Add(this));
huf = Exts.Lazy(() => self.TraitOrDefault<HiddenUnderFog>());
fuf = Exts.Lazy(() => self.TraitOrDefault<FrozenUnderFog>());
disguise = Exts.Lazy(() => self.TraitOrDefault<Disguise>());
cloak = Exts.Lazy(() => self.TraitOrDefault<Cloak>());
frozen = new Cache<Player, FrozenActorLayer>(p => p.PlayerActor.Trait<FrozenActorLayer>());
stateByPlayer = self.World.Players.ToDictionary(p => p, p => new DotState(p.PlayerActor.Trait<GpsWatcher>()));
}
public bool IsDotVisible(Player toPlayer)
{
return stateByPlayer[toPlayer].IsTargetable;
}
bool IsTargetableBy(Player toPlayer, out bool shouldRenderIndicator)
{
shouldRenderIndicator = false;
if (cloak.Value != null && cloak.Value.Cloaked)
return false;
if (disguise.Value != null && disguise.Value.Disguised)
return false;
if (huf.Value != null && !huf.Value.IsVisible(self, toPlayer)
&& toPlayer.Shroud.IsExplored(self.CenterPosition))
{
var f1 = FrozenActorForPlayer(toPlayer);
shouldRenderIndicator = f1 == null || !f1.HasRenderables;
return true;
}
if (fuf.Value == null)
return false;
var f2 = FrozenActorForPlayer(toPlayer);
if (f2 == null)
return false;
shouldRenderIndicator = !f2.HasRenderables;
return f2.Visible && !f2.Shrouded;
}
FrozenActor FrozenActorForPlayer(Player player)
{
return frozen[player].FromID(self.ActorID);
}
public void Tick(World world)
{
if (self.Disposed)
world.AddFrameEndTask(w => w.Remove(this));
if (!self.IsInWorld || self.IsDead)
return;
foreach (var player in self.World.Players)
{
var state = stateByPlayer[player];
var shouldRender = false;
var targetable = (state.Gps.Granted || state.Gps.GrantedAllies) && IsTargetableBy(player, out shouldRender);
state.IsTargetable = targetable;
state.ShouldRender = targetable && shouldRender;
}
}
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
if (self.World.RenderPlayer == null || !stateByPlayer[self.World.RenderPlayer].ShouldRender || self.Disposed)
return SpriteRenderable.None;
var palette = wr.Palette(info.IndicatorPalettePrefix + self.Owner.InternalName);
return anim.Render(self.CenterPosition, palette);
}
}
}