New special powers mechanism. Only cnc Airstrike has been reimplemented so far. Special power crates, and spy bonuses have also been disabled.
This commit is contained in:
@@ -26,53 +26,39 @@ namespace OpenRA.Mods.RA
|
||||
public override object Create(ActorInitializer init) { return new AirstrikePower(init.self, this); }
|
||||
}
|
||||
|
||||
class AirstrikePower : SupportPower, IResolveOrder
|
||||
class AirstrikePower : SupportPower
|
||||
{
|
||||
public AirstrikePower(Actor self, AirstrikePowerInfo info) : base(self, info) { }
|
||||
|
||||
protected override void OnActivate()
|
||||
public override void Activate(Actor self, Order order)
|
||||
{
|
||||
Self.World.OrderGenerator = new GenericSelectTarget(Owner.PlayerActor, Info.OrderName, "ability");
|
||||
}
|
||||
|
||||
public void ResolveOrder(Actor self, Order order)
|
||||
{
|
||||
if (!IsReady) return;
|
||||
|
||||
if (order.OrderString == Info.OrderName)
|
||||
var startPos = self.World.ChooseRandomEdgeCell();
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
var startPos = Owner.World.ChooseRandomEdgeCell();
|
||||
var info = (Info as AirstrikePowerInfo);
|
||||
var flare = info.FlareType != null ? w.CreateActor(info.FlareType, new TypeDictionary
|
||||
{
|
||||
new LocationInit( order.TargetLocation ),
|
||||
new OwnerInit( self.Owner ),
|
||||
}) : null;
|
||||
|
||||
var a = w.CreateActor(info.UnitType, new TypeDictionary
|
||||
{
|
||||
new LocationInit( startPos ),
|
||||
new OwnerInit( self.Owner ),
|
||||
new FacingInit( Util.GetFacing(order.TargetLocation - startPos, 0) ),
|
||||
new AltitudeInit( Rules.Info[info.UnitType].Traits.Get<PlaneInfo>().CruiseAltitude ),
|
||||
});
|
||||
a.Trait<CarpetBomb>().SetTarget(order.TargetLocation);
|
||||
|
||||
Owner.World.AddFrameEndTask(w =>
|
||||
{
|
||||
var info = (Info as AirstrikePowerInfo);
|
||||
var flare = info.FlareType != null ? w.CreateActor(info.FlareType, new TypeDictionary
|
||||
{
|
||||
new LocationInit( order.TargetLocation ),
|
||||
new OwnerInit( Owner ),
|
||||
}) : null;
|
||||
|
||||
var a = w.CreateActor(info.UnitType, new TypeDictionary
|
||||
{
|
||||
new LocationInit( startPos ),
|
||||
new OwnerInit( Owner ),
|
||||
new FacingInit( Util.GetFacing(order.TargetLocation - startPos, 0) ),
|
||||
new AltitudeInit( Rules.Info[info.UnitType].Traits.Get<PlaneInfo>().CruiseAltitude ),
|
||||
});
|
||||
a.Trait<CarpetBomb>().SetTarget(order.TargetLocation);
|
||||
a.CancelActivity();
|
||||
a.QueueActivity(Fly.ToCell(order.TargetLocation));
|
||||
|
||||
a.CancelActivity();
|
||||
a.QueueActivity(Fly.ToCell(order.TargetLocation));
|
||||
if (flare != null)
|
||||
a.QueueActivity(new CallFunc(() => flare.Destroy()));
|
||||
|
||||
if (flare != null)
|
||||
a.QueueActivity(new CallFunc(() => flare.Destroy()));
|
||||
|
||||
a.QueueActivity(new FlyOffMap { Interruptible = false });
|
||||
a.QueueActivity(new RemoveSelf());
|
||||
});
|
||||
|
||||
FinishActivate();
|
||||
}
|
||||
a.QueueActivity(new FlyOffMap { Interruptible = false });
|
||||
a.QueueActivity(new RemoveSelf());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ using OpenRA.Graphics;
|
||||
using OpenRA.Mods.RA.Render;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Mods.RA.Effects;
|
||||
|
||||
/*
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
class ChronoshiftPowerInfo : SupportPowerInfo
|
||||
@@ -255,3 +255,4 @@ namespace OpenRA.Mods.RA
|
||||
class ChronosphereInfo : TraitInfo<Chronosphere> {}
|
||||
class Chronosphere {}
|
||||
}
|
||||
*/
|
||||
@@ -12,7 +12,7 @@ using System.Linq;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.Mods.RA.Effects;
|
||||
using OpenRA.Traits;
|
||||
|
||||
/*
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
class GpsPowerInfo : SupportPowerInfo
|
||||
@@ -51,3 +51,4 @@ namespace OpenRA.Mods.RA
|
||||
class GpsLaunchSiteInfo : TraitInfo<GpsLaunchSite> { }
|
||||
class GpsLaunchSite { }
|
||||
}
|
||||
*/
|
||||
@@ -16,7 +16,7 @@ using OpenRA.Mods.RA.Effects;
|
||||
using OpenRA.Mods.RA.Render;
|
||||
using OpenRA.Traits;
|
||||
using TUtil = OpenRA.Traits.Util;
|
||||
|
||||
/*
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
class IronCurtainPowerInfo : SupportPowerInfo
|
||||
@@ -139,3 +139,4 @@ namespace OpenRA.Mods.RA
|
||||
class IronCurtainInfo : TraitInfo<IronCurtain> { }
|
||||
class IronCurtain { }
|
||||
}
|
||||
*/
|
||||
@@ -20,11 +20,11 @@ namespace OpenRA.Mods.RA
|
||||
{
|
||||
public override object Create(ActorInitializer init) { return new NukePower(init.self, this); }
|
||||
}
|
||||
|
||||
class NukePower : SupportPower, IResolveOrder
|
||||
|
||||
class NukePower : SupportPower
|
||||
{
|
||||
public NukePower(Actor self, NukePowerInfo info) : base(self, info) { }
|
||||
|
||||
/*
|
||||
protected override void OnActivate()
|
||||
{
|
||||
Self.World.OrderGenerator =
|
||||
@@ -52,6 +52,7 @@ namespace OpenRA.Mods.RA
|
||||
FinishActivate();
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// tag trait for the building
|
||||
|
||||
@@ -13,7 +13,7 @@ using OpenRA.Orders;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Mods.RA.Air;
|
||||
|
||||
/*
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
class ParatroopersPowerInfo : SupportPowerInfo
|
||||
@@ -82,3 +82,4 @@ namespace OpenRA.Mods.RA
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -9,7 +9,7 @@
|
||||
#endregion
|
||||
|
||||
using OpenRA.Traits;
|
||||
|
||||
/*
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
public class SonarPulsePowerInfo : SupportPowerInfo
|
||||
@@ -44,3 +44,4 @@ namespace OpenRA.Mods.RA
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -14,7 +14,7 @@ using OpenRA.Traits;
|
||||
using OpenRA.Traits.Activities;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Mods.RA.Air;
|
||||
|
||||
/*
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
class SpyPlanePowerInfo : SupportPowerInfo
|
||||
@@ -71,3 +71,4 @@ namespace OpenRA.Mods.RA
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -14,136 +14,40 @@ using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
public abstract class SupportPowerInfo : ITraitInfo, ITraitPrerequisite<TechTreeInfo>, ITraitPrerequisite<PowerManagerInfo>
|
||||
public abstract class SupportPowerInfo : ITraitInfo
|
||||
{
|
||||
public readonly bool RequiresPower = true;
|
||||
public readonly bool OneShot = false;
|
||||
public readonly float ChargeTime = 0;
|
||||
public readonly int ChargeTime = 0;
|
||||
public readonly string Image = null;
|
||||
public readonly string Description = "";
|
||||
public readonly string LongDesc = "";
|
||||
[ActorReference]
|
||||
public readonly string[] Prerequisites = { };
|
||||
public readonly bool GivenAuto = true;
|
||||
|
||||
public readonly string OrderName;
|
||||
|
||||
public readonly string BeginChargeSound = null;
|
||||
public readonly string EndChargeSound = null;
|
||||
public readonly string SelectTargetSound = null;
|
||||
public readonly string LaunchSound = null;
|
||||
|
||||
public readonly bool AllowMultiple = false;
|
||||
public abstract object Create(ActorInitializer init);
|
||||
|
||||
public SupportPowerInfo() { OrderName = GetType().Name + "Order"; }
|
||||
}
|
||||
|
||||
public class SupportPower : ITick, ITechTreeElement
|
||||
public class SupportPower
|
||||
{
|
||||
public readonly Actor self;
|
||||
public readonly SupportPowerInfo Info;
|
||||
public int RemainingTime { get; private set; }
|
||||
public int TotalTime { get { return (int)(Info.ChargeTime * 60 * 25); } }
|
||||
public bool IsUsed;
|
||||
public bool IsAvailable;
|
||||
public bool IsReady { get { return IsAvailable && RemainingTime == 0; } }
|
||||
|
||||
protected readonly Actor Self;
|
||||
protected readonly Player Owner;
|
||||
|
||||
bool notifiedCharging;
|
||||
bool notifiedReady;
|
||||
|
||||
readonly PowerManager PlayerPower;
|
||||
public SupportPower(Actor self, SupportPowerInfo info)
|
||||
{
|
||||
Info = info;
|
||||
RemainingTime = TotalTime;
|
||||
Self = self;
|
||||
Owner = self.Owner;
|
||||
PlayerPower = self.Trait<PowerManager>();
|
||||
|
||||
self.Trait<TechTree>().Add( Info.OrderName, Info.Prerequisites.Select( a => a.ToLowerInvariant() ).ToList(), this );
|
||||
this.self = self;
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
public virtual void Activate(Actor self, Order order) { }
|
||||
public virtual IOrderGenerator OrderGenerator(string order, SupportPowerManager manager)
|
||||
{
|
||||
if (Info.OneShot && IsUsed)
|
||||
return;
|
||||
|
||||
if (Info.GivenAuto)
|
||||
IsAvailable = hasPrerequisites;
|
||||
|
||||
if (IsAvailable && (!Info.RequiresPower || PlayerPower.PowerState == PowerState.Normal))
|
||||
{
|
||||
if (self.World.LobbyInfo.GlobalSettings.AllowCheats && self.Trait<DeveloperMode>().FastCharge) RemainingTime = 0;
|
||||
|
||||
if (RemainingTime > 0) --RemainingTime;
|
||||
if (!notifiedCharging)
|
||||
{
|
||||
Sound.PlayToPlayer(Owner, Info.BeginChargeSound);
|
||||
OnBeginCharging();
|
||||
notifiedCharging = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (RemainingTime == 0
|
||||
&& !notifiedReady)
|
||||
{
|
||||
Sound.PlayToPlayer(Owner, Info.EndChargeSound);
|
||||
OnFinishCharging();
|
||||
notifiedReady = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void FinishActivate()
|
||||
{
|
||||
if (Info.OneShot)
|
||||
{
|
||||
IsUsed = true;
|
||||
IsAvailable = false;
|
||||
}
|
||||
RemainingTime = TotalTime;
|
||||
notifiedReady = false;
|
||||
notifiedCharging = false;
|
||||
}
|
||||
|
||||
public void Give(float charge)
|
||||
{
|
||||
IsAvailable = true;
|
||||
IsUsed = false;
|
||||
RemainingTime = (int)(charge * TotalTime);
|
||||
}
|
||||
|
||||
protected virtual void OnBeginCharging() { }
|
||||
protected virtual void OnFinishCharging() { }
|
||||
protected virtual void OnActivate() { }
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
if (!IsAvailable || !IsReady)
|
||||
return;
|
||||
|
||||
if (Info.RequiresPower && PlayerPower.PowerState != PowerState.Normal)
|
||||
{
|
||||
var eva = Owner.World.WorldActor.Info.Traits.Get<EvaAlertsInfo>();
|
||||
Sound.Play(eva.AbilityInsufficientPower);
|
||||
return;
|
||||
}
|
||||
|
||||
Sound.PlayToPlayer(Owner, Info.SelectTargetSound);
|
||||
OnActivate();
|
||||
}
|
||||
|
||||
bool hasPrerequisites;
|
||||
|
||||
public void PrerequisitesAvailable(string key)
|
||||
{
|
||||
hasPrerequisites = true;
|
||||
}
|
||||
|
||||
public void PrerequisitesUnavailable(string key)
|
||||
{
|
||||
hasPrerequisites = false;
|
||||
Sound.PlayToPlayer(manager.self.Owner, Info.SelectTargetSound);
|
||||
return new SelectGenericPowerTarget(order, manager, "ability", MouseButton.Left);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
194
OpenRA.Mods.RA/SupportPowers/SupportPowerManager.cs
Executable file
194
OpenRA.Mods.RA/SupportPowers/SupportPowerManager.cs
Executable file
@@ -0,0 +1,194 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 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 LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.RA.Buildings;
|
||||
using OpenRA.Traits;
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Graphics;
|
||||
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
public class SupportPowerManagerInfo : ITraitInfo
|
||||
{
|
||||
public object Create(ActorInitializer init) { return new SupportPowerManager(init); }
|
||||
}
|
||||
|
||||
public class SupportPowerManager : ITick, IResolveOrder
|
||||
{
|
||||
public readonly Actor self;
|
||||
public Dictionary<string, SupportPowerInstance> Powers = new Dictionary<string, SupportPowerInstance>();
|
||||
|
||||
public SupportPowerManager(ActorInitializer init)
|
||||
{
|
||||
self = init.self;
|
||||
|
||||
init.world.ActorAdded += ActorAdded;
|
||||
init.world.ActorRemoved += ActorRemoved;
|
||||
}
|
||||
|
||||
void ActorAdded(Actor a)
|
||||
{
|
||||
if (a.Owner != self.Owner || !a.HasTrait<SupportPower>())
|
||||
return;
|
||||
|
||||
foreach (var t in a.TraitsImplementing<SupportPower>())
|
||||
{
|
||||
var key = (t.Info.AllowMultiple) ? t.Info.OrderName+"_"+a.ActorID : t.Info.OrderName;
|
||||
|
||||
if (Powers.ContainsKey(key))
|
||||
{
|
||||
Powers[key].Instances.Add(t);
|
||||
}
|
||||
else
|
||||
{
|
||||
var si = new SupportPowerInstance(this)
|
||||
{
|
||||
Instances = new List<SupportPower>() { t },
|
||||
RemainingTime = t.Info.ChargeTime * 25,
|
||||
TotalTime = t.Info.ChargeTime * 25,
|
||||
};
|
||||
|
||||
Powers.Add(key, si);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActorRemoved(Actor a)
|
||||
{
|
||||
if (a.Owner != self.Owner || !a.HasTrait<SupportPower>())
|
||||
return;
|
||||
|
||||
foreach (var t in a.TraitsImplementing<SupportPower>())
|
||||
{
|
||||
var key = (t.Info.AllowMultiple) ? t.Info.OrderName+"_"+a.ActorID : t.Info.OrderName;
|
||||
Powers[key].Instances.Remove(t);
|
||||
if (Powers[key].Instances.Count == 0)
|
||||
Powers.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public void Target(string key)
|
||||
{
|
||||
if (Powers.ContainsKey(key))
|
||||
Powers[key].Target(key);
|
||||
}
|
||||
|
||||
public class SupportPowerInstance
|
||||
{
|
||||
SupportPowerManager Manager;
|
||||
public List<SupportPower> Instances;
|
||||
public int RemainingTime;
|
||||
public int TotalTime;
|
||||
public bool Active;
|
||||
|
||||
public SupportPowerInfo Info { get { return Instances.First().Info; } }
|
||||
public bool Ready { get { return Active && RemainingTime == 0; } }
|
||||
|
||||
public SupportPowerInstance(SupportPowerManager manager)
|
||||
{
|
||||
Manager = manager;
|
||||
}
|
||||
|
||||
bool notifiedCharging;
|
||||
bool notifiedReady;
|
||||
public void Tick()
|
||||
{
|
||||
Active = Instances.Any(i => !i.self.TraitsImplementing<IDisable>().Any(d => d.Disabled));
|
||||
|
||||
if (Active)
|
||||
{
|
||||
if (RemainingTime > 0) --RemainingTime;
|
||||
if (!notifiedCharging)
|
||||
{
|
||||
Sound.PlayToPlayer(Instances.First().self.Owner, Info.BeginChargeSound);
|
||||
//instance.OnBeginCharging();
|
||||
notifiedCharging = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (RemainingTime == 0
|
||||
&& !notifiedReady)
|
||||
{
|
||||
Sound.PlayToPlayer(Instances.First().self.Owner, Info.EndChargeSound);
|
||||
//instance.FinishCharging();
|
||||
notifiedReady = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Target(string key)
|
||||
{
|
||||
if (!Ready)
|
||||
return;
|
||||
|
||||
Manager.self.World.OrderGenerator = Instances.First().OrderGenerator(key, Manager);
|
||||
}
|
||||
|
||||
public void Activate(Order order)
|
||||
{
|
||||
if (!Ready)
|
||||
return;
|
||||
|
||||
var power = Instances.First();
|
||||
// Note: order.Subject is the *player* actor
|
||||
power.Activate(power.self, order);
|
||||
RemainingTime = TotalTime;
|
||||
notifiedCharging = notifiedReady = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, int2 xy, MouseInput mi)
|
||||
{
|
||||
world.CancelInputMode();
|
||||
if (mi.Button == expectedButton && world.Map.IsInMap(xy))
|
||||
yield return new Order(order, manager.self, false) { TargetLocation = xy };
|
||||
}
|
||||
|
||||
public virtual void Tick(World world)
|
||||
{
|
||||
// Cancel the OG if we can't use the power
|
||||
if (!manager.Powers.ContainsKey(order))
|
||||
world.CancelInputMode();
|
||||
}
|
||||
|
||||
public void RenderBeforeWorld(WorldRenderer wr, World world) { }
|
||||
public void RenderAfterWorld(WorldRenderer wr, World world) { }
|
||||
public string GetCursor(World world, int2 xy, MouseInput mi) { return world.Map.IsInMap(xy) ? cursor : "generic-blocked"; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user