protected virtual bool ShouldFlee(SquadCA squad, Func <IEnumerable <Actor>, bool> flee) { if (!squad.IsValid) { return(false); } var randomSquadUnit = squad.Units.Random(squad.Random); var dangerRadius = squad.SquadManager.Info.DangerScanRadius; var units = squad.World.FindActorsInCircle(randomSquadUnit.CenterPosition, WDist.FromCells(dangerRadius)).ToList(); // If there are any own buildings within the DangerRadius, don't flee // PERF: Avoid LINQ foreach (var u in units) { if (u.Owner == squad.Bot.Player && u.Info.HasTraitInfo <BuildingInfo>()) { return(false); } } var enemyAroundUnit = units.Where(unit => squad.SquadManager.IsEnemyUnit(unit) && unit.Info.HasTraitInfo <AttackBaseInfo>()); if (!enemyAroundUnit.Any()) { return(false); } return(flee(enemyAroundUnit)); }
protected Actor ThreatScan(SquadCA owner, Actor teamLeader, WDist scanRadius) { var enemies = owner.World.FindActorsInCircle(teamLeader.CenterPosition, scanRadius) .Where(a => owner.SquadManager.IsEnemyUnit(a) && owner.SquadManager.IsNotHiddenUnit(a)); return(enemies.ClosestTo(teamLeader.CenterPosition)); }
protected Actor FindClosestEnemy(SquadCA owner) { var first = owner.Units.First(); // Navy squad AI can exploit enemy naval production to find path, if any. // (Way better than finding a nearest target which is likely to be on Ground) // You might be tempted to move these lookups into Activate() but that causes null reference exception. var domainIndex = first.World.WorldActor.Trait <DomainIndex>(); var locomotorInfo = first.Info.TraitInfo <MobileInfo>().LocomotorInfo; var navalProductions = owner.World.ActorsHavingTrait <Building>().Where(a => owner.SquadManager.Info.NavalProductionTypes.Contains(a.Info.Name) && domainIndex.IsPassable(first.Location, a.Location, locomotorInfo) && a.AppearsHostileTo(first)); if (navalProductions.Any()) { var nearest = navalProductions.ClosestTo(first); // Return nearest when it is FAR enough. // If the naval production is within MaxBaseRadius, it implies that // this squad is close to enemy territory and they should expect a naval combat; // closest enemy makes more sense in that case. if ((nearest.Location - first.Location).LengthSquared > owner.SquadManager.Info.MaxBaseRadius * owner.SquadManager.Info.MaxBaseRadius) { return(nearest); } } return(owner.SquadManager.FindClosestEnemy(first.CenterPosition)); }
protected static Actor FindDefenselessTarget(SquadCA owner) { Actor target = null; FindSafePlace(owner, out target, true); return(target); }
public void Tick(SquadCA owner) { if (!owner.IsValid) { return; } if (!owner.IsTargetValid) { var closestEnemy = FindClosestEnemy(owner); if (closestEnemy != null) { owner.TargetActor = closestEnemy; } else { owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsFleeState(), true); return; } } foreach (var a in owner.Units) { if (!BusyAttack(a)) { owner.Bot.QueueOrder(new Order("Attack", a, Target.FromCell(owner.World, owner.TargetActor.Location), false)); } } if (ShouldFlee(owner)) { owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsFleeState(), true); } }
protected static CPos?FindSafePlace(SquadCA owner, out Actor detectedEnemyTarget, bool needTarget) { var map = owner.World.Map; var dangerRadius = owner.SquadManager.Info.DangerScanRadius; detectedEnemyTarget = null; var columnCount = (map.MapSize.X + dangerRadius - 1) / dangerRadius; var rowCount = (map.MapSize.Y + dangerRadius - 1) / dangerRadius; var checkIndices = Exts.MakeArray(columnCount * rowCount, i => i).Shuffle(owner.World.LocalRandom); foreach (var i in checkIndices) { var pos = new CPos((i % columnCount) * dangerRadius + dangerRadius / 2, (i / columnCount) * dangerRadius + dangerRadius / 2); if (NearToPosSafely(owner, map.CenterOfCell(pos), out detectedEnemyTarget)) { if (needTarget && detectedEnemyTarget == null) { continue; } return(pos); } } return(null); }
public static SquadCA Deserialize(IBot bot, SquadManagerBotModuleCA squadManager, MiniYaml yaml) { var type = SquadTypeCA.Rush; Actor targetActor = null; var typeNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Type"); if (typeNode != null) { type = FieldLoader.GetValue <SquadTypeCA>("Type", typeNode.Value.Value); } var targetNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Target"); if (targetNode != null) { targetActor = squadManager.World.GetActorById(FieldLoader.GetValue <uint>("ActiveUnits", targetNode.Value.Value)); } var squad = new SquadCA(bot, squadManager, type, targetActor); var unitsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Units"); if (unitsNode != null) { squad.Units.AddRange(FieldLoader.GetValue <uint[]>("Units", unitsNode.Value.Value) .Select(a => squadManager.World.GetActorById(a))); } return(squad); }
// Retreat units from combat, or for supply only in idle protected void Retreat(SquadCA owner, bool resupplyonly) { // Reload units. foreach (var a in owner.Units) { var ammoPools = a.TraitsImplementing <AmmoPool>().ToArray(); if (!ReloadsAutomatically(ammoPools, a.TraitOrDefault <Rearmable>()) && !FullAmmo(ammoPools)) { if (IsRearming(a)) { continue; } owner.Bot.QueueOrder(new Order("ReturnToBase", a, false)); continue; } else if (!resupplyonly) { owner.Bot.QueueOrder(new Order("Move", a, Target.FromCell(owner.World, RandomBuildingLocation(owner)), false)); } } // Repair units. One by one to avoid give out mass orders foreach (var a in owner.Units) { if (IsRearming(a)) { continue; } Actor repairBuilding = null; var orderId = "Repair"; var health = a.TraitOrDefault <IHealth>(); if (health != null && health.DamageState > DamageState.Undamaged) { var repairable = a.TraitOrDefault <Repairable>(); if (repairable != null) { repairBuilding = repairable.FindRepairBuilding(a); } else { var repairableNear = a.TraitOrDefault <RepairableNearCA>(); if (repairableNear != null) { orderId = "RepairNear"; repairBuilding = repairableNear.FindRepairBuilding(a); } } if (repairBuilding != null) { owner.Bot.QueueOrder(new Order(orderId, a, Target.FromActor(repairBuilding), true)); break; } } } }
public void Tick(SquadCA owner) { if (!owner.IsValid) { return; } // rescan target to prevent being ambushed and die without fight // return to AttackMove state for formation var teamLeader = owner.Units.ClosestTo(owner.TargetActor.CenterPosition); if (teamLeader == null) { return; } var teamTail = owner.Units.MaxByOrDefault(a => (a.CenterPosition - owner.TargetActor.CenterPosition).LengthSquared); var attackScanRadius = WDist.FromCells(owner.SquadManager.Info.AttackScanRadius); var cannotRetaliate = false; var targetActor = ThreatScan(owner, teamLeader, attackScanRadius) ?? ThreatScan(owner, teamTail, attackScanRadius); if (targetActor == null) { owner.TargetActor = null; owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsAttackMoveState(), true); return; } else { cannotRetaliate = true; owner.TargetActor = targetActor; foreach (var a in owner.Units) { if (!BusyAttack(a)) { if (CanAttackTarget(a, targetActor)) { owner.Bot.QueueOrder(new Order("Attack", a, Target.FromActor(owner.TargetActor), false)); cannotRetaliate = false; } else { owner.Bot.QueueOrder(new Order("AttackMove", a, Target.FromCell(owner.World, teamLeader.Location), false)); } } else { cannotRetaliate = false; } } } if (ShouldFlee(owner) || cannotRetaliate) { owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsFleeState(), true); return; } }
public void Tick(SquadCA owner) { var cannotRetaliate = false; // Basic check if (!owner.IsValid) { return; } TeamLeader = owner.Units.FirstOrDefault(); if (TeamLeader == null) { return; } // Rescan target to prevent being ambushed and die without fight // If there is no threat around, return to AttackMove state for formation var attackScanRadius = WDist.FromCells(owner.SquadManager.Info.AttackScanRadius); var targetActor = ThreatScan(owner, TeamLeader, attackScanRadius); if (targetActor == null) { owner.TargetActor = GetRandomValuableTarget(owner); owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsAttackMoveState(), true); return; } else { cannotRetaliate = true; owner.TargetActor = targetActor; foreach (var a in owner.Units) { if (!BusyAttack(a)) { if (CanAttackTarget(a, targetActor)) { owner.Bot.QueueOrder(new Order("Attack", a, Target.FromActor(owner.TargetActor), false)); cannotRetaliate = false; } else { owner.Bot.QueueOrder(new Order("AttackMove", a, Target.FromCell(owner.World, TeamLeader.Location), false)); } } else { cannotRetaliate = false; } } } if (ShouldFlee(owner) || cannotRetaliate) { owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsFleeState(), true); } }
public void Tick(SquadCA owner) { if (!owner.IsValid) { return; } if (!owner.IsTargetValid) { var closestEnemy = FindClosestEnemy(owner); if (closestEnemy != null) { owner.TargetActor = closestEnemy; } else { owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsFleeStateCA(), false); return; } } var leader = owner.Units.ClosestTo(owner.TargetActor.CenterPosition); if (leader.Location != lastLeaderLocation) { lastLeaderLocation = leader.Location; lastUpdatedTick = owner.World.WorldTick; } if (owner.TargetActor != lastTarget) { lastTarget = owner.TargetActor; lastUpdatedTick = owner.World.WorldTick; } // HACK: Drop back to the idle state if we haven't moved in 2.5 seconds // This works around the squad being stuck trying to attack-move to a location // that they cannot path to, generating expensive pathfinding calls each tick. if (owner.World.WorldTick > lastUpdatedTick + 63) { owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsIdleState(), true); return; } foreach (var a in owner.Units) { if (!BusyAttack(a)) { owner.Bot.QueueOrder(new Order("Attack", a, Target.FromActor(owner.TargetActor), false)); } } if (ShouldFlee(owner)) { owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsFleeStateCA(), false); } }
protected static void GoToRandomOwnBuilding(SquadCA squad) { var loc = RandomBuildingLocation(squad); foreach (var a in squad.Units) { squad.Bot.QueueOrder(new Order("Move", a, Target.FromCell(squad.World, loc), false)); } }
public void Tick(SquadCA owner) { if (!owner.IsValid) { return; } Retreat(owner, true, true, true); owner.FuzzyStateMachine.ChangeState(owner, new UnitsForProtectionIdleState(), true); }
public void Tick(SquadCA owner) { if (!owner.IsValid) { return; } Retreat(owner, false); owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsIdleState(), true); }
public void Tick(SquadCA owner) { if (!owner.IsValid) { return; } Retreat(owner, flee: true, rearm: true, repair: true); owner.FuzzyStateMachine.ChangeState(owner, new AirIdleStateCA(), false); }
protected Actor GetRandomValuableTarget(SquadCA owner) { var manager = owner.SquadManager; var mustDestroyedEnemy = manager.World.ActorsHavingTrait <MustBeDestroyed>(t => t.Info.RequiredForShortGame) .Where(a => manager.IsEnemyUnit(a) && manager.IsNotHiddenUnit(a)).ToArray(); if (!mustDestroyedEnemy.Any()) { return(FindClosestEnemy(owner)); } return(mustDestroyedEnemy.Random(owner.World.LocalRandom)); }
protected static CPos RandomBuildingLocation(SquadCA squad) { var location = squad.SquadManager.GetRandomBaseCenter(); var buildings = squad.World.ActorsHavingTrait <Building>() .Where(a => a.Owner == squad.Bot.Player).ToList(); if (buildings.Count > 0) { location = buildings.Random(squad.Random).Location; } return(location); }
// Retreat units from combat, or for supply only in idle protected virtual void Retreat(SquadCA owner, bool resupplyonly) { // Repair units. One by one to avoid give out mass orders var alreadysend = false; foreach (var a in owner.Units) { if (IsRearming(a)) { continue; } Actor repairBuilding = null; var orderId = "Repair"; var health = a.TraitOrDefault <IHealth>(); if (!alreadysend && health != null && health.DamageState > DamageState.Undamaged) { var repairable = a.TraitOrDefault <Repairable>(); if (repairable != null) { repairBuilding = repairable.FindRepairBuilding(a); } else { var repairableNear = a.TraitOrDefault <RepairableNearCA>(); if (repairableNear != null) { orderId = "RepairNear"; repairBuilding = repairableNear.FindRepairBuilding(a); } } if (repairBuilding != null) { owner.Bot.QueueOrder(new Order(orderId, a, Target.FromActor(repairBuilding), false)); alreadysend = true; continue; } else if (!resupplyonly) { owner.Bot.QueueOrder(new Order("Move", a, Target.FromCell(owner.World, RandomBuildingLocation(owner)), false)); } } else if (!resupplyonly) { owner.Bot.QueueOrder(new Order("Move", a, Target.FromCell(owner.World, RandomBuildingLocation(owner)), false)); } } }
public void Tick(SquadCA owner) { if (!owner.IsValid) { return; } // Check if we have an enemy naval yard this tick var first = owner.Units.First(); var domainIndex = first.World.WorldActor.Trait <DomainIndex>(); var locomotorInfo = first.Info.TraitInfo <MobileInfo>().LocomotorInfo; var navalProductions = owner.World.ActorsHavingTrait <Building>().Where(a => owner.SquadManager.Info.NavalProductionTypes.Contains(a.Info.Name) && domainIndex.IsPassable(first.Location, a.Location, locomotorInfo) && a.AppearsHostileTo(first)); // if target is dead or if we have a newly built naval yard this tick invalidate the current target and select a new one. if (!owner.IsTargetValid || (navalProductions.Any() && !hadNavalYard)) { var closestEnemy = FindClosestEnemy(owner); if (closestEnemy != null) { owner.TargetActor = closestEnemy; } else { owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsFleeState(), true); hadNavalYard = false; return; } } // Save whether we had an Enemy naval yard this tick. hadNavalYard = navalProductions.Any(); foreach (var a in owner.Units) { owner.Bot.QueueOrder(new Order("AttackMove", a, Target.FromCell(owner.World, owner.TargetActor.Location), false)); } if (ShouldFlee(owner)) { owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsFleeState(), true); hadNavalYard = false; } }
public void Tick(SquadCA owner) { if (!owner.IsValid) { return; } if (!owner.IsTargetValid) { var closestEnemy = FindClosestEnemy(owner); if (closestEnemy == null) { return; } owner.TargetActor = closestEnemy; } var enemyUnits = owner.World.FindActorsInCircle(owner.TargetActor.CenterPosition, WDist.FromCells(owner.SquadManager.Info.IdleScanRadius)) .Where(owner.SquadManager.IsEnemyUnit).ToList(); if (enemyUnits.Count == 0) { Retreat(owner, false, true, true); return; } if (AttackOrFleeFuzzyCA.Default.CanAttack(owner.Units, enemyUnits)) { foreach (var u in owner.Units) { owner.Bot.QueueOrder(new Order("AttackMove", u, Target.FromCell(owner.World, owner.TargetActor.Location), false)); } // We have gathered sufficient units. Attack the nearest enemy unit. owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsAttackMoveState(), true); } else { owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsFleeState(), true); } }
public void Tick(SquadCA owner) { if (!owner.IsValid) { return; } if (!owner.IsTargetValid) { var closestEnemy = FindClosestEnemy(owner); if (closestEnemy == null) { return; } owner.TargetActor = closestEnemy; } var enemyUnits = owner.World.FindActorsInCircle(owner.TargetActor.CenterPosition, WDist.FromCells(owner.SquadManager.Info.IdleScanRadius)) .Where(owner.SquadManager.IsEnemyUnit).ToList(); if (enemyUnits.Count == 0) { Retreat(owner, false, true, true); return; } if (AttackOrFleeFuzzyCA.Default.CanAttack(owner.Units, enemyUnits)) { // We have gathered sufficient units. Attack the nearest enemy unit. // Inform human allies about AI's rush attack. owner.Bot.QueueOrder(new Order("PlaceBeacon", owner.SquadManager.Player.PlayerActor, Target.FromCell(owner.World, owner.TargetActor.Location), false) { SuppressVisualFeedback = true }); owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsAttackMoveState(), true); } else { owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsFleeState(), true); } }
protected static bool NearToPosSafely(SquadCA owner, WPos loc, out Actor detectedEnemyTarget) { detectedEnemyTarget = null; var dangerRadius = owner.SquadManager.Info.DangerScanRadius; var unitsAroundPos = owner.World.FindActorsInCircle(loc, WDist.FromCells(dangerRadius)) .Where(owner.SquadManager.IsPreferredEnemyUnit).ToList(); if (!unitsAroundPos.Any()) { return(true); } if (CountAntiAirUnits(unitsAroundPos) < owner.Units.Count) { detectedEnemyTarget = unitsAroundPos.Random(owner.Random); return(true); } return(false); }
public void Tick(SquadCA owner) { if (!owner.IsValid) { return; } if (!owner.IsTargetValid) { owner.TargetActor = owner.SquadManager.FindClosestEnemy(owner.CenterPosition, WDist.FromCells(owner.SquadManager.Info.ProtectionScanRadius)); if (owner.TargetActor == null) { Retreat(owner, false, true, true); return; } } owner.FuzzyStateMachine.ChangeState(owner, new UnitsForProtectionAttackState(), true); }
public void Tick(SquadCA owner) { if (!owner.IsValid) { return; } if (!owner.IsTargetValid) { var closestEnemy = FindClosestEnemy(owner); if (closestEnemy == null) { return; } owner.TargetActor = closestEnemy; } var enemyUnits = owner.World.FindActorsInCircle(owner.TargetActor.CenterPosition, WDist.FromCells(owner.SquadManager.Info.IdleScanRadius)) .Where(owner.SquadManager.IsPreferredEnemyUnit).ToList(); if (enemyUnits.Count == 0) { Retreat(owner, flee: false, rearm: true, repair: true); return; } if (AttackOrFleeFuzzyCA.Default.CanAttack(owner.Units, enemyUnits)) { // We have gathered sufficient units. Attack the nearest enemy unit. owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsAttackMoveState(), false); } else { owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsFleeStateCA(), false); } }
public void Tick(SquadCA owner) { if (!owner.IsValid) { return; } if (ShouldFlee(owner)) { owner.FuzzyStateMachine.ChangeState(owner, new AirFleeStateCA(), false); return; } var e = FindDefenselessTarget(owner); if (e == null) { Retreat(owner, flee: false, rearm: true, repair: true); return; } owner.TargetActor = e; owner.FuzzyStateMachine.ChangeState(owner, new AirAttackStateCA(), false); }
public void Deactivate(SquadCA owner) { }
public void Activate(SquadCA owner) { }
public void Tick(SquadCA owner) { if (!owner.IsValid) { return; } if (!owner.IsTargetValid) { var a = owner.Units.Random(owner.Random); var closestEnemy = owner.SquadManager.FindClosestEnemy(a.CenterPosition); if (closestEnemy != null) { owner.TargetActor = closestEnemy; } else { owner.FuzzyStateMachine.ChangeState(owner, new AirFleeStateCA(), false); return; } } var leader = owner.Units.ClosestTo(owner.TargetActor.CenterPosition); var unitsAroundPos = owner.World.FindActorsInCircle(leader.CenterPosition, WDist.FromCells(owner.SquadManager.Info.DangerScanRadius)) .Where(a => owner.SquadManager.IsPreferredEnemyUnit(a) && owner.SquadManager.IsNotHiddenUnit(a)); // Check if get ambushed. if (CountAntiAirUnits(unitsAroundPos) > owner.Units.Count) { owner.FuzzyStateMachine.ChangeState(owner, new AirFleeStateCA(), false); return; } var cannotRetaliate = true; List <Actor> resupplyingUnits = new List <Actor>(); List <Actor> backingoffUnits = new List <Actor>(); List <Actor> attackingUnits = new List <Actor>(); foreach (var a in owner.Units) { if (BusyAttack(a)) { cannotRetaliate = false; continue; } var ammoPools = a.TraitsImplementing <AmmoPool>().ToArray(); if (!ReloadsAutomatically(ammoPools, a.TraitOrDefault <Rearmable>())) { if (IsRearming(a)) { continue; } if (!HasAmmo(ammoPools)) { resupplyingUnits.Add(a); continue; } } if (CanAttackTarget(a, owner.TargetActor)) { cannotRetaliate = false; attackingUnits.Add(a); } else { if (!FullAmmo(ammoPools)) { resupplyingUnits.Add(a); continue; } backingoffUnits.Add(a); } } if (cannotRetaliate) { owner.FuzzyStateMachine.ChangeState(owner, new AirFleeStateCA(), false); return; } owner.Bot.QueueOrder(new Order("ReturnToBase", null, false, groupedActors: resupplyingUnits.ToArray())); owner.Bot.QueueOrder(new Order("Attack", null, Target.FromActor(owner.TargetActor), false, groupedActors: attackingUnits.ToArray())); owner.Bot.QueueOrder(new Order("Move", null, Target.FromCell(owner.World, RandomBuildingLocation(owner)), false, groupedActors: backingoffUnits.ToArray())); }
// Checks the number of anti air enemies around units protected virtual bool ShouldFlee(SquadCA owner) { return(ShouldFlee(owner, enemies => CountAntiAirUnits(enemies) > owner.Units.Count)); }
public static bool NearToPosSafely(SquadCA owner, WPos loc) { return(NearToPosSafely(owner, loc, out _)); }