diff --git a/OpenRA.Mods.RA/Effects/Beacon.cs b/OpenRA.Mods.RA/Effects/Beacon.cs index ec3d5889ce..a192cc8f00 100644 --- a/OpenRA.Mods.RA/Effects/Beacon.cs +++ b/OpenRA.Mods.RA/Effects/Beacon.cs @@ -8,6 +8,7 @@ */ #endregion +using System; using System.Collections.Generic; using System.Linq; using OpenRA.Effects; @@ -24,16 +25,18 @@ namespace OpenRA.Mods.RA.Effects readonly Animation arrow; readonly Animation circles; readonly Animation poster; + readonly Animation clock; + static readonly int maxArrowHeight = 512; int arrowHeight = maxArrowHeight; int arrowSpeed = 50; - public Beacon(Player owner, WPos position, int duration, string palettePrefix, string posterType, string posterPalette) + // Player-placed beacons are removed after a delay + public Beacon(Player owner, WPos position, int duration, string palettePrefix) { this.owner = owner; this.position = position; this.palettePrefix = palettePrefix; - this.posterPalette = posterPalette; arrow = new Animation(owner.World, "beacon"); circles = new Animation(owner.World, "beacon"); @@ -41,14 +44,27 @@ namespace OpenRA.Mods.RA.Effects arrow.Play("arrow"); circles.Play("circles"); + if (duration > 0) + owner.World.Add(new DelayedAction(duration, () => owner.World.Remove(this))); + } + + // Support power beacons are expected to clean themselves up + public Beacon(Player owner, WPos position, string palettePrefix, string posterType, string posterPalette, Func clockFraction) + : this(owner, position, -1, palettePrefix) + { + this.posterPalette = posterPalette; + if (posterType != null) { poster = new Animation(owner.World, "beacon"); poster.Play(posterType); - } - if (duration > 0) - owner.World.Add(new DelayedAction(duration, () => owner.World.Remove(this))); + if (clockFraction != null) + { + clock = new Animation(owner.World, "beacon"); + clock.PlayFetchIndex("clock", () => Exts.Clamp((int)(clockFraction() * (clock.CurrentSequence.Length - 1)), 0, clock.CurrentSequence.Length - 1)); + } + } } public void Tick(World world) @@ -63,6 +79,9 @@ namespace OpenRA.Mods.RA.Effects arrow.Tick(); circles.Tick(); + + if (clock != null) + clock.Tick(); } public IEnumerable Render(WorldRenderer r) @@ -78,8 +97,14 @@ namespace OpenRA.Mods.RA.Effects yield return a; if (poster != null) + { foreach (var a in poster.Render(position, r.Palette(posterPalette))) yield return a; + + if (clock != null) + foreach (var a in clock.Render(position, r.Palette(posterPalette))) + yield return a; + } } } } diff --git a/OpenRA.Mods.RA/Effects/NukeLaunch.cs b/OpenRA.Mods.RA/Effects/NukeLaunch.cs index 577eefa0ce..4b466e5003 100755 --- a/OpenRA.Mods.RA/Effects/NukeLaunch.cs +++ b/OpenRA.Mods.RA/Effects/NukeLaunch.cs @@ -90,5 +90,7 @@ namespace OpenRA.Mods.RA.Effects { return anim.Render(pos, wr.Palette("effect")); } + + public float FractionComplete { get { return ticks * 1f / delay; } } } } diff --git a/OpenRA.Mods.RA/Player/PlaceBeacon.cs b/OpenRA.Mods.RA/Player/PlaceBeacon.cs index 658e7cc7b6..4b335ea6ed 100644 --- a/OpenRA.Mods.RA/Player/PlaceBeacon.cs +++ b/OpenRA.Mods.RA/Player/PlaceBeacon.cs @@ -49,7 +49,7 @@ namespace OpenRA.Mods.RA if (playerBeacon != null) self.World.Remove(playerBeacon); - playerBeacon = new Beacon(self.Owner, pos, info.Duration, info.PalettePrefix, null, null); + playerBeacon = new Beacon(self.Owner, pos, info.Duration, info.PalettePrefix); self.World.Add(playerBeacon); if (self.Owner.IsAlliedWith(self.World.RenderPlayer)) diff --git a/OpenRA.Mods.RA/SupportPowers/AirstrikePower.cs b/OpenRA.Mods.RA/SupportPowers/AirstrikePower.cs index 496e536e4c..e0eb70c3b5 100644 --- a/OpenRA.Mods.RA/SupportPowers/AirstrikePower.cs +++ b/OpenRA.Mods.RA/SupportPowers/AirstrikePower.cs @@ -43,6 +43,9 @@ namespace OpenRA.Mods.RA [Desc("Amount of time to keep the camera alive after the aircraft have finished attacking")] public readonly int CameraRemoveDelay = 25; + [Desc("Weapon range offset to apply during the beacon clock calculation")] + public readonly WRange BeaconDistanceOffset = WRange.FromCells(6); + public override object Create(ActorInitializer init) { return new AirstrikePower(init.self, this); } } @@ -55,20 +58,6 @@ namespace OpenRA.Mods.RA { base.Activate(self, order, manager); - Beacon beacon = null; - if (Info.DisplayBeacon) - { - beacon = new Beacon( - order.Player, - order.TargetLocation.CenterPosition, - -1, - Info.BeaconPalettePrefix, - Info.BeaconPoster, - Info.BeaconPosterPalette); - - self.World.Add(beacon); - } - var info = Info as AirstrikePowerInfo; var attackFacing = Util.QuantizeFacing(self.World.SharedRandom.Next(256), info.QuantizedFacings) * (256 / info.QuantizedFacings); var attackRotation = WRot.FromFacing(attackFacing); @@ -81,6 +70,7 @@ namespace OpenRA.Mods.RA Actor flare = null; Actor camera = null; + Beacon beacon = null; Dictionary aircraftInRange = new Dictionary(); Action onEnterRange = a => @@ -147,6 +137,7 @@ namespace OpenRA.Mods.RA var notification = self.Owner.IsAlliedWith(self.World.RenderPlayer) ? Info.LaunchSound : Info.IncomingSound; Sound.Play(notification); + Actor distanceTestActor = null; for (var i = -info.SquadSize / 2; i <= info.SquadSize / 2; i++) { // Even-sized squads skip the lead plane @@ -174,6 +165,23 @@ namespace OpenRA.Mods.RA a.QueueActivity(new Fly(a, Target.FromPos(finishEdge + spawnOffset))); a.QueueActivity(new RemoveSelf()); aircraftInRange.Add(a, false); + distanceTestActor = a; + } + + if (Info.DisplayBeacon) + { + var distance = (target - startEdge).HorizontalLength; + + beacon = new Beacon( + order.Player, + order.TargetLocation.CenterPosition, + Info.BeaconPalettePrefix, + Info.BeaconPoster, + Info.BeaconPosterPalette, + () => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Range) * 1f / distance + ); + + w.Add(beacon); } }); } diff --git a/OpenRA.Mods.RA/SupportPowers/NukePower.cs b/OpenRA.Mods.RA/SupportPowers/NukePower.cs index b4f997ec1d..b9c4609719 100755 --- a/OpenRA.Mods.RA/SupportPowers/NukePower.cs +++ b/OpenRA.Mods.RA/SupportPowers/NukePower.cs @@ -68,20 +68,6 @@ namespace OpenRA.Mods.RA { base.Activate(self, order, manager); - Beacon beacon = null; - if (Info.DisplayBeacon) - { - beacon = new Beacon( - order.Player, - order.TargetLocation.CenterPosition, - -1, - Info.BeaconPalettePrefix, - Info.BeaconPoster, - Info.BeaconPosterPalette); - - self.World.Add(beacon); - } - if (self.Owner.IsAlliedWith(self.World.RenderPlayer)) Sound.Play(Info.LaunchSound); else @@ -91,10 +77,12 @@ namespace OpenRA.Mods.RA var rb = self.Trait(); rb.PlayCustomAnim(self, "active"); - self.World.AddFrameEndTask(w => w.Add(new NukeLaunch(self.Owner, npi.MissileWeapon, + var missile = new NukeLaunch(self.Owner, npi.MissileWeapon, self.CenterPosition + body.LocalToWorld(npi.SpawnOffset), order.TargetLocation.CenterPosition, - npi.FlightVelocity, npi.FlightDelay, npi.SkipAscent))); + npi.FlightVelocity, npi.FlightDelay, npi.SkipAscent); + + self.World.AddFrameEndTask(w => w.Add(missile)); if (npi.CameraActor != null) { @@ -111,15 +99,29 @@ namespace OpenRA.Mods.RA self.World.AddFrameEndTask(w => w.Add(new DelayedAction(npi.FlightDelay - npi.CameraSpawnAdvance, addCamera))); } - if (beacon != null) + if (Info.DisplayBeacon) { + var beacon = new Beacon( + order.Player, + order.TargetLocation.CenterPosition, + Info.BeaconPalettePrefix, + Info.BeaconPoster, + Info.BeaconPosterPalette, + () => missile.FractionComplete + ); + + Action removeBeacon = () => self.World.AddFrameEndTask(w => { w.Remove(beacon); beacon = null; }); - self.World.AddFrameEndTask(w => w.Add(new DelayedAction(npi.FlightDelay - npi.BeaconRemoveAdvance, removeBeacon))); + self.World.AddFrameEndTask(w => + { + w.Add(beacon); + w.Add(new DelayedAction(npi.FlightDelay - npi.BeaconRemoveAdvance, removeBeacon)); + }); } } } diff --git a/mods/cnc/bits/beaconclock.shp b/mods/cnc/bits/beaconclock.shp new file mode 100644 index 0000000000..bab5673837 Binary files /dev/null and b/mods/cnc/bits/beaconclock.shp differ diff --git a/mods/cnc/sequences/misc.yaml b/mods/cnc/sequences/misc.yaml index 33aa39ef28..8d5095f4c0 100644 --- a/mods/cnc/sequences/misc.yaml +++ b/mods/cnc/sequences/misc.yaml @@ -169,6 +169,10 @@ beacon: Start: 0 Length: 1 Offset: 0,-42 + clock: beaconclock + Start: 0 + Length: * + Offset: 0,-42 select: repair: diff --git a/mods/ra/bits/beaconclock.shp b/mods/ra/bits/beaconclock.shp new file mode 100644 index 0000000000..c18bc3e760 Binary files /dev/null and b/mods/ra/bits/beaconclock.shp differ diff --git a/mods/ra/sequences/misc.yaml b/mods/ra/sequences/misc.yaml index 3789f4f5d3..1c46d17d88 100644 --- a/mods/ra/sequences/misc.yaml +++ b/mods/ra/sequences/misc.yaml @@ -125,6 +125,10 @@ beacon: Start: 0 Length: * Offset: 0,-42 + clock: beaconclock + Start: 0 + Length: * + Offset: 0,-42 smoke_m: idle: