#region Copyright & License Information /* * Copyright (c) The OpenRA Developers and Contributors * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. For more * information, see COPYING. */ #endregion using System.Linq; using OpenRA.Mods.Cnc.Traits.Render; using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits.Render; using OpenRA.Traits; namespace OpenRA.Mods.Cnc.Traits { [Desc("Actor's turret rises from the ground before attacking.")] sealed class AttackPopupTurretedInfo : AttackTurretedInfo, Requires, Requires { [Desc("How many game ticks should pass before closing the actor's turret.")] public readonly int CloseDelay = 125; public readonly WAngle DefaultFacing = WAngle.Zero; [Desc("The percentage of damage that is received while this actor is closed.")] public readonly int ClosedDamageMultiplier = 50; [SequenceReference] [Desc("Sequence to play when opening.")] public readonly string OpeningSequence = "opening"; [SequenceReference] [Desc("Sequence to play when closing.")] public readonly string ClosingSequence = "closing"; [SequenceReference] [Desc("Idle sequence to play when closed.")] public readonly string ClosedIdleSequence = "closed-idle"; [Desc("Which sprite body to play the animation on.")] public readonly string Body = "body"; public override object Create(ActorInitializer init) { return new AttackPopupTurreted(init, this); } } sealed class AttackPopupTurreted : AttackTurreted, INotifyIdle, IDamageModifier { enum PopupState { Open, Rotating, Transitioning, Closed } readonly AttackPopupTurretedInfo info; readonly WithSpriteBody wsb; readonly Turreted turret; int idleTicks = 0; PopupState state = PopupState.Open; readonly bool skippedMakeAnimation; public AttackPopupTurreted(ActorInitializer init, AttackPopupTurretedInfo info) : base(init.Self, info) { this.info = info; turret = turrets.FirstOrDefault(); wsb = init.Self.TraitsImplementing().Single(w => w.Info.Name == info.Body); skippedMakeAnimation = init.Contains(info); } protected override void Created(Actor self) { base.Created(self); // Map placed actors are created in the closed state if (skippedMakeAnimation) { state = PopupState.Closed; wsb.PlayCustomAnimationRepeating(self, info.ClosedIdleSequence); turret.FaceTarget(self, Target.Invalid); } } protected override bool CanAttack(Actor self, in Target target) { if (IsTraitPaused) return false; if (state == PopupState.Closed) { state = PopupState.Transitioning; wsb.PlayCustomAnimation(self, info.OpeningSequence, () => { state = PopupState.Open; wsb.PlayCustomAnimationRepeating(self, wsb.Info.Sequence); }); idleTicks = 0; } if (state == PopupState.Transitioning || !base.CanAttack(self, target)) return false; idleTicks = 0; return true; } void INotifyIdle.TickIdle(Actor self) { if (IsTraitDisabled || IsTraitPaused) return; if (state == PopupState.Open && idleTicks++ > info.CloseDelay) { var facingOffset = new WVec(0, -1024, 0).Rotate(WRot.FromYaw(info.DefaultFacing)); turret.FaceTarget(self, Target.FromPos(self.CenterPosition + facingOffset)); state = PopupState.Rotating; } else if (state == PopupState.Rotating && turret.HasAchievedDesiredFacing) { state = PopupState.Transitioning; wsb.PlayCustomAnimation(self, info.ClosingSequence, () => { state = PopupState.Closed; wsb.PlayCustomAnimationRepeating(self, info.ClosedIdleSequence); turret.FaceTarget(self, Target.Invalid); }); } } int IDamageModifier.GetDamageModifier(Actor attacker, Damage damage) { return state == PopupState.Closed ? info.ClosedDamageMultiplier : 100; } } }