From 3d1d4a1affb99d3167906e0035c831a5a11198d0 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Tue, 23 Nov 2010 16:36:20 +1300 Subject: [PATCH] AOE Chronosphere --- OpenRA.Mods.RA/Chronoshiftable.cs | 10 +- OpenRA.Mods.RA/DefaultShellmapScript.cs | 4 +- OpenRA.Mods.RA/DemoTruck.cs | 2 +- .../SupportPowers/ChronoshiftPower.cs | 240 +++++++++++------- .../SupportPowers/IronCurtainPower.cs | 19 +- mods/ra/rules/structures.yaml | 2 - mods/ra/rules/system.yaml | 2 + 7 files changed, 164 insertions(+), 115 deletions(-) diff --git a/OpenRA.Mods.RA/Chronoshiftable.cs b/OpenRA.Mods.RA/Chronoshiftable.cs index 046f0bb3fa..a7bfff917e 100755 --- a/OpenRA.Mods.RA/Chronoshiftable.cs +++ b/OpenRA.Mods.RA/Chronoshiftable.cs @@ -39,8 +39,16 @@ namespace OpenRA.Mods.RA self.QueueActivity(new Teleport(chronoshiftOrigin)); } } + + public virtual bool CanChronoshiftTo(Actor self, int2 targetLocation) + { + // Todo: Allow enemy units to be chronoshifted into bad terrain to kill them + return self.HasTrait() && + self.Trait().CanEnterCell(targetLocation) && + self.World.LocalPlayer.Shroud.IsExplored(targetLocation); + } - public virtual bool Activate(Actor self, int2 targetLocation, int duration, bool killCargo, Actor chronosphere) + public virtual bool Teleport(Actor self, int2 targetLocation, int duration, bool killCargo, Actor chronosphere) { /// Set up return-to-sender info chronoshiftOrigin = self.Location; diff --git a/OpenRA.Mods.RA/DefaultShellmapScript.cs b/OpenRA.Mods.RA/DefaultShellmapScript.cs index 6c826305f3..24a1b9acad 100755 --- a/OpenRA.Mods.RA/DefaultShellmapScript.cs +++ b/OpenRA.Mods.RA/DefaultShellmapScript.cs @@ -45,8 +45,8 @@ namespace OpenRA.Mods.RA if (ticks == 250) { - Actors["pdox"].Trait().Teleport(Actors["ca1"], new int2(90, 70)); - Actors["pdox"].Trait().Teleport(Actors["ca2"], new int2(92, 71)); + //Actors["pdox"].Trait().Teleport(Actors["ca1"], new int2(90, 70)); + //Actors["pdox"].Trait().Teleport(Actors["ca2"], new int2(92, 71)); } if (ticks == 100) Actors["mslo1"].Trait().Attack(new int2(98, 52)); diff --git a/OpenRA.Mods.RA/DemoTruck.cs b/OpenRA.Mods.RA/DemoTruck.cs index bdac2df8ef..e2f8a59055 100644 --- a/OpenRA.Mods.RA/DemoTruck.cs +++ b/OpenRA.Mods.RA/DemoTruck.cs @@ -17,7 +17,7 @@ namespace OpenRA.Mods.RA class DemoTruck : Chronoshiftable, INotifyDamage { // Explode on chronoshift - public override bool Activate(Actor self, int2 targetLocation, int duration, bool killCargo, Actor chronosphere) + public override bool Teleport(Actor self, int2 targetLocation, int duration, bool killCargo, Actor chronosphere) { Detonate(self, chronosphere); return false; diff --git a/OpenRA.Mods.RA/SupportPowers/ChronoshiftPower.cs b/OpenRA.Mods.RA/SupportPowers/ChronoshiftPower.cs index 4eb8339fe9..4e6855b2ae 100755 --- a/OpenRA.Mods.RA/SupportPowers/ChronoshiftPower.cs +++ b/OpenRA.Mods.RA/SupportPowers/ChronoshiftPower.cs @@ -14,74 +14,94 @@ using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.RA.Render; using OpenRA.Traits; +using OpenRA.Mods.RA.Effects; namespace OpenRA.Mods.RA { class ChronoshiftPowerInfo : SupportPowerInfo { + public readonly int Range = 2; // Range in cells + public readonly int Duration = 30; + public readonly bool KillCargo = true; + public override object Create(ActorInitializer init) { return new ChronoshiftPower(init.self,this); } } class ChronoshiftPower : SupportPower, IResolveOrder { public ChronoshiftPower(Actor self, ChronoshiftPowerInfo info) : base(self, info) { } - protected override void OnActivate() { Self.World.OrderGenerator = new SelectTarget(); } + protected override void OnActivate() { Self.World.OrderGenerator = new SelectTarget(this); } public void ResolveOrder(Actor self, Order order) { if (!IsReady) return; - if (order.OrderString == "ChronosphereSelect" && self.Owner == self.World.LocalPlayer) + if (order.OrderString == "Chronoshift") { - //self.World.OrderGenerator = new SelectDestination(order.TargetActor); - } - - if (order.OrderString == "ChronosphereActivate") - { - // Ensure the target cell is valid for the unit - var movement = order.TargetActor.TraitOrDefault(); - if (!movement.CanEnterCell(order.TargetLocation)) - return; - var chronosphere = self.World.Queries .OwnedBy[self.Owner] .WithTrait() .Select(x => x.Actor).FirstOrDefault(); - chronosphere.Trait().Teleport(order.TargetActor, order.TargetLocation); + if (chronosphere != null) + chronosphere.Trait().PlayCustomAnim(chronosphere, "active"); + + // Trigger screen desaturate effect + foreach (var a in self.World.Queries.WithTrait()) + a.Trait.Enable(); + + Sound.Play("chrono2.aud", Game.CellSize * order.TargetLocation); + Sound.Play("chrono2.aud", Game.CellSize * order.ExtraLocation); + + var targets = UnitsInRange(order.ExtraLocation); + foreach (var target in targets) + { + var cs = target.Trait(); + var targetCell = target.Location + order.TargetLocation - order.ExtraLocation; + + if (cs.CanChronoshiftTo(target, targetCell)) + target.Trait().Teleport(target, + targetCell, + (int)((Info as ChronoshiftPowerInfo).Duration * 25 * 60), + (Info as ChronoshiftPowerInfo).KillCargo, + chronosphere); + } FinishActivate(); } } + public IEnumerable UnitsInRange(int2 xy) + { + int range = (Info as ChronoshiftPowerInfo).Range; + var uim = Self.World.WorldActor.Trait(); + var tiles = Self.World.FindTilesInCircle(xy, range); + var units = new List(); + foreach (var t in tiles) + units.AddRange(uim.GetUnitsAt(t)); + + return units.Distinct().Where(a => a.HasTrait()); + } + class SelectTarget : IOrderGenerator { + ChronoshiftPower power; + int range; + Sprite tile; + + public SelectTarget(ChronoshiftPower power) + { + this.power = power; + this.range = (power.Info as ChronoshiftPowerInfo).Range; + tile = UiOverlay.SynthesizeTile(0x04); + } + public IEnumerable Order(World world, int2 xy, MouseInput mi) { if (mi.Button == MouseButton.Right) world.CancelInputMode(); - - var ret = OrderInner( world, xy, mi ).ToList(); - foreach( var order in ret ) - { - world.OrderGenerator = new SelectDestination(order.TargetActor); - break; - } - return ret; - } - - IEnumerable OrderInner(World world, int2 xy, MouseInput mi) - { - if (mi.Button == MouseButton.Left) - { - var underCursor = world.FindUnitsAtMouse(mi.Location) - .Where(a => a.Owner != null && a.HasTrait() - && a.HasTrait()).FirstOrDefault(); - - if (underCursor != null) - yield return new Order("ChronosphereSelect", world.LocalPlayer.PlayerActor, false) { TargetActor = underCursor }; - } - + + world.OrderGenerator = new SelectDestination(power, xy); yield break; } @@ -93,46 +113,74 @@ namespace OpenRA.Mods.RA if (!hasChronosphere) world.CancelInputMode(); - - // TODO: Check if the selected unit is still alive } - public void RenderAfterWorld( WorldRenderer wr, World world ) { } - public void RenderBeforeWorld( WorldRenderer wr, World world ) { } + public void RenderAfterWorld(WorldRenderer wr, World world) + { + var xy = Game.viewport.ViewToWorld(Viewport.LastMousePos).ToInt2(); + var targetUnits = power.UnitsInRange(xy); + //foreach (var r in targetUnits.SelectMany(a => a.Render())) + // r.Sprite.DrawAt(wr,r.Pos,"highlight"); + foreach (var unit in targetUnits) + wr.DrawSelectionBox(unit, Color.Red); + } + + public void RenderBeforeWorld(WorldRenderer wr, World world) + { + var xy = Game.viewport.ViewToWorld(Viewport.LastMousePos).ToInt2(); + var tiles = world.FindTilesInCircle(xy, range); + foreach (var t in tiles) + tile.DrawAt( wr, Game.CellSize * t, "terrain" ); + } public string GetCursor(World world, int2 xy, MouseInput mi) { - mi.Button = MouseButton.Left; - return OrderInner(world, xy, mi).Any() - ? "chrono-select" : "move-blocked"; + return "chrono-select"; } } class SelectDestination : IOrderGenerator { - Actor self; - public SelectDestination(Actor self) { this.self = self; } + ChronoshiftPower power; + int2 sourceLocation; + int range; + Sprite validTile, invalidTile, sourceTile; + + public SelectDestination(ChronoshiftPower power, int2 sourceLocation) + { + this.power = power; + this.sourceLocation = sourceLocation; + this.range = (power.Info as ChronoshiftPowerInfo).Range; + validTile = UiOverlay.SynthesizeTile(0x0f); + invalidTile = UiOverlay.SynthesizeTile(0x08); + sourceTile = UiOverlay.SynthesizeTile(0x04); + } public IEnumerable Order(World world, int2 xy, MouseInput mi) { if (mi.Button == MouseButton.Right) + { world.CancelInputMode(); - - var ret = OrderInner( world, xy, mi ).ToList(); - if (ret.Count > 0) - world.CancelInputMode(); - return ret; + yield break; + } + + var ret = OrderInner( world, xy, mi ).FirstOrDefault(); + if (ret == null) + yield break; + + world.CancelInputMode(); + yield return ret; } IEnumerable OrderInner(World world, int2 xy, MouseInput mi) { // Cannot chronoshift into unexplored location - if (world.LocalPlayer.Shroud.IsExplored(xy)) - yield return new Order("ChronosphereActivate", world.LocalPlayer.PlayerActor, false) - { - TargetActor = self, - TargetLocation = xy - }; + if (isValidTarget(xy)) + yield return new Order("Chronoshift", world.LocalPlayer.PlayerActor, false) + { + TargetLocation = xy, + ExtraLocation = sourceLocation + }; } public void Tick(World world) @@ -143,60 +191,58 @@ namespace OpenRA.Mods.RA if (!hasChronosphere) world.CancelInputMode(); - - // TODO: Check if the selected unit is still alive } public void RenderAfterWorld(WorldRenderer wr, World world) { - wr.DrawSelectionBox(self, Color.Red); + foreach (var unit in power.UnitsInRange(sourceLocation)) + wr.DrawSelectionBox(unit, Color.Red); } - public void RenderBeforeWorld(WorldRenderer wr, World world) { } + public void RenderBeforeWorld(WorldRenderer wr, World world) + { + var xy = Game.viewport.ViewToWorld(Viewport.LastMousePos).ToInt2(); + // Source tiles + foreach (var t in world.FindTilesInCircle(sourceLocation, range)) + sourceTile.DrawAt( wr, Game.CellSize * t, "terrain" ); + + // Destination tiles + foreach (var t in world.FindTilesInCircle(xy, range)) + sourceTile.DrawAt( wr, Game.CellSize * t, "terrain" ); + + // Unit tiles + foreach (var unit in power.UnitsInRange(sourceLocation)) + { + var targetCell = unit.Location + xy - sourceLocation; + var canEnter = unit.Trait().CanChronoshiftTo(unit,targetCell); + var tile = canEnter ? validTile : invalidTile; + tile.DrawAt( wr, Game.CellSize * targetCell, "terrain" ); + } + } + + bool isValidTarget(int2 xy) + { + var canTeleport = false; + foreach (var unit in power.UnitsInRange(sourceLocation)) + { + var targetCell = unit.Location + xy - sourceLocation; + if (unit.Trait().CanChronoshiftTo(unit,targetCell)) + { + canTeleport = true; + break; + } + } + return canTeleport; + } public string GetCursor(World world, int2 xy, MouseInput mi) { - if (!world.LocalPlayer.Shroud.IsExplored(xy)) - return "move-blocked"; - - var movement = self.TraitOrDefault(); - return (movement.CanEnterCell(xy)) ? "chrono-target" : "move-blocked"; + return isValidTarget(xy) ? "chrono-target" : "move-blocked"; } } } // tag trait for the building - class ChronosphereInfo : ITraitInfo - { - public readonly int Duration = 30; - public readonly bool KillCargo = true; - public object Create(ActorInitializer init) { return new Chronosphere(init.self); } - } - - class Chronosphere - { - Actor self; - public Chronosphere(Actor self) - { - this.self = self; - } - - public void Teleport(Actor targetActor, int2 targetLocation) - { - var info = self.Info.Traits.Get(); - bool success = targetActor.Trait().Activate(targetActor, targetLocation, info.Duration * 25, info.KillCargo, self); - - if (success) - { - Sound.Play("chrono2.aud", self.CenterLocation); - Sound.Play("chrono2.aud", targetActor.CenterLocation); - - // Trigger screen desaturate effect - foreach (var a in self.World.Queries.WithTrait()) - a.Trait.Enable(); - - self.Trait().PlayCustomAnim(self, "active"); - } - } - } + class ChronosphereInfo : TraitInfo {} + class Chronosphere {} } diff --git a/OpenRA.Mods.RA/SupportPowers/IronCurtainPower.cs b/OpenRA.Mods.RA/SupportPowers/IronCurtainPower.cs index 56f31a73ff..00e0e71ecf 100755 --- a/OpenRA.Mods.RA/SupportPowers/IronCurtainPower.cs +++ b/OpenRA.Mods.RA/SupportPowers/IronCurtainPower.cs @@ -45,22 +45,17 @@ namespace OpenRA.Mods.RA if (order.OrderString == "IronCurtain") { - var curtain = self.World.Queries.WithTrait() - .Where(a => a.Actor.Owner == self.Owner) - .FirstOrDefault().Actor; + var curtain = self.World.Queries + .OwnedBy[self.Owner] + .WithTrait() + .Select(x => x.Actor).FirstOrDefault(); if (curtain != null) curtain.Trait().PlayCustomAnim(curtain, "active"); Sound.Play("ironcur9.aud", Game.CellSize * order.TargetLocation); - - var targets = UnitsInRange(order.TargetLocation); - - foreach (var target in targets) - { - if (target.HasTrait()) - target.Trait().Activate(target, (int)((Info as IronCurtainPowerInfo).Duration * 25 * 60)); - } + foreach (var target in UnitsInRange(order.TargetLocation)) + target.Trait().Activate(target, (int)((Info as IronCurtainPowerInfo).Duration * 25 * 60)); FinishActivate(); } @@ -89,7 +84,7 @@ namespace OpenRA.Mods.RA { this.power = power; this.range = (power.Info as IronCurtainPowerInfo).Range; - tile = UiOverlay.SynthesizeTile(0x0f); + tile = UiOverlay.SynthesizeTile(0x04); } public IEnumerable Order(World world, int2 xy, MouseInput mi) diff --git a/mods/ra/rules/structures.yaml b/mods/ra/rules/structures.yaml index 955a551555..2024c5a2da 100755 --- a/mods/ra/rules/structures.yaml +++ b/mods/ra/rules/structures.yaml @@ -221,8 +221,6 @@ PDOX: Range: 10 Bib: Chronosphere: - Duration: 180 - KillCargo: yes IronCurtainable: TSLA: diff --git a/mods/ra/rules/system.yaml b/mods/ra/rules/system.yaml index c18c7f609e..0d506d1805 100644 --- a/mods/ra/rules/system.yaml +++ b/mods/ra/rules/system.yaml @@ -43,6 +43,8 @@ Player: SelectTargetSound: slcttgt1.aud BeginChargeSound: chrochr1.aud EndChargeSound: chrordy1.aud + Duration: 2 + KillCargo: yes IronCurtainPower: Image: infxicon ChargeTime: 2