Moves SupportPowers and related Widgets to Mods.Common
This commit is contained in:
182
OpenRA.Mods.Common/Traits/SupportPowers/AirstrikePower.cs
Normal file
182
OpenRA.Mods.Common/Traits/SupportPowers/AirstrikePower.cs
Normal file
@@ -0,0 +1,182 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 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,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Common.Activities;
|
||||
using OpenRA.Mods.Common.Effects;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
public class AirstrikePowerInfo : SupportPowerInfo
|
||||
{
|
||||
[ActorReference]
|
||||
public readonly string UnitType = "badr.bomber";
|
||||
public readonly int SquadSize = 1;
|
||||
public readonly WVec SquadOffset = new WVec(-1536, 1536, 0);
|
||||
|
||||
public readonly int QuantizedFacings = 32;
|
||||
public readonly WRange Cordon = new WRange(5120);
|
||||
|
||||
[ActorReference]
|
||||
[Desc("Actor to spawn when the aircraft start attacking")]
|
||||
public readonly string CameraActor = null;
|
||||
|
||||
[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); }
|
||||
}
|
||||
|
||||
public class AirstrikePower : SupportPower
|
||||
{
|
||||
public AirstrikePower(Actor self, AirstrikePowerInfo info)
|
||||
: base(self, info) { }
|
||||
|
||||
public override void Activate(Actor self, Order order, SupportPowerManager manager)
|
||||
{
|
||||
base.Activate(self, order, manager);
|
||||
|
||||
SendAirstrike(self, self.World.Map.CenterOfCell(order.TargetLocation));
|
||||
}
|
||||
|
||||
public void SendAirstrike(Actor self, WPos target, bool randomize = true, int attackFacing = 0)
|
||||
{
|
||||
var info = Info as AirstrikePowerInfo;
|
||||
|
||||
if (randomize)
|
||||
attackFacing = Util.QuantizeFacing(self.World.SharedRandom.Next(256), info.QuantizedFacings) * (256 / info.QuantizedFacings);
|
||||
|
||||
var altitude = self.World.Map.Rules.Actors[info.UnitType].Traits.Get<PlaneInfo>().CruiseAltitude.Range;
|
||||
var attackRotation = WRot.FromFacing(attackFacing);
|
||||
var delta = new WVec(0, -1024, 0).Rotate(attackRotation);
|
||||
target = target + new WVec(0, 0, altitude);
|
||||
var startEdge = target - (self.World.Map.DistanceToEdge(target, -delta) + info.Cordon).Range * delta / 1024;
|
||||
var finishEdge = target + (self.World.Map.DistanceToEdge(target, delta) + info.Cordon).Range * delta / 1024;
|
||||
|
||||
Actor camera = null;
|
||||
Beacon beacon = null;
|
||||
var aircraftInRange = new Dictionary<Actor, bool>();
|
||||
|
||||
Action<Actor> onEnterRange = a =>
|
||||
{
|
||||
// Spawn a camera and remove the beacon when the first plane enters the target area
|
||||
if (info.CameraActor != null && !aircraftInRange.Any(kv => kv.Value))
|
||||
{
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
camera = w.CreateActor(info.CameraActor, new TypeDictionary
|
||||
{
|
||||
new LocationInit(self.World.Map.CellContaining(target)),
|
||||
new OwnerInit(self.Owner),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (beacon != null)
|
||||
{
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
w.Remove(beacon);
|
||||
beacon = null;
|
||||
});
|
||||
}
|
||||
|
||||
aircraftInRange[a] = true;
|
||||
};
|
||||
|
||||
Action<Actor> onExitRange = a =>
|
||||
{
|
||||
aircraftInRange[a] = false;
|
||||
|
||||
// Remove the camera when the final plane leaves the target area
|
||||
if (!aircraftInRange.Any(kv => kv.Value))
|
||||
{
|
||||
if (camera != null)
|
||||
{
|
||||
camera.QueueActivity(new Wait(info.CameraRemoveDelay));
|
||||
camera.QueueActivity(new RemoveSelf());
|
||||
}
|
||||
|
||||
camera = null;
|
||||
|
||||
if (beacon != null)
|
||||
{
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
w.Remove(beacon);
|
||||
beacon = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
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
|
||||
if (i == 0 && (info.SquadSize & 1) == 0)
|
||||
continue;
|
||||
|
||||
// Includes the 90 degree rotation between body and world coordinates
|
||||
var so = info.SquadOffset;
|
||||
var spawnOffset = new WVec(i * so.Y, -Math.Abs(i) * so.X, 0).Rotate(attackRotation);
|
||||
var targetOffset = new WVec(i * so.Y, 0, 0).Rotate(attackRotation);
|
||||
|
||||
var a = w.CreateActor(info.UnitType, new TypeDictionary
|
||||
{
|
||||
new CenterPositionInit(startEdge + spawnOffset),
|
||||
new OwnerInit(self.Owner),
|
||||
new FacingInit(attackFacing),
|
||||
});
|
||||
|
||||
var attack = a.Trait<AttackBomber>();
|
||||
attack.SetTarget(w, target + targetOffset);
|
||||
attack.OnEnteredAttackRange += onEnterRange;
|
||||
attack.OnExitedAttackRange += onExitRange;
|
||||
attack.OnRemovedFromWorld += onExitRange;
|
||||
|
||||
a.QueueActivity(new Fly(a, Target.FromPos(target + spawnOffset)));
|
||||
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(
|
||||
self.Owner,
|
||||
target,
|
||||
Info.BeaconPalettePrefix,
|
||||
Info.BeaconPoster,
|
||||
Info.BeaconPosterPalette,
|
||||
() => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Range) * 1f / distance);
|
||||
|
||||
w.Add(beacon);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
148
OpenRA.Mods.Common/Traits/SupportPowers/GrantUpgradePower.cs
Normal file
148
OpenRA.Mods.Common/Traits/SupportPowers/GrantUpgradePower.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 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,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
class GrantUpgradePowerInfo : SupportPowerInfo
|
||||
{
|
||||
[Desc("The upgrades to apply.")]
|
||||
public readonly string[] Upgrades = { };
|
||||
|
||||
[Desc("Duration of the upgrade (in ticks). Set to 0 for a permanent upgrade.")]
|
||||
public readonly int Duration = 0;
|
||||
|
||||
[Desc("Cells")]
|
||||
public readonly int Range = 1;
|
||||
public readonly string GrantUpgradeSound = "ironcur9.aud";
|
||||
|
||||
public override object Create(ActorInitializer init) { return new GrantUpgradePower(init.Self, this); }
|
||||
}
|
||||
|
||||
class GrantUpgradePower : SupportPower
|
||||
{
|
||||
GrantUpgradePowerInfo info;
|
||||
|
||||
public GrantUpgradePower(Actor self, GrantUpgradePowerInfo info)
|
||||
: base(self, info)
|
||||
{
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public override IOrderGenerator OrderGenerator(string order, SupportPowerManager manager)
|
||||
{
|
||||
Sound.PlayToPlayer(manager.Self.Owner, Info.SelectTargetSound);
|
||||
return new SelectTarget(Self.World, order, manager, this);
|
||||
}
|
||||
|
||||
public override void Activate(Actor self, Order order, SupportPowerManager manager)
|
||||
{
|
||||
base.Activate(self, order, manager);
|
||||
|
||||
self.Trait<RenderBuilding>().PlayCustomAnim(self, "active");
|
||||
|
||||
Sound.Play(info.GrantUpgradeSound, self.World.Map.CenterOfCell(order.TargetLocation));
|
||||
|
||||
foreach (var a in UnitsInRange(order.TargetLocation))
|
||||
{
|
||||
var um = a.TraitOrDefault<UpgradeManager>();
|
||||
if (um == null)
|
||||
continue;
|
||||
|
||||
foreach (var u in info.Upgrades)
|
||||
{
|
||||
if (!um.AcceptsUpgrade(a, u))
|
||||
continue;
|
||||
|
||||
if (info.Duration > 0)
|
||||
um.GrantTimedUpgrade(a, u, info.Duration);
|
||||
else
|
||||
um.GrantUpgrade(a, u, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Actor> UnitsInRange(CPos xy)
|
||||
{
|
||||
var range = info.Range;
|
||||
var tiles = Self.World.Map.FindTilesInCircle(xy, range);
|
||||
var units = new List<Actor>();
|
||||
foreach (var t in tiles)
|
||||
units.AddRange(Self.World.ActorMap.GetUnitsAt(t));
|
||||
|
||||
return units.Distinct().Where(a =>
|
||||
{
|
||||
if (!a.Owner.IsAlliedWith(Self.Owner))
|
||||
return false;
|
||||
|
||||
var um = a.TraitOrDefault<UpgradeManager>();
|
||||
return um != null && info.Upgrades.Any(u => um.AcceptsUpgrade(a, u));
|
||||
});
|
||||
}
|
||||
|
||||
class SelectTarget : IOrderGenerator
|
||||
{
|
||||
readonly GrantUpgradePower power;
|
||||
readonly int range;
|
||||
readonly Sprite tile;
|
||||
readonly SupportPowerManager manager;
|
||||
readonly string order;
|
||||
|
||||
public SelectTarget(World world, string order, SupportPowerManager manager, GrantUpgradePower power)
|
||||
{
|
||||
this.manager = manager;
|
||||
this.order = order;
|
||||
this.power = power;
|
||||
this.range = power.info.Range;
|
||||
tile = world.Map.SequenceProvider.GetSequence("overlay", "target-select").GetSprite(0);
|
||||
}
|
||||
|
||||
public IEnumerable<Order> Order(World world, CPos xy, MouseInput mi)
|
||||
{
|
||||
world.CancelInputMode();
|
||||
if (mi.Button == MouseButton.Left && power.UnitsInRange(xy).Any())
|
||||
yield return new Order(order, manager.Self, false) { TargetLocation = xy, SuppressVisualFeedback = true };
|
||||
}
|
||||
|
||||
public void Tick(World world)
|
||||
{
|
||||
// Cancel the OG if we can't use the power
|
||||
if (!manager.Powers.ContainsKey(order))
|
||||
world.CancelInputMode();
|
||||
}
|
||||
|
||||
public IEnumerable<IRenderable> RenderAfterWorld(WorldRenderer wr, World world)
|
||||
{
|
||||
var xy = wr.Viewport.ViewToWorld(Viewport.LastMousePos);
|
||||
foreach (var unit in power.UnitsInRange(xy))
|
||||
yield return new SelectionBoxRenderable(unit, Color.Red);
|
||||
}
|
||||
|
||||
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world)
|
||||
{
|
||||
var xy = wr.Viewport.ViewToWorld(Viewport.LastMousePos);
|
||||
var pal = wr.Palette("terrain");
|
||||
|
||||
foreach (var t in world.Map.FindTilesInCircle(xy, range))
|
||||
yield return new SpriteRenderable(tile, wr.World.Map.CenterOfCell(t), WVec.Zero, -511, pal, 1f, true);
|
||||
}
|
||||
|
||||
public string GetCursor(World world, CPos xy, MouseInput mi)
|
||||
{
|
||||
return power.UnitsInRange(xy).Any() ? "ability" : "move-blocked";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
127
OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs
Normal file
127
OpenRA.Mods.Common/Traits/SupportPowers/NukePower.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 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,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.Mods.Common.Activities;
|
||||
using OpenRA.Mods.Common.Effects;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
class NukePowerInfo : SupportPowerInfo, Requires<IBodyOrientationInfo>
|
||||
{
|
||||
[WeaponReference]
|
||||
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); }
|
||||
}
|
||||
|
||||
class NukePower : SupportPower
|
||||
{
|
||||
IBodyOrientation body;
|
||||
|
||||
public NukePower(Actor self, NukePowerInfo info)
|
||||
: base(self, info)
|
||||
{
|
||||
body = self.Trait<IBodyOrientation>();
|
||||
}
|
||||
|
||||
public override IOrderGenerator OrderGenerator(string order, SupportPowerManager manager)
|
||||
{
|
||||
Sound.PlayToPlayer(manager.Self.Owner, Info.SelectTargetSound);
|
||||
return new SelectGenericPowerTarget(order, manager, "nuke", MouseButton.Left);
|
||||
}
|
||||
|
||||
public override void Activate(Actor self, Order order, SupportPowerManager manager)
|
||||
{
|
||||
base.Activate(self, order, manager);
|
||||
|
||||
if (self.Owner.IsAlliedWith(self.World.RenderPlayer))
|
||||
Sound.Play(Info.LaunchSound);
|
||||
else
|
||||
Sound.Play(Info.IncomingSound);
|
||||
|
||||
var npi = Info as NukePowerInfo;
|
||||
var rb = self.Trait<RenderSimple>();
|
||||
rb.PlayCustomAnim(self, "active");
|
||||
|
||||
var targetPosition = self.World.Map.CenterOfCell(order.TargetLocation);
|
||||
var missile = new NukeLaunch(self.Owner, npi.MissileWeapon,
|
||||
self.CenterPosition + body.LocalToWorld(npi.SpawnOffset),
|
||||
targetPosition,
|
||||
npi.FlightVelocity, npi.FlightDelay, npi.SkipAscent);
|
||||
|
||||
self.World.AddFrameEndTask(w => w.Add(missile));
|
||||
|
||||
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 (Info.DisplayBeacon)
|
||||
{
|
||||
var beacon = new Beacon(
|
||||
order.Player,
|
||||
targetPosition,
|
||||
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(beacon);
|
||||
w.Add(new DelayedAction(npi.FlightDelay - npi.BeaconRemoveAdvance, removeBeacon));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
67
OpenRA.Mods.Common/Traits/SupportPowers/SpawnActorPower.cs
Normal file
67
OpenRA.Mods.Common/Traits/SupportPowers/SpawnActorPower.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 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,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.Mods.Common.Activities;
|
||||
using OpenRA.Mods.Common.Effects;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("Spawns an actor that stays for a limited amount of time.")]
|
||||
public class SpawnActorPowerInfo : SupportPowerInfo
|
||||
{
|
||||
[Desc("Actor to spawn.")]
|
||||
public readonly string Actor = null;
|
||||
|
||||
[Desc("Amount of time to keep the actor alive in ticks.")]
|
||||
public readonly int LifeTime = 250;
|
||||
|
||||
public readonly string DeploySound = null;
|
||||
|
||||
public readonly string EffectSequence = null;
|
||||
public readonly string EffectPalette = null;
|
||||
|
||||
public override object Create(ActorInitializer init) { return new SpawnActorPower(init.Self, this); }
|
||||
}
|
||||
|
||||
public class SpawnActorPower : SupportPower
|
||||
{
|
||||
public SpawnActorPower(Actor self, SpawnActorPowerInfo info) : base(self, info) { }
|
||||
public override void Activate(Actor self, Order order, SupportPowerManager manager)
|
||||
{
|
||||
base.Activate(self, order, manager);
|
||||
|
||||
var info = Info as SpawnActorPowerInfo;
|
||||
|
||||
if (info.Actor != null)
|
||||
{
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
var location = self.World.Map.CenterOfCell(order.TargetLocation);
|
||||
|
||||
Sound.Play(info.DeploySound, location);
|
||||
|
||||
if (!string.IsNullOrEmpty(info.EffectSequence) && !string.IsNullOrEmpty(info.EffectPalette))
|
||||
w.Add(new SpriteEffect(location, w, info.EffectSequence, info.EffectPalette));
|
||||
|
||||
var actor = w.CreateActor(info.Actor, new TypeDictionary
|
||||
{
|
||||
new LocationInit(order.TargetLocation),
|
||||
new OwnerInit(self.Owner),
|
||||
});
|
||||
|
||||
actor.QueueActivity(new Wait(info.LifeTime));
|
||||
actor.QueueActivity(new RemoveSelf());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
89
OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs
Normal file
89
OpenRA.Mods.Common/Traits/SupportPowers/SupportPower.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 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,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
public abstract class SupportPowerInfo : ITraitInfo
|
||||
{
|
||||
public readonly int ChargeTime = 0;
|
||||
public readonly string Icon = null;
|
||||
public readonly string Description = "";
|
||||
public readonly string LongDesc = "";
|
||||
public readonly bool AllowMultiple = false;
|
||||
public readonly bool OneShot = false;
|
||||
public readonly string[] Prerequisites = { };
|
||||
|
||||
public readonly string BeginChargeSound = null;
|
||||
public readonly string EndChargeSound = null;
|
||||
public readonly string SelectTargetSound = null;
|
||||
public readonly string InsufficientPowerSound = null;
|
||||
public readonly string LaunchSound = null;
|
||||
public readonly string IncomingSound = null;
|
||||
|
||||
public readonly bool DisplayTimer = false;
|
||||
|
||||
[Desc("Beacons are only supported on the Airstrike and Nuke powers")]
|
||||
public readonly bool DisplayBeacon = false;
|
||||
public readonly string BeaconPalettePrefix = "player";
|
||||
public readonly string BeaconPoster = null;
|
||||
public readonly string BeaconPosterPalette = "chrome";
|
||||
|
||||
public readonly bool DisplayRadarPing = false;
|
||||
public readonly int RadarPingDuration = 5 * 25;
|
||||
|
||||
public readonly string OrderName;
|
||||
public abstract object Create(ActorInitializer init);
|
||||
|
||||
public SupportPowerInfo() { OrderName = GetType().Name + "Order"; }
|
||||
}
|
||||
|
||||
public class SupportPower
|
||||
{
|
||||
public readonly Actor Self;
|
||||
public readonly SupportPowerInfo Info;
|
||||
protected RadarPing ping;
|
||||
|
||||
public SupportPower(Actor self, SupportPowerInfo info)
|
||||
{
|
||||
Info = info;
|
||||
Self = self;
|
||||
}
|
||||
|
||||
public virtual void Charging(Actor self, string key)
|
||||
{
|
||||
Sound.PlayToPlayer(self.Owner, Info.BeginChargeSound);
|
||||
}
|
||||
|
||||
public virtual void Charged(Actor self, string key)
|
||||
{
|
||||
Sound.PlayToPlayer(self.Owner, Info.EndChargeSound);
|
||||
}
|
||||
|
||||
public virtual void Activate(Actor self, Order order, SupportPowerManager manager)
|
||||
{
|
||||
if (Info.DisplayRadarPing && manager.RadarPings != null)
|
||||
{
|
||||
ping = manager.RadarPings.Value.Add(
|
||||
() => order.Player.IsAlliedWith(self.World.RenderPlayer),
|
||||
self.World.Map.CenterOfCell(order.TargetLocation),
|
||||
order.Player.Color.RGB,
|
||||
Info.RadarPingDuration);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IOrderGenerator OrderGenerator(string order, SupportPowerManager manager)
|
||||
{
|
||||
Sound.PlayToPlayer(manager.Self.Owner, Info.SelectTargetSound);
|
||||
return new SelectGenericPowerTarget(order, manager, "ability", MouseButton.Left);
|
||||
}
|
||||
}
|
||||
}
|
||||
266
OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs
Normal file
266
OpenRA.Mods.Common/Traits/SupportPowers/SupportPowerManager.cs
Normal file
@@ -0,0 +1,266 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2015 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,
|
||||
* see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("Attach this to the player actor.")]
|
||||
public class SupportPowerManagerInfo : ITraitInfo, Requires<DeveloperModeInfo>, Requires<TechTreeInfo>
|
||||
{
|
||||
public object Create(ActorInitializer init) { return new SupportPowerManager(init); }
|
||||
}
|
||||
|
||||
public class SupportPowerManager : ITick, IResolveOrder, ITechTreeElement
|
||||
{
|
||||
public readonly Actor Self;
|
||||
public readonly Dictionary<string, SupportPowerInstance> Powers = new Dictionary<string, SupportPowerInstance>();
|
||||
|
||||
public readonly DeveloperMode DevMode;
|
||||
public readonly TechTree TechTree;
|
||||
public readonly Lazy<RadarPings> RadarPings;
|
||||
|
||||
public SupportPowerManager(ActorInitializer init)
|
||||
{
|
||||
Self = init.Self;
|
||||
DevMode = Self.Trait<DeveloperMode>();
|
||||
TechTree = Self.Trait<TechTree>();
|
||||
RadarPings = Exts.Lazy(() => init.World.WorldActor.TraitOrDefault<RadarPings>());
|
||||
|
||||
init.World.ActorAdded += ActorAdded;
|
||||
init.World.ActorRemoved += ActorRemoved;
|
||||
}
|
||||
|
||||
static string MakeKey(SupportPower sp)
|
||||
{
|
||||
return sp.Info.AllowMultiple ? sp.Info.OrderName + "_" + sp.Self.ActorID : sp.Info.OrderName;
|
||||
}
|
||||
|
||||
void ActorAdded(Actor a)
|
||||
{
|
||||
if (a.Owner != Self.Owner)
|
||||
return;
|
||||
|
||||
foreach (var t in a.TraitsImplementing<SupportPower>())
|
||||
{
|
||||
var key = MakeKey(t);
|
||||
|
||||
if (!Powers.ContainsKey(key))
|
||||
{
|
||||
Powers.Add(key, new SupportPowerInstance(key, this)
|
||||
{
|
||||
Instances = new List<SupportPower>(),
|
||||
RemainingTime = t.Info.ChargeTime * 25,
|
||||
TotalTime = t.Info.ChargeTime * 25,
|
||||
});
|
||||
|
||||
if (t.Info.Prerequisites.Any())
|
||||
{
|
||||
TechTree.Add(key, t.Info.Prerequisites, 0, this);
|
||||
TechTree.Update();
|
||||
}
|
||||
}
|
||||
|
||||
Powers[key].Instances.Add(t);
|
||||
}
|
||||
}
|
||||
|
||||
void ActorRemoved(Actor a)
|
||||
{
|
||||
if (a.Owner != Self.Owner || !a.HasTrait<SupportPower>())
|
||||
return;
|
||||
|
||||
foreach (var t in a.TraitsImplementing<SupportPower>())
|
||||
{
|
||||
var key = MakeKey(t);
|
||||
Powers[key].Instances.Remove(t);
|
||||
|
||||
if (Powers[key].Instances.Count == 0 && !Powers[key].Disabled)
|
||||
{
|
||||
Powers.Remove(key);
|
||||
TechTree.Remove(key);
|
||||
TechTree.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
foreach (var power in Powers.Values)
|
||||
power.Tick();
|
||||
}
|
||||
|
||||
public void ResolveOrder(Actor self, Order order)
|
||||
{
|
||||
// order.OrderString is the key of the support power
|
||||
if (Powers.ContainsKey(order.OrderString))
|
||||
Powers[order.OrderString].Activate(order);
|
||||
}
|
||||
|
||||
// Deprecated. Remove after SupportPowerBinWidget is removed.
|
||||
public void Target(string key)
|
||||
{
|
||||
if (Powers.ContainsKey(key))
|
||||
Powers[key].Target();
|
||||
}
|
||||
|
||||
static readonly SupportPowerInstance[] NoInstances = { };
|
||||
|
||||
public IEnumerable<SupportPowerInstance> GetPowersForActor(Actor a)
|
||||
{
|
||||
if (a.Owner != Self.Owner || !a.HasTrait<SupportPower>())
|
||||
return NoInstances;
|
||||
|
||||
return a.TraitsImplementing<SupportPower>()
|
||||
.Select(t => Powers[MakeKey(t)]);
|
||||
}
|
||||
|
||||
public void PrerequisitesAvailable(string key)
|
||||
{
|
||||
SupportPowerInstance sp;
|
||||
if (!Powers.TryGetValue(key, out sp))
|
||||
return;
|
||||
|
||||
sp.Disabled = false;
|
||||
}
|
||||
|
||||
public void PrerequisitesUnavailable(string key)
|
||||
{
|
||||
SupportPowerInstance sp;
|
||||
if (!Powers.TryGetValue(key, out sp))
|
||||
return;
|
||||
|
||||
sp.Disabled = true;
|
||||
sp.RemainingTime = sp.TotalTime;
|
||||
}
|
||||
|
||||
public void PrerequisitesItemHidden(string key) { }
|
||||
public void PrerequisitesItemVisible(string key) { }
|
||||
}
|
||||
|
||||
public class SupportPowerInstance
|
||||
{
|
||||
readonly SupportPowerManager manager;
|
||||
readonly string key;
|
||||
|
||||
public List<SupportPower> Instances;
|
||||
public int RemainingTime;
|
||||
public int TotalTime;
|
||||
public bool Active { get; private set; }
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
public SupportPowerInfo Info { get { return Instances.Select(i => i.Info).FirstOrDefault(); } }
|
||||
public bool Ready { get { return Active && RemainingTime == 0; } }
|
||||
|
||||
public SupportPowerInstance(string key, SupportPowerManager manager)
|
||||
{
|
||||
this.manager = manager;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
static bool InstanceDisabled(SupportPower sp)
|
||||
{
|
||||
return sp.Self.TraitsImplementing<IDisable>().Any(d => d.Disabled);
|
||||
}
|
||||
|
||||
bool notifiedCharging;
|
||||
bool notifiedReady;
|
||||
public void Tick()
|
||||
{
|
||||
Active = !Disabled && Instances.Any(i => !i.Self.IsDisabled());
|
||||
if (!Active)
|
||||
return;
|
||||
|
||||
if (Active)
|
||||
{
|
||||
var power = Instances.First();
|
||||
if (manager.DevMode.FastCharge && RemainingTime > 25)
|
||||
RemainingTime = 25;
|
||||
|
||||
if (RemainingTime > 0)
|
||||
--RemainingTime;
|
||||
if (!notifiedCharging)
|
||||
{
|
||||
power.Charging(power.Self, key);
|
||||
notifiedCharging = true;
|
||||
}
|
||||
|
||||
if (RemainingTime == 0
|
||||
&& !notifiedReady)
|
||||
{
|
||||
power.Charged(power.Self, key);
|
||||
notifiedReady = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Target()
|
||||
{
|
||||
if (!Ready)
|
||||
return;
|
||||
|
||||
manager.Self.World.OrderGenerator = Instances.First().OrderGenerator(key, manager);
|
||||
}
|
||||
|
||||
public void Activate(Order order)
|
||||
{
|
||||
if (!Ready)
|
||||
return;
|
||||
|
||||
var power = Instances.First(i => !InstanceDisabled(i));
|
||||
|
||||
// Note: order.Subject is the *player* actor
|
||||
power.Activate(power.Self, order, manager);
|
||||
RemainingTime = TotalTime;
|
||||
notifiedCharging = notifiedReady = false;
|
||||
|
||||
if (Info.OneShot)
|
||||
Disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public class SelectGenericPowerTarget : IOrderGenerator
|
||||
{
|
||||
readonly SupportPowerManager manager;
|
||||
readonly string order;
|
||||
readonly string cursor;
|
||||
readonly MouseButton expectedButton;
|
||||
|
||||
public SelectGenericPowerTarget(string order, SupportPowerManager manager, string cursor, MouseButton button)
|
||||
{
|
||||
this.manager = manager;
|
||||
this.order = order;
|
||||
this.cursor = cursor;
|
||||
expectedButton = button;
|
||||
}
|
||||
|
||||
public IEnumerable<Order> Order(World world, CPos xy, MouseInput mi)
|
||||
{
|
||||
world.CancelInputMode();
|
||||
if (mi.Button == expectedButton && world.Map.Contains(xy))
|
||||
yield return new Order(order, manager.Self, false) { TargetLocation = xy, SuppressVisualFeedback = true };
|
||||
}
|
||||
|
||||
public virtual void Tick(World world)
|
||||
{
|
||||
// Cancel the OG if we can't use the power
|
||||
if (!manager.Powers.ContainsKey(order))
|
||||
world.CancelInputMode();
|
||||
}
|
||||
|
||||
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world) { yield break; }
|
||||
public IEnumerable<IRenderable> RenderAfterWorld(WorldRenderer wr, World world) { yield break; }
|
||||
public string GetCursor(World world, CPos xy, MouseInput mi) { return world.Map.Contains(xy) ? cursor : "generic-blocked"; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user