Implemented: Stances
Added: Basic stances (Aggressive, Guard (Hold Ground), Hold Fire, None (dummy), Return Fire) Added: WorldCommandWidget (to be able to set said stances) Added: WorldCommandWidget to ra (cnc will follow, later on) Changed: Added support to AttackBase for firing with movement disabled + utility method ScanForTarget (used by stances) Added: AssignUnitStance (attach this to unit-producing actors, together with what stances can be picked as 'default')
This commit is contained in:
@@ -20,16 +20,24 @@ namespace OpenRA.Mods.RA.Activities
|
||||
{
|
||||
Target Target;
|
||||
int Range;
|
||||
bool AllowMovement;
|
||||
|
||||
public Attack(Target target, int range)
|
||||
public Attack(Target target, int range, bool allowMovement)
|
||||
{
|
||||
Target = target;
|
||||
Range = range;
|
||||
AllowMovement = allowMovement;
|
||||
}
|
||||
|
||||
public Attack(Target target, int range) : this(target, range, true)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override IActivity Tick( Actor self )
|
||||
{
|
||||
var attack = self.Trait<AttackBase>();
|
||||
|
||||
var ret = InnerTick( self, attack );
|
||||
attack.IsAttacking = ( ret == this );
|
||||
return ret;
|
||||
@@ -42,8 +50,8 @@ namespace OpenRA.Mods.RA.Activities
|
||||
if (!Target.IsValid)
|
||||
return NextActivity;
|
||||
|
||||
if (!Combat.IsInRange( self.CenterLocation, Range, Target))
|
||||
return Util.SequenceActivities( self.Trait<Mobile>().MoveWithinRange( Target, Range ), this );
|
||||
if (!Combat.IsInRange(self.CenterLocation, Range, Target))
|
||||
return (AllowMovement) ? Util.SequenceActivities(self.Trait<Mobile>().MoveWithinRange(Target, Range), this) : NextActivity;
|
||||
|
||||
var desiredFacing = Util.GetFacing(Target.CenterLocation - self.CenterLocation, 0);
|
||||
var renderUnit = self.TraitOrDefault<RenderUnit>();
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace OpenRA.Mods.RA.Air
|
||||
{
|
||||
public AttackHeli(Actor self, AttackHeliInfo info) : base(self, info) { }
|
||||
|
||||
protected override IActivity GetAttackActivity(Actor self, Target newTarget)
|
||||
protected override IActivity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
|
||||
{
|
||||
return new HeliAttack( newTarget );
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace OpenRA.Mods.RA.Air
|
||||
{
|
||||
public AttackPlane(Actor self, AttackPlaneInfo info) : base(self, info) { }
|
||||
|
||||
protected override IActivity GetAttackActivity(Actor self, Target newTarget)
|
||||
protected override IActivity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
|
||||
{
|
||||
return new FlyAttack( newTarget );
|
||||
}
|
||||
|
||||
@@ -147,9 +147,10 @@ namespace OpenRA.Mods.RA
|
||||
|
||||
public void ResolveOrder(Actor self, Order order)
|
||||
{
|
||||
if (order.OrderString == "Attack")
|
||||
if (order.OrderString == "Attack" || order.OrderString == "AttackHold")
|
||||
{
|
||||
self.QueueActivity(order.Queued, GetAttackActivity(self, Target.FromOrder(order)));
|
||||
bool allowMove = order.OrderString == "Attack";
|
||||
self.QueueActivity(order.Queued, GetAttackActivity(self, Target.FromOrder(order), allowMove));
|
||||
|
||||
if (self.Owner == self.World.LocalPlayer)
|
||||
self.World.AddFrameEndTask(w =>
|
||||
@@ -163,24 +164,29 @@ namespace OpenRA.Mods.RA
|
||||
if (order.TargetActor != null) line.SetTarget(self, Target.FromOrder(order), Color.Red);
|
||||
else line.SetTarget(self, Target.FromOrder(order), Color.Red);
|
||||
});
|
||||
}
|
||||
else
|
||||
return;
|
||||
} // else not an attack order
|
||||
|
||||
// StopAttack order cancels the current activity IF it is an attack one
|
||||
if (order.OrderString == "StopAttack")
|
||||
{
|
||||
target = Target.None;
|
||||
|
||||
/* hack */
|
||||
if (self.HasTrait<Turreted>() && self.Info.Traits.Get<AttackBaseInfo>().AlignIdleTurrets)
|
||||
self.Trait<Turreted>().desiredFacing = null;
|
||||
if (self.GetCurrentActivity() is Activities.Attack)
|
||||
self.GetCurrentActivity().Cancel(self);
|
||||
}
|
||||
|
||||
target = Target.None;
|
||||
|
||||
/* hack */
|
||||
if (self.HasTrait<Turreted>() && self.Info.Traits.Get<AttackBaseInfo>().AlignIdleTurrets)
|
||||
self.Trait<Turreted>().desiredFacing = null;
|
||||
}
|
||||
|
||||
public string VoicePhraseForOrder(Actor self, Order order)
|
||||
{
|
||||
return (order.OrderString == "Attack") ? "Attack" : null;
|
||||
return (order.OrderString == "Attack" || order.OrderString == "AttackHold") ? "Attack" : null;
|
||||
}
|
||||
|
||||
protected abstract IActivity GetAttackActivity(Actor self, Target newTarget);
|
||||
protected abstract IActivity GetAttackActivity(Actor self, Target newTarget, bool allowMove);
|
||||
|
||||
public bool HasAnyValidWeapons(Target t) { return Weapons.Any(w => w.IsValidAgainst(self.World, t)); }
|
||||
public float GetMaximumRange() { return Weapons.Max(w => w.Info.Range); }
|
||||
@@ -188,26 +194,30 @@ namespace OpenRA.Mods.RA
|
||||
public Weapon ChooseWeaponForTarget(Target t) { return Weapons.FirstOrDefault(w => w.IsValidAgainst(self.World, t)); }
|
||||
|
||||
public void AttackTarget(Actor self, Actor target, bool allowMovement)
|
||||
{
|
||||
AttackTarget(self, target, allowMovement, false);
|
||||
}
|
||||
|
||||
public void AttackTarget(Actor self, Actor target, bool allowMovement, bool holdStill)
|
||||
{
|
||||
var attack = self.Trait<AttackBase>();
|
||||
if (target != null)
|
||||
{
|
||||
if (allowMovement)
|
||||
attack.ResolveOrder(self, new Order("Attack", self, target, false));
|
||||
attack.ResolveOrder(self, new Order((holdStill) ? "AttackHold" : "Attack", self, target, false));
|
||||
else
|
||||
attack.target = Target.FromActor(target); // for turreted things on rails.
|
||||
}
|
||||
}
|
||||
|
||||
public void ScanAndAttack(Actor self, bool allowMovement)
|
||||
public void ScanAndAttack(Actor self, bool allowMovement, bool holdStill)
|
||||
{
|
||||
if (--nextScanTime <= 0)
|
||||
{
|
||||
var attack = self.Trait<AttackBase>();
|
||||
var range = attack.GetMaximumRange();
|
||||
var targetActor = ScanForTarget(self);
|
||||
|
||||
if (!attack.target.IsValid || !Combat.IsInRange( self.CenterLocation, range, attack.target ))
|
||||
AttackTarget(self, ChooseTarget(self, range), allowMovement);
|
||||
if (targetActor != null)
|
||||
AttackTarget(self, targetActor, allowMovement, holdStill);
|
||||
|
||||
var info = self.Info.Traits.Get<AttackBaseInfo>();
|
||||
nextScanTime = (int)(25 * (info.ScanTimeAverage +
|
||||
@@ -215,6 +225,22 @@ namespace OpenRA.Mods.RA
|
||||
}
|
||||
}
|
||||
|
||||
public Actor ScanForTarget(Actor self)
|
||||
{
|
||||
var attack = self.Trait<AttackBase>();
|
||||
var range = attack.GetMaximumRange();
|
||||
|
||||
if ((!attack.target.IsValid || self.IsIdle) || !Combat.IsInRange(self.CenterLocation, range, attack.target))
|
||||
return ChooseTarget(self, range);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ScanAndAttack(Actor self, bool allowMovement)
|
||||
{
|
||||
ScanAndAttack(self, allowMovement, false);
|
||||
}
|
||||
|
||||
Actor ChooseTarget(Actor self, float range)
|
||||
{
|
||||
var inRange = self.World.FindUnitsInCircle(self.CenterLocation, Game.CellSize * range);
|
||||
|
||||
@@ -40,12 +40,12 @@ namespace OpenRA.Mods.RA
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override IActivity GetAttackActivity(Actor self, Target newTarget)
|
||||
protected override IActivity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
|
||||
{
|
||||
var weapon = ChooseWeaponForTarget(newTarget);
|
||||
if( weapon == null )
|
||||
return null;
|
||||
return new Activities.Attack(newTarget, Math.Max(0, (int)weapon.Info.Range));
|
||||
return new Activities.Attack(newTarget, Math.Max(0, (int)weapon.Info.Range), allowMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,12 +40,12 @@ namespace OpenRA.Mods.RA
|
||||
self.QueueActivity(new Leap(self, target));
|
||||
}
|
||||
|
||||
protected override IActivity GetAttackActivity(Actor self, Target newTarget)
|
||||
protected override IActivity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
|
||||
{
|
||||
var weapon = ChooseWeaponForTarget(newTarget);
|
||||
if( weapon == null )
|
||||
return null;
|
||||
return new Activities.Attack(newTarget, Math.Max(0, (int)weapon.Info.Range));
|
||||
return new Activities.Attack(newTarget, Math.Max(0, (int)weapon.Info.Range), allowMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace OpenRA.Mods.RA
|
||||
DoAttack(self, target);
|
||||
}
|
||||
|
||||
protected override IActivity GetAttackActivity(Actor self, Target newTarget)
|
||||
protected override IActivity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
|
||||
{
|
||||
return new SetTarget( newTarget );
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace OpenRA.Mods.RA
|
||||
DoAttack( self, target );
|
||||
}
|
||||
|
||||
protected override IActivity GetAttackActivity(Actor self, Target newTarget)
|
||||
protected override IActivity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
|
||||
{
|
||||
return new AttackActivity( newTarget );
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<Compile Include="Activities\Demolish.cs" />
|
||||
<Compile Include="Activities\Enter.cs" />
|
||||
<Compile Include="Activities\EnterTransport.cs" />
|
||||
<Compile Include="UnitStances\UnitStanceAggressive.cs" />
|
||||
<Compile Include="Air\Fly.cs" />
|
||||
<Compile Include="Air\FlyAttack.cs" />
|
||||
<Compile Include="Air\FlyTimed.cs" />
|
||||
@@ -80,6 +81,7 @@
|
||||
<Compile Include="Activities\Turn.cs" />
|
||||
<Compile Include="Activities\UnloadCargo.cs" />
|
||||
<Compile Include="Activities\Wait.cs" />
|
||||
<Compile Include="UnitStances\UnitStance.cs" />
|
||||
<Compile Include="AttackBase.cs" />
|
||||
<Compile Include="AttackMove.cs" />
|
||||
<Compile Include="BaseBuilding.cs" />
|
||||
@@ -94,6 +96,11 @@
|
||||
<Compile Include="Buildings\Sell.cs" />
|
||||
<Compile Include="Buildings\TechTree.cs" />
|
||||
<Compile Include="Buildings\Util.cs" />
|
||||
<Compile Include="UnitStances\AssignUnitStance.cs" />
|
||||
<Compile Include="UnitStances\UnitStanceHoldFire.cs" />
|
||||
<Compile Include="UnitStances\UnitStanceNone.cs" />
|
||||
<Compile Include="UnitStances\UnitStanceReturnFire.cs" />
|
||||
<Compile Include="UnitStances\UnitStanceGuard.cs" />
|
||||
<Compile Include="Valued.cs" />
|
||||
<Compile Include="Combat.cs" />
|
||||
<Compile Include="Player\SurrenderOnDisconnect.cs" />
|
||||
@@ -252,6 +259,7 @@
|
||||
<Compile Include="Widgets\PowerBinWidget.cs" />
|
||||
<Compile Include="Widgets\RadarBinWidget.cs" />
|
||||
<Compile Include="Widgets\SpecialPowerBinWidget.cs" />
|
||||
<Compile Include="Widgets\WorldCommandWidget.cs" />
|
||||
<Compile Include="Widgets\WorldTooltipWidget.cs" />
|
||||
<Compile Include="WithMuzzleFlash.cs" />
|
||||
<Compile Include="WithShadow.cs" />
|
||||
|
||||
29
OpenRA.Mods.RA/UnitStances/AssignUnitStance.cs
Normal file
29
OpenRA.Mods.RA/UnitStances/AssignUnitStance.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA.UnitStances
|
||||
{
|
||||
public class AssignUnitStanceInfo : TraitInfo<AssignUnitStance>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class AssignUnitStance : INotifyProduction
|
||||
{
|
||||
public void UnitProduced(Actor self, Actor other, int2 exit)
|
||||
{
|
||||
var stance = UnitStance.GetActive(self);
|
||||
if (stance == null)
|
||||
return;
|
||||
|
||||
var target = other.TraitsImplementing<UnitStance>().Where(t => t.GetType() == stance.GetType()).FirstOrDefault();
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
target.Activate(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
218
OpenRA.Mods.RA/UnitStances/UnitStance.cs
Normal file
218
OpenRA.Mods.RA/UnitStances/UnitStance.cs
Normal file
@@ -0,0 +1,218 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
public interface IUnitStance
|
||||
{
|
||||
bool Active { get; set; }
|
||||
bool IsDefault { get; }
|
||||
void Activate(Actor self);
|
||||
void Deactivate(Actor self);
|
||||
}
|
||||
|
||||
public class UnitStanceInfo : ITraitInfo
|
||||
{
|
||||
public readonly bool Default;
|
||||
|
||||
#region ITraitInfo Members
|
||||
|
||||
public virtual object Create(ActorInitializer init)
|
||||
{
|
||||
throw new Exception("Do not use UnitStance at the rules!");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public abstract class UnitStance : IUnitStance, ITick
|
||||
{
|
||||
public int NextScantime;
|
||||
public int ScanDelay = 12; // 2x - second
|
||||
private bool _unsetFirstTick;
|
||||
|
||||
public UnitStanceInfo Info { get; protected set; }
|
||||
|
||||
public bool IsFirstTick { get; private set; }
|
||||
|
||||
public bool IsScanAvailable
|
||||
{
|
||||
get
|
||||
{
|
||||
NextScantime--;
|
||||
if (NextScantime <= 0)
|
||||
{
|
||||
NextScantime = ScanDelay;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#region ITick Members
|
||||
|
||||
public virtual void Tick(Actor self)
|
||||
{
|
||||
if (!Active) return;
|
||||
|
||||
if (IsFirstTick && _unsetFirstTick)
|
||||
{
|
||||
IsFirstTick = false;
|
||||
_unsetFirstTick = false;
|
||||
}
|
||||
else if (IsFirstTick)
|
||||
{
|
||||
_unsetFirstTick = true;
|
||||
OnFirstTick(self);
|
||||
}
|
||||
|
||||
if (IsScanAvailable)
|
||||
{
|
||||
OnScan(self);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IUnitStance Members
|
||||
|
||||
public bool Active { get; set; }
|
||||
|
||||
public virtual bool IsDefault
|
||||
{
|
||||
get { return Info.Default; }
|
||||
}
|
||||
|
||||
public virtual void Activate(Actor self)
|
||||
{
|
||||
if (Active) return;
|
||||
|
||||
Active = true;
|
||||
IsFirstTick = true;
|
||||
NextScantime = 0;
|
||||
_unsetFirstTick = false;
|
||||
|
||||
DeactivateOthers(self);
|
||||
}
|
||||
|
||||
public virtual void Deactivate(Actor self)
|
||||
{
|
||||
if (Active)
|
||||
{
|
||||
Active = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public virtual void DeactivateOthers(Actor self)
|
||||
{
|
||||
DeactivateOthers(self, this);
|
||||
}
|
||||
|
||||
public static bool IsActive<T>(Actor self) where T : UnitStance
|
||||
{
|
||||
var stance = self.TraitOrDefault<T>();
|
||||
|
||||
return stance != null && stance.Active;
|
||||
}
|
||||
|
||||
public static void ActivateDefault(Actor self)
|
||||
{
|
||||
if (!self.TraitsImplementing<IUnitStance>().Where(t => t.IsDefault).Any())
|
||||
{
|
||||
// deactive all of them as a default if nobody has a default
|
||||
DeactivateOthers(self, null);
|
||||
return;
|
||||
}
|
||||
|
||||
self.TraitsImplementing<IUnitStance>().Where(t => t.IsDefault).First().Activate(self);
|
||||
}
|
||||
|
||||
public static void DeactivateOthers(Actor self, IUnitStance stance)
|
||||
{
|
||||
self.TraitsImplementing<IUnitStance>().Where(t => t != stance).Do(t => t.Deactivate(self));
|
||||
}
|
||||
|
||||
public static bool ReturnFire(Actor self, AttackInfo e, bool allowActivity, bool allowTargetSwitch, bool holdStill)
|
||||
{
|
||||
if (!self.IsIdle && !allowActivity) return false;
|
||||
if (e.Attacker.Destroyed) return false;
|
||||
|
||||
var attack = self.TraitOrDefault<AttackBase>();
|
||||
|
||||
// this unit cannot fight back at all (no guns)
|
||||
if (attack == null) return false;
|
||||
|
||||
// if attacking already and force was used, return (ie to respond to attacks while moving around)
|
||||
if (attack.IsAttacking && (!allowTargetSwitch)) return false;
|
||||
|
||||
// don't fight back if we dont have the guns to do so
|
||||
if (!attack.HasAnyValidWeapons(Target.FromActor(e.Attacker))) return false;
|
||||
|
||||
// don't retaliate against allies
|
||||
if (self.Owner.Stances[e.Attacker.Owner] == Stance.Ally) return false;
|
||||
|
||||
// don't retaliate against healers
|
||||
if (e.Damage < 0) return false;
|
||||
|
||||
// perform the attack
|
||||
AttackTarget(self, e.Attacker, holdStill);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ReturnFire(Actor self, AttackInfo e, bool allowActivity, bool allowTargetSwitch)
|
||||
{
|
||||
return ReturnFire(self, e, allowActivity, allowTargetSwitch, false);
|
||||
}
|
||||
|
||||
public static bool ReturnFire(Actor self, AttackInfo e, bool allowActivity)
|
||||
{
|
||||
return ReturnFire(self, e, allowActivity, false);
|
||||
}
|
||||
|
||||
public static UnitStance GetActive(Actor self)
|
||||
{
|
||||
return self.TraitsImplementing<UnitStance>().Where(t => t.Active).FirstOrDefault();
|
||||
}
|
||||
|
||||
public static void AttackTarget(Actor self, Actor target, bool holdStill)
|
||||
{
|
||||
var attack = self.Trait<AttackBase>();
|
||||
|
||||
if (attack != null && target != null)
|
||||
{
|
||||
self.World.IssueOrder(new Order((holdStill) ? "AttackHold" : "Attack", self, target, false));
|
||||
}
|
||||
}
|
||||
|
||||
public static void StopAttack(Actor self)
|
||||
{
|
||||
self.World.IssueOrder(new Order("StopAttack", self, self, false));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when on the first tick after the stance has been activated
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
protected virtual void OnScan(Actor self)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when on the first tick after the stance has been activated
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
protected virtual void OnFirstTick(Actor self)
|
||||
{
|
||||
}
|
||||
|
||||
public static Actor ScanForTarget(Actor self)
|
||||
{
|
||||
return self.Trait<AttackBase>().ScanForTarget(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
OpenRA.Mods.RA/UnitStances/UnitStanceAggressive.cs
Normal file
56
OpenRA.Mods.RA/UnitStances/UnitStanceAggressive.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
public class UnitStanceAggressiveInfo : UnitStanceInfo
|
||||
{
|
||||
public override object Create(ActorInitializer init) { return new UnitStanceAggressive(init.self, this); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inherits the Return Fire damage handler!
|
||||
/// </summary>
|
||||
public class UnitStanceAggressive : UnitStance, INotifyDamage, ISelectionColorModifier
|
||||
{
|
||||
public UnitStanceAggressive(Actor self, UnitStanceAggressiveInfo info)
|
||||
{
|
||||
Info = info;
|
||||
Active = (self.World.LocalPlayer == self.Owner || (self.Owner.IsBot && Game.IsHost)) ? Info.Default : false;
|
||||
}
|
||||
|
||||
protected override void OnScan(Actor self)
|
||||
{
|
||||
if (!self.IsIdle) return;
|
||||
if (!self.HasTrait<AttackBase>()) return;
|
||||
|
||||
var target = ScanForTarget(self);
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
AttackTarget(self, target, false);
|
||||
}
|
||||
|
||||
protected override void OnFirstTick(Actor self)
|
||||
{
|
||||
if (!self.HasTrait<AttackBase>()) return;
|
||||
|
||||
if (self.Trait<AttackBase>().IsAttacking)
|
||||
StopAttack(self);
|
||||
}
|
||||
|
||||
public virtual void Damaged(Actor self, AttackInfo e)
|
||||
{
|
||||
if (!Active) return;
|
||||
if (!self.HasTrait<AttackBase>()) return;
|
||||
|
||||
ReturnFire(self, e, false); // only triggers when standing still
|
||||
}
|
||||
|
||||
public Color GetSelectionColorModifier(Actor self, Color defaultColor)
|
||||
{
|
||||
return Active ? Color.Red : defaultColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
54
OpenRA.Mods.RA/UnitStances/UnitStanceGuard.cs
Normal file
54
OpenRA.Mods.RA/UnitStances/UnitStanceGuard.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using OpenRA.Mods.RA.Move;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
public class UnitStanceGuardInfo : UnitStanceInfo
|
||||
{
|
||||
public override object Create(ActorInitializer init) { return new UnitStanceGuard(init.self, this); }
|
||||
}
|
||||
|
||||
public class UnitStanceGuard : UnitStance, INotifyDamage, ISelectionColorModifier
|
||||
{
|
||||
public UnitStanceGuard(Actor self, UnitStanceGuardInfo info)
|
||||
{
|
||||
Info = info;
|
||||
Active = (self.World.LocalPlayer == self.Owner || (self.Owner.IsBot && Game.IsHost)) ? Info.Default : false;
|
||||
}
|
||||
|
||||
protected override void OnScan(Actor self)
|
||||
{
|
||||
if (!self.IsIdle) return;
|
||||
if (!self.HasTrait<AttackBase>()) return;
|
||||
|
||||
var target = ScanForTarget(self);
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
AttackTarget(self, target, true);
|
||||
}
|
||||
|
||||
protected override void OnFirstTick(Actor self)
|
||||
{
|
||||
if (!self.HasTrait<AttackBase>()) return;
|
||||
|
||||
if (self.Trait<AttackBase>().IsAttacking)
|
||||
StopAttack(self);
|
||||
}
|
||||
|
||||
public void Damaged(Actor self, AttackInfo e)
|
||||
{
|
||||
if (!Active) return;
|
||||
if (!self.HasTrait<AttackBase>()) return;
|
||||
|
||||
ReturnFire(self, e, false, false, true); // only triggers when standing still
|
||||
}
|
||||
|
||||
public virtual Color GetSelectionColorModifier(Actor self, Color defaultColor)
|
||||
{
|
||||
return Active ? Color.Yellow : defaultColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
OpenRA.Mods.RA/UnitStances/UnitStanceHoldFire.cs
Normal file
38
OpenRA.Mods.RA/UnitStances/UnitStanceHoldFire.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
public class UnitStanceHoldFireInfo : UnitStanceInfo
|
||||
{
|
||||
public override object Create(ActorInitializer init) { return new UnitStanceHoldFire(init.self, this); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hold Fire
|
||||
///
|
||||
/// Will not perform any attacks automaticly
|
||||
/// </summary>
|
||||
public class UnitStanceHoldFire : UnitStance, ISelectionColorModifier
|
||||
{
|
||||
public UnitStanceHoldFire(Actor self, UnitStanceHoldFireInfo info)
|
||||
{
|
||||
Info = info;
|
||||
Active = (self.World.LocalPlayer == self.Owner || (self.Owner.IsBot && Game.IsHost)) ? Info.Default : false;
|
||||
}
|
||||
|
||||
protected override void OnFirstTick(Actor self)
|
||||
{
|
||||
if (!self.HasTrait<AttackBase>()) return;
|
||||
|
||||
if (self.Trait<AttackBase>().IsAttacking)
|
||||
StopAttack(self);
|
||||
}
|
||||
|
||||
public Color GetSelectionColorModifier(Actor self, Color defaultColor)
|
||||
{
|
||||
return Active ? Color.SpringGreen : defaultColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
OpenRA.Mods.RA/UnitStances/UnitStanceNone.cs
Normal file
21
OpenRA.Mods.RA/UnitStances/UnitStanceNone.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
public class UnitStanceNoneInfo : ITraitInfo
|
||||
{
|
||||
public readonly bool Default = false;
|
||||
|
||||
public object Create(ActorInitializer init) { return new UnitStanceNone(init.self, this); }
|
||||
}
|
||||
public class UnitStanceNone : UnitStance
|
||||
{
|
||||
public readonly UnitStanceNoneInfo Info;
|
||||
|
||||
public UnitStanceNone(Actor self, UnitStanceNoneInfo info)
|
||||
{
|
||||
Info = info;
|
||||
Active = (self.World.LocalPlayer == self.Owner || (self.Owner.IsBot && Game.IsHost)) ? Info.Default : false;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
OpenRA.Mods.RA/UnitStances/UnitStanceReturnFire.cs
Normal file
38
OpenRA.Mods.RA/UnitStances/UnitStanceReturnFire.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
public class UnitStanceReturnFireInfo : UnitStanceInfo
|
||||
{
|
||||
public override object Create(ActorInitializer init) { return new UnitStanceReturnFire(init.self, this); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return Fire
|
||||
///
|
||||
/// Will fire only when fired upon
|
||||
/// </summary>
|
||||
public class UnitStanceReturnFire : UnitStance, INotifyDamage, ISelectionColorModifier
|
||||
{
|
||||
public UnitStanceReturnFire(Actor self, UnitStanceReturnFireInfo info)
|
||||
{
|
||||
Info = info;
|
||||
Active = (self.World.LocalPlayer == self.Owner || (self.Owner.IsBot && Game.IsHost)) ? Info.Default : false;
|
||||
}
|
||||
|
||||
public void Damaged(Actor self, AttackInfo e)
|
||||
{
|
||||
if (!Active) return;
|
||||
if (!self.HasTrait<AttackBase>()) return;
|
||||
|
||||
ReturnFire(self, e, false); // only triggers when standing still
|
||||
}
|
||||
|
||||
public Color GetSelectionColorModifier(Actor self, Color defaultColor)
|
||||
{
|
||||
return Active ? Color.Orange : defaultColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
110
OpenRA.Mods.RA/Widgets/WorldCommandWidget.cs
Normal file
110
OpenRA.Mods.RA/Widgets/WorldCommandWidget.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Orders;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.RA.Widgets
|
||||
{
|
||||
public class WorldCommandWidget : Widget
|
||||
{
|
||||
public World World { get { return OrderManager.world; } }
|
||||
|
||||
public char AttackMoveKey = 'a';
|
||||
public char GuardKey = 'g'; // (G)uard
|
||||
// public char DefensiveKey = 'd'; // (D)efensive
|
||||
public char AggressiveKey = 'a'; // (A)ggressive
|
||||
public char ReturnFireKey = 'r'; // (R)eturn Fire
|
||||
public char HoldFire = 'h'; // (h)old fire
|
||||
public readonly OrderManager OrderManager;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public WorldCommandWidget([ObjectCreator.Param] OrderManager orderManager )
|
||||
{
|
||||
OrderManager = orderManager;
|
||||
}
|
||||
|
||||
public override void DrawInner(WorldRenderer wr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override string GetCursor(int2 pos)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public override bool HandleKeyPressInner(KeyInput e)
|
||||
{
|
||||
if (World == null) return false;
|
||||
if (World.LocalPlayer == null) return false;
|
||||
|
||||
return ProcessInput(e);
|
||||
}
|
||||
|
||||
private bool ProcessInput(KeyInput e)
|
||||
{
|
||||
// command: AttackMove
|
||||
if (e.KeyChar == AttackMoveKey && e.Modifiers == Modifiers.None)
|
||||
{
|
||||
return PerformAttackMove();
|
||||
}
|
||||
|
||||
// command: GuardStance
|
||||
if (e.KeyChar == GuardKey && (e.Modifiers.HasModifier(Modifiers.Alt)))
|
||||
{
|
||||
return EnableStance<UnitStanceGuard>();
|
||||
}
|
||||
|
||||
// command: AggressiveStance
|
||||
if (e.KeyChar == AggressiveKey && (e.Modifiers.HasModifier(Modifiers.Alt)))
|
||||
{
|
||||
return EnableStance<UnitStanceAggressive>();
|
||||
}
|
||||
|
||||
// stance: Return Fire
|
||||
// description: Fires only when fired upon, stops firing if no longer under attack
|
||||
if (e.KeyChar == ReturnFireKey && (e.Modifiers.HasModifier(Modifiers.Alt)))
|
||||
{
|
||||
return EnableStance<UnitStanceReturnFire>();
|
||||
}
|
||||
|
||||
// stance: Hold Fire
|
||||
// description: Prevents attacking (ie no autotarget is being done)
|
||||
if (e.KeyChar == HoldFire && (e.Modifiers.HasModifier(Modifiers.Alt)))
|
||||
{
|
||||
return EnableStance<UnitStanceHoldFire>();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool EnableStance<T>() where T : UnitStance
|
||||
{
|
||||
if (World.Selection.Actors.Count() == 0)
|
||||
return false;
|
||||
|
||||
var traits =
|
||||
World.Selection.Actors.Where(a => !a.Destroyed && a.Owner == World.LocalPlayer && a.TraitOrDefault<T>() != null && !UnitStance.IsActive<T>(a)).
|
||||
Select(a => new Pair<Actor, T>(a, a.TraitOrDefault<T>()) );
|
||||
|
||||
World.AddFrameEndTask(w => traits.Do(p => p.Second.Activate(p.First)));
|
||||
|
||||
return traits.Any();
|
||||
}
|
||||
|
||||
private bool PerformAttackMove()
|
||||
{
|
||||
if (World.Selection.Actors.Count() > 0)
|
||||
{
|
||||
World.OrderGenerator = new GenericSelectTarget(World.Selection.Actors, "AttackMove", "attackmove", MouseButton.Right);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,11 @@ Container@INGAME_ROOT:
|
||||
Y:0
|
||||
Width:WINDOW_RIGHT
|
||||
Height:WINDOW_BOTTOM
|
||||
WorldCommand:
|
||||
X:0
|
||||
Y:0
|
||||
Width:WINDOW_RIGHT
|
||||
Height:WINDOW_BOTTOM
|
||||
Timer@GAME_TIMER:
|
||||
Id:GAME_TIMER
|
||||
X: WINDOW_RIGHT/2
|
||||
|
||||
Reference in New Issue
Block a user