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; 13 using System.Collections.Generic; 14 using System.Linq; 15 using OpenRA.Graphics; 16 using OpenRA.Primitives; 17 using OpenRA.Traits; 18 19 namespace OpenRA.Mods.Common.Traits.Render 20 { 21 [Desc("Renders the MuzzleSequence from the Armament trait.")] 22 class WithMuzzleOverlayInfo : ConditionalTraitInfo, Requires<RenderSpritesInfo>, Requires<AttackBaseInfo>, Requires<ArmamentInfo> 23 { 24 [Desc("Ignore the weapon position, and always draw relative to the center of the actor")] 25 public readonly bool IgnoreOffset = false; 26 Create(ActorInitializer init)27 public override object Create(ActorInitializer init) { return new WithMuzzleOverlay(init.Self, this); } 28 } 29 30 class WithMuzzleOverlay : ConditionalTrait<WithMuzzleOverlayInfo>, INotifyAttack, IRender, ITick 31 { 32 readonly Dictionary<Barrel, bool> visible = new Dictionary<Barrel, bool>(); 33 readonly Dictionary<Barrel, AnimationWithOffset> anims = new Dictionary<Barrel, AnimationWithOffset>(); 34 readonly Func<int> getFacing; 35 readonly Armament[] armaments; 36 WithMuzzleOverlay(Actor self, WithMuzzleOverlayInfo info)37 public WithMuzzleOverlay(Actor self, WithMuzzleOverlayInfo info) 38 : base(info) 39 { 40 var render = self.Trait<RenderSprites>(); 41 var facing = self.TraitOrDefault<IFacing>(); 42 43 armaments = self.TraitsImplementing<Armament>() 44 .Where(arm => arm.Info.MuzzleSequence != null) 45 .ToArray(); 46 47 foreach (var arm in armaments) 48 { 49 foreach (var b in arm.Barrels) 50 { 51 var barrel = b; 52 var turreted = self.TraitsImplementing<Turreted>() 53 .FirstOrDefault(t => t.Name == arm.Info.Turret); 54 55 // Workaround for broken ternary operators in certain versions of mono (3.10 and 56 // certain versions of the 3.8 series): https://bugzilla.xamarin.com/show_bug.cgi?id=23319 57 if (turreted != null) 58 getFacing = () => turreted.TurretFacing; 59 else if (facing != null) 60 getFacing = () => facing.Facing; 61 else 62 getFacing = () => 0; 63 64 var muzzleFlash = new Animation(self.World, render.GetImage(self), getFacing); 65 visible.Add(barrel, false); 66 anims.Add(barrel, 67 new AnimationWithOffset(muzzleFlash, 68 () => info.IgnoreOffset ? WVec.Zero : arm.MuzzleOffset(self, barrel), 69 () => IsTraitDisabled || !visible[barrel], 70 p => RenderUtils.ZOffsetFromCenter(self, p, 2))); 71 } 72 } 73 } 74 INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel)75 void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel) 76 { 77 if (a == null || barrel == null || !armaments.Contains(a)) 78 return; 79 80 var sequence = a.Info.MuzzleSequence; 81 if (a.Info.MuzzleSplitFacings > 0) 82 sequence += Util.QuantizeFacing(getFacing(), a.Info.MuzzleSplitFacings).ToString(); 83 84 visible[barrel] = true; 85 anims[barrel].Animation.PlayThen(sequence, () => visible[barrel] = false); 86 } 87 INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel barrel)88 void INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel barrel) { } 89 IRender.Render(Actor self, WorldRenderer wr)90 IEnumerable<IRenderable> IRender.Render(Actor self, WorldRenderer wr) 91 { 92 foreach (var arm in armaments) 93 { 94 var palette = wr.Palette(arm.Info.MuzzlePalette); 95 foreach (var b in arm.Barrels) 96 { 97 var anim = anims[b]; 98 if (anim.DisableFunc != null && anim.DisableFunc()) 99 continue; 100 101 foreach (var r in anim.Render(self, wr, palette, 1f)) 102 yield return r; 103 } 104 } 105 } 106 IRender.ScreenBounds(Actor self, WorldRenderer wr)107 IEnumerable<Rectangle> IRender.ScreenBounds(Actor self, WorldRenderer wr) 108 { 109 // Muzzle flashes don't contribute to actor bounds 110 yield break; 111 } 112 ITick.Tick(Actor self)113 void ITick.Tick(Actor self) 114 { 115 foreach (var a in anims.Values) 116 a.Animation.Tick(); 117 } 118 } 119 } 120