diff --git a/CHANGELOG b/CHANGELOG index 549ad55732..19551994e3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -34,6 +34,7 @@ NEW: Added a warning dialog when force starting a match. Added a new mod selection window, which now appears on the first game start. Fixed walls not updating when neighboring sections were sold or destroyed. + Nuclear missile delay is now independent of map size, and reveals the target area immediately before detonation. Dune 2000: Engineers can now regain control over husks. Added the Atreides grenadier from the 1.06 patch. @@ -46,6 +47,7 @@ NEW: Turrets now integrate with walls (visually, and as line building targets). Fixed unclickable area on the right edge of the production palette. Buildings now throw damaging shrapnel when they explode. + Airstrike will now reveal (only) the target area when it strikes. Red Alert: Mechanics can now regain control over husks. Engineers are now remapped to team colors. @@ -109,7 +111,7 @@ NEW: Removed health bars and selection boxes from walls. Changed Nod Obelisk HP to 600 from 400, same as Advanced Guard Tower Fixed dinosaurs not being able to collect crates. - A10 air strike will only reveal the target area. + A10 air strike and the ion cannon will now reveal (only) the target area when they strike. Reduced Guard Tower vision from 7 to 6. Increased Guard Tower build time from 12 seconds to 24 seconds. Reduced Gun Turret vision from 7 to 6. diff --git a/OpenRA.Mods.Cnc/IonCannonPower.cs b/OpenRA.Mods.Cnc/IonCannonPower.cs index 78be5acc6a..bfccab5135 100644 --- a/OpenRA.Mods.Cnc/IonCannonPower.cs +++ b/OpenRA.Mods.Cnc/IonCannonPower.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) * 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. For more information, @@ -10,11 +10,21 @@ using OpenRA.Mods.Cnc.Effects; using OpenRA.Mods.RA; +using OpenRA.Mods.RA.Activities; +using OpenRA.Primitives; +using OpenRA.Traits; namespace OpenRA.Mods.Cnc { class IonCannonPowerInfo : SupportPowerInfo { + [ActorReference] + [Desc("Actor to spawn when the attack starts")] + public readonly string CameraActor = null; + + [Desc("Amount of time to keep the camera alive")] + public readonly int CameraRemoveDelay = 25; + public override object Create(ActorInitializer init) { return new IonCannonPower(init.self, this); } } @@ -34,8 +44,21 @@ namespace OpenRA.Mods.Cnc self.World.AddFrameEndTask(w => { + var info = Info as IonCannonPowerInfo; Sound.Play(Info.LaunchSound, order.TargetLocation.CenterPosition); w.Add(new IonCannon(self, w, order.TargetLocation)); + + if (info.CameraActor == null) + return; + + var camera = w.CreateActor(info.CameraActor, new TypeDictionary + { + new LocationInit(order.TargetLocation), + new OwnerInit(self.Owner), + }); + + camera.QueueActivity(new Wait(info.CameraRemoveDelay)); + camera.QueueActivity(new RemoveSelf()); }); } } diff --git a/OpenRA.Mods.RA/Effects/Beacon.cs b/OpenRA.Mods.RA/Effects/Beacon.cs index 90abc83f7d..f5433e3469 100644 --- a/OpenRA.Mods.RA/Effects/Beacon.cs +++ b/OpenRA.Mods.RA/Effects/Beacon.cs @@ -44,7 +44,8 @@ namespace OpenRA.Mods.RA.Effects poster.Play(posterType); } - owner.World.Add(new DelayedAction(duration, () => owner.World.Remove(this))); + if (duration > 0) + owner.World.Add(new DelayedAction(duration, () => owner.World.Remove(this))); } public void Tick(World world) diff --git a/OpenRA.Mods.RA/Effects/NukeLaunch.cs b/OpenRA.Mods.RA/Effects/NukeLaunch.cs index d17275b253..645c146732 100755 --- a/OpenRA.Mods.RA/Effects/NukeLaunch.cs +++ b/OpenRA.Mods.RA/Effects/NukeLaunch.cs @@ -18,18 +18,33 @@ namespace OpenRA.Mods.RA.Effects { public class NukeLaunch : IEffect { - readonly Player firedBy; - Animation anim; - WPos pos; - CPos targetLocation; - bool goingUp = true; - string weapon; + readonly Actor firedBy; + readonly Animation anim; + readonly string weapon; - public NukeLaunch(Player firedBy, Actor silo, string weapon, WPos launchPos, CPos targetLocation) + readonly WPos ascendSource; + readonly WPos ascendTarget; + readonly WPos descendSource; + readonly WPos descendTarget; + readonly int delay; + readonly int turn; + + WPos pos; + int ticks; + + public NukeLaunch(Actor firedBy, string weapon, WPos launchPos, WPos targetPos, WRange velocity, int delay, bool skipAscent) { this.firedBy = firedBy; - this.targetLocation = targetLocation; this.weapon = weapon; + this.delay = delay; + this.turn = delay / 2; + + var offset = new WVec(WRange.Zero, WRange.Zero, velocity * turn); + ascendSource = launchPos; + ascendTarget = launchPos + offset; + descendSource = targetPos + offset; + descendTarget = targetPos; + anim = new Animation(weapon); anim.PlayRepeating("up"); @@ -37,40 +52,34 @@ namespace OpenRA.Mods.RA.Effects var weaponRules = Rules.Weapons[weapon.ToLowerInvariant()]; if (weaponRules.Report != null && weaponRules.Report.Any()) Sound.Play(weaponRules.Report.Random(firedBy.World.SharedRandom), pos); - if (silo == null) - StartDescent(firedBy.World); + + if (skipAscent) + ticks = turn; } - void StartDescent(World world) - { - pos = targetLocation.CenterPosition + new WVec(0, 0, 1024*firedBy.World.Map.Bounds.Height); - anim.PlayRepeating("down"); - goingUp = false; - } public void Tick(World world) { anim.Tick(); - var delta = new WVec(0,0,427); - if (goingUp) - { - pos += delta; - if (pos.Z >= world.Map.Bounds.Height*1024) - StartDescent(world); - } + if (ticks == turn) + anim.PlayRepeating("down"); + + if (ticks <= turn) + pos = WPos.LerpQuadratic(ascendSource, ascendTarget, WAngle.Zero, ticks, turn); else - { - pos -= delta; - if (pos.Z <= 0) - Explode(world); - } + pos = WPos.LerpQuadratic(descendSource, descendTarget, WAngle.Zero, ticks - turn, delay - turn); + + if (ticks == delay) + Explode(world); + + ticks++; } void Explode(World world) { world.AddFrameEndTask(w => w.Remove(this)); - Combat.DoExplosion(firedBy.PlayerActor, weapon, pos); + Combat.DoExplosion(firedBy, weapon, pos); world.WorldActor.Trait().AddEffect(20, pos, 5); foreach (var a in world.ActorsWithTrait()) diff --git a/OpenRA.Mods.RA/SupportPowers/NukePower.cs b/OpenRA.Mods.RA/SupportPowers/NukePower.cs index d62da05d6d..4bc7c90c87 100755 --- a/OpenRA.Mods.RA/SupportPowers/NukePower.cs +++ b/OpenRA.Mods.RA/SupportPowers/NukePower.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) * 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. For more information, @@ -8,7 +8,11 @@ */ #endregion +using System; +using OpenRA.Effects; +using OpenRA.Mods.RA.Activities; using OpenRA.Mods.RA.Effects; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.RA @@ -19,6 +23,28 @@ namespace OpenRA.Mods.RA public readonly string MissileWeapon = ""; public readonly WVec SpawnOffset = WVec.Zero; + [Desc("Travel time - split equally between ascent and descent")] + public readonly int FlightDelay = 400; + + [Desc("Visual ascent velocity in WRange / tick")] + public readonly WRange FlightVelocity = new WRange(512); + + [Desc("Descend immediately on the target, with half the FlightDelay")] + public readonly bool SkipAscent = false; + + [Desc("Amount of time before detonation to remove the beacon")] + public readonly int BeaconRemoveAdvance = 25; + + [ActorReference] + [Desc("Actor to spawn before detonation")] + public readonly string CameraActor = null; + + [Desc("Amount of time before detonation to spawn the camera")] + public readonly int CameraSpawnAdvance = 25; + + [Desc("Amount of time after detonation to remove the camera")] + public readonly int CameraRemoveDelay = 25; + public override object Create(ActorInitializer init) { return new NukePower(init.self, this); } } @@ -50,8 +76,37 @@ namespace OpenRA.Mods.RA var npi = Info as NukePowerInfo; var rb = self.Trait(); rb.PlayCustomAnim(self, "active"); - self.World.AddFrameEndTask(w => w.Add( - new NukeLaunch(self.Owner, self, npi.MissileWeapon, self.CenterPosition + body.LocalToWorld(npi.SpawnOffset), order.TargetLocation))); + + self.World.AddFrameEndTask(w => w.Add(new NukeLaunch(self, npi.MissileWeapon, + self.CenterPosition + body.LocalToWorld(npi.SpawnOffset), + order.TargetLocation.CenterPosition, + npi.FlightVelocity, npi.FlightDelay, npi.SkipAscent))); + + if (npi.CameraActor != null) + { + var camera = self.World.CreateActor(false, npi.CameraActor, new TypeDictionary + { + new LocationInit(order.TargetLocation), + new OwnerInit(self.Owner), + }); + + camera.QueueActivity(new Wait(npi.CameraSpawnAdvance + npi.CameraRemoveDelay)); + camera.QueueActivity(new RemoveSelf()); + + Action addCamera = () => self.World.AddFrameEndTask(w => w.Add(camera)); + self.World.AddFrameEndTask(w => w.Add(new DelayedAction(npi.FlightDelay - npi.CameraSpawnAdvance, addCamera))); + } + + if (beacon != null) + { + Action removeBeacon = () => self.World.AddFrameEndTask(w => + { + w.Remove(beacon); + beacon = null; + }); + + self.World.AddFrameEndTask(w => w.Add(new DelayedAction(npi.FlightDelay - npi.BeaconRemoveAdvance, removeBeacon))); + } } } } diff --git a/mods/cnc/maps/gdi03/map.yaml b/mods/cnc/maps/gdi03/map.yaml index b8be6ef4b5..355e39aca1 100644 --- a/mods/cnc/maps/gdi03/map.yaml +++ b/mods/cnc/maps/gdi03/map.yaml @@ -972,6 +972,11 @@ Rules: SelectTargetSound: select1.aud IncomingSound: enemya.aud UnitType: a10 + DisplayBeacon: True + BeaconDuration: -1 + BeaconPoster: airstrike + DisplayRadarPing: True + CameraActor: camera Sequences: diff --git a/mods/cnc/rules/misc.yaml b/mods/cnc/rules/misc.yaml index 9b4fe0aeb8..a724f2c0e1 100644 --- a/mods/cnc/rules/misc.yaml +++ b/mods/cnc/rules/misc.yaml @@ -49,7 +49,14 @@ CAMERA: HP: 1000 RevealsShroud: Range: 10c0 - ProximityCaptor: - Types: Camera + BodyOrientation: + +CAMERA.small: + Immobile: + OccupiesSpace: false + Health: + HP: 1000 + RevealsShroud: + Range: 6c0 BodyOrientation: diff --git a/mods/cnc/rules/structures.yaml b/mods/cnc/rules/structures.yaml index 8641a294a9..a1f07b2c2b 100644 --- a/mods/cnc/rules/structures.yaml +++ b/mods/cnc/rules/structures.yaml @@ -386,6 +386,7 @@ HQ: IncomingSound: enemya.aud UnitType: a10 DisplayBeacon: True + BeaconDuration: -1 BeaconPoster: airstrike DisplayRadarPing: True CameraActor: camera @@ -456,6 +457,7 @@ EYE: SelectTargetSound: select1.aud InsufficientPowerSound: nopower1.aud DisplayRadarPing: True + CameraActor: camera.small SupportPowerChargeBar: TMPL: @@ -496,8 +498,10 @@ TMPL: IncomingSound: nuke1.aud MissileWeapon: atomic DisplayBeacon: True + BeaconDuration: -1 BeaconPoster: atomic DisplayRadarPing: True + CameraActor: camera SupportPowerChargeBar: GUN: diff --git a/mods/d2k/rules/aircraft.yaml b/mods/d2k/rules/aircraft.yaml index 13a5455f41..5199d9e902 100644 --- a/mods/d2k/rules/aircraft.yaml +++ b/mods/d2k/rules/aircraft.yaml @@ -101,8 +101,6 @@ ORNI.bomber: HP: 100 Armor: Type: Light - RevealsShroud: - Range: 10c0 Plane: ROT: 5 Speed: 350 diff --git a/mods/d2k/rules/atreides.yaml b/mods/d2k/rules/atreides.yaml index c601c4d67d..e854b7b35a 100644 --- a/mods/d2k/rules/atreides.yaml +++ b/mods/d2k/rules/atreides.yaml @@ -59,6 +59,9 @@ PALACEA: LongDesc: Ornithopter drops a load of parachuted\nbombs on your target UnitType: orni.bomber SelectTargetSound: + DisplayBeacon: True + BeaconDuration: -1 + CameraActor: camera CanPowerDown: DisabledOverlay: RequiresPower: diff --git a/mods/d2k/rules/harkonnen.yaml b/mods/d2k/rules/harkonnen.yaml index a22a46a697..2fc05e8d94 100644 --- a/mods/d2k/rules/harkonnen.yaml +++ b/mods/d2k/rules/harkonnen.yaml @@ -98,6 +98,8 @@ PALACEH: SpawnOffset: -512,1c171,0 DisplayBeacon: True DisplayRadarPing: True + BeaconDuration: -1 + CameraActor: camera CanPowerDown: DisabledOverlay: RequiresPower: diff --git a/mods/d2k/rules/misc.yaml b/mods/d2k/rules/misc.yaml index 4ee97c7222..44040506e8 100644 --- a/mods/d2k/rules/misc.yaml +++ b/mods/d2k/rules/misc.yaml @@ -115,3 +115,11 @@ SPICEBLOOM: Terrain: Spice BodyOrientation: +CAMERA: + Immobile: + OccupiesSpace: false + Health: + HP: 1000 + RevealsShroud: + Range: 8c0 + BodyOrientation: diff --git a/mods/d2k/rules/ordos.yaml b/mods/d2k/rules/ordos.yaml index 00cb703721..434a772e14 100644 --- a/mods/d2k/rules/ordos.yaml +++ b/mods/d2k/rules/ordos.yaml @@ -88,6 +88,9 @@ PALACEO: LongDesc: Ornithopter drops a load of parachuted\nbombs on your target UnitType: orni.bomber SelectTargetSound: + DisplayBeacon: True + BeaconDuration: -1 + CameraActor: camera CanPowerDown: DisabledOverlay: RequiresPower: diff --git a/mods/ra/rules/structures.yaml b/mods/ra/rules/structures.yaml index 4d4dae93dd..aead769339 100644 --- a/mods/ra/rules/structures.yaml +++ b/mods/ra/rules/structures.yaml @@ -38,8 +38,10 @@ MSLO: SpawnOffset: 0,427,0 DisplayTimer: True DisplayBeacon: True + BeaconDuration: -1 DisplayRadarPing: True BeaconPoster: atomicon + CameraActor: camera CanPowerDown: RequiresPower: DisabledOverlay: