Split HackyAI into a more manageable set of files.

This commit is contained in:
Paul Chote
2013-08-23 20:01:28 +12:00
parent b44d5a87f6
commit de8c736ca4
7 changed files with 693 additions and 606 deletions

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2013 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,
@@ -12,16 +12,15 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Mods.RA.Air;
using OpenRA.Mods.RA.Buildings;
using OpenRA.Mods.RA.Move;
using OpenRA.Mods.RA.Effects;
using OpenRA.Mods.RA.Air;
using OpenRA.Traits;
using XRandom = OpenRA.Thirdparty.Random;
namespace OpenRA.Mods.RA.AI
{
class HackyAIInfo : IBotInfo, ITraitInfo
public class HackyAIInfo : IBotInfo, ITraitInfo
{
public readonly string Name = "Unnamed Bot";
public readonly int SquadSize = 8;
@@ -82,610 +81,11 @@ namespace OpenRA.Mods.RA.AI
public object Create(ActorInitializer init) { return new HackyAI(this); }
}
/* a pile of hacks, which control a local player on the host. */
public class Enemy { public int Aggro; }
class Enemy { public int Aggro; }
public enum BuildingType { Building, Defense, Refinery }
enum SquadType { Assault, Air, Rush, Protection }
enum BuildingType { Building, Defense, Refinery }
class Squad
{
public List<Actor> units = new List<Actor>();
public SquadType type;
World world;
HackyAI bot;
XRandom random;
Target target;
StateMachine fsm;
//fuzzy
AttackOrFleeFuzzy attackOrFleeFuzzy = new AttackOrFleeFuzzy();
public Squad(HackyAI bot, SquadType type) : this(bot, type, null) { }
public Squad(HackyAI bot, SquadType type, Actor target)
{
this.bot = bot;
this.world = bot.world;
this.random = bot.random;
this.type = type;
this.target = Traits.Target.FromActor(target);
fsm = new StateMachine(this);
switch (type)
{
case SquadType.Assault:
case SquadType.Rush:
fsm.ChangeState(new GroundUnitsIdleState(), true);
break;
case SquadType.Air:
fsm.ChangeState(new AirIdleState(), true);
break;
case SquadType.Protection:
fsm.ChangeState(new UnitsForProtectionIdleState(), true);
break;
}
}
public void Update()
{
if (IsEmpty) return;
fsm.UpdateFsm();
}
public bool IsEmpty
{
get { return !units.Any(); }
}
public Actor Target
{
get { return target.Actor; }
set { target = Traits.Target.FromActor(value); }
}
public bool TargetIsValid
{
get { return target.IsValidFor(units.FirstOrDefault()) && !target.Actor.HasTrait<Husk>(); }
}
//**********************************************************************************
// Squad AI States
/* Include general functional for all states */
abstract class StateBase
{
protected const int dangerRadius = 10;
protected virtual bool MayBeFlee(Squad owner, Func<List<Actor>, bool> flee)
{
if (owner.IsEmpty) return false;
var u = owner.units.Random(owner.random);
var units = owner.world.FindActorsInCircle(u.CenterPosition, WRange.FromCells(dangerRadius)).ToList();
var ownBaseBuildingAround = units.Where(unit => unit.Owner == owner.bot.p && unit.HasTrait<Building>()).ToList();
if (ownBaseBuildingAround.Count > 0) return false;
var enemyAroundUnit = units.Where(unit => owner.bot.p.Stances[unit.Owner] == Stance.Enemy && unit.HasTrait<AttackBase>()).ToList();
if (!enemyAroundUnit.Any()) return false;
return flee(enemyAroundUnit);
}
protected static CPos? AverageUnitsPosition(List<Actor> units)
{
int x = 0;
int y = 0;
int countUnits = 0;
foreach (var u in units)
{
x += u.Location.X;
y += u.Location.Y;
countUnits++;
}
x = x / countUnits;
y = y / countUnits;
return (x != 0 && y != 0) ? new CPos?(new CPos(x, y)) : null;
}
protected static void GoToRandomOwnBuilding(Squad owner)
{
var loc = RandomBuildingLocation(owner);
foreach (var a in owner.units)
owner.world.IssueOrder(new Order("Move", a, false) { TargetLocation = loc });
}
protected static CPos RandomBuildingLocation(Squad owner)
{
var location = owner.bot.baseCenter;
var buildings = owner.world.ActorsWithTrait<Building>()
.Where(a => a.Actor.Owner == owner.bot.p).Select(a => a.Actor).ToArray();
if (buildings.Length > 0)
location = buildings.Random(owner.random).Location;
return location;
}
protected static bool BusyAttack(Actor a)
{
if (!a.IsIdle)
if (a.GetCurrentActivity().GetType() == typeof(OpenRA.Mods.RA.Activities.Attack) ||
a.GetCurrentActivity().GetType() == typeof(FlyAttack) ||
(a.GetCurrentActivity().NextActivity != null &&
(a.GetCurrentActivity().NextActivity.GetType() == typeof(OpenRA.Mods.RA.Activities.Attack) ||
a.GetCurrentActivity().NextActivity.GetType() == typeof(FlyAttack)) )
)
return true;
return false;
}
protected static bool CanAttackTarget(Actor a, Actor target)
{
if (!a.HasTrait<AttackBase>())
return false;
var targetable = target.TraitOrDefault<ITargetable>();
if (targetable == null)
return false;
var arms = a.TraitsImplementing<Armament>();
foreach (var arm in arms)
if (arm.Weapon.ValidTargets.Intersect(targetable.TargetTypes) != null)
return true;
return false;
}
}
/* States for air units */
abstract class AirStateBase : StateBase
{
protected const int missileUnitsMultiplier = 3;
protected static int CountAntiAirUnits(List<Actor> units)
{
int missileUnitsCount = 0;
foreach (var unit in units)
if (unit != null && unit.HasTrait<AttackBase>() && !unit.HasTrait<Aircraft>()
&& !unit.IsDisabled())
{
var arms = unit.TraitsImplementing<Armament>();
foreach (var a in arms)
if (a.Weapon.ValidTargets.Contains("Air"))
{
missileUnitsCount++;
break;
}
}
return missileUnitsCount;
}
//checks the number of anti air enemies around units
protected virtual bool MayBeFlee(Squad owner)
{
return base.MayBeFlee(owner, (enemyAroundUnit) =>
{
int missileUnitsCount = 0;
if (enemyAroundUnit.Count > 0)
missileUnitsCount = CountAntiAirUnits(enemyAroundUnit);
if (missileUnitsCount * missileUnitsMultiplier > owner.units.Count)
return true;
return false;
});
}
protected static Actor FindDefenselessTarget(Squad owner)
{
Actor target = null;
FindSafePlace(owner, out target, true);
return target == null ? null : target;
}
protected static CPos? FindSafePlace(Squad owner, out Actor detectedEnemyTarget, bool needTarget)
{
World world = owner.world;
detectedEnemyTarget = null;
int x = (world.Map.MapSize.X % dangerRadius) == 0 ? world.Map.MapSize.X : world.Map.MapSize.X + dangerRadius;
int y = (world.Map.MapSize.Y % dangerRadius) == 0 ? world.Map.MapSize.Y : world.Map.MapSize.Y + dangerRadius;
for (int i = 0; i < x; i += dangerRadius * 2)
for (int j = 0; j < y; j += dangerRadius * 2)
{
CPos pos = new CPos(i, j);
if (NearToPosSafely(owner, pos.CenterPosition, out detectedEnemyTarget))
{
if (needTarget)
{
if (detectedEnemyTarget == null)
continue;
else
return pos;
}
return pos;
}
}
return null;
}
protected static bool NearToPosSafely(Squad owner, WPos loc)
{
Actor a;
return NearToPosSafely(owner, loc, out a);
}
protected static bool NearToPosSafely(Squad owner, WPos loc, out Actor detectedEnemyTarget)
{
detectedEnemyTarget = null;
var unitsAroundPos = owner.world.FindActorsInCircle(loc, WRange.FromCells(dangerRadius))
.Where(unit => owner.bot.p.Stances[unit.Owner] == Stance.Enemy).ToList();
int missileUnitsCount = 0;
if (unitsAroundPos.Count > 0)
{
missileUnitsCount = CountAntiAirUnits(unitsAroundPos);
if (missileUnitsCount * missileUnitsMultiplier < owner.units.Count)
{
detectedEnemyTarget = unitsAroundPos.Random(owner.random);
return true;
}
else
return false;
}
return true;
}
protected static bool FullAmmo(Actor a)
{
var limitedAmmo = a.TraitOrDefault<LimitedAmmo>();
return (limitedAmmo != null && limitedAmmo.FullAmmo());
}
protected static bool HasAmmo(Actor a)
{
var limitedAmmo = a.TraitOrDefault<LimitedAmmo>();
return (limitedAmmo != null && limitedAmmo.HasAmmo());
}
protected static bool IsReloadable(Actor a)
{
return a.HasTrait<Reloads>();
}
protected static bool IsRearm(Actor a)
{
if (a.GetCurrentActivity() == null) return false;
if (a.GetCurrentActivity().GetType() == typeof(OpenRA.Mods.RA.Activities.Rearm) ||
a.GetCurrentActivity().GetType() == typeof(ResupplyAircraft) ||
(a.GetCurrentActivity().NextActivity != null &&
(a.GetCurrentActivity().NextActivity.GetType() == typeof(OpenRA.Mods.RA.Activities.Rearm) ||
a.GetCurrentActivity().NextActivity.GetType() == typeof(ResupplyAircraft)))
)
return true;
return false;
}
}
class AirIdleState : AirStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
if (MayBeFlee(owner))
{
owner.fsm.ChangeState(new AirFleeState(), true);
return;
}
var e = FindDefenselessTarget(owner);
if (e == null)
return;
else
{
owner.Target = e;
owner.fsm.ChangeState(new AirAttackState(), true);
}
}
public void Exit(Squad owner) { }
}
class AirAttackState : AirStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
if (!owner.TargetIsValid)
{
var a = owner.units.Random(owner.random);
var closestEnemy = owner.bot.FindClosestEnemy(a.CenterPosition);
if (closestEnemy != null)
owner.Target = closestEnemy;
else
{
owner.fsm.ChangeState(new AirFleeState(), true);
return;
}
}
if (!NearToPosSafely(owner, owner.Target.CenterPosition))
{
owner.fsm.ChangeState(new AirFleeState(), true);
return;
}
foreach (var a in owner.units)
{
if (BusyAttack(a))
continue;
if (!IsReloadable(a))
{
if (!HasAmmo(a))
{
if (IsRearm(a))
continue;
owner.world.IssueOrder(new Order("ReturnToBase", a, false));
continue;
}
if (IsRearm(a))
continue;
}
if (owner.Target.HasTrait<ITargetable>() && CanAttackTarget(a, owner.Target))
owner.world.IssueOrder(new Order("Attack", a, false) { TargetActor = owner.Target });
}
}
public void Exit(Squad owner) { }
}
class AirFleeState : AirStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
foreach (var a in owner.units)
{
if (!IsReloadable(a))
if (!FullAmmo(a))
{
if (IsRearm(a))
continue;
owner.world.IssueOrder(new Order("ReturnToBase", a, false));
continue;
}
owner.world.IssueOrder(new Order("Move", a, false) { TargetLocation = RandomBuildingLocation(owner) });
}
owner.fsm.ChangeState(new AirIdleState(), true);
}
public void Exit(Squad owner) { }
}
/* States for ground units */
abstract class GroundStateBase : StateBase
{
protected virtual bool MayBeFlee(Squad owner)
{
return base.MayBeFlee(owner, (enemyAroundUnit) =>
{
owner.attackOrFleeFuzzy.CalculateFuzzy(owner.units, enemyAroundUnit);
if (!owner.attackOrFleeFuzzy.CanAttack)
return true;
return false;
});
}
}
class GroundUnitsIdleState : GroundStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
if (!owner.TargetIsValid)
{
var t = owner.bot.FindClosestEnemy(owner.units.FirstOrDefault().CenterPosition);
if (t == null) return;
owner.Target = t;
}
var enemyUnits = owner.world.FindActorsInCircle(owner.Target.CenterPosition, WRange.FromCells(10))
.Where(unit => owner.bot.p.Stances[unit.Owner] == Stance.Enemy).ToList();
if (enemyUnits.Any())
{
owner.attackOrFleeFuzzy.CalculateFuzzy(owner.units, enemyUnits);
if (owner.attackOrFleeFuzzy.CanAttack)
{
foreach(var u in owner.units)
owner.world.IssueOrder(new Order("AttackMove", u, false) { TargetLocation = owner.Target.CenterPosition.ToCPos() });
// We have gathered sufficient units. Attack the nearest enemy unit.
owner.fsm.ChangeState(new GroundUnitsAttackMoveState(), true);
return;
}
else
owner.fsm.ChangeState(new GroundUnitsFleeState(), true);
}
}
public void Exit(Squad owner) { }
}
class GroundUnitsAttackMoveState : GroundStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
if (!owner.TargetIsValid)
{
var closestEnemy = owner.bot.FindClosestEnemy(owner.units.Random(owner.random).CenterPosition);
if (closestEnemy != null)
owner.Target = closestEnemy;
else
{
owner.fsm.ChangeState(new GroundUnitsFleeState(), true);
return;
}
}
Actor leader = owner.units.ClosestTo(owner.Target.CenterPosition);
if (leader == null)
return;
var ownUnits = owner.world.FindActorsInCircle(leader.CenterPosition, WRange.FromCells(owner.units.Count) / 3)
.Where(a => a.Owner == owner.units.FirstOrDefault().Owner && owner.units.Contains(a)).ToList();
if (ownUnits.Count < owner.units.Count)
{
owner.world.IssueOrder(new Order("Stop", leader, false));
foreach (var unit in owner.units.Where(a => !ownUnits.Contains(a)))
owner.world.IssueOrder(new Order("AttackMove", unit, false) { TargetLocation = leader.CenterPosition.ToCPos() });
}
else
{
var enemys = owner.world.FindActorsInCircle(leader.CenterPosition, WRange.FromCells(12))
.Where(a1 => !a1.Destroyed && !a1.IsDead()).ToList();
var enemynearby = enemys.Where(a1 => a1.HasTrait<ITargetable>() && leader.Owner.Stances[a1.Owner] == Stance.Enemy).ToList();
if (enemynearby.Any())
{
owner.Target = enemynearby.ClosestTo(leader.CenterPosition);
owner.fsm.ChangeState(new GroundUnitsAttackState(), true);
return;
}
else
foreach (var a in owner.units)
owner.world.IssueOrder(new Order("AttackMove", a, false) { TargetLocation = owner.Target.Location });
}
if (MayBeFlee(owner))
{
owner.fsm.ChangeState(new GroundUnitsFleeState(), true);
return;
}
}
public void Exit(Squad owner) { }
}
class GroundUnitsAttackState : GroundStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
if (!owner.TargetIsValid)
{
var closestEnemy = owner.bot.FindClosestEnemy(owner.units.Random(owner.random).CenterPosition);
if (closestEnemy != null)
owner.Target = closestEnemy;
else
{
owner.fsm.ChangeState(new GroundUnitsFleeState(), true);
return;
}
}
foreach (var a in owner.units)
if (!BusyAttack(a))
owner.world.IssueOrder(new Order("Attack", a, false) { TargetActor = owner.bot.FindClosestEnemy(a.CenterPosition) });
if (MayBeFlee(owner))
{
owner.fsm.ChangeState(new GroundUnitsFleeState(), true);
return;
}
}
public void Exit(Squad owner) { }
}
class GroundUnitsFleeState : GroundStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
GoToRandomOwnBuilding(owner);
owner.fsm.ChangeState(new GroundUnitsIdleState(), true);
}
public void Exit(Squad owner) { owner.units.Clear(); }
}
class UnitsForProtectionIdleState : GroundStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner) { owner.fsm.ChangeState(new UnitsForProtectionAttackState(), true); }
public void Exit(Squad owner) { }
}
class UnitsForProtectionAttackState : GroundStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
if (!owner.TargetIsValid)
{
var circaPostion = AverageUnitsPosition(owner.units);
if (circaPostion == null) return;
owner.Target = owner.bot.FindClosestEnemy(circaPostion.Value.CenterPosition, WRange.FromCells(8));
if (owner.Target == null)
{
owner.fsm.ChangeState(new UnitsForProtectionFleeState(), true);
return;
}
}
foreach (var a in owner.units)
owner.world.IssueOrder(new Order("AttackMove", a, false) { TargetLocation = owner.Target.Location });
}
public void Exit(Squad owner) { }
}
class UnitsForProtectionFleeState : GroundStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
GoToRandomOwnBuilding(owner);
owner.fsm.ChangeState(new UnitsForProtectionIdleState(), true);
}
public void Exit(Squad owner) { owner.units.Clear(); }
}
}
class HackyAI : ITick, IBot, INotifyDamage
public class HackyAI : ITick, IBot, INotifyDamage
{
bool enabled;
public int ticks;

View File

@@ -0,0 +1,85 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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.Traits;
using XRandom = OpenRA.Thirdparty.Random;
namespace OpenRA.Mods.RA.AI
{
public enum SquadType { Assault, Air, Rush, Protection }
public class Squad
{
public List<Actor> units = new List<Actor>();
public SquadType type;
internal World world;
internal HackyAI bot;
internal XRandom random;
internal Actor target;
internal StateMachine fsm;
//fuzzy
internal AttackOrFleeFuzzy attackOrFleeFuzzy = new AttackOrFleeFuzzy();
public Squad(HackyAI bot, SquadType type) : this(bot, type, null) { }
public Squad(HackyAI bot, SquadType type, Actor target)
{
this.bot = bot;
this.world = bot.world;
this.random = bot.random;
this.type = type;
this.target = target;
fsm = new StateMachine(this);
switch (type)
{
case SquadType.Assault:
case SquadType.Rush:
fsm.ChangeState(new GroundUnitsIdleState(), true);
break;
case SquadType.Air:
fsm.ChangeState(new AirIdleState(), true);
break;
case SquadType.Protection:
fsm.ChangeState(new UnitsForProtectionIdleState(), true);
break;
}
}
public void Update()
{
if (IsEmpty) return;
fsm.UpdateFsm();
}
public bool IsEmpty
{
get { return !units.Any(); }
}
public Actor Target
{
get { return target; }
set { target = value; }
}
public bool TargetIsValid
{
get { return (target != null && !target.IsDead() && !target.Destroyed
&& target.IsInWorld && !target.HasTrait<Husk>()); }
}
}
}

View File

@@ -0,0 +1,251 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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.Linq;
using OpenRA.Mods.RA.Air;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.AI
{
abstract class AirStateBase : StateBase
{
protected const int missileUnitsMultiplier = 3;
protected static int CountAntiAirUnits(List<Actor> units)
{
int missileUnitsCount = 0;
foreach (var unit in units)
if (unit != null && unit.HasTrait<AttackBase>() && !unit.HasTrait<Aircraft>()
&& !unit.IsDisabled())
{
var arms = unit.TraitsImplementing<Armament>();
foreach (var a in arms)
if (a.Weapon.ValidTargets.Contains("Air"))
{
missileUnitsCount++;
break;
}
}
return missileUnitsCount;
}
//checks the number of anti air enemies around units
protected virtual bool MayBeFlee(Squad owner)
{
return base.MayBeFlee(owner, (enemyAroundUnit) =>
{
int missileUnitsCount = 0;
if (enemyAroundUnit.Count > 0)
missileUnitsCount = CountAntiAirUnits(enemyAroundUnit);
if (missileUnitsCount * missileUnitsMultiplier > owner.units.Count)
return true;
return false;
});
}
protected static Actor FindDefenselessTarget(Squad owner)
{
Actor target = null;
FindSafePlace(owner, out target, true);
return target == null ? null : target;
}
protected static CPos? FindSafePlace(Squad owner, out Actor detectedEnemyTarget, bool needTarget)
{
World world = owner.world;
detectedEnemyTarget = null;
int x = (world.Map.MapSize.X % dangerRadius) == 0 ? world.Map.MapSize.X : world.Map.MapSize.X + dangerRadius;
int y = (world.Map.MapSize.Y % dangerRadius) == 0 ? world.Map.MapSize.Y : world.Map.MapSize.Y + dangerRadius;
for (int i = 0; i < x; i += dangerRadius * 2)
for (int j = 0; j < y; j += dangerRadius * 2)
{
CPos pos = new CPos(i, j);
if (NearToPosSafely(owner, pos.CenterPosition, out detectedEnemyTarget))
{
if (needTarget)
{
if (detectedEnemyTarget == null)
continue;
else
return pos;
}
return pos;
}
}
return null;
}
protected static bool NearToPosSafely(Squad owner, WPos loc)
{
Actor a;
return NearToPosSafely(owner, loc, out a);
}
protected static bool NearToPosSafely(Squad owner, WPos loc, out Actor detectedEnemyTarget)
{
detectedEnemyTarget = null;
var unitsAroundPos = owner.world.FindActorsInCircle(loc, WRange.FromCells(dangerRadius))
.Where(unit => owner.bot.p.Stances[unit.Owner] == Stance.Enemy).ToList();
int missileUnitsCount = 0;
if (unitsAroundPos.Count > 0)
{
missileUnitsCount = CountAntiAirUnits(unitsAroundPos);
if (missileUnitsCount * missileUnitsMultiplier < owner.units.Count)
{
detectedEnemyTarget = unitsAroundPos.Random(owner.random);
return true;
}
else
return false;
}
return true;
}
protected static bool FullAmmo(Actor a)
{
var limitedAmmo = a.TraitOrDefault<LimitedAmmo>();
return (limitedAmmo != null && limitedAmmo.FullAmmo());
}
protected static bool HasAmmo(Actor a)
{
var limitedAmmo = a.TraitOrDefault<LimitedAmmo>();
return (limitedAmmo != null && limitedAmmo.HasAmmo());
}
protected static bool IsReloadable(Actor a)
{
return a.HasTrait<Reloads>();
}
protected static bool IsRearm(Actor a)
{
if (a.GetCurrentActivity() == null) return false;
if (a.GetCurrentActivity().GetType() == typeof(OpenRA.Mods.RA.Activities.Rearm) ||
a.GetCurrentActivity().GetType() == typeof(ResupplyAircraft) ||
(a.GetCurrentActivity().NextActivity != null &&
(a.GetCurrentActivity().NextActivity.GetType() == typeof(OpenRA.Mods.RA.Activities.Rearm) ||
a.GetCurrentActivity().NextActivity.GetType() == typeof(ResupplyAircraft)))
)
return true;
return false;
}
}
class AirIdleState : AirStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
if (MayBeFlee(owner))
{
owner.fsm.ChangeState(new AirFleeState(), true);
return;
}
var e = FindDefenselessTarget(owner);
if (e == null)
return;
else
{
owner.Target = e;
owner.fsm.ChangeState(new AirAttackState(), true);
}
}
public void Exit(Squad owner) { }
}
class AirAttackState : AirStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
if (!owner.TargetIsValid)
{
var a = owner.units.Random(owner.random);
var closestEnemy = owner.bot.FindClosestEnemy(a.CenterPosition);
if (closestEnemy != null)
owner.Target = closestEnemy;
else
{
owner.fsm.ChangeState(new AirFleeState(), true);
return;
}
}
if (!NearToPosSafely(owner, owner.Target.CenterPosition))
{
owner.fsm.ChangeState(new AirFleeState(), true);
return;
}
foreach (var a in owner.units)
{
if (BusyAttack(a))
continue;
if (!IsReloadable(a))
{
if (!HasAmmo(a))
{
if (IsRearm(a))
continue;
owner.world.IssueOrder(new Order("ReturnToBase", a, false));
continue;
}
if (IsRearm(a))
continue;
}
if (owner.Target.HasTrait<ITargetable>() && CanAttackTarget(a, owner.Target))
owner.world.IssueOrder(new Order("Attack", a, false) { TargetActor = owner.Target });
}
}
public void Exit(Squad owner) { }
}
class AirFleeState : AirStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
foreach (var a in owner.units)
{
if (!IsReloadable(a))
if (!FullAmmo(a))
{
if (IsRearm(a))
continue;
owner.world.IssueOrder(new Order("ReturnToBase", a, false));
continue;
}
owner.world.IssueOrder(new Order("Move", a, false) { TargetLocation = RandomBuildingLocation(owner) });
}
owner.fsm.ChangeState(new AirIdleState(), true);
}
public void Exit(Squad owner) { }
}
}

