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.Collections.Generic;
13 using System.Linq;
14 using OpenRA.Mods.Common.Traits;
15 using OpenRA.Traits;
16 
17 namespace OpenRA.Mods.Cnc.Traits
18 {
19 	[Desc("Required for `GpsPower`. Attach this to the player actor.")]
20 	class GpsWatcherInfo : ITraitInfo
21 	{
Create(ActorInitializer init)22 		public object Create(ActorInitializer init) { return new GpsWatcher(init.Self.Owner); }
23 	}
24 
OnGpsRefresh(Actor self, Player player)25 	interface IOnGpsRefreshed { void OnGpsRefresh(Actor self, Player player); }
26 
27 	class GpsWatcher : ISync, IPreventsShroudReset
28 	{
29 		[Sync]
30 		public bool Launched { get; private set; }
31 
32 		[Sync]
33 		public bool GrantedAllies { get; private set; }
34 
35 		[Sync]
36 		public bool Granted { get; private set; }
37 
38 		// Whether this watcher has explored the terrain (by becoming Launched, or an ally becoming Launched)
39 		[Sync]
40 		bool explored;
41 
42 		readonly Player owner;
43 
44 		readonly List<Actor> actors = new List<Actor>();
45 		readonly HashSet<TraitPair<IOnGpsRefreshed>> notifyOnRefresh = new HashSet<TraitPair<IOnGpsRefreshed>>();
46 
GpsWatcher(Player owner)47 		public GpsWatcher(Player owner)
48 		{
49 			this.owner = owner;
50 		}
51 
GpsRemove(Actor atek)52 		public void GpsRemove(Actor atek)
53 		{
54 			actors.Remove(atek);
55 			RefreshGps(atek.Owner);
56 		}
57 
GpsAdd(Actor atek)58 		public void GpsAdd(Actor atek)
59 		{
60 			actors.Add(atek);
61 			RefreshGps(atek.Owner);
62 		}
63 
ReachedOrbit(Player launcher)64 		public void ReachedOrbit(Player launcher)
65 		{
66 			Launched = true;
67 			RefreshGps(launcher);
68 		}
69 
RefreshGps(Player launcher)70 		public void RefreshGps(Player launcher)
71 		{
72 			RefreshGranted();
73 
74 			foreach (var i in launcher.World.ActorsWithTrait<GpsWatcher>())
75 				i.Trait.RefreshGranted();
76 		}
77 
RefreshGranted()78 		void RefreshGranted()
79 		{
80 			var wasGranted = Granted;
81 			var wasGrantedAllies = GrantedAllies;
82 			var allyWatchers = owner.World.ActorsWithTrait<GpsWatcher>().Where(kv => kv.Actor.Owner.IsAlliedWith(owner));
83 
84 			Granted = actors.Count > 0 && Launched;
85 			GrantedAllies = allyWatchers.Any(w => w.Trait.Granted);
86 
87 			var allyLaunched = allyWatchers.Any(w => w.Trait.Launched);
88 			if ((Launched || allyLaunched) && !explored)
89 			{
90 				explored = true;
91 				owner.Shroud.ExploreAll();
92 			}
93 
94 			if (wasGranted != Granted || wasGrantedAllies != GrantedAllies)
95 				foreach (var tp in notifyOnRefresh.ToList())
96 					tp.Trait.OnGpsRefresh(tp.Actor, owner);
97 		}
98 
IPreventsShroudReset.PreventShroudReset(Actor self)99 		bool IPreventsShroudReset.PreventShroudReset(Actor self)
100 		{
101 			return Granted || GrantedAllies;
102 		}
103 
RegisterForOnGpsRefreshed(Actor actor, IOnGpsRefreshed toBeNotified)104 		public void RegisterForOnGpsRefreshed(Actor actor, IOnGpsRefreshed toBeNotified)
105 		{
106 			notifyOnRefresh.Add(new TraitPair<IOnGpsRefreshed>(actor, toBeNotified));
107 		}
108 
UnregisterForOnGpsRefreshed(Actor actor, IOnGpsRefreshed toBeNotified)109 		public void UnregisterForOnGpsRefreshed(Actor actor, IOnGpsRefreshed toBeNotified)
110 		{
111 			notifyOnRefresh.Remove(new TraitPair<IOnGpsRefreshed>(actor, toBeNotified));
112 		}
113 	}
114 }
115