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.Support;
15 using OpenRA.Traits;
16 
17 namespace OpenRA.Mods.Common.Traits.BotModules.Squads
18 {
19 	public enum SquadType { Assault, Air, Rush, Protection, Naval }
20 
21 	public class Squad
22 	{
23 		public List<Actor> Units = new List<Actor>();
24 		public SquadType Type;
25 
26 		internal IBot Bot;
27 		internal World World;
28 		internal SquadManagerBotModule SquadManager;
29 		internal MersenneTwister Random;
30 
31 		internal Target Target;
32 		internal StateMachine FuzzyStateMachine;
33 
Squad(IBot bot, SquadManagerBotModule squadManager, SquadType type)34 		public Squad(IBot bot, SquadManagerBotModule squadManager, SquadType type)
35 			: this(bot, squadManager, type, null) { }
36 
Squad(IBot bot, SquadManagerBotModule squadManager, SquadType type, Actor target)37 		public Squad(IBot bot, SquadManagerBotModule squadManager, SquadType type, Actor target)
38 		{
39 			Bot = bot;
40 			SquadManager = squadManager;
41 			World = bot.Player.PlayerActor.World;
42 			Random = World.LocalRandom;
43 			Type = type;
44 			Target = Target.FromActor(target);
45 			FuzzyStateMachine = new StateMachine();
46 
47 			switch (type)
48 			{
49 				case SquadType.Assault:
50 				case SquadType.Rush:
51 					FuzzyStateMachine.ChangeState(this, new GroundUnitsIdleState(), true);
52 					break;
53 				case SquadType.Air:
54 					FuzzyStateMachine.ChangeState(this, new AirIdleState(), true);
55 					break;
56 				case SquadType.Protection:
57 					FuzzyStateMachine.ChangeState(this, new UnitsForProtectionIdleState(), true);
58 					break;
59 				case SquadType.Naval:
60 					FuzzyStateMachine.ChangeState(this, new NavyUnitsIdleState(), true);
61 					break;
62 			}
63 		}
64 
Update()65 		public void Update()
66 		{
67 			if (IsValid)
68 				FuzzyStateMachine.Update(this);
69 		}
70 
71 		public bool IsValid { get { return Units.Any(); } }
72 
73 		public Actor TargetActor
74 		{
75 			get { return Target.Actor; }
76 			set { Target = Target.FromActor(value); }
77 		}
78 
79 		public bool IsTargetValid
80 		{
81 			get { return Target.IsValidFor(Units.FirstOrDefault()) && !Target.Actor.Info.HasTraitInfo<HuskInfo>(); }
82 		}
83 
84 		public bool IsTargetVisible
85 		{
86 			get { return TargetActor.CanBeViewedByPlayer(Bot.Player); }
87 		}
88 
89 		public WPos CenterPosition { get { return Units.Select(u => u.CenterPosition).Average(); } }
90 
Serialize()91 		public MiniYaml Serialize()
92 		{
93 			var nodes = new MiniYaml("", new List<MiniYamlNode>()
94 			{
95 				new MiniYamlNode("Type", FieldSaver.FormatValue(Type)),
96 				new MiniYamlNode("Units", FieldSaver.FormatValue(Units.Select(a => a.ActorID).ToArray())),
97 			});
98 
99 			if (Target.Type == TargetType.Actor)
100 				nodes.Nodes.Add(new MiniYamlNode("Target", FieldSaver.FormatValue(Target.Actor.ActorID)));
101 
102 			return nodes;
103 		}
104 
Deserialize(IBot bot, SquadManagerBotModule squadManager, MiniYaml yaml)105 		public static Squad Deserialize(IBot bot, SquadManagerBotModule squadManager, MiniYaml yaml)
106 		{
107 			var type = SquadType.Rush;
108 			Actor targetActor = null;
109 
110 			var typeNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Type");
111 			if (typeNode != null)
112 				type = FieldLoader.GetValue<SquadType>("Type", typeNode.Value.Value);
113 
114 			var targetNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Target");
115 			if (targetNode != null)
116 				targetActor = squadManager.World.GetActorById(FieldLoader.GetValue<uint>("ActiveUnits", targetNode.Value.Value));
117 
118 			var squad = new Squad(bot, squadManager, type, targetActor);
119 
120 			var unitsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Units");
121 			if (unitsNode != null)
122 				squad.Units.AddRange(FieldLoader.GetValue<uint[]>("Units", unitsNode.Value.Value)
123 					.Select(a => squadManager.World.GetActorById(a)));
124 
125 			return squad;
126 		}
127 	}
128 }
129