1 #region Copyright & License Information
2 /*
3  * Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
4  * This file is part of OpenRA, which is free software. It is made
5  * available to you under the terms of the GNU General Public License
6  * as published by the Free Software Foundation, either version 3 of
7  * the License, or (at your option) any later version. For more
8  * information, see COPYING.
9  */
10 #endregion
11 
12 using System;
13 using System.Collections.Generic;
14 using OpenRA.Effects;
15 using OpenRA.Graphics;
16 using OpenRA.Scripting;
17 
18 namespace OpenRA.Mods.Common.Effects
19 {
20 	public class Beacon : IEffect, IScriptBindable, IEffectAboveShroud
21 	{
22 		static readonly int MaxArrowHeight = 512;
23 
24 		readonly Player owner;
25 		readonly WPos position;
26 		readonly bool isPlayerPalette;
27 		readonly string beaconPalette, posterPalette;
28 		readonly Animation arrow, beacon, circles, clock, poster;
29 		readonly int duration;
30 
31 		int delay;
32 		int arrowHeight = MaxArrowHeight;
33 		int arrowSpeed = 50;
34 		int tick;
35 
36 		// Player-placed beacons are removed after a delay
Beacon(Player owner, WPos position, int duration, string beaconPalette, bool isPlayerPalette, string beaconCollection, string beaconSequence, string arrowSprite, string circleSprite, int delay = 0)37 		public Beacon(Player owner, WPos position, int duration, string beaconPalette, bool isPlayerPalette,
38 			string beaconCollection, string beaconSequence, string arrowSprite, string circleSprite, int delay = 0)
39 		{
40 			this.owner = owner;
41 			this.position = position;
42 			this.beaconPalette = beaconPalette;
43 			this.isPlayerPalette = isPlayerPalette;
44 			this.duration = duration;
45 			this.delay = delay;
46 
47 			if (!string.IsNullOrEmpty(beaconSequence))
48 			{
49 				beacon = new Animation(owner.World, beaconCollection);
50 				beacon.PlayRepeating(beaconSequence);
51 			}
52 
53 			if (!string.IsNullOrEmpty(arrowSprite))
54 			{
55 				arrow = new Animation(owner.World, beaconCollection);
56 				arrow.Play(arrowSprite);
57 			}
58 
59 			if (!string.IsNullOrEmpty(circleSprite))
60 			{
61 				circles = new Animation(owner.World, beaconCollection);
62 				circles.Play(circleSprite);
63 			}
64 		}
65 
66 		// By default, support power beacons are expected to clean themselves up
Beacon(Player owner, WPos position, bool isPlayerPalette, string palette, string posterCollection, string posterType, string posterPalette, string beaconSequence, string arrowSequence, string circleSequence, string clockSequence, Func<float> clockFraction, int delay = 0, int duration = -1)67 		public Beacon(Player owner, WPos position, bool isPlayerPalette, string palette, string posterCollection, string posterType, string posterPalette,
68 			string beaconSequence, string arrowSequence, string circleSequence, string clockSequence, Func<float> clockFraction, int delay = 0, int duration = -1)
69 				: this(owner, position, duration, palette, isPlayerPalette, posterCollection, beaconSequence, arrowSequence, circleSequence, delay)
70 		{
71 			this.posterPalette = posterPalette;
72 
73 			if (posterType != null)
74 			{
75 				poster = new Animation(owner.World, posterCollection);
76 				poster.Play(posterType);
77 
78 				if (clockFraction != null)
79 				{
80 					clock = new Animation(owner.World, posterCollection);
81 					clock.PlayFetchIndex(clockSequence, () => Exts.Clamp((int)(clockFraction() * (clock.CurrentSequence.Length - 1)), 0, clock.CurrentSequence.Length - 1));
82 				}
83 			}
84 		}
85 
IEffect.Tick(World world)86 		void IEffect.Tick(World world)
87 		{
88 			if (delay-- > 0)
89 				return;
90 
91 			arrowHeight += arrowSpeed;
92 			var clamped = arrowHeight.Clamp(0, MaxArrowHeight);
93 			if (arrowHeight != clamped)
94 			{
95 				arrowHeight = clamped;
96 				arrowSpeed *= -1;
97 			}
98 
99 			if (arrow != null)
100 				arrow.Tick();
101 
102 			if (beacon != null)
103 				beacon.Tick();
104 
105 			if (circles != null)
106 				circles.Tick();
107 
108 			if (clock != null)
109 				clock.Tick();
110 
111 			if (duration > 0 && duration <= tick++)
112 				owner.World.AddFrameEndTask(w => w.Remove(this));
113 		}
114 
IEffect.Render(WorldRenderer r)115 		IEnumerable<IRenderable> IEffect.Render(WorldRenderer r) { return SpriteRenderable.None; }
116 
IEffectAboveShroud.RenderAboveShroud(WorldRenderer r)117 		IEnumerable<IRenderable> IEffectAboveShroud.RenderAboveShroud(WorldRenderer r)
118 		{
119 			if (delay > 0)
120 				yield break;
121 
122 			if (!owner.IsAlliedWith(owner.World.RenderPlayer))
123 				yield break;
124 
125 			var palette = r.Palette(isPlayerPalette ? beaconPalette + owner.InternalName : beaconPalette);
126 
127 			if (beacon != null)
128 				foreach (var a in beacon.Render(position, palette))
129 					yield return a;
130 
131 			if (circles != null)
132 				foreach (var a in circles.Render(position, palette))
133 					yield return a;
134 
135 			if (arrow != null)
136 				foreach (var a in arrow.Render(position + new WVec(0, 0, arrowHeight), palette))
137 					yield return a;
138 
139 			if (poster != null)
140 			{
141 				foreach (var a in poster.Render(position, r.Palette(posterPalette)))
142 					yield return a;
143 
144 				if (clock != null)
145 					foreach (var a in clock.Render(position, r.Palette(posterPalette)))
146 						yield return a;
147 			}
148 		}
149 	}
150 }
151