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:
geckosoft
2010-11-11 04:28:11 +01:00
committed by Bob
parent 6d67ab2240
commit 6b40abb58c
18 changed files with 640 additions and 29 deletions

View 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);
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}
}