#region Copyright & License Information /* * Copyright 2007-2020 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, either version 3 of * the License, or (at your option) any later version. For more * information, see COPYING. */ #endregion using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; using OpenRA.Support; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits.Render { public enum BlinkState { Off, On } public abstract class WithDecorationBaseInfo : ConditionalTraitInfo { [Desc("Position in the actor's selection box to draw the decoration.")] public readonly DecorationPosition Position = DecorationPosition.TopLeft; [Desc("Player stances who can view the decoration.")] public readonly Stance ValidStances = Stance.Ally; [Desc("Should this be visible only when selected?")] public readonly bool RequiresSelection = false; [Desc("Offset sprite center position from the selection box edge.")] public readonly int2 Margin = int2.Zero; [Desc("Screen-space offsets to apply when defined conditions are enabled.", "A dictionary of [condition string]: [x, y offset].")] public readonly Dictionary Offsets = new Dictionary(); [Desc("The number of ticks that each step in the blink pattern in active.")] public readonly int BlinkInterval = 5; [Desc("A pattern of ticks (BlinkInterval long) where the decoration is visible or hidden.")] public readonly BlinkState[] BlinkPattern = { }; [Desc("Override blink conditions to use when defined conditions are enabled.", "A dictionary of [condition string]: [pattern].")] public readonly Dictionary BlinkPatterns = new Dictionary(); [ConsumedConditionReference] public IEnumerable ConsumedConditions { get { return Offsets.Keys.Concat(BlinkPatterns.Keys).SelectMany(r => r.Variables).Distinct(); } } } public abstract class WithDecorationBase : ConditionalTrait, IDecoration where InfoType : WithDecorationBaseInfo { protected readonly Actor self; int2 conditionalOffset; BlinkState[] blinkPattern; public WithDecorationBase(Actor self, InfoType info) : base(info) { this.self = self; blinkPattern = info.BlinkPattern; } protected virtual bool ShouldRender(Actor self) { if (self.World.FogObscures(self)) return false; if (blinkPattern != null && blinkPattern.Any()) { var i = (self.World.WorldTick / Info.BlinkInterval) % blinkPattern.Length; if (blinkPattern[i] != BlinkState.On) return false; } if (self.World.RenderPlayer != null) { var stance = self.Owner.Stances[self.World.RenderPlayer]; if (!Info.ValidStances.HasStance(stance)) return false; } return true; } DecorationPosition IDecoration.Position { get { return Info.Position; } } bool IDecoration.Enabled { get { return !IsTraitDisabled && self.IsInWorld && ShouldRender(self); } } bool IDecoration.RequiresSelection { get { return Info.RequiresSelection; } } protected abstract IEnumerable RenderDecoration(Actor self, WorldRenderer wr, int2 pos); IEnumerable IDecoration.RenderDecoration(Actor self, WorldRenderer wr, int2 pos) { if (IsTraitDisabled || self.IsDead || !self.IsInWorld || !ShouldRender(self)) return Enumerable.Empty(); var screenPos = wr.Viewport.WorldToViewPx(pos) + Info.Position.CreateMargin(Info.Margin) + conditionalOffset; return RenderDecoration(self, wr, screenPos); } public override IEnumerable GetVariableObservers() { foreach (var observer in base.GetVariableObservers()) yield return observer; foreach (var condition in Info.Offsets.Keys) yield return new VariableObserver(OffsetConditionChanged, condition.Variables); foreach (var condition in Info.BlinkPatterns.Keys) yield return new VariableObserver(BlinkConditionsChanged, condition.Variables); } void OffsetConditionChanged(Actor self, IReadOnlyDictionary conditions) { conditionalOffset = int2.Zero; foreach (var kv in Info.Offsets) { if (kv.Key.Evaluate(conditions)) { conditionalOffset = kv.Value; break; } } } void BlinkConditionsChanged(Actor self, IReadOnlyDictionary conditions) { blinkPattern = Info.BlinkPattern; foreach (var kv in Info.BlinkPatterns) { if (kv.Key.Evaluate(conditions)) { blinkPattern = kv.Value; return; } } } } }