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 using OpenRA.GameRules;
14 using OpenRA.Traits;
15 
16 namespace OpenRA.Mods.Common.Traits
17 {
18 	[Desc("Throws particles when the actor is destroyed that do damage on impact.")]
19 	public class ThrowsShrapnelInfo : ConditionalTraitInfo, IRulesetLoaded
20 	{
21 		[WeaponReference]
22 		[FieldLoader.Require]
23 		[Desc("The weapons used for shrapnel.")]
24 		public readonly string[] Weapons = { };
25 
26 		[Desc("The amount of pieces of shrapnel to expel. Two values indicate a range.")]
27 		public readonly int[] Pieces = { 3, 10 };
28 
29 		[Desc("The minimum and maximum distances the shrapnel may travel.")]
30 		public readonly WDist[] Range = { WDist.FromCells(2), WDist.FromCells(5) };
31 
32 		public WeaponInfo[] WeaponInfos { get; private set; }
33 
Create(ActorInitializer actor)34 		public override object Create(ActorInitializer actor) { return new ThrowsShrapnel(this); }
RulesetLoaded(Ruleset rules, ActorInfo ai)35 		public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
36 		{
37 			base.RulesetLoaded(rules, ai);
38 
39 			WeaponInfos = Weapons.Select(w =>
40 			{
41 				WeaponInfo weapon;
42 				var weaponToLower = w.ToLowerInvariant();
43 				if (!rules.Weapons.TryGetValue(weaponToLower, out weapon))
44 					throw new YamlException("Weapons Ruleset does not contain an entry '{0}'".F(weaponToLower));
45 				return weapon;
46 			}).ToArray();
47 		}
48 	}
49 
50 	class ThrowsShrapnel : ConditionalTrait<ThrowsShrapnelInfo>, INotifyKilled
51 	{
ThrowsShrapnel(ThrowsShrapnelInfo info)52 		public ThrowsShrapnel(ThrowsShrapnelInfo info)
53 			: base(info) { }
54 
Killed(Actor self, AttackInfo attack)55 		public void Killed(Actor self, AttackInfo attack)
56 		{
57 			if (IsTraitDisabled)
58 				return;
59 
60 			foreach (var wep in Info.WeaponInfos)
61 			{
62 				var pieces = self.World.SharedRandom.Next(Info.Pieces[0], Info.Pieces[1]);
63 				var range = self.World.SharedRandom.Next(Info.Range[0].Length, Info.Range[1].Length);
64 
65 				for (var i = 0; pieces > i; i++)
66 				{
67 					var rotation = WRot.FromFacing(self.World.SharedRandom.Next(1024));
68 					var args = new ProjectileArgs
69 					{
70 						Weapon = wep,
71 						Facing = self.World.SharedRandom.Next(-1, 255),
72 						CurrentMuzzleFacing = () => 0,
73 
74 						DamageModifiers = self.TraitsImplementing<IFirepowerModifier>()
75 							.Select(a => a.GetFirepowerModifier()).ToArray(),
76 
77 						InaccuracyModifiers = self.TraitsImplementing<IInaccuracyModifier>()
78 							.Select(a => a.GetInaccuracyModifier()).ToArray(),
79 
80 						RangeModifiers = self.TraitsImplementing<IRangeModifier>()
81 							.Select(a => a.GetRangeModifier()).ToArray(),
82 
83 						Source = self.CenterPosition,
84 						CurrentSource = () => self.CenterPosition,
85 						SourceActor = self,
86 						PassiveTarget = self.CenterPosition + new WVec(range, 0, 0).Rotate(rotation)
87 					};
88 
89 					self.World.AddFrameEndTask(x =>
90 					{
91 						if (args.Weapon.Projectile != null)
92 						{
93 							var projectile = args.Weapon.Projectile.Create(args);
94 							if (projectile != null)
95 								self.World.Add(projectile);
96 
97 							if (args.Weapon.Report != null && args.Weapon.Report.Any())
98 								Game.Sound.Play(SoundType.World, args.Weapon.Report, self.World, self.CenterPosition);
99 						}
100 					});
101 				}
102 			}
103 		}
104 	}
105 }
106