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.GameRules; 15 using OpenRA.Mods.Common.Traits; 16 using OpenRA.Traits; 17 18 namespace OpenRA.Mods.Common.Warheads 19 { 20 public class FireClusterWarhead : Warhead, IRulesetLoaded<WeaponInfo> 21 { 22 [WeaponReference] 23 [FieldLoader.Require] 24 [Desc("Has to be defined in weapons.yaml as well.")] 25 public readonly string Weapon = null; 26 27 [Desc("Number of weapons fired at random 'x' cells. Negative values will result in a number equal to 'x' footprint cells fired.")] 28 public readonly int RandomClusterCount = -1; 29 30 [FieldLoader.Require] 31 [Desc("Size of the cluster footprint")] 32 public readonly CVec Dimensions = CVec.Zero; 33 34 [FieldLoader.Require] 35 [Desc("Cluster footprint. Cells marked as X will be attacked.", 36 "Cells marked as x will be attacked randomly until RandomClusterCount is reached.")] 37 public readonly string Footprint = string.Empty; 38 39 WeaponInfo weapon; 40 RulesetLoaded(Ruleset rules, WeaponInfo info)41 public void RulesetLoaded(Ruleset rules, WeaponInfo info) 42 { 43 if (!rules.Weapons.TryGetValue(Weapon.ToLowerInvariant(), out weapon)) 44 throw new YamlException("Weapons Ruleset does not contain an entry '{0}'".F(Weapon.ToLowerInvariant())); 45 } 46 DoImpact(Target target, WarheadArgs args)47 public override void DoImpact(Target target, WarheadArgs args) 48 { 49 var firedBy = args.SourceActor; 50 if (!target.IsValidFor(firedBy)) 51 return; 52 53 var map = firedBy.World.Map; 54 var targetCell = map.CellContaining(target.CenterPosition); 55 var damageModifiers = args.DamageModifiers; 56 57 var targetCells = CellsMatching(targetCell, false); 58 foreach (var c in targetCells) 59 FireProjectileAtCell(map, firedBy, target, c, damageModifiers); 60 61 if (RandomClusterCount != 0) 62 { 63 var randomTargetCells = CellsMatching(targetCell, true); 64 var clusterCount = RandomClusterCount < 0 ? randomTargetCells.Count() : RandomClusterCount; 65 if (randomTargetCells.Any()) 66 for (var i = 0; i < clusterCount; i++) 67 FireProjectileAtCell(map, firedBy, target, randomTargetCells.Random(firedBy.World.SharedRandom), damageModifiers); 68 } 69 } 70 FireProjectileAtCell(Map map, Actor firedBy, Target target, CPos targetCell, IEnumerable<int> damageModifiers)71 void FireProjectileAtCell(Map map, Actor firedBy, Target target, CPos targetCell, IEnumerable<int> damageModifiers) 72 { 73 var tc = Target.FromCell(firedBy.World, targetCell); 74 75 if (!weapon.IsValidAgainst(tc, firedBy.World, firedBy)) 76 return; 77 78 var args = new ProjectileArgs 79 { 80 Weapon = weapon, 81 Facing = (map.CenterOfCell(targetCell) - target.CenterPosition).Yaw.Facing, 82 83 DamageModifiers = damageModifiers.ToArray(), 84 InaccuracyModifiers = new int[0], 85 RangeModifiers = new int[0], 86 87 Source = target.CenterPosition, 88 CurrentSource = () => target.CenterPosition, 89 SourceActor = firedBy, 90 PassiveTarget = map.CenterOfCell(targetCell), 91 GuidedTarget = tc 92 }; 93 94 if (args.Weapon.Projectile != null) 95 { 96 var projectile = args.Weapon.Projectile.Create(args); 97 if (projectile != null) 98 firedBy.World.AddFrameEndTask(w => w.Add(projectile)); 99 100 if (args.Weapon.Report != null && args.Weapon.Report.Any()) 101 Game.Sound.Play(SoundType.World, args.Weapon.Report, firedBy.World, target.CenterPosition); 102 } 103 } 104 CellsMatching(CPos location, bool random)105 IEnumerable<CPos> CellsMatching(CPos location, bool random) 106 { 107 var cellType = !random ? 'X' : 'x'; 108 var index = 0; 109 var footprint = Footprint.Where(c => !char.IsWhiteSpace(c)).ToArray(); 110 var x = location.X - (Dimensions.X - 1) / 2; 111 var y = location.Y - (Dimensions.Y - 1) / 2; 112 for (var j = 0; j < Dimensions.Y; j++) 113 for (var i = 0; i < Dimensions.X; i++) 114 if (footprint[index++] == cellType) 115 yield return new CPos(x + i, y + j); 116 } 117 } 118 } 119