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 OpenRA.Graphics;
15 using OpenRA.Mods.Common.Graphics;
16 using OpenRA.Traits;
17 
18 namespace OpenRA.Mods.Common.Traits.Render
19 {
20 	[Desc("Renders a decorative animation on units and buildings.")]
21 	public class WithIdleOverlayInfo : PausableConditionalTraitInfo, IRenderActorPreviewSpritesInfo, Requires<RenderSpritesInfo>, Requires<BodyOrientationInfo>
22 	{
23 		[SequenceReference]
24 		[Desc("Animation to play when the actor is created.")]
25 		public readonly string StartSequence = null;
26 
27 		[SequenceReference]
28 		[Desc("Sequence name to use")]
29 		public readonly string Sequence = "idle-overlay";
30 
31 		[Desc("Position relative to body")]
32 		public readonly WVec Offset = WVec.Zero;
33 
34 		[PaletteReference("IsPlayerPalette")]
35 		[Desc("Custom palette name")]
36 		public readonly string Palette = null;
37 
38 		[Desc("Custom palette is a player palette BaseName")]
39 		public readonly bool IsPlayerPalette = false;
40 
Create(ActorInitializer init)41 		public override object Create(ActorInitializer init) { return new WithIdleOverlay(init.Self, this); }
42 
RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)43 		public IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
44 		{
45 			if (!EnabledByDefault)
46 				yield break;
47 
48 			if (Palette != null)
49 				p = init.WorldRenderer.Palette(Palette);
50 
51 			Func<int> facing;
52 			if (init.Contains<DynamicFacingInit>())
53 				facing = init.Get<DynamicFacingInit, Func<int>>();
54 			else
55 			{
56 				var f = init.Contains<FacingInit>() ? init.Get<FacingInit, int>() : 0;
57 				facing = () => f;
58 			}
59 
60 			var anim = new Animation(init.World, image, facing);
61 			anim.PlayRepeating(RenderSprites.NormalizeSequence(anim, init.GetDamageState(), Sequence));
62 
63 			var body = init.Actor.TraitInfo<BodyOrientationInfo>();
64 			Func<WRot> orientation = () => body.QuantizeOrientation(WRot.FromFacing(facing()), facings);
65 			Func<WVec> offset = () => body.LocalToWorld(Offset.Rotate(orientation()));
66 			Func<int> zOffset = () =>
67 			{
68 				var tmpOffset = offset();
69 				return tmpOffset.Y + tmpOffset.Z + 1;
70 			};
71 
72 			yield return new SpriteActorPreview(anim, offset, zOffset, p, rs.Scale);
73 		}
74 	}
75 
76 	public class WithIdleOverlay : PausableConditionalTrait<WithIdleOverlayInfo>, INotifyDamageStateChanged
77 	{
78 		readonly Animation overlay;
79 
WithIdleOverlay(Actor self, WithIdleOverlayInfo info)80 		public WithIdleOverlay(Actor self, WithIdleOverlayInfo info)
81 			: base(info)
82 		{
83 			var rs = self.Trait<RenderSprites>();
84 			var body = self.Trait<BodyOrientation>();
85 
86 			overlay = new Animation(self.World, rs.GetImage(self), () => IsTraitPaused);
87 			if (info.StartSequence != null)
88 				overlay.PlayThen(RenderSprites.NormalizeSequence(overlay, self.GetDamageState(), info.StartSequence),
89 					() => overlay.PlayRepeating(RenderSprites.NormalizeSequence(overlay, self.GetDamageState(), info.Sequence)));
90 			else
91 				overlay.PlayRepeating(RenderSprites.NormalizeSequence(overlay, self.GetDamageState(), info.Sequence));
92 
93 			var anim = new AnimationWithOffset(overlay,
94 				() => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))),
95 				() => IsTraitDisabled,
96 				p => RenderUtils.ZOffsetFromCenter(self, p, 1));
97 
98 			rs.Add(anim, info.Palette, info.IsPlayerPalette);
99 		}
100 
INotifyDamageStateChanged.DamageStateChanged(Actor self, AttackInfo e)101 		void INotifyDamageStateChanged.DamageStateChanged(Actor self, AttackInfo e)
102 		{
103 			overlay.ReplaceAnim(RenderSprites.NormalizeSequence(overlay, e.DamageState, overlay.CurrentSequence.Name));
104 		}
105 	}
106 }
107