void TryToRushAttack(IBot bot) { var allEnemyBaseBuilder = AIUtils.FindEnemiesByCommonName(Info.ConstructionYardTypes, Player); // TODO: This should use common names & ExcludeFromSquads instead of hardcoding TraitInfo checks var ownUnits = activeUnits .Where(unit => unit.IsIdle && unit.Info.HasTraitInfo <AttackBaseInfo>() && !unit.Info.HasTraitInfo <AircraftInfo>() && !unit.Info.HasTraitInfo <HarvesterInfo>()).ToList(); if (!allEnemyBaseBuilder.Any() || ownUnits.Count < Info.SquadSize) { return; } foreach (var b in allEnemyBaseBuilder) { // Don't rush enemy aircraft! var enemies = World.FindActorsInCircle(b.CenterPosition, WDist.FromCells(Info.RushAttackScanRadius)) .Where(unit => IsEnemyUnit(unit) && unit.Info.HasTraitInfo <AttackBaseInfo>() && !unit.Info.HasTraitInfo <AircraftInfo>()).ToList(); if (AttackOrFleeFuzzy.Rush.CanAttack(ownUnits, enemies)) { var target = enemies.Any() ? enemies.Random(World.LocalRandom) : b; var rush = GetSquadOfType(SquadType.Rush); if (rush == null) { rush = RegisterNewSquad(bot, SquadType.Rush, target); } foreach (var a3 in ownUnits) { rush.Units.Add(a3); } return; } } }
/// <summary>Detail scans an area, evaluating positions.</summary> CPos?FindFineAttackLocationToSupportPower(SupportPowerInstance readyPower, CPos checkPos, int extendedRange = 1) { CPos?bestLocation = null; var bestAttractiveness = 0; var powerDecision = powerDecisions[readyPower.Info.OrderName]; if (powerDecision == null) { AIUtils.BotDebug("Bot Bug: FindAttackLocationToSupportPower, couldn't find powerDecision for {0}", readyPower.Info.OrderName); return(null); } var checkRadius = powerDecision.CoarseScanRadius; var fineCheck = powerDecision.FineScanRadius; for (var i = 0 - extendedRange; i <= (checkRadius + extendedRange); i += fineCheck) { var x = checkPos.X + i; for (var j = 0 - extendedRange; j <= (checkRadius + extendedRange); j += fineCheck) { var y = checkPos.Y + j; var pos = world.Map.CenterOfCell(new CPos(x, y)); var consideredAttractiveness = 0; consideredAttractiveness += powerDecision.GetAttractiveness(pos, player); if (consideredAttractiveness <= bestAttractiveness || consideredAttractiveness < powerDecision.MinimumAttractiveness) { continue; } bestAttractiveness = consideredAttractiveness; bestLocation = new CPos(x, y); } } return(bestLocation); }
protected void OrderHarvesters(IBot bot) { var toRemove = harvesters.Keys.Where(unitCannotBeOrdered).ToList(); foreach (var a in toRemove) { harvesters.Remove(a); } // Find new harvesters // TODO: Look for a more performance-friendly way to update this list var newHarvesters = world.ActorsHavingTrait <Harvester>().Where(a => a.Owner == player && !harvesters.ContainsKey(a)); foreach (var a in newHarvesters) { harvesters[a] = new HarvesterTraitWrapper(a); } // Find idle harvesters and give them orders: foreach (var h in harvesters) { if (!h.Key.IsIdle) { var act = h.Key.CurrentActivity as FindAndDeliverResources; // Ignore this actor if FindAndDeliverResources is working fine or it is performing a different activity if (act == null || !act.LastSearchFailed) { continue; } } // Tell the idle harvester to quit slacking: var newSafeResourcePatch = FindNextResource(h.Key, h.Value); AIUtils.BotDebug("AI: Harvester {0} is idle. Ordering to {1} in search for new resources.".F(h.Key, newSafeResourcePatch)); bot.QueueOrder(new Order("Harvest", h.Key, newSafeResourcePatch, false)); } }
public override void OnEnter() { controller.ClearVelocity(); controller.ActiveGravity = false; intervalTimer = Interval; currentCount = 0; target = Owner.GetComponent <NodeCanvas.Framework.Blackboard> ().GetValue <Transform> ("Target"); var hit = Physics2D.Raycast(target.position, Vector2.down, float.MaxValue, LayerMask.GetMask("Platform", "OneWayPlatform", "Wall")); if (hit.collider != null) { heightY = hit.collider.bounds.max.y + height; rangeLeft = target.position.x - range * 0.5f; rangeRight = target.position.x + range * 0.5f; AIUtils.ClampPlatformEdgePoint(hit.collider, ref rangeLeft, ref rangeRight); } else { Debug.LogError($"Action_Boss08_Atk3_Teleport没找到平台"); } }
void IBotTick.BotTick(IBot bot) { var colonies = AIUtils.GetActorsWithTrait <Colony>(world).Where(c => c.Owner == player).ToArray(); foreach (var colony in colonies) { var health = colony.Trait <IHealth>(); if (health.DamageState == Info.DamageState) { var queue = colony.TraitOrDefault <ProductionQueue>(); // Turrets don't produce if (queue == null) { continue; } foreach (var current in queue.AllQueued()) { bot.QueueOrder(Order.CancelProduction(queue.Actor, current.Item, 1)); AIUtils.BotDebug("{0}: Stopping production of {1} at {2} to heal.".F(player.PlayerName, current.Item, colony)); } } } }
private void Crossover(List <double> mum, List <double> dad, ref List <double> baby1, ref List <double> baby2) { if ((AIUtils.Random(1.0) > _crossoverRate) || (mum == dad)) { baby1 = mum; baby2 = dad; return; } int cp = AIUtils.RandomInt(0, _chromosomeLength - 1); for (int i = 0; i < cp; ++i) { baby1.Add(mum[i]); baby2.Add(dad[i]); } for (int i = cp; i < mum.Count; ++i) { baby1.Add(dad[i]); baby2.Add(mum[i]); } }
Target FindNextResource(Actor actor, MinerTraitWrapper miner) { var towerInfo = AIUtils.GetInfoByCommonName(Info.DeployedActorTypes, player); var buildingInfo = towerInfo.TraitInfo <BuildingInfo>(); Func <CPos, bool> isValidResource = cell => domainIndex.IsPassable(actor.Location, cell, miner.Locomotor.Info) && Info.DeployableTerrainTypes.Contains(world.Map.GetTerrainInfo(cell).Type) && miner.Locomotor.CanStayInCell(cell) && world.CanPlaceBuilding(cell + miner.Transforms.Info.Offset, towerInfo, buildingInfo, actor); var path = pathfinder.FindPath( PathSearch.Search(world, miner.Locomotor, actor, BlockedByActor.Stationary, isValidResource) .WithCustomCost(loc => world.FindActorsInCircle(world.Map.CenterOfCell(loc), Info.EnemyAvoidanceRadius) .Where(u => !u.IsDead && actor.Owner.Stances[u.Owner] == Stance.Enemy) .Sum(u => Math.Max(WDist.Zero.Length, Info.EnemyAvoidanceRadius.Length - (world.Map.CenterOfCell(loc) - u.CenterPosition).Length))) .FromPoint(actor.Location)); if (path.Count == 0) { return(Target.Invalid); } return(Target.FromCell(world, path[0])); }
void IBotTick.BotTick(IBot bot) { foreach (var sp in supportPowerManager.Powers.Values) { if (sp.Disabled) { continue; } // Add power to dictionary if not in delay dictionary yet if (!waitingPowers.ContainsKey(sp)) { waitingPowers.Add(sp, 0); } if (waitingPowers[sp] > 0) { waitingPowers[sp]--; } // If we have recently tried and failed to find a use location for a power, then do not try again until later var isDelayed = waitingPowers[sp] > 0; if (sp.Ready && !isDelayed && powerDecisions.ContainsKey(sp.Info.OrderName)) { var powerDecision = powerDecisions[sp.Info.OrderName]; if (powerDecision == null) { AIUtils.BotDebug("Bot Bug: FindAttackLocationToSupportPower, couldn't find powerDecision for {0}", sp.Info.OrderName); continue; } if (sp.Info.Cost != 0 && playerResource.Cash + playerResource.Resources < sp.Info.Cost) { AIUtils.BotDebug("AI: {1} can't afford the activation of support power {0}. Delaying rescan.", sp.Info.OrderName, player.PlayerName); waitingPowers[sp] += powerDecision.GetNextScanTime(world); continue; } var attackLocation = FindAttackLocationToSupportPower(sp); if (attackLocation == null) { AIUtils.BotDebug("AI: {1} can't find suitable attack location for support power {0}. Delaying rescan.", sp.Info.OrderName, player.PlayerName); waitingPowers[sp] += powerDecision.GetNextScanTime(world); continue; } // Valid target found, delay by a few ticks to avoid rescanning before power fires via order AIUtils.BotDebug("AI: {2} found new target location {0} for support power {1}.", attackLocation, sp.Info.OrderName, player.PlayerName); waitingPowers[sp] += 10; bot.QueueOrder(new Order(sp.Key, supportPowerManager.Self, Target.FromCell(world, attackLocation.Value), false) { SuppressVisualFeedback = true }); } } // Remove stale powers stalePowers.AddRange(waitingPowers.Keys.Where(wp => !supportPowerManager.Powers.ContainsKey(wp.Key))); foreach (var p in stalePowers) { waitingPowers.Remove(p); } stalePowers.Clear(); }
private bool IsAttackable(Character target) { bool targetTooFar = Vector2.Distance(target.iso.pos, character.iso.pos) > maxAgroDistance; return(!targetTooFar && AIUtils.IsAttackable(character, target)); }
protected override void OnTick() { //持球进攻 if (m_player.m_bWithBall) { PlayerState.State state = m_player.m_StateMachine.m_curState.m_eState; if (m_match.m_ruler.m_bToCheckBall) //带出三分线 { if (state == PlayerState.State.eStand || state == PlayerState.State.eRun || state == PlayerState.State.eRush || state == PlayerState.State.eHold) { bool inTakeOver = false; if (m_player.m_inputDispatcher != null) { inTakeOver = m_player.m_inputDispatcher.inTakeOver; } if (!inTakeOver) { Player passTarget = AIUtils.ChoosePassTargetCheckBall(m_player, m_match); if (passTarget != null) { AI_Pass pass = m_system.GetState(Type.ePass) as AI_Pass; pass.m_toPass = passTarget; m_system.SetTransaction(pass); } else { m_system.SetTransaction(AIState.Type.eCheckBall); } } else { m_system.SetTransaction(AIState.Type.eCheckBall); } } } else { if (state == PlayerState.State.eStand || state == PlayerState.State.eRun || state == PlayerState.State.eRush) { Offense(); } else if (state == PlayerState.State.eHold) { if ((m_match.mCurScene.mGround.GetArea(m_player) == Area.eNear || !m_player.IsDefended(m_system.AI.devAngleAttacker, m_system.AI.devDistAOD)) && arriveFirstTarget) { Offense(); } else { m_system.SetTransaction(AIState.Type.eIdle); } } } } else //非持球进攻 { //空切 if (arriveFirstTarget && m_ball.m_owner != null && IM.Random.value < new IM.Number(0, 250) && m_player.IsDefended(m_system.AI.devAngleAttacker, m_system.AI.devDistAOD)) { //if( !(m_match is GameMatch_PVP) ) m_system.SetTransaction(AIState.Type.eCutIn); } //要球 else if (m_ball.m_owner != null && m_ball.m_owner.m_team == m_player.m_team && m_system.m_bNotDefended && !m_match.m_ruler.m_bToCheckBall) { //if( !(m_match is GameMatch_PVP) ) m_system.SetTransaction(AIState.Type.eRequireBall); } //靠近球6米,追球 else if (AIUtils.ShouldTraceBall(m_ball, m_player)) { m_system.SetTransaction(AIState.Type.eIdle); } //挡拆 else { int result = AIUtils.CanPickAndRoll(m_player); if (result == 1 && m_player.m_StateMachine.m_curState.IsCommandValid(Command.PickAndRoll)) { m_system.SetTransaction(AIState.Type.ePickAndRoll); } else if (result == 2) { positionForPR = true; m_system.SetTransaction(AIState.Type.ePositioning, new IM.Number(100), true); } } } }
private Dictionary <EDirection, Vector2Int> GetNeighbours( Vector2Int position, bool onlyAccessible = false) { return(AIUtils.GetNeighbours(position, _map, onlyAccessible)); }
bool TickQueue(IBot bot, ProductionQueue queue) { var currentBuilding = queue.AllQueued().FirstOrDefault(); // Waiting to build something if (currentBuilding == null && failCount < baseBuilder.Info.MaximumFailedPlacementAttempts) { var item = ChooseBuildingToBuild(queue); if (item == null) { return(false); } bot.QueueOrder(Order.StartProduction(queue.Actor, item.Name, 1)); } else if (currentBuilding != null && currentBuilding.Done) { // Production is complete // Choose the placement logic // HACK: HACK HACK HACK // TODO: Derive this from BuildingCommonNames instead var type = BuildingType.Building; CPos? location = null; string orderString = "PlaceBuilding"; // Check if Building is a plug for other Building var actorInfo = world.Map.Rules.Actors[currentBuilding.Item]; var plugInfo = actorInfo.TraitInfoOrDefault <PlugInfo>(); if (plugInfo != null) { var possibleBuilding = world.ActorsWithTrait <Pluggable>().FirstOrDefault(a => a.Actor.Owner == player && a.Trait.AcceptsPlug(a.Actor, plugInfo.Type)); if (possibleBuilding.Actor != null) { orderString = "PlacePlug"; location = possibleBuilding.Actor.Location + possibleBuilding.Trait.Info.Offset; } } else { // Check if Building is a defense and if we should place it towards the enemy or not. if (actorInfo.HasTraitInfo <AttackBaseInfo>() && world.LocalRandom.Next(100) < baseBuilder.Info.PlaceDefenseTowardsEnemyChance) { type = BuildingType.Defense; } else if (baseBuilder.Info.RefineryTypes.Contains(actorInfo.Name)) { type = BuildingType.Refinery; } location = ChooseBuildLocation(currentBuilding.Item, true, type); } if (location == null) { AIUtils.BotDebug("{0} has nowhere to place {1}".F(player, currentBuilding.Item)); bot.QueueOrder(Order.CancelProduction(queue.Actor, currentBuilding.Item, 1)); failCount += failCount; // If we just reached the maximum fail count, cache the number of current structures if (failCount == baseBuilder.Info.MaximumFailedPlacementAttempts) { cachedBuildings = world.ActorsHavingTrait <Building>().Count(a => a.Owner == player); cachedBases = world.ActorsHavingTrait <BaseProvider>().Count(a => a.Owner == player); } } else { failCount = 0; bot.QueueOrder(new Order(orderString, player.PlayerActor, Target.FromCell(world, location.Value), false) { // Building to place TargetString = currentBuilding.Item, // Actor ID to associate the placement with ExtraData = queue.Actor.ActorID, SuppressVisualFeedback = true }); return(true); } } return(true); }
private bool Checkproximity(float distanceFromStart) { bool detonate = false; if (distanceFromStart < blastRadius) { return(detonate = false); } using (var hitsEnu = Physics.OverlapSphere(transform.position, detonationRange, 557057).AsEnumerable().GetEnumerator()) { while (hitsEnu.MoveNext()) { if (hitsEnu.Current == null) { continue; } Part partHit = hitsEnu.Current.GetComponentInParent <Part>(); if (partHit == null || partHit.vessel == null) { continue; } if (partHit.vessel == vessel || partHit.vessel == sourcevessel) { continue; } if (partHit.vessel.vesselType == VesselType.Debris) { continue; } if (sourcevessel != null && partHit.vessel.vesselName.Contains(sourcevessel.vesselName)) { continue; } var weaponManager = partHit.vessel.FindPartModuleImplementing <MissileFire>(); if (IFF_On && (weaponManager == null || weaponManager.teamString == IFFID)) { continue; } if (detonateAtMinimumDistance) { var distance = Vector3.Distance(partHit.transform.position + partHit.CoMOffset, transform.position); var predictedDistance = Vector3.Distance(AIUtils.PredictPosition(partHit.transform.position + partHit.CoMOffset, partHit.vessel.Velocity(), partHit.vessel.acceleration, Time.fixedDeltaTime), AIUtils.PredictPosition(transform.position, vessel.Velocity(), vessel.acceleration, Time.fixedDeltaTime)); if (distance > predictedDistance && distance > Time.fixedDeltaTime * (float)vessel.srfSpeed) // If we're closing and not going to hit within the next update, then wait. { return(detonate = false); } } if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BDArmory.BDExplosivePart]: Proxifuze triggered by " + partHit.partName + " from " + partHit.vessel.vesselName); } return(detonate = true); } } return(detonate); }
void IBotTick.BotTick(IBot bot) { if (--scanForBitsTicks > 0) { return; } scanForBitsTicks = Info.ScanInterval; var bits = world.ActorsHavingTrait <ColonyBit>().ToList(); if (!bits.Any()) { return; } if (Info.CheckTargetsForVisibility) { bits.RemoveAll(c => !c.CanBeViewedByPlayer(player)); } var units = world.ActorsHavingTrait <Mobile>().Where(a => a.Owner == player && a.IsIdle && (Info.IncludedUnitTypes.Contains(a.Info.Name) || (!Info.IncludedUnitTypes.Any() && !Info.ExcludedUnitTypes.Contains(a.Info.Name)))).ToList(); if (!units.Any()) { return; } foreach (var bit in bits) { var bitCollector = units.ClosestTo(bit); if (bitCollector == null) { continue; } if ((bit.Location - bitCollector.Location).Length > maxProximity) { continue; } units.Remove(bitCollector); if (squadManagerBotModule == null) { squadManagerBotModule = bot.Player.PlayerActor.TraitsImplementing <SquadManagerBotModule>().FirstEnabledTraitOrDefault(); } if (squadManagerBotModule != null) { // You got ONE job! var squad = squadManagerBotModule.Squads.FirstOrDefault(s => s.Units.Contains(bitCollector)); if (squad != null) { squad.Units.Remove(bitCollector); } } var target = Target.FromCell(world, bit.Location); AIUtils.BotDebug("AI: Ordering unit {0} to {1} for colony bit pick up.".F(bitCollector, target)); bot.QueueOrder(new Order("Stop", bitCollector, false)); bot.QueueOrder(new Order("Move", bitCollector, target, false)); } }
void QueueCaptureOrders(IBot bot) { if (player.WinState != WinState.Undefined) { return; } var newUnits = world.ActorsHavingTrait <Captures>() .Where(a => a.Owner == player && !a.IsDead && a.IsInWorld); if (!newUnits.Any()) { return; } var capturers = newUnits .Where(a => a.IsIdle && Info.CapturingActorTypes.Contains(a.Info.Name)) .Select(a => new TraitPair <CaptureManager>(a, a.TraitOrDefault <CaptureManager>())) .Where(tp => tp.Trait != null); if (!capturers.Any()) { return; } var baseCenter = world.Map.CenterOfCell(initialBaseCenter); if (world.LocalRandom.Next(100) < Info.PriorityCaptureChance) { var priorityTargets = world.Actors.Where(a => !a.IsDead && a.IsInWorld && Info.CapturableStances.HasRelationship(player.RelationshipWith(a.Owner)) && Info.PriorityCapturableActorTypes.Contains(a.Info.Name.ToLowerInvariant())); if (Info.CheckCaptureTargetsForVisibility) { priorityTargets = priorityTargets.Where(a => a.CanBeViewedByPlayer(player)); } if (priorityTargets.Any()) { priorityTargets = priorityTargets.OrderBy(a => (a.CenterPosition - baseCenter).LengthSquared); var priorityCaptures = Math.Min(capturers.Count(), priorityTargets.Count()); for (int i = 0; i < priorityCaptures; i++) { var capturer = capturers.First(); var priorityTarget = priorityTargets.First(); var captureManager = priorityTarget.TraitOrDefault <CaptureManager>(); if (captureManager != null && captureManager.CanBeTargetedBy(priorityTarget, capturer.Actor, capturer.Trait)) { var safeTarget = SafePath(capturer.Actor, priorityTarget); if (safeTarget.Type == TargetType.Invalid) { priorityTargets = priorityTargets.Skip(1); capturers = capturers.Skip(1); continue; } bot.QueueOrder(new Order("CaptureActor", capturer.Actor, safeTarget, true)); AIUtils.BotDebug("AI ({0}): Ordered {1} {2} to capture {3} {4} in priority mode.", player.ClientIndex, capturer.Actor, capturer.Actor.ActorID, priorityTarget, priorityTarget.ActorID); } priorityTargets = priorityTargets.Skip(1); capturers = capturers.Skip(1); } if (!capturers.Any()) { return; } } } var randomPlayer = world.Players.Where(p => !p.Spectating && Info.CapturableStances.HasRelationship(player.RelationshipWith(p))).Random(world.LocalRandom); var targetOptions = Info.CheckCaptureTargetsForVisibility ? GetVisibleActorsBelongingToPlayer(randomPlayer) : GetActorsThatCanBeOrderedByPlayer(randomPlayer); var capturableTargetOptions = targetOptions .Where(target => { var captureManager = target.TraitOrDefault <CaptureManager>(); if (captureManager == null) { return(false); } return(capturers.Any(tp => captureManager.CanBeTargetedBy(target, tp.Actor, tp.Trait))); }) .OrderBy(target => (target.CenterPosition - baseCenter).LengthSquared) .Take(maximumCaptureTargetOptions); if (Info.CapturableActorTypes.Any()) { capturableTargetOptions = capturableTargetOptions.Where(target => Info.CapturableActorTypes.Contains(target.Info.Name.ToLowerInvariant())); } if (!capturableTargetOptions.Any()) { return; } foreach (var capturer in capturers) { var nearestTargetActors = capturableTargetOptions.OrderBy(target => (target.CenterPosition - capturer.Actor.CenterPosition).LengthSquared); foreach (var nearestTargetActor in nearestTargetActors) { var safeTarget = SafePath(capturer.Actor, nearestTargetActor); if (safeTarget.Type == TargetType.Invalid) { continue; } bot.QueueOrder(new Order("CaptureActor", capturer.Actor, safeTarget, true)); AIUtils.BotDebug("AI: Ordered {0} to capture {1}", capturer.Actor, nearestTargetActor); break; } } }
public static Player ChoosePassTargetCheckBall(Player player, GameMatch match) { List <Player> teammates = new List <Player>(player.m_team.members); teammates.Remove(player); if (teammates.Count == 0) { return(null); } // Teammates in far area. List <Player> matesFar = new List <Player>(); foreach (Player mate in teammates) { if (match.mCurScene.mGround.GetArea(mate) == Area.eFar) { matesFar.Add(mate); } } if (matesFar.Count > 0) // There are teammates in far area, pass to most prior mate. { return(AIUtils.GetMostPriorPassTarget(matesFar, player)); } else // No teammates in far area. { // Teammates in PG or SG's favor area. List <Player> matesOutSide = new List <Player>(); RoadPathManager.SectorArea areaPG = Player.positionFavorSectors[PositionType.PT_PG]; RoadPathManager.SectorArea areaSG = Player.positionFavorSectors[PositionType.PT_SG]; foreach (Player mate in player.m_team.members) { if (RoadPathManager.Instance.InSectors(areaPG, mate.position) || RoadPathManager.Instance.InSectors(areaSG, mate.position)) { matesOutSide.Add(mate); } } if (matesOutSide.Count > 0) // There are teammates in PG or SG's favor area, pass to most prior mate if its not myself. { Player passTarget = AIUtils.GetMostPriorPassTarget(matesOutSide, player); return(passTarget == player ? null : passTarget); } else // No teammates in PG or SG's favor area. { List <Player> teammatesPGSG = new List <Player>(); foreach (Player mate in teammates) { if (mate.m_matchPosition == PositionType.PT_PG || mate.m_matchPosition == PositionType.PT_SG) { teammatesPGSG.Add(mate); } } if (player.m_matchPosition == PositionType.PT_C) { if (teammatesPGSG.Count > 0) // Pass to most prior PG or SG. { return(AIUtils.GetMostPriorPassTarget(teammatesPGSG, player)); } else // Pass to nearest non-C teammate. { Player nearestNonC = null; IM.Number minDist = IM.Number.max; foreach (Player mate in teammates) { if (mate.m_matchPosition != PositionType.PT_C) { IM.Number curDist = GameUtils.HorizonalDistance(mate.position, player.position); if (curDist < minDist) { minDist = curDist; nearestNonC = mate; } } } return(nearestNonC); } } else if (player.m_matchPosition == PositionType.PT_PF) { if (teammatesPGSG.Count > 0) // Pass to most prior PG or SG. { return(AIUtils.GetMostPriorPassTarget(teammatesPGSG, player)); } else // Pass to nearest non-PF teammate. { Player nearestNonPF = null; IM.Number minDist = IM.Number.max; foreach (Player mate in teammates) { if (mate.m_matchPosition != PositionType.PT_PF) { IM.Number curDist = GameUtils.HorizonalDistance(mate.position, player.position); if (curDist < minDist) { minDist = curDist; nearestNonPF = mate; } } } return(nearestNonPF); } } else { return(null); } } } }
void IBotTick.BotTick(IBot bot) { if (resourceLayer == null || resourceLayer.IsResourceLayerEmpty) { return; } if (--scanForIdleMinersTicks > 0) { return; } scanForIdleMinersTicks = Info.MinimumScanDelay; var toRemove = miners.Keys.Where(unitCannotBeOrdered).ToList(); foreach (var a in toRemove) { miners.Remove(a); } // TODO: Look for a more performance friendly way to update this list var newMiners = world.Actors.Where(a => Info.DeployableActorTypes.Contains(a.Info.Name) && a.Owner == player && !miners.ContainsKey(a)); foreach (var a in newMiners) { miners[a] = new MinerTraitWrapper(a); } foreach (var miner in miners) { if (!miner.Key.IsIdle) { continue; } if (Info.DeployableTerrainTypes.Contains(world.Map.GetTerrainInfo(miner.Key.Location).Type)) { bot.QueueOrder(new Order("DeployTransform", miner.Key, true)); continue; } // Tell the idle miner to quit slacking: var newSafeResourcePatch = FindNextResource(miner.Key, miner.Value); if (newSafeResourcePatch.Type == TargetType.Invalid) { scanForIdleMinersTicks = Info.LastSearchFailedDelay; return; } AIUtils.BotDebug("AI: Miner {0} is idle. Ordering to {1} in search for new resources.".F(miner.Key, newSafeResourcePatch)); bot.QueueOrder(new Order("Move", miner.Key, newSafeResourcePatch, true)); } // Keep the economy running before starving out. var unitBuilder = requestUnitProduction.FirstOrDefault(Exts.IsTraitEnabled); if (unitBuilder != null) { var minerInfo = AIUtils.GetInfoByCommonName(Info.DeployableActorTypes, player); var miningTowers = AIUtils.CountBuildingByCommonName(Info.DeployedActorTypes, player); if (miningTowers < Info.MinimumDeployedActors && unitBuilder.RequestedProductionCount(bot, minerInfo.Name) == 0) { unitBuilder.RequestUnitProduction(bot, minerInfo.Name); } } }
// Line indices are "lower towards enemy" - 0th = skirmishers, first = hastatus, third = triarius, highest = reserves public EncounterPosition PositionFor(FormationFacing facing, int line, int interval = 15) { // The facing has to be swapped in order for the position to work - an army FACING north should be DEPLOYED south return(AIUtils.RotateAndProject(LaneCenter, 0, -interval * line - interval / 5, facing.Opposite())); }
void IBotTick.BotTick(IBot bot) { if (cubeSpawner == null || !cubeSpawner.IsTraitEnabled() || !cubeSpawner.Enabled) { return; } if (--scanForcubesTicks > 0) { return; } scanForcubesTicks = Info.ScanForCubesInterval; var cubes = world.ActorsHavingTrait <Crate>().ToList(); if (!cubes.Any()) { return; } if (Info.CheckTargetsForVisibility) { cubes.RemoveAll(c => !c.CanBeViewedByPlayer(player)); } var idleUnits = world.ActorsHavingTrait <Mobile>().Where(a => a.Owner == player && a.IsIdle && (Info.IncludedUnitTypes.Contains(a.Info.Name) || (!Info.IncludedUnitTypes.Any() && !Info.ExcludedUnitTypes.Contains(a.Info.Name)))).ToList(); if (!idleUnits.Any()) { return; } foreach (var cube in cubes) { if (alreadyPursuitcubes.Contains(cube)) { continue; } if (!cube.IsAtGroundLevel()) { continue; } var cubeCollector = idleUnits.ClosestTo(cube); if (cubeCollector == null) { continue; } if ((cube.Location - cubeCollector.Location).Length > maxProximity) { continue; } idleUnits.Remove(cubeCollector); var target = PathToNextcube(cubeCollector, cube); if (target.Type == TargetType.Invalid) { continue; } var cell = world.Map.CellContaining(target.CenterPosition); AIUtils.BotDebug("AI: Ordering {0} to {1} for cube pick up.".F(cubeCollector, cell)); bot.QueueOrder(new Order("Move", cubeCollector, target, true)); alreadyPursuitcubes.Add(cube); } }
void AttitudeControl(FlightCtrlState s) { const float terrainOffset = 5; Vector3 yawTarget = Vector3.ProjectOnPlane(targetDirection, vesselTransform.forward); // limit "aoa" if we're moving float driftMult = 1; if (vessel.horizontalSrfSpeed * 10 > CruiseSpeed) { driftMult = Mathf.Max(Vector3.Angle(vessel.srf_velocity, yawTarget) / MaxDrift, 1); yawTarget = Vector3.RotateTowards(vessel.srf_velocity, yawTarget, MaxDrift * Mathf.Deg2Rad, 0); } float yawError = VectorUtils.SignedAngle(vesselTransform.up, yawTarget, vesselTransform.right) + (aimingMode ? 0 : weaveAdjustment); DebugLine($"yaw target: {yawTarget}, yaw error: {yawError}"); DebugLine($"drift multiplier: {driftMult}"); Vector3 baseForward = vessel.transform.up * terrainOffset; float basePitch = Mathf.Atan2( AIUtils.GetTerrainAltitude(vessel.CoM + baseForward, vessel.mainBody, false) - AIUtils.GetTerrainAltitude(vessel.CoM - baseForward, vessel.mainBody, false), terrainOffset * 2) * Mathf.Rad2Deg; float pitchAngle = basePitch + TargetPitch * Mathf.Clamp01((float)vessel.horizontalSrfSpeed / CruiseSpeed); if (aimingMode) { pitchAngle = VectorUtils.SignedAngle(vesselTransform.up, Vector3.ProjectOnPlane(targetDirection, vesselTransform.right), -vesselTransform.forward); } DebugLine($"terrain fw slope: {basePitch}, target pitch: {pitchAngle}"); float pitch = 90 - Vector3.Angle(vesselTransform.up, upDir); float pitchError = pitchAngle - pitch; Vector3 baseLateral = vessel.transform.right * terrainOffset; float baseRoll = Mathf.Atan2( AIUtils.GetTerrainAltitude(vessel.CoM + baseLateral, vessel.mainBody, false) - AIUtils.GetTerrainAltitude(vessel.CoM - baseLateral, vessel.mainBody, false), terrainOffset * 2) * Mathf.Rad2Deg; float drift = VectorUtils.SignedAngle(vesselTransform.up, Vector3.ProjectOnPlane(vessel.GetSrfVelocity(), upDir), vesselTransform.right); float bank = VectorUtils.SignedAngle(-vesselTransform.forward, upDir, -vesselTransform.right); float targetRoll = baseRoll + BankAngle * Mathf.Clamp01(drift / MaxDrift) * Mathf.Clamp01((float)vessel.srfSpeed / CruiseSpeed); float rollError = targetRoll - bank; DebugLine($"terrain sideways slope: {baseRoll}, target roll: {targetRoll}"); Vector3 localAngVel = vessel.angularVelocity; s.roll = steerMult * 0.006f * rollError - 0.4f * steerDamping * -localAngVel.y; s.pitch = ((aimingMode ? 0.02f : 0.015f) * steerMult * pitchError) - (steerDamping * -localAngVel.x); s.yaw = (((aimingMode ? 0.007f : 0.005f) * steerMult * yawError) - (steerDamping * 0.2f * -localAngVel.z)) * driftMult; s.wheelSteer = -(((aimingMode? 0.005f : 0.003f) * steerMult * yawError) - (steerDamping * 0.1f * -localAngVel.z)); if (ManeuverRCS && (Mathf.Abs(s.roll) >= 1 || Mathf.Abs(s.pitch) >= 1 || Mathf.Abs(s.yaw) >= 1)) { vessel.ActionGroups.SetGroup(KSPActionGroup.RCS, true); } }
bool TickQueue(IBot bot, ProductionQueue queue) { var currentBuilding = queue.AllQueued().FirstOrDefault(); // Waiting to build something if (currentBuilding == null && failCount < baseBuilder.Info.MaximumFailedPlacementAttempts) { var item = ChooseBuildingToBuild(queue); if (item == null) { return(false); } bot.QueueOrder(Order.StartProduction(queue.Actor, item.Name, 1)); } else if (currentBuilding != null && currentBuilding.Done) { // Production is complete // Choose the placement logic // HACK: HACK HACK HACK // TODO: Derive this from BuildingCommonNames instead var type = BuildingType.Building; if (world.Map.Rules.Actors[currentBuilding.Item].HasTraitInfo <AttackBaseInfo>()) { type = BuildingType.Defense; } else if (baseBuilder.Info.RefineryTypes.Contains(world.Map.Rules.Actors[currentBuilding.Item].Name)) { type = BuildingType.Refinery; } var location = ChooseBuildLocation(currentBuilding.Item, true, type); if (location == null) { AIUtils.BotDebug("AI: {0} has nowhere to place {1}".F(player, currentBuilding.Item)); bot.QueueOrder(Order.CancelProduction(queue.Actor, currentBuilding.Item, 1)); failCount += failCount; // If we just reached the maximum fail count, cache the number of current structures if (failCount == baseBuilder.Info.MaximumFailedPlacementAttempts) { cachedBuildings = world.ActorsHavingTrait <Building>().Count(a => a.Owner == player); cachedBases = world.ActorsHavingTrait <BaseProvider>().Count(a => a.Owner == player); } } else { failCount = 0; bot.QueueOrder(new Order("PlaceBuilding", player.PlayerActor, Target.FromCell(world, location.Value), false) { // Building to place TargetString = currentBuilding.Item, // Actor ID to associate the placement with ExtraData = queue.Actor.ActorID, SuppressVisualFeedback = true }); return(true); } } return(true); }
public void Tick(IBot bot) { // If failed to place something N consecutive times, wait M ticks until resuming building production if (failCount >= baseBuilder.Info.MaximumFailedPlacementAttempts && --failRetryTicks <= 0) { var currentBuildings = world.ActorsHavingTrait <Building>().Count(a => a.Owner == player); var baseProviders = world.ActorsHavingTrait <BaseProvider>().Count(a => a.Owner == player); // Only bother resetting failCount if either a) the number of buildings has decreased since last failure M ticks ago, // or b) number of BaseProviders (construction yard or similar) has increased since then. // Otherwise reset failRetryTicks instead to wait again. if (currentBuildings < cachedBuildings || baseProviders > cachedBases) { failCount = 0; } else { failRetryTicks = baseBuilder.Info.StructureProductionResumeDelay; } } if (waterState == WaterCheck.NotChecked) { if (AIUtils.IsAreaAvailable <BaseProvider>(world, player, world.Map, baseBuilder.Info.MaxBaseRadius, baseBuilder.Info.WaterTerrainTypes)) { waterState = WaterCheck.EnoughWater; } else { waterState = WaterCheck.NotEnoughWater; checkForBasesTicks = baseBuilder.Info.CheckForNewBasesDelay; } } if (waterState == WaterCheck.NotEnoughWater && --checkForBasesTicks <= 0) { var currentBases = world.ActorsHavingTrait <BaseProvider>().Count(a => a.Owner == player); if (currentBases > cachedBases) { cachedBases = currentBases; waterState = WaterCheck.NotChecked; } } // Only update once per second or so if (--waitTicks > 0) { return; } playerBuildings = world.ActorsHavingTrait <Building>().Where(a => a.Owner == player).ToArray(); var excessPowerBonus = baseBuilder.Info.ExcessPowerIncrement * (playerBuildings.Count() / baseBuilder.Info.ExcessPowerIncreaseThreshold.Clamp(1, int.MaxValue)); minimumExcessPower = (baseBuilder.Info.MinimumExcessPower + excessPowerBonus).Clamp(baseBuilder.Info.MinimumExcessPower, baseBuilder.Info.MaximumExcessPower); var active = false; foreach (var queue in AIUtils.FindQueues(player, category)) { if (TickQueue(bot, queue)) { active = true; } } // Add a random factor so not every AI produces at the same tick early in the game. // Minimum should not be negative as delays in HackyAI could be zero. var randomFactor = world.LocalRandom.Next(0, baseBuilder.Info.StructureProductionRandomBonusDelay); // Needs to be at least 4 * OrderLatency because otherwise the AI frequently duplicates build orders (i.e. makes the same build decision twice) waitTicks = active ? 4 * world.OrderLatency + baseBuilder.Info.StructureProductionActiveDelay + randomFactor : baseBuilder.Info.StructureProductionInactiveDelay + randomFactor; }
private bool IsAttackable(Unit target) { bool targetTooFar = Vector2.Distance(target.iso.pos, _unit.iso.pos) > maxAgroDistance; return(!targetTooFar && AIUtils.IsAttackable(_unit, target)); }
void IBotTick.BotTick(IBot bot) { if (resourceLayer == null || resourceLayer.IsEmpty) { return; } if (--scanForIdleHarvestersTicks > 0) { return; } var toRemove = harvesters.Keys.Where(unitCannotBeOrdered).ToList(); foreach (var a in toRemove) { harvesters.Remove(a); } scanForIdleHarvestersTicks = Info.ScanForIdleHarvestersInterval; // Find new harvesters // TODO: Look for a more performance-friendly way to update this list var newHarvesters = world.ActorsHavingTrait <Harvester>().Where(a => a.Owner == player && !harvesters.ContainsKey(a)); foreach (var a in newHarvesters) { harvesters[a] = new HarvesterTraitWrapper(a); } // Find idle harvesters and give them orders: foreach (var h in harvesters) { if (!h.Key.IsIdle) { // Ignore this actor if FindAndDeliverResources is working fine or it is performing a different activity if (!(h.Key.CurrentActivity is FindAndDeliverResources act) || !act.LastSearchFailed) { continue; } } if (h.Value.Parachutable != null && h.Value.Parachutable.IsInAir) { continue; } // Tell the idle harvester to quit slacking: var newSafeResourcePatch = FindNextResource(h.Key, h.Value); AIUtils.BotDebug("AI: Harvester {0} is idle. Ordering to {1} in search for new resources.".F(h.Key, newSafeResourcePatch)); bot.QueueOrder(new Order("Harvest", h.Key, newSafeResourcePatch, false)); } // Less harvesters than refineries - build a new harvester var unitBuilder = requestUnitProduction.FirstOrDefault(Exts.IsTraitEnabled); if (unitBuilder != null && Info.HarvesterTypes.Any()) { var harvInfo = AIUtils.GetInfoByCommonName(Info.HarvesterTypes, player); var harvCountTooLow = AIUtils.CountActorByCommonName(Info.HarvesterTypes, player) < AIUtils.CountBuildingByCommonName(Info.RefineryTypes, player); if (harvCountTooLow && unitBuilder.RequestedProductionCount(bot, harvInfo.Name) == 0) { unitBuilder.RequestUnitProduction(bot, harvInfo.Name); } } }
ActorInfo ChooseBuildingToBuild(ProductionQueue queue) { var buildableThings = queue.BuildableItems(); // This gets used quite a bit, so let's cache it here var power = GetProducibleBuilding(baseBuilder.Info.PowerTypes, buildableThings, a => a.TraitInfos <PowerInfo>().Where(i => i.EnabledByDefault).Sum(p => p.Amount)); // First priority is to get out of a low power situation if (powerManager != null && powerManager.ExcessPower < minimumExcessPower) { if (power != null && power.TraitInfos <PowerInfo>().Where(i => i.EnabledByDefault).Sum(p => p.Amount) > 0) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (low power)", queue.Actor.Owner, power.Name); return(power); } } // Next is to build up a strong economy if (!baseBuilder.HasAdequateRefineryCount) { var refinery = GetProducibleBuilding(baseBuilder.Info.RefineryTypes, buildableThings); if (refinery != null && HasSufficientPowerForActor(refinery)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (refinery)", queue.Actor.Owner, refinery.Name); return(refinery); } if (power != null && refinery != null && !HasSufficientPowerForActor(refinery)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return(power); } } // Make sure that we can spend as fast as we are earning if (baseBuilder.Info.NewProductionCashThreshold > 0 && playerResources.Resources > baseBuilder.Info.NewProductionCashThreshold) { var production = GetProducibleBuilding(baseBuilder.Info.ProductionTypes, buildableThings); if (production != null && HasSufficientPowerForActor(production)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (production)", queue.Actor.Owner, production.Name); return(production); } if (power != null && production != null && !HasSufficientPowerForActor(production)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return(power); } } // Only consider building this if there is enough water inside the base perimeter and there are close enough adjacent buildings if (waterState == WaterCheck.EnoughWater && baseBuilder.Info.NewProductionCashThreshold > 0 && playerResources.Resources > baseBuilder.Info.NewProductionCashThreshold && AIUtils.IsAreaAvailable <GivesBuildableArea>(world, player, world.Map, baseBuilder.Info.CheckForWaterRadius, baseBuilder.Info.WaterTerrainTypes)) { var navalproduction = GetProducibleBuilding(baseBuilder.Info.NavalProductionTypes, buildableThings); if (navalproduction != null && HasSufficientPowerForActor(navalproduction)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (navalproduction)", queue.Actor.Owner, navalproduction.Name); return(navalproduction); } if (power != null && navalproduction != null && !HasSufficientPowerForActor(navalproduction)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return(power); } } // Create some head room for resource storage if we really need it if (playerResources.Resources > 0.8 * playerResources.ResourceCapacity) { var silo = GetProducibleBuilding(baseBuilder.Info.SiloTypes, buildableThings); if (silo != null && HasSufficientPowerForActor(silo)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (silo)", queue.Actor.Owner, silo.Name); return(silo); } if (power != null && silo != null && !HasSufficientPowerForActor(silo)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return(power); } } // Build everything else foreach (var frac in baseBuilder.Info.BuildingFractions.Shuffle(world.LocalRandom)) { var name = frac.Key; // Does this building have initial delay, if so have we passed it? if (baseBuilder.Info.BuildingDelays != null && baseBuilder.Info.BuildingDelays.ContainsKey(name) && baseBuilder.Info.BuildingDelays[name] > world.WorldTick) { continue; } // Can we build this structure? if (!buildableThings.Any(b => b.Name == name)) { continue; } // Do we want to build this structure? var count = playerBuildings.Count(a => a.Info.Name == name); if (count * 100 > frac.Value * playerBuildings.Length) { continue; } if (baseBuilder.Info.BuildingLimits.ContainsKey(name) && baseBuilder.Info.BuildingLimits[name] <= count) { continue; } // If we're considering to build a naval structure, check whether there is enough water inside the base perimeter // and any structure providing buildable area close enough to that water. // TODO: Extend this check to cover any naval structure, not just production. if (baseBuilder.Info.NavalProductionTypes.Contains(name) && (waterState == WaterCheck.NotEnoughWater || !AIUtils.IsAreaAvailable <GivesBuildableArea>(world, player, world.Map, baseBuilder.Info.CheckForWaterRadius, baseBuilder.Info.WaterTerrainTypes))) { continue; } // Will this put us into low power? var actor = world.Map.Rules.Actors[name]; if (powerManager != null && (powerManager.ExcessPower < minimumExcessPower || !HasSufficientPowerForActor(actor))) { // Try building a power plant instead if (power != null && power.TraitInfos <PowerInfo>().Where(i => i.EnabledByDefault).Sum(pi => pi.Amount) > 0) { if (powerManager.PowerOutageRemainingTicks > 0) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (is low power)", queue.Actor.Owner, power.Name); } else { AIUtils.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); } return(power); } } // Lets build this AIUtils.BotDebug("{0} decided to build {1}: Desired is {2} ({3} / {4}); current is {5} / {4}", queue.Actor.Owner, name, frac.Value, frac.Value * playerBuildings.Length, playerBuildings.Length, count); return(actor); } // Too spammy to keep enabled all the time, but very useful when debugging specific issues. // AIUtils.BotDebug("{0} couldn't decide what to build for queue {1}.", queue.Actor.Owner, queue.Info.Group); return(null); }
public override void CollectDiscreteActionMasks(DiscreteActionMasker actionMasker) { List <AgentAction> disableActions = new List <AgentAction>() { AgentAction.Up, AgentAction.Right, AgentAction.Down, AgentAction.Left, AgentAction.Bomb }; if (!_player.IsDead) { if (_isMoving) { // We only allow the same action (so direction) than the previous one disableActions.Remove(_previousAction); disableActions.Add(AgentAction.Nothing); } else if (_canTakeDecision) { // Check accessible cells around var cellPosition = _map.CellPosition(transform.position); var neighbours = AIUtils.GetNeighbours(cellPosition, _map, true, true, false); foreach (var neighbourDirection in neighbours.Keys) { switch (neighbourDirection) { case EDirection.None: break; case EDirection.Up: disableActions.Remove(AgentAction.Up); break; case EDirection.Right: disableActions.Remove(AgentAction.Right); break; case EDirection.Down: disableActions.Remove(AgentAction.Down); break; case EDirection.Left: disableActions.Remove(AgentAction.Left); break; } } // Only allow the agent to plant a bomb if he can if (_player.BombCount > 0) { disableActions.Remove(AgentAction.Bomb); } } } int[] actionsMask = new int[disableActions.Count]; for (int i = 0; i < disableActions.Count; i++) { actionsMask[i] = (int)disableActions[i]; } actionMasker.SetMask(0, actionsMask); }
void QueueCaptureOrders(IBot bot) { if (!Info.CapturingActorTypes.Any() || player.WinState != WinState.Undefined) { return; } activeCapturers.RemoveAll(unitCannotBeOrderedOrIsIdle); var newUnits = world.ActorsHavingTrait <IPositionable>() .Where(a => a.Owner == player && !activeCapturers.Contains(a)); var capturers = newUnits .Where(a => a.IsIdle && Info.CapturingActorTypes.Contains(a.Info.Name) && a.Info.HasTraitInfo <CapturesInfo>()) .Select(a => new TraitPair <CaptureManager>(a, a.TraitOrDefault <CaptureManager>())) .Where(tp => tp.Trait != null) .ToArray(); if (capturers.Length == 0) { return; } var randPlayer = world.Players.Where(p => !p.Spectating && Info.CapturableStances.HasStance(player.Stances[p])).Random(world.LocalRandom); var targetOptions = Info.CheckCaptureTargetsForVisibility ? GetVisibleActorsBelongingToPlayer(randPlayer) : GetActorsThatCanBeOrderedByPlayer(randPlayer); var capturableTargetOptions = targetOptions .Where(target => { var captureManager = target.TraitOrDefault <CaptureManager>(); if (captureManager == null) { return(false); } return(capturers.Any(tp => captureManager.CanBeTargetedBy(target, tp.Actor, tp.Trait))); }) .OrderByDescending(target => target.GetSellValue()) .Take(maximumCaptureTargetOptions); if (Info.CapturableActorTypes.Any()) { capturableTargetOptions = capturableTargetOptions.Where(target => Info.CapturableActorTypes.Contains(target.Info.Name.ToLowerInvariant())); } if (!capturableTargetOptions.Any()) { return; } var failedAttempts = captureHistory.Where(a => a.Key.IsDead).Select(a => a.Value); foreach (var capturer in capturers) { var targetActor = capturableTargetOptions.MinByOrDefault(t => (t.CenterPosition - capturer.Actor.CenterPosition).LengthSquared); if (targetActor == null) { continue; } if (failedAttempts.Any(f => f.Actor == targetActor)) { AIUtils.BotDebug("AI ({0}): skipping capture of {1} as there was a previously failed attempt.", player.ClientIndex, targetActor); continue; } var target = Target.FromActor(targetActor); bot.QueueOrder(new Order("CaptureActor", capturer.Actor, target, true)); AIUtils.BotDebug("AI ({0}): Ordered {1} to capture {2}", player.ClientIndex, capturer.Actor, targetActor); activeCapturers.Add(capturer.Actor); if (!captureHistory.ContainsKey(capturer.Actor)) { captureHistory.Add(capturer.Actor, target); } } }
void QueueCaptureOrders(IBot bot) { if (!Info.CapturingActorTypes.Any() || player.WinState != WinState.Undefined) { return; } activeCapturers.RemoveAll(unitCannotBeOrderedOrIsIdle); var newUnits = world.ActorsHavingTrait <IPositionable>() .Where(a => a.Owner == player && !activeCapturers.Contains(a)); var capturers = newUnits .Where(a => a.IsIdle && Info.CapturingActorTypes.Contains(a.Info.Name) && a.Info.HasTraitInfo <CapturesInfo>()) .Select(a => new TraitPair <CaptureManager>(a, a.TraitOrDefault <CaptureManager>())) .Where(tp => tp.Trait != null) .ToArray(); if (capturers.Length == 0) { return; } var randomPlayer = world.Players.Where(p => !p.Spectating && Info.CapturableStances.HasStance(player.RelationshipWith(p))).Random(world.LocalRandom); var targetOptions = Info.CheckCaptureTargetsForVisibility ? GetVisibleActorsBelongingToPlayer(randomPlayer) : GetActorsThatCanBeOrderedByPlayer(randomPlayer); var capturableTargetOptions = targetOptions .Where(target => { var captureManager = target.TraitOrDefault <CaptureManager>(); if (captureManager == null) { return(false); } return(capturers.Any(tp => captureManager.CanBeTargetedBy(target, tp.Actor, tp.Trait))); }) .OrderByDescending(target => target.GetSellValue()) .Take(maximumCaptureTargetOptions); if (Info.CapturableActorTypes.Any()) { capturableTargetOptions = capturableTargetOptions.Where(target => Info.CapturableActorTypes.Contains(target.Info.Name.ToLowerInvariant())); } if (!capturableTargetOptions.Any()) { return; } foreach (var capturer in capturers) { var nearestTargetActors = capturableTargetOptions.OrderBy(target => (target.CenterPosition - capturer.Actor.CenterPosition).LengthSquared); foreach (var nearestTargetActor in nearestTargetActors) { if (activeCapturers.Contains(capturer.Actor)) { continue; } var safeTarget = SafePath(capturer.Actor, nearestTargetActor); if (safeTarget.Type == TargetType.Invalid) { continue; } bot.QueueOrder(new Order("CaptureActor", capturer.Actor, safeTarget, true)); AIUtils.BotDebug("AI ({0}): Ordered {1} to capture {2}", player.ClientIndex, capturer.Actor, nearestTargetActor); activeCapturers.Add(capturer.Actor); } } }
void IBotTick.BotTick(IBot bot) { foreach (var sp in supportPowerManager.Powers.Values) { if (sp.Disabled) { continue; } // Add power to dictionary if not in delay dictionary yet if (!waitingPowers.ContainsKey(sp)) { waitingPowers.Add(sp, 0); } if (waitingPowers[sp] > 0) { waitingPowers[sp]--; } // If we have recently tried and failed to find a use location for a power, then do not try again until later var isDelayed = waitingPowers[sp] > 0; if (sp.Ready && !isDelayed && powerDecisions.ContainsKey(sp.Info.OrderName)) { var powerDecision = powerDecisions[sp.Info.OrderName]; if (powerDecision == null) { AIUtils.BotDebug("{0} couldn't find powerDecision for {1}", player.PlayerName, sp.Info.OrderName); continue; } var attackLocation = FindCoarseAttackLocationToSupportPower(sp); if (attackLocation == null) { AIUtils.BotDebug("{0} can't find suitable coarse attack location for support power {1}. Delaying rescan.", player.PlayerName, sp.Info.OrderName); waitingPowers[sp] += powerDecision.GetNextScanTime(world); continue; } // Found a target location, check for precise target attackLocation = FindFineAttackLocationToSupportPower(sp, (CPos)attackLocation); if (attackLocation == null) { AIUtils.BotDebug("{0} can't find suitable final attack location for support power {1}. Delaying rescan.", player.PlayerName, sp.Info.OrderName); waitingPowers[sp] += powerDecision.GetNextScanTime(world); continue; } // Valid target found, delay by a few ticks to avoid rescanning before power fires via order AIUtils.BotDebug("{0} found new target location {1} for support power {2}.", player.PlayerName, attackLocation, sp.Info.OrderName); waitingPowers[sp] += 10; // Note: SelectDirectionalTarget uses uint.MaxValue in ExtraData to indicate that the player did not pick a direction. bot.QueueOrder(new Order(sp.Key, supportPowerManager.Self, Target.FromCell(world, attackLocation.Value), false) { SuppressVisualFeedback = true, ExtraData = uint.MaxValue }); } } // Remove stale powers stalePowers.AddRange(waitingPowers.Keys.Where(wp => !supportPowerManager.Powers.ContainsKey(wp.Key))); foreach (var p in stalePowers) { waitingPowers.Remove(p); } stalePowers.Clear(); }
void Offense() { if (unreasonable) { if (m_player.CanLayup()) { m_system.SetTransaction(AIState.Type.eLayup); Debug.LogError("Unreasonable layup."); return; } } if (!arriveFirstTarget) { return; } bool inTakeOver = false; if (m_player.m_inputDispatcher != null) { inTakeOver = m_player.m_inputDispatcher.inTakeOver; } bool isPvp = m_system.IsPvp; if (!inTakeOver) { if (!isPvp) { //进攻时间快结束了,投球 if (m_match.IsFinalTime(new IM.Number(3) - m_system.AI.devTime)) { AIUtils.AttackByPosition(m_player, new IM.Number(100)); return; } //玩家要球,传给他 Player mainRole = m_match.GetMainRole(m_player.m_roleInfo.acc_id); if (mainRole != null && mainRole.m_team == m_player.m_team && mainRole != m_player && mainRole.m_StateMachine.m_curState.m_eState == PlayerState.State.eRequireBall) { AI_Pass pass = m_system.GetState(Type.ePass) as AI_Pass; pass.m_toPass = mainRole; m_system.SetTransaction(pass, new IM.Number(100)); return; } //进攻篮筐 _Attack(); } //传球 Pass(); if (!isPvp) { Area area = m_match.mCurScene.mGround.GetArea(m_player); if (area != Area.eNear) //非篮下区 { if (!inTakeOver) { //突破 CrossOver(); if (!AIUtils.InAttackableDistance(m_match, m_player)) { m_system.SetTransaction(Type.ePositioning, new IM.Number(50), true); } } } } } else { m_system.SetTransaction(Type.ePositioning, new IM.Number(100), true); } }