diff --git a/OpenRA.Mods.Common/Effects/Beacon.cs b/OpenRA.Mods.Common/Effects/Beacon.cs index 434df3b015..059e7ab9b4 100644 --- a/OpenRA.Mods.Common/Effects/Beacon.cs +++ b/OpenRA.Mods.Common/Effects/Beacon.cs @@ -32,18 +32,21 @@ namespace OpenRA.Mods.Common.Effects readonly Animation clock; readonly int duration; + int delay; int arrowHeight = MaxArrowHeight; int arrowSpeed = 50; int tick; // Player-placed beacons are removed after a delay - public Beacon(Player owner, WPos position, int duration, string beaconPalette, bool isPlayerPalette, string beaconCollection, string arrowSprite, string circleSprite) + public Beacon(Player owner, WPos position, int duration, string beaconPalette, bool isPlayerPalette, + string beaconCollection, string arrowSprite, string circleSprite, int delay = 0) { this.owner = owner; this.position = position; this.beaconPalette = beaconPalette; this.isPlayerPalette = isPlayerPalette; this.duration = duration; + this.delay = delay; if (!string.IsNullOrEmpty(arrowSprite)) { @@ -58,10 +61,10 @@ namespace OpenRA.Mods.Common.Effects } } - // Support power beacons are expected to clean themselves up + // By default, support power beacons are expected to clean themselves up public Beacon(Player owner, WPos position, bool isPlayerPalette, string palette, string posterCollection, string posterType, string posterPalette, - string arrowSequence, string circleSequence, string clockSequence, Func clockFraction) - : this(owner, position, -1, palette, isPlayerPalette, posterCollection, arrowSequence, circleSequence) + string arrowSequence, string circleSequence, string clockSequence, Func clockFraction, int delay = 0, int duration = -1) + : this(owner, position, duration, palette, isPlayerPalette, posterCollection, arrowSequence, circleSequence, delay) { this.posterPalette = posterPalette; @@ -80,6 +83,9 @@ namespace OpenRA.Mods.Common.Effects void IEffect.Tick(World world) { + if (delay-- > 0) + return; + arrowHeight += arrowSpeed; var clamped = arrowHeight.Clamp(0, MaxArrowHeight); if (arrowHeight != clamped) @@ -105,6 +111,9 @@ namespace OpenRA.Mods.Common.Effects IEnumerable IEffectAboveShroud.RenderAboveShroud(WorldRenderer r) { + if (delay > 0) + yield break; + if (!owner.IsAlliedWith(owner.World.RenderPlayer)) yield break; diff --git a/OpenRA.Mods.Common/Effects/RevealShroudEffect.cs b/OpenRA.Mods.Common/Effects/RevealShroudEffect.cs new file mode 100644 index 0000000000..116bffd7a2 --- /dev/null +++ b/OpenRA.Mods.Common/Effects/RevealShroudEffect.cs @@ -0,0 +1,81 @@ +#region Copyright & License Information +/* + * Copyright 2007-2016 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, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.Effects; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Effects +{ + public class RevealShroudEffect : IEffect + { + static readonly PPos[] NoCells = { }; + + readonly WPos pos; + readonly Player player; + readonly Shroud.SourceType sourceType; + readonly WDist revealRadius; + readonly Stance validStances; + readonly int duration; + + int ticks; + + public RevealShroudEffect(WPos pos, WDist radius, Shroud.SourceType type, Player forPlayer, Stance stances, int delay = 0, int duration = 50) + { + this.pos = pos; + player = forPlayer; + revealRadius = radius; + validStances = stances; + sourceType = type; + this.duration = duration; + ticks = -delay; + } + + void AddCellsToPlayerShroud(Player p, PPos[] uv) { if (!validStances.HasStance(p.Stances[player])) return; p.Shroud.AddSource(this, sourceType, uv); } + + void RemoveCellsFromPlayerShroud(Player p) { p.Shroud.RemoveSource(this); } + + PPos[] ProjectedCells(World world) + { + var map = world.Map; + var range = revealRadius; + if (range == WDist.Zero) + return NoCells; + + return Shroud.ProjectedCellsInRange(map, pos, range) + .ToArray(); + } + + public void Tick(World world) + { + if (ticks == 0) + { + var cells = ProjectedCells(world); + foreach (var p in world.Players) + AddCellsToPlayerShroud(p, cells); + } + + if (ticks == duration) + { + foreach (var p in world.Players) + RemoveCellsFromPlayerShroud(p); + + world.AddFrameEndTask(w => w.Remove(this)); + } + + ticks++; + } + + public IEnumerable Render(WorldRenderer wr) { return SpriteRenderable.None; } + } +} diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 93aaa12c61..51e210ca26 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -138,6 +138,7 @@ + diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/AirstrikePower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/AirstrikePower.cs index 6ae356a9ea..f5107d0d35 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/AirstrikePower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/AirstrikePower.cs @@ -176,7 +176,8 @@ namespace OpenRA.Mods.Common.Traits Info.ArrowSequence, Info.CircleSequence, Info.ClockSequence, - () => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Length) * 1f / distance); + () => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Length) * 1f / distance, + Info.BeaconDelay); w.Add(beacon); } diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs index 5630466860..d5a2438a44 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs @@ -39,29 +39,34 @@ namespace OpenRA.Mods.Common.Traits [Desc("Palette to use for the missile weapon image.")] [PaletteReference("IsPlayerPalette")] public readonly string MissilePalette = "effect"; - [Desc("Custom palette is a player palette BaseName")] + [Desc("Custom palette is a player palette BaseName.")] public readonly bool IsPlayerPalette = false; - [Desc("Travel time - split equally between ascent and descent")] + [Desc("Travel time - split equally between ascent and descent.")] public readonly int FlightDelay = 400; - [Desc("Visual ascent velocity in WDist / tick")] + [Desc("Visual ascent velocity in WDist / tick.")] public readonly WDist FlightVelocity = new WDist(512); - [Desc("Descend immediately on the target, with half the FlightDelay")] + [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")] + [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("Range of cells the camera should reveal around target cell.")] + public readonly WDist CameraRange = WDist.Zero; - [Desc("Amount of time before detonation to spawn the camera")] + [Desc("Can the camera reveal shroud generated by the GeneratesShroud trait?")] + public readonly bool RevealGeneratedShroud = true; + + [Desc("Reveal cells to players with these stances only.")] + public readonly Stance CameraStances = Stance.Ally; + + [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")] + [Desc("Amount of time after detonation to remove the camera.")] public readonly int CameraRemoveDelay = 25; [Desc("Corresponds to `Type` from `FlashPaletteEffect` on the world actor.")] @@ -114,19 +119,13 @@ namespace OpenRA.Mods.Common.Traits self.World.AddFrameEndTask(w => w.Add(missile)); - if (info.CameraActor != null) + if (info.CameraRange != WDist.Zero) { - var camera = self.World.CreateActor(false, info.CameraActor, new TypeDictionary - { - new LocationInit(order.TargetLocation), - new OwnerInit(self.Owner), - }); + var type = info.RevealGeneratedShroud ? Shroud.SourceType.Visibility + : Shroud.SourceType.PassiveVisibility; - camera.QueueActivity(new Wait(info.CameraSpawnAdvance + info.CameraRemoveDelay)); - camera.QueueActivity(new RemoveSelf()); - - Action addCamera = () => self.World.AddFrameEndTask(w => w.Add(camera)); - self.World.AddFrameEndTask(w => w.Add(new DelayedAction(info.FlightDelay - info.CameraSpawnAdvance, addCamera))); + self.World.AddFrameEndTask(w => w.Add(new RevealShroudEffect(targetPosition, info.CameraRange, type, self.Owner, info.CameraStances, + info.FlightDelay - info.CameraSpawnAdvance, info.CameraSpawnAdvance + info.CameraRemoveDelay))); } if (Info.DisplayBeacon) @@ -142,18 +141,13 @@ namespace OpenRA.Mods.Common.Traits Info.ArrowSequence, Info.CircleSequence, Info.ClockSequence, - () => missile.FractionComplete); - - Action removeBeacon = () => self.World.AddFrameEndTask(w => - { - w.Remove(beacon); - beacon = null; - }); + () => missile.FractionComplete, + Info.BeaconDelay, + info.FlightDelay - info.BeaconRemoveAdvance); self.World.AddFrameEndTask(w => { w.Add(beacon); - w.Add(new DelayedAction(info.FlightDelay - info.BeaconRemoveAdvance, removeBeacon)); }); } } diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs index 6432579b9c..d323dd8b2e 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs @@ -64,6 +64,9 @@ namespace OpenRA.Mods.Common.Traits [SequenceReference("BeaconImage")] public readonly string ArrowSequence = "arrow"; [SequenceReference("BeaconImage")] public readonly string CircleSequence = "circles"; + [Desc("Delay after launch, measured in ticks.")] + public readonly int BeaconDelay = 0; + public readonly bool DisplayRadarPing = false; [Desc("Measured in ticks.")] diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index f43ce3a431..5c03999e99 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -704,6 +704,22 @@ namespace OpenRA.Mods.Common.UtilityCommands RenameNodeKey(node, "-ConditionManager"); } + // Replaced NukePower CameraActor with CameraRange (effect-based reveal) + if (engineVersion < 20161227) + { + var nukePower = node.Value.Nodes.FirstOrDefault(n => n.Key.StartsWith("NukePower")); + if (nukePower != null) + { + var cameraActor = nukePower.Value.Nodes.FirstOrDefault(n => n.Key == "CameraActor"); + if (cameraActor != null) + { + nukePower.Value.Nodes.Remove(cameraActor); + nukePower.Value.Nodes.Add(new MiniYamlNode("CameraRange", "10")); + Console.WriteLine("If your camera actor had a different reveal range than 10, you'll need to correct that manually"); + } + } + } + UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1); } diff --git a/OpenRA.Mods.RA/Traits/SupportPowers/ParatroopersPower.cs b/OpenRA.Mods.RA/Traits/SupportPowers/ParatroopersPower.cs index a48efa983a..c97d234220 100644 --- a/OpenRA.Mods.RA/Traits/SupportPowers/ParatroopersPower.cs +++ b/OpenRA.Mods.RA/Traits/SupportPowers/ParatroopersPower.cs @@ -212,7 +212,8 @@ namespace OpenRA.Mods.RA.Traits Info.ArrowSequence, Info.CircleSequence, Info.ClockSequence, - () => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Length) * 1f / distance); + () => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Length) * 1f / distance, + Info.BeaconDelay); w.Add(beacon); } diff --git a/OpenRA.Mods.TS/Effects/AnimatedBeacon.cs b/OpenRA.Mods.TS/Effects/AnimatedBeacon.cs index 317dd2b596..c537af4a9e 100644 --- a/OpenRA.Mods.TS/Effects/AnimatedBeacon.cs +++ b/OpenRA.Mods.TS/Effects/AnimatedBeacon.cs @@ -27,15 +27,17 @@ namespace OpenRA.Mods.TS.Effects readonly Animation beacon; readonly int duration; + int delay; int tick; - public AnimatedBeacon(Player owner, WPos position, int duration, string beaconPalette, bool isPlayerPalette, string beaconImage, string beaconSequence) + public AnimatedBeacon(Player owner, WPos position, int duration, string beaconPalette, bool isPlayerPalette, string beaconImage, string beaconSequence, int delay = 0) { this.owner = owner; this.position = position; this.beaconPalette = beaconPalette; this.isPlayerPalette = isPlayerPalette; this.duration = duration; + this.delay = delay; if (!string.IsNullOrEmpty(beaconSequence)) { @@ -49,6 +51,9 @@ namespace OpenRA.Mods.TS.Effects void IEffect.Tick(World world) { + if (delay-- > 0) + return; + if (beacon != null) beacon.Tick(); @@ -60,6 +65,9 @@ namespace OpenRA.Mods.TS.Effects IEnumerable IEffectAboveShroud.RenderAboveShroud(WorldRenderer r) { + if (delay > 0) + return SpriteRenderable.None; + if (beacon == null) return SpriteRenderable.None; diff --git a/mods/cnc/rules/structures.yaml b/mods/cnc/rules/structures.yaml index bdd61aa228..06a1c452a5 100644 --- a/mods/cnc/rules/structures.yaml +++ b/mods/cnc/rules/structures.yaml @@ -633,7 +633,7 @@ TMPL: BeaconPoster: atomic BeaconPosterPalette: beaconposter DisplayRadarPing: True - CameraActor: camera + CameraRange: 10c0 SupportPowerChargeBar: Power: Amount: -150 diff --git a/mods/d2k/rules/structures.yaml b/mods/d2k/rules/structures.yaml index 4630126854..769e780d10 100644 --- a/mods/d2k/rules/structures.yaml +++ b/mods/d2k/rules/structures.yaml @@ -951,7 +951,7 @@ palace: SpawnOffset: -512,1c171,0 DisplayBeacon: True DisplayRadarPing: True - CameraActor: camera + CameraRange: 10c0 ActivationSequence: ProduceActorPower@fremen: Description: Recruit Fremen diff --git a/mods/ra/rules/structures.yaml b/mods/ra/rules/structures.yaml index 2520993e2b..490a26c410 100644 --- a/mods/ra/rules/structures.yaml +++ b/mods/ra/rules/structures.yaml @@ -36,8 +36,8 @@ MSLO: DisplayBeacon: True DisplayRadarPing: True BeaconPoster: atomicon - CameraActor: camera FlashType: Nuke + CameraRange: 10c0 CanPowerDown: PowerupSound: EnablePower PowerdownSound: DisablePower diff --git a/mods/ts/rules/nod-support.yaml b/mods/ts/rules/nod-support.yaml index 62606a522c..0739de0296 100644 --- a/mods/ts/rules/nod-support.yaml +++ b/mods/ts/rules/nod-support.yaml @@ -272,6 +272,6 @@ NAMISL: DisplayBeacon: False DisplayRadarPing: True BeaconPoster: - CameraActor: camera + CameraRange: 10c0 SelectionDecorations: VisualBounds: 75,48