View File

@@ -0,0 +1,172 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.AI
{
abstract class GroundStateBase : StateBase
{
protected virtual bool MayBeFlee(Squad owner)
{
return base.MayBeFlee(owner, (enemyAroundUnit) =>
{
owner.attackOrFleeFuzzy.CalculateFuzzy(owner.units, enemyAroundUnit);
if (!owner.attackOrFleeFuzzy.CanAttack)
return true;
return false;
});
}
}
class GroundUnitsIdleState : GroundStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
if (!owner.TargetIsValid)
{
var t = owner.bot.FindClosestEnemy(owner.units.FirstOrDefault().CenterPosition);
if (t == null) return;
owner.Target = t;
}
var enemyUnits = owner.world.FindActorsInCircle(owner.Target.CenterPosition, WRange.FromCells(10))
.Where(unit => owner.bot.p.Stances[unit.Owner] == Stance.Enemy).ToList();
if (enemyUnits.Any())
{
owner.attackOrFleeFuzzy.CalculateFuzzy(owner.units, enemyUnits);
if (owner.attackOrFleeFuzzy.CanAttack)
{
foreach(var u in owner.units)
owner.world.IssueOrder(new Order("AttackMove", u, false) { TargetLocation = owner.Target.CenterPosition.ToCPos() });
// We have gathered sufficient units. Attack the nearest enemy unit.
owner.fsm.ChangeState(new GroundUnitsAttackMoveState(), true);
return;
}
else
owner.fsm.ChangeState(new GroundUnitsFleeState(), true);
}
}
public void Exit(Squad owner) { }
}
class GroundUnitsAttackMoveState : GroundStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
if (!owner.TargetIsValid)
{
var closestEnemy = owner.bot.FindClosestEnemy(owner.units.Random(owner.random).CenterPosition);
if (closestEnemy != null)
owner.Target = closestEnemy;
else
{
owner.fsm.ChangeState(new GroundUnitsFleeState(), true);
return;
}
}
Actor leader = owner.units.ClosestTo(owner.Target.CenterPosition);
if (leader == null)
return;
var ownUnits = owner.world.FindActorsInCircle(leader.CenterPosition, WRange.FromCells(owner.units.Count) / 3)
.Where(a => a.Owner == owner.units.FirstOrDefault().Owner && owner.units.Contains(a)).ToList();
if (ownUnits.Count < owner.units.Count)
{
owner.world.IssueOrder(new Order("Stop", leader, false));
foreach (var unit in owner.units.Where(a => !ownUnits.Contains(a)))
owner.world.IssueOrder(new Order("AttackMove", unit, false) { TargetLocation = leader.CenterPosition.ToCPos() });
}
else
{
var enemys = owner.world.FindActorsInCircle(leader.CenterPosition, WRange.FromCells(12))
.Where(a1 => !a1.Destroyed && !a1.IsDead()).ToList();
var enemynearby = enemys.Where(a1 => a1.HasTrait<ITargetable>() && leader.Owner.Stances[a1.Owner] == Stance.Enemy).ToList();
if (enemynearby.Any())
{
owner.Target = enemynearby.ClosestTo(leader.CenterPosition);
owner.fsm.ChangeState(new GroundUnitsAttackState(), true);
return;
}
else
foreach (var a in owner.units)
owner.world.IssueOrder(new Order("AttackMove", a, false) { TargetLocation = owner.Target.Location });
}
if (MayBeFlee(owner))
{
owner.fsm.ChangeState(new GroundUnitsFleeState(), true);
return;
}
}
public void Exit(Squad owner) { }
}
class GroundUnitsAttackState : GroundStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
if (!owner.TargetIsValid)
{
var closestEnemy = owner.bot.FindClosestEnemy(owner.units.Random(owner.random).CenterPosition);
if (closestEnemy != null)
owner.Target = closestEnemy;
else
{
owner.fsm.ChangeState(new GroundUnitsFleeState(), true);
return;
}
}
foreach (var a in owner.units)
if (!BusyAttack(a))
owner.world.IssueOrder(new Order("Attack", a, false) { TargetActor = owner.bot.FindClosestEnemy(a.CenterPosition) });
if (MayBeFlee(owner))
{
owner.fsm.ChangeState(new GroundUnitsFleeState(), true);
return;
}
}
public void Exit(Squad owner) { }
}
class GroundUnitsFleeState : GroundStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
GoToRandomOwnBuilding(owner);
owner.fsm.ChangeState(new GroundUnitsIdleState(), true);
}
public void Exit(Squad owner) { owner.units.Clear(); }
}
}

