Split HackyAI into a more manageable set of files.
This commit is contained in:
@@ -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;
|
||||
|
||||
85
OpenRA.Mods.RA/AI/Squad.cs
Normal file
85
OpenRA.Mods.RA/AI/Squad.cs
Normal 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>()); }
|
||||
}
|
||||
}
|
||||
}
|
||||
251
OpenRA.Mods.RA/AI/States/AirStates.cs
Normal file
251
OpenRA.Mods.RA/AI/States/AirStates.cs
Normal 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) { }
|
||||
}
|
||||
}
|
||||
172
OpenRA.Mods.RA/AI/States/GroundStates.cs
Normal file
172
OpenRA.Mods.RA/AI/States/GroundStates.cs
Normal 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(); }
|
||||
}
|
||||
}
|
||||
64
OpenRA.Mods.RA/AI/States/ProtectionStates.cs
Normal file
64
OpenRA.Mods.RA/AI/States/ProtectionStates.cs
Normal 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(); }
|
||||
}
|
||||
}
|
||||
107
OpenRA.Mods.RA/AI/States/StateBase.cs
Normal file
107
OpenRA.Mods.RA/AI/States/StateBase.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user