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.Linq;
13 
14 namespace OpenRA.Mods.Common.Traits
15 {
16 	[Desc("This actor blocks bullets and missiles with 'Blockable' property.")]
17 	public class BlocksProjectilesInfo : ConditionalTraitInfo, IBlocksProjectilesInfo
18 	{
19 		public readonly WDist Height = WDist.FromCells(1);
20 
Create(ActorInitializer init)21 		public override object Create(ActorInitializer init) { return new BlocksProjectiles(init.Self, this); }
22 	}
23 
24 	public class BlocksProjectiles : ConditionalTrait<BlocksProjectilesInfo>, IBlocksProjectiles
25 	{
BlocksProjectiles(Actor self, BlocksProjectilesInfo info)26 		public BlocksProjectiles(Actor self, BlocksProjectilesInfo info)
27 			: base(info) { }
28 
29 		WDist IBlocksProjectiles.BlockingHeight { get { return Info.Height; } }
30 
AnyBlockingActorAt(World world, WPos pos)31 		public static bool AnyBlockingActorAt(World world, WPos pos)
32 		{
33 			var dat = world.Map.DistanceAboveTerrain(pos);
34 
35 			return world.ActorMap.GetActorsAt(world.Map.CellContaining(pos))
36 				.Any(a => a.TraitsImplementing<IBlocksProjectiles>()
37 					.Where(t => t.BlockingHeight > dat)
38 					.Any(Exts.IsTraitEnabled));
39 		}
40 
AnyBlockingActorsBetween(World world, WPos start, WPos end, WDist width, out WPos hit)41 		public static bool AnyBlockingActorsBetween(World world, WPos start, WPos end, WDist width, out WPos hit)
42 		{
43 			var actors = world.FindBlockingActorsOnLine(start, end, width);
44 			var length = (end - start).Length;
45 
46 			foreach (var a in actors)
47 			{
48 				var blockers = a.TraitsImplementing<IBlocksProjectiles>()
49 					.Where(Exts.IsTraitEnabled).ToList();
50 
51 				if (!blockers.Any())
52 					continue;
53 
54 				var hitPos = WorldExtensions.MinimumPointLineProjection(start, end, a.CenterPosition);
55 				var dat = world.Map.DistanceAboveTerrain(hitPos);
56 				if ((hitPos - start).Length < length && blockers.Any(t => t.BlockingHeight > dat))
57 				{
58 					hit = hitPos;
59 					return true;
60 				}
61 			}
62 
63 			hit = WPos.Zero;
64 			return false;
65 		}
66 	}
67 }
68