View File

@@ -0,0 +1,64 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.AI
{
class UnitsForProtectionIdleState : GroundStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner) { owner.fsm.ChangeState(new UnitsForProtectionAttackState(), true); }
public void Exit(Squad owner) { }
}
class UnitsForProtectionAttackState : GroundStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
if (!owner.TargetIsValid)
{
var circaPostion = AverageUnitsPosition(owner.units);
if (circaPostion == null) return;
owner.Target = owner.bot.FindClosestEnemy(circaPostion.Value.CenterPosition, WRange.FromCells(8));
if (owner.Target == null)
{
owner.fsm.ChangeState(new UnitsForProtectionFleeState(), true);
return;
}
}
foreach (var a in owner.units)
owner.world.IssueOrder(new Order("AttackMove", a, false) { TargetLocation = owner.Target.Location });
}
public void Exit(Squad owner) { }
}
class UnitsForProtectionFleeState : GroundStateBase, IState
{
public void Enter(Squad owner) { }
public void Execute(Squad owner)
{
if (owner.IsEmpty) return;
GoToRandomOwnBuilding(owner);
owner.fsm.ChangeState(new UnitsForProtectionIdleState(), true);
}
public void Exit(Squad owner) { owner.units.Clear(); }
}
}

View File

@@ -0,0 +1,107 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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.RA.Air;
using OpenRA.Mods.RA.Buildings;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.AI
{
//**********************************************************************************
// Squad AI States
/* Include general functional for all states */
abstract class StateBase
{
protected const int dangerRadius = 10;
protected virtual bool MayBeFlee(Squad owner, Func<List<Actor>, bool> flee)
{
if (owner.IsEmpty) return false;
var u = owner.units.Random(owner.random);
var units = owner.world.FindActorsInCircle(u.CenterPosition, WRange.FromCells(dangerRadius)).ToList();
var ownBaseBuildingAround = units.Where(unit => unit.Owner == owner.bot.p && unit.HasTrait<Building>()).ToList();
if (ownBaseBuildingAround.Count > 0) return false;
var enemyAroundUnit = units.Where(unit => owner.bot.p.Stances[unit.Owner] == Stance.Enemy && unit.HasTrait<AttackBase>()).ToList();
if (!enemyAroundUnit.Any()) return false;
return flee(enemyAroundUnit);
}
protected static CPos? AverageUnitsPosition(List<Actor> units)
{
int x = 0;
int y = 0;
int countUnits = 0;
foreach (var u in units)
{
x += u.Location.X;
y += u.Location.Y;
countUnits++;
}
x = x / countUnits;
y = y / countUnits;
return (x != 0 && y != 0) ? new CPos?(new CPos(x, y)) : null;
}
protected static void GoToRandomOwnBuilding(Squad owner)
{
var loc = RandomBuildingLocation(owner);
foreach (var a in owner.units)
owner.world.IssueOrder(new Order("Move", a, false) { TargetLocation = loc });
}
protected static CPos RandomBuildingLocation(Squad owner)
{
var location = owner.bot.baseCenter;
var buildings = owner.world.ActorsWithTrait<Building>()
.Where(a => a.Actor.Owner == owner.bot.p).Select(a => a.Actor).ToArray();
if (buildings.Length > 0)
location = buildings.Random(owner.random).Location;
return location;
}
protected static bool BusyAttack(Actor a)
{
if (!a.IsIdle)
if (a.GetCurrentActivity().GetType() == typeof(OpenRA.Mods.RA.Activities.Attack) ||
a.GetCurrentActivity().GetType() == typeof(FlyAttack) ||
(a.GetCurrentActivity().NextActivity != null &&
(a.GetCurrentActivity().NextActivity.GetType() == typeof(OpenRA.Mods.RA.Activities.Attack) ||
a.GetCurrentActivity().NextActivity.GetType() == typeof(FlyAttack)) )
)
return true;
return false;
}
protected static bool CanAttackTarget(Actor a, Actor target)
{
if (!a.HasTrait<AttackBase>())
return false;
var targetable = target.TraitOrDefault<ITargetable>();
if (targetable == null)
return false;
var arms = a.TraitsImplementing<Armament>();
foreach (var arm in arms)
if (arm.Weapon.ValidTargets.Intersect(targetable.TargetTypes) != null)
return true;
return false;
}
}
}

View File

@@ -466,6 +466,11 @@
<Compile Include="Orders\EnterAlliedActorTargeter.cs" />
<Compile Include="Render\WithIdleOverlay.cs" />
<Compile Include="Tooltip.cs" />
<Compile Include="AI\Squad.cs" />
<Compile Include="AI\States\StateBase.cs" />
<Compile Include="AI\States\GroundStates.cs" />
<Compile Include="AI\States\ProtectionStates.cs" />
<Compile Include="AI\States\AirStates.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
@@ -519,4 +524,7 @@ cd "$(SolutionDir)thirdparty/"
copy "FuzzyLogicLibrary.dll" "$(SolutionDir)"
cd "$(SolutionDir)"</PostBuildEvent>
</PropertyGroup>
<ItemGroup>
<Folder Include="AI\States\" />
</ItemGroup>
</Project>