public void ResolveOrder(Actor self, Order order) { TargetLocation = null; if (order.OrderString == "AttackMove") { TargetLocation = move.NearestMoveableCell(order.TargetLocation); self.SetTargetLine(Target.FromCell(self.World, TargetLocation.Value), Color.Red); Activate(self); } }
public void ResolveOrder(Actor self, Order order) { TargetLocation = null; if (order.OrderString == "AttackMove") { if (Info.JustMove) mobile.ResolveOrder(self, new Order("Move", order)); else { TargetLocation = mobile.NearestMoveableCell(order.TargetLocation); Activate(self); } } }
void INotifyCreated.Created(Actor self) { // Find the map tunnel associated with this entrance var sensor = self.Location + info.Sensor; var tunnel = self.World.WorldActor.Info.TraitInfos <TerrainTunnelInfo>() .FirstOrDefault(tti => tti.PortalCells().Contains(sensor)); if (tunnel != null) { // Find the matching entrance at the other end of the tunnel // Run at the end of the tick to make sure that all the entrances exist in the world self.World.AddFrameEndTask(w => { var portalCells = tunnel.PortalCells().ToList(); var other = self.World.ActorsWithTrait <TunnelEntrance>() .FirstOrDefault(x => x.Actor != self && portalCells.Contains(x.Actor.Location + x.Trait.info.Sensor)); if (other.Trait != null) { Exit = other.Trait.Entrance; } }); } }
public Move(Actor self, CPos destination, WDist nearEnough, Actor ignoreActor = null, bool evaluateNearestMovableCell = false, Color?targetLineColor = null) { mobile = self.Trait <Mobile>(); getPath = () => { if (!this.destination.HasValue) { return(NoPath); } return(self.World.WorldActor.Trait <IPathFinder>() .FindUnitPath(mobile.ToCell, this.destination.Value, self, ignoreActor)); }; // Note: Will be recalculated from OnFirstRun if evaluateNearestMovableCell is true this.destination = destination; this.nearEnough = nearEnough; this.ignoreActor = ignoreActor; this.evaluateNearestMovableCell = evaluateNearestMovableCell; this.targetLineColor = targetLineColor; }
public Move(Actor self, CPos destination, WDist nearEnough, Actor ignoreActor = null, bool evaluateNearestMovableCell = false, Color?targetLineColor = null) { // PERF: Because we can be sure that OccupiesSpace is Mobile here, we can save some performance by avoiding querying for the trait. mobile = (Mobile)self.OccupiesSpace; getPath = check => { if (!this.destination.HasValue) { return(PathFinder.NoPath); } return(mobile.Pathfinder.FindUnitPath(mobile.ToCell, this.destination.Value, self, ignoreActor, check)); }; // Note: Will be recalculated from OnFirstRun if evaluateNearestMovableCell is true this.destination = destination; this.nearEnough = nearEnough; this.ignoreActor = ignoreActor; this.evaluateNearestMovableCell = evaluateNearestMovableCell; this.targetLineColor = targetLineColor; }
public void ResolveOrder(Actor self, Order order) { if (order.OrderString == "Harvest") { // NOTE: An explicit harvest order allows the harvester to decide which refinery to deliver to. LinkProc(self, OwnerLinkedProc = null); idleSmart = true; self.CancelActivity(); var mobile = self.Trait <Mobile>(); if (order.TargetLocation != CPos.Zero) { var loc = order.TargetLocation; var territory = self.World.WorldActor.TraitOrDefault <ResourceClaimLayer>(); if (territory != null) { // Find the nearest claimable cell to the order location (useful for group-select harvest): loc = mobile.NearestCell(loc, p => mobile.CanEnterCell(p) && territory.ClaimResource(self, p), 1, 6); } else { // Find the nearest cell to the order location (useful for group-select harvest): var taken = new HashSet <CPos>(); loc = mobile.NearestCell(loc, p => mobile.CanEnterCell(p) && taken.Add(p), 1, 6); } self.QueueActivity(mobile.MoveTo(loc, 0)); self.SetTargetLine(Target.FromCell(self.World, loc), Color.Red); LastOrderLocation = loc; } else { // A bot order gives us a CPos.Zero TargetLocation, so find some good resources for him: var loc = FindNextResourceForBot(self); // No more resources? Oh well. if (!loc.HasValue) { return; } self.QueueActivity(mobile.MoveTo(loc.Value, 0)); self.SetTargetLine(Target.FromCell(self.World, loc.Value), Color.Red); LastOrderLocation = loc; } // This prevents harvesters returning to an empty patch when the player orders them to a new patch: LastHarvestedCell = LastOrderLocation; self.QueueActivity(new FindResources()); } else if (order.OrderString == "Deliver") { // NOTE: An explicit deliver order forces the harvester to always deliver to this refinery. var iao = order.TargetActor.TraitOrDefault <IAcceptOre>(); if (iao == null || !iao.AllowDocking || !IsAcceptableProcType(order.TargetActor)) { return; } if (order.TargetActor != OwnerLinkedProc) { LinkProc(self, OwnerLinkedProc = order.TargetActor); } if (IsEmpty) { return; } idleSmart = true; self.SetTargetLine(Target.FromOrder(self.World, order), Color.Green); self.CancelActivity(); self.QueueActivity(new DeliverResources()); } else if (order.OrderString == "Stop" || order.OrderString == "Move") { // Turn off idle smarts to obey the stop/move: idleSmart = false; } }
public override Activity Tick(Actor self) { //ChildActivity is the top priority ,unlike other activities. //Even if this activity is canceled,we must let the child be run so that units //will not end up in an odd place. //ChildActivity的优先级最高,它不像其他的活动。即使这个活动被取消,我们也必须让它继续运转,这样单位不会最终停留在一个奇怪的地方。 if (ChildActivity != null) { ChildActivity = ActivityUtils.RunActivity(self, ChildActivity); //Child activities such as Turn might have finished. //If we "return this" in this situationi,the unit loses one tick and pauses movement briefly. if (ChildActivity != null) { return(this); } } //If the actor is inside a tunnel then we must let them move //all the way through before moving to the next activity. if (IsCanceled && self.Location.Layer != CustomMovementLayerType.Tunnel) { return(NextActivity); } if (mobile.IsTraitDisabled) { return(this); } if (destination == mobile.ToCell) { return(NextActivity); } if (path == null) { if (mobile.TicksBeforePathing > 0) { --mobile.TicksBeforePathing; return(this); } path = EvalPath(); SanityCheckPath(mobile); } if (path.Count == 0) { destination = mobile.ToCell; return(this); } destination = path[0]; var nextCell = PopPath(self); if (nextCell == null) { return(this); } var firstFacing = self.World.Map.FacingBetween(mobile.FromCell, nextCell.Value.First, mobile.Facing); if (firstFacing != mobile.Facing) { path.Add(nextCell.Value.First); //return ActivityUtils.SequenceActivities(new Turn(self, firstFacing), this); QueueChild(new Turn(self, firstFacing)); return(this); } mobile.SetLocation(mobile.FromCell, mobile.FromSubCell, nextCell.Value.First, nextCell.Value.Second); var map = self.World.Map; var from = (mobile.FromCell.Layer == 0 ? map.CenterOfCell(mobile.FromCell) : self.World.GetCustomMovementLayers()[mobile.FromCell.Layer].CenterOfCell(mobile.FromCell)) + map.Grid.OffsetOfSubCell(mobile.FromSubCell); var to = Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + (map.Grid.OffsetOfSubCell(mobile.FromSubCell) + map.Grid.OffsetOfSubCell(mobile.ToSubCell)) / 2; //var move = new MoveFirstHalf(this, from, to, mobile.Facing, mobile.Facing, 0); QueueChild(new MoveFirstHalf(this, from, to, mobile.Facing, mobile.Facing, 0)); //While carrying out one Move order,MoveSecondHalf finishes its work from time to time and returns null.//在执行一个Move命令时,MoveSecondHalf 不时完成其工作,并返回null. //That causes the ChildActivity to be null and makes us return to this part of code. //这会导致ChildActivity 为空,并使我们返回到这部分代码 //If we only queue the activity and not run it ,units will lose one tick and pause briefly! //如果我们只排队而不运行它,单位将会失去一个滴答,暂定一下! ChildActivity = ActivityUtils.RunActivity(self, ChildActivity); return(this); }
public void ResolveOrder(Actor self, Order order) { if (order.OrderString == "Harvest") { // NOTE: An explicit harvest order allows the harvester to decide which refinery to deliver to. LinkProc(self, OwnerLinkedProc = null); idleSmart = true; self.CancelActivity(); var mobile = self.Trait<Mobile>(); if (order.TargetLocation != CPos.Zero) { var loc = order.TargetLocation; var territory = self.World.WorldActor.TraitOrDefault<ResourceClaimLayer>(); if (territory != null) { // Find the nearest claimable cell to the order location (useful for group-select harvest): loc = mobile.NearestCell(loc, p => mobile.CanEnterCell(p) && territory.ClaimResource(self, p), 1, 6); } else { // Find the nearest cell to the order location (useful for group-select harvest): var taken = new HashSet<CPos>(); loc = mobile.NearestCell(loc, p => mobile.CanEnterCell(p) && taken.Add(p), 1, 6); } self.QueueActivity(mobile.MoveTo(loc, 0)); self.SetTargetLine(Target.FromCell(self.World, loc), Color.Red); LastOrderLocation = loc; } else { // A bot order gives us a CPos.Zero TargetLocation, so find some good resources for him: var loc = FindNextResourceForBot(self); // No more resources? Oh well. if (!loc.HasValue) return; self.QueueActivity(mobile.MoveTo(loc.Value, 0)); self.SetTargetLine(Target.FromCell(self.World, loc.Value), Color.Red); LastOrderLocation = loc; } // This prevents harvesters returning to an empty patch when the player orders them to a new patch: LastHarvestedCell = LastOrderLocation; self.QueueActivity(new FindResources()); } else if (order.OrderString == "Deliver") { // NOTE: An explicit deliver order forces the harvester to always deliver to this refinery. var iao = order.TargetActor.TraitOrDefault<IAcceptOre>(); if (iao == null || !iao.AllowDocking || !IsAcceptableProcType(order.TargetActor)) return; if (order.TargetActor != OwnerLinkedProc) LinkProc(self, OwnerLinkedProc = order.TargetActor); if (IsEmpty) return; idleSmart = true; self.SetTargetLine(Target.FromOrder(self.World, order), Color.Green); self.CancelActivity(); self.QueueActivity(new DeliverResources()); } else if (order.OrderString == "Stop" || order.OrderString == "Move") { // Turn off idle smarts to obey the stop/move: idleSmart = false; } }
public static Actor CreateActor(this World world, bool addToWorld, string name, Player owner, CPos?location, int?facing) { var td = new TypeDictionary { new OwnerInit(owner) }; if (location.HasValue) { td.Add(new LocationInit(location.Value)); } if (facing.HasValue) { td.Add(new FacingInit(facing.Value)); } return(world.CreateActor(addToWorld, name, td)); }
public Move(CPos destination, int nearEnough) { this.getPath = (self, mobile) => self.World.WorldActor.Trait <PathFinder>().FindUnitPath(mobile.toCell, destination, self); this.destination = destination; this.nearEnough = nearEnough; }
public override bool Tick(Actor self) { mobile.TurnToMove = false; if (IsCanceling && mobile.CanStayInCell(mobile.ToCell)) { if (path != null) { path.Clear(); } return(true); } if (mobile.IsTraitDisabled || mobile.IsTraitPaused) { return(false); } if (destination == mobile.ToCell) { return(true); } if (path.Count == 0) { destination = mobile.ToCell; return(false); } destination = path[0]; var nextCell = PopPath(self); if (nextCell == null) { return(false); } var firstFacing = self.World.Map.FacingBetween(mobile.FromCell, nextCell.Value.First, mobile.Facing); if (firstFacing != mobile.Facing) { path.Add(nextCell.Value.First); QueueChild(new Turn(self, firstFacing)); mobile.TurnToMove = true; return(false); } mobile.SetLocation(mobile.FromCell, mobile.FromSubCell, nextCell.Value.First, nextCell.Value.Second); var map = self.World.Map; var from = (mobile.FromCell.Layer == 0 ? map.CenterOfCell(mobile.FromCell) : self.World.GetCustomMovementLayers()[mobile.FromCell.Layer].CenterOfCell(mobile.FromCell)) + map.Grid.OffsetOfSubCell(mobile.FromSubCell); var to = Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + (map.Grid.OffsetOfSubCell(mobile.FromSubCell) + map.Grid.OffsetOfSubCell(mobile.ToSubCell)) / 2; QueueChild(new MoveFirstHalf(this, from, to, mobile.Facing, mobile.Facing, 0)); return(false); }
public FindResources(Actor self, CPos avoidCell) : this(self) { this.avoidCell = avoidCell; }
public void Tick(Actor self) { if (remainingTime > 0 && canCloak) if (--remainingTime <= 0) Sound.Play(info.CloakSound, self.CenterLocation); if (self.IsDisabled()) Uncloak(); if (info.UncloakOnMove && (lastPos == null || lastPos.Value != self.Location)) { Uncloak(); lastPos = self.Location; } }
public FindResources(CPos avoidCell) { this.avoidCell = avoidCell; }
public DeployMiner(Actor self, CPos?location, HashSet <string> terrainTypes) { movement = self.Trait <IMove>(); this.location = location; this.terrainTypes = terrainTypes; }
public void Tick(Actor self) { if (remainingTime > 0 && !crateDisabled && !damageDisabled && --remainingTime <= 0) { self.Generation++; Sound.Play(info.CloakSound, self.CenterPosition); } if (self.IsDisabled()) Uncloak(); if (info.UncloakOnMove && (lastPos == null || lastPos.Value != self.Location)) { Uncloak(); lastPos = self.Location; } }
void AssignRolesToIdleUnits(Actor self) { //HACK: trim these lists -- we really shouldn't be hanging onto all this state //when it's invalidated so easily, but that's Matthew/Alli's problem. activeUnits.RemoveAll(a => a.Destroyed); unitsHangingAroundTheBase.RemoveAll(a => a.Destroyed); attackForce.RemoveAll(a => a.Destroyed); if (--assignRolesTicks > 0) return; else assignRolesTicks = Info.AssignRolesInterval; // Find idle harvesters and give them orders: foreach (var a in activeUnits) { var harv = a.TraitOrDefault<Harvester>(); if (harv == null) continue; if (!a.IsIdle) { Activity act = a.GetCurrentActivity(); // A Wait activity is technically idle: if (!(act is Activities.Wait) && (act.NextActivity == null || !(act.NextActivity is Activities.FindResources))) continue; } if (!harv.IsEmpty) continue; // Tell the idle harvester to quit slacking: world.IssueOrder(new Order("Harvest", a, false)); } var newUnits = self.World.ActorsWithTrait<IMove>() .Where(a => a.Actor.Owner == p && !a.Actor.HasTrait<BaseBuilding>() && !activeUnits.Contains(a.Actor)) .Select(a => a.Actor).ToArray(); foreach (var a in newUnits) { BotDebug("AI: Found a newly built unit"); if (a.HasTrait<Harvester>()) world.IssueOrder( new Order( "Harvest", a, false ) ); else unitsHangingAroundTheBase.Add(a); activeUnits.Add(a); } /* Create an attack force when we have enough units around our base. */ // (don't bother leaving any behind for defense.) int randomizedSquadSize = Info.SquadSize - 4 + random.Next(200); if (unitsHangingAroundTheBase.Count >= randomizedSquadSize) { BotDebug("Launch an attack."); if (attackForce.Count == 0) { attackTarget = ChooseEnemyTarget(); if (attackTarget == null) return; foreach (var a in unitsHangingAroundTheBase) if (TryToMove(a, attackTarget.Value, true)) attackForce.Add(a); unitsHangingAroundTheBase.Clear(); } } // If we have any attackers, let them scan for enemy units and stop and regroup if they spot any if (attackForce.Count > 0) { bool foundEnemy = false; foreach (var a1 in attackForce) { var enemyUnits = world.FindUnitsInCircle(a1.CenterLocation, Game.CellSize * 10) .Where(unit => p.Stances[unit.Owner] == Stance.Enemy && !unit.HasTrait<Husk>()).ToList(); if (enemyUnits.Count > 0) { // Found enemy units nearby. foundEnemy = true; var enemy = enemyUnits.ClosestTo( a1.CenterLocation ); // Check how many own units we have gathered nearby... var ownUnits = world.FindUnitsInCircle(a1.CenterLocation, Game.CellSize * 2) .Where(unit => unit.Owner == p).ToList(); if (ownUnits.Count < randomizedSquadSize) { // Not enough to attack. Send more units. world.IssueOrder(new Order("Stop", a1, false)); foreach (var a2 in attackForce) if (a2 != a1) world.IssueOrder(new Order("AttackMove", a2, false) { TargetLocation = a1.Location }); } else { // We have gathered sufficient units. Attack the nearest enemy unit. foreach (var a2 in attackForce) world.IssueOrder(new Order("Attack", a2, false) { TargetActor = enemy }); } return; } } if (!foundEnemy) { attackTarget = ChooseEnemyTarget(); if (attackTarget != null) foreach (var a in attackForce) TryToMove(a, attackTarget.Value, true); } } }
public override Activity Tick(Actor self) { var mobile = self.Trait <Mobile>(); var info = self.Info.Traits.Get <MobileInfo>(); if (mobile.Altitude != info.Altitude) { if (mobile.Altitude < info.Altitude) { ++mobile.Altitude; } return(this); } if (destination == mobile.toCell) { return(NextActivity); } if (path == null) { if (mobile.ticksBeforePathing > 0) { --mobile.ticksBeforePathing; return(this); } path = EvalPath(self, mobile); SanityCheckPath(mobile); } if (path.Count == 0) { destination = mobile.toCell; return(this); } destination = path[0]; var nextCell = PopPath(self, mobile); if (nextCell == null) { return(this); } var dir = nextCell.Value.First - mobile.fromCell; var firstFacing = Util.GetFacing(dir, mobile.Facing); if (firstFacing != mobile.Facing) { path.Add(nextCell.Value.First); return(Util.SequenceActivities(new Turn(firstFacing), this)); } else { mobile.SetLocation(mobile.fromCell, mobile.fromSubCell, nextCell.Value.First, nextCell.Value.Second); var move = new MoveFirstHalf( this, Util.CenterOfCell(mobile.fromCell) + mobile.Info.SubCellOffsets[mobile.fromSubCell], Util.BetweenCells(mobile.fromCell, mobile.toCell) + (mobile.Info.SubCellOffsets[mobile.fromSubCell] + mobile.Info.SubCellOffsets[mobile.toSubCell]) / 2, mobile.Facing, mobile.Facing, 0); return(move); } }
Actor CreateActor(Player owner, string actorType, bool addToWorld, CPos?entryLocation = null, CPos?nextLocation = null) { ActorInfo ai; if (!Context.World.Map.Rules.Actors.TryGetValue(actorType, out ai)) { throw new LuaException("Unknown actor type '{0}'".F(actorType)); } var initDict = new TypeDictionary(); initDict.Add(new OwnerInit(owner)); if (entryLocation.HasValue) { var pi = ai.TraitInfoOrDefault <AircraftInfo>(); initDict.Add(new CenterPositionInit(owner.World.Map.CenterOfCell(entryLocation.Value) + new WVec(0, 0, pi != null ? pi.CruiseAltitude.Length : 0))); initDict.Add(new LocationInit(entryLocation.Value)); } if (entryLocation.HasValue && nextLocation.HasValue) { initDict.Add(new FacingInit(Context.World.Map.FacingBetween(CPos.Zero, CPos.Zero + (nextLocation.Value - entryLocation.Value), 0))); } var actor = Context.World.CreateActor(addToWorld, actorType, initDict); return(actor); }
public Move(Func <List <CPos> > getPath) { this.getPath = (_1, _2) => getPath(); this.destination = null; this.nearEnough = WRange.Zero; }
public override Activity Tick(Actor self) { if (moveDisablers.Any(d => d.MoveDisabled(self))) { return(this); } if (destination == mobile.ToCell) { return(NextActivity); } if (path == null) { if (mobile.TicksBeforePathing > 0) { --mobile.TicksBeforePathing; return(this); } path = EvalPath(); SanityCheckPath(mobile); } if (path.Count == 0) { destination = mobile.ToCell; return(this); } destination = path[0]; var nextCell = PopPath(self); if (nextCell == null) { return(this); } var firstFacing = self.World.Map.FacingBetween(mobile.FromCell, nextCell.Value.First, mobile.Facing); if (firstFacing != mobile.Facing) { path.Add(nextCell.Value.First); return(Util.SequenceActivities(new Turn(self, firstFacing), this)); } else { mobile.SetLocation(mobile.FromCell, mobile.FromSubCell, nextCell.Value.First, nextCell.Value.Second); var from = self.World.Map.CenterOfSubCell(mobile.FromCell, mobile.FromSubCell); var to = Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + (self.World.Map.OffsetOfSubCell(mobile.FromSubCell) + self.World.Map.OffsetOfSubCell(mobile.ToSubCell)) / 2; var move = new MoveFirstHalf( this, from, to, mobile.Facing, mobile.Facing, 0); return(move); } }
public FindResources(CPos avoidCell) { this.avoidCell = avoidCell; }
public FindAndDeliverResources(Actor self, CPos orderLocation) : this(self, null) { this.orderLocation = orderLocation; }
void Reset() { ticksTillCheck = 0; destination = null; nextActivity = null; }
CPos?ChooseMcvDeployLocation(string actorType, CVec offset, bool distanceToBaseIsImportant) { var actorInfo = world.Map.Rules.Actors[actorType]; var bi = actorInfo.TraitInfoOrDefault <BuildingInfo>(); if (bi == null) { return(null); } // Find the buildable cell that is closest to pos and centered around center Func <CPos, CPos, int, int, CPos?> findPos = (center, target, minRange, maxRange) => { var cells = world.Map.FindTilesInAnnulus(center, minRange, maxRange); // Sort by distance to target if we have one if (center != target) { cells = cells.OrderBy(c => (c - target).LengthSquared); } else { cells = cells.Shuffle(world.LocalRandom); } foreach (var cell in cells) { if (world.CanPlaceBuilding(cell + offset, actorInfo, bi, null)) { return(cell); } } return(null); }; var baseCenter = GetRandomBaseCenter(distanceToBaseIsImportant); CPos?bc = findPos(baseCenter, baseCenter, Info.MinBaseRadius, distanceToBaseIsImportant ? Info.MaxBaseRadius : world.Map.Grid.MaximumTileSearchRange); if (!bc.HasValue) { return(null); } baseCenter = bc.Value; var wPos = world.Map.CenterOfCell(bc.Value); var newBaseRadius = new WDist(Info.MaxBaseRadius * 1024); var enemies = world.FindActorsInCircle(wPos, newBaseRadius) .Where(a => !a.Disposed && player.Stances[a.Owner] == Stance.Enemy && a.Info.HasTraitInfo <BuildingInfo>()); if (enemies.Count() > 0) { return(null); } var self = world.FindActorsInCircle(wPos, newBaseRadius) .Where(a => !a.Disposed && a.Owner == player && (Info.McvTypes.Contains(a.Info.Name) || (Info.ConstructionYardTypes.Contains(a.Info.Name) && a.Info.HasTraitInfo <BuildingInfo>()))); if (self.Count() > 0) { return(null); } return(baseCenter); }
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); }
public FindResources(Actor self, CPos avoidCell) : this(self) { this.avoidCell = avoidCell; }
/// <summary> /// Finds the closest harvestable pos between the current position of the harvester /// and the last order location /// </summary> CPos?ClosestHarvestablePos(Actor self) { // Harvesters should respect an explicit harvest order instead of harvesting the current cell. if (orderLocation == null) { if (harv.CanHarvestCell(self, self.Location) && claimLayer.CanClaimCell(self, self.Location)) { return(self.Location); } } else { if (harv.CanHarvestCell(self, orderLocation.Value) && claimLayer.CanClaimCell(self, orderLocation.Value)) { return(orderLocation); } orderLocation = null; } // Determine where to search from and how far to search: var procLoc = GetSearchFromProcLocation(self); var searchFromLoc = lastHarvestedCell ?? procLoc ?? self.Location; var searchRadius = lastHarvestedCell.HasValue ? harvInfo.SearchFromHarvesterRadius : harvInfo.SearchFromProcRadius; var searchRadiusSquared = searchRadius * searchRadius; var map = self.World.Map; var procPos = procLoc.HasValue ? (WPos?)map.CenterOfCell(procLoc.Value) : null; var harvPos = self.CenterPosition; // Find any harvestable resources: List <CPos> path; using (var search = PathSearch.Search(self.World, mobile.Locomotor, self, BlockedByActor.Stationary, loc => domainIndex.IsPassable(self.Location, loc, mobile.Locomotor) && harv.CanHarvestCell(self, loc) && claimLayer.CanClaimCell(self, loc)) .WithCustomCost(loc => { if ((loc - searchFromLoc).LengthSquared > searchRadiusSquared) { return(PathGraph.PathCostForInvalidPath); } // Add a cost modifier to harvestable cells to prefer resources that are closer to the refinery. // This reduces the tendency for harvesters to move in straight lines if (procPos.HasValue && harvInfo.ResourceRefineryDirectionPenalty > 0 && harv.CanHarvestCell(self, loc)) { var pos = map.CenterOfCell(loc); // Calculate harv-cell-refinery angle (cosine rule) var b = pos - procPos.Value; if (b != WVec.Zero) { var c = pos - harvPos; if (c != WVec.Zero) { var a = harvPos - procPos.Value; var cosA = (int)(512 * (b.LengthSquared + c.LengthSquared - a.LengthSquared) / b.Length / c.Length); // Cost modifier varies between 0 and ResourceRefineryDirectionPenalty return(Math.Abs(harvInfo.ResourceRefineryDirectionPenalty / 2) + harvInfo.ResourceRefineryDirectionPenalty * cosA / 2048); } } } return(0); }) .FromPoint(searchFromLoc) .FromPoint(self.Location)) path = mobile.Pathfinder.FindPath(search); if (path.Count > 0) { return(path[0]); } return(null); }
public SpawnerRefineryHarvest(Actor self, CPos avoidCell) : this(self) { this.avoidCell = avoidCell; }
public override bool Tick(Actor self) { if (IsCanceling || harv.IsTraitDisabled) { return(true); } if (NextActivity != null) { // Interrupt automated harvesting after clearing the first cell. if (!harvInfo.QueueFullLoad && (hasHarvestedCell || LastSearchFailed)) { return(true); } // Interrupt automated harvesting after first complete harvest cycle. if (hasDeliveredLoad || harv.IsFull) { return(true); } } // Are we full or have nothing more to gather? Deliver resources. if (harv.IsFull || (!harv.IsEmpty && LastSearchFailed)) { QueueChild(new DeliverResources(self)); hasDeliveredLoad = true; return(false); } // After a failed search, wait and sit still for a bit before searching again. if (LastSearchFailed && !hasWaited) { QueueChild(new Wait(harv.Info.WaitDuration)); hasWaited = true; return(false); } hasWaited = false; // Scan for resources. If no resources are found near the current field, search near the refinery // instead. If that doesn't help, give up for now. var closestHarvestableCell = ClosestHarvestablePos(self); if (!closestHarvestableCell.HasValue) { if (lastHarvestedCell != null) { lastHarvestedCell = null; // Forces search from backup position. closestHarvestableCell = ClosestHarvestablePos(self); LastSearchFailed = !closestHarvestableCell.HasValue; } else { LastSearchFailed = true; } } else { LastSearchFailed = false; } // If no harvestable position could be found and we are at the refinery, get out of the way // of the refinery entrance. if (LastSearchFailed) { var lastproc = harv.LastLinkedProc ?? harv.LinkedProc; if (lastproc != null && !lastproc.Disposed) { var deliveryLoc = lastproc.Location + lastproc.Trait <IAcceptResources>().DeliveryOffset; if (self.Location == deliveryLoc && harv.IsEmpty) { var unblockCell = deliveryLoc + harv.Info.UnblockCell; var moveTo = mobile.NearestMoveableCell(unblockCell, 1, 5); QueueChild(mobile.MoveTo(moveTo, 1)); } } return(false); } // If we get here, our search for resources was successful. Commence harvesting. QueueChild(new HarvestResource(self, closestHarvestableCell.Value)); lastHarvestedCell = closestHarvestableCell.Value; hasHarvestedCell = true; return(false); }
public void ForceMove(CPos pos) { force = true; forceMovePos = pos; transforms.DeployTransform(true); }
public static Actor CreateActor(this World world, string name, Player owner, CPos?location, int?facing) { return(CreateActor(world, true, name, owner, location, facing)); }
void Reset() { ticksTillCheck = 0; destination = null; refinery = null; }
public void Tick(Squad 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; } } var leader = owner.Units.ClosestTo(owner.TargetActor.CenterPosition); if (leader == null) { return; } 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; } var ownUnits = owner.World.FindActorsInCircle(leader.CenterPosition, WDist.FromCells(owner.Units.Count) / 3) .Where(a => a.Owner == owner.Units.First().Owner&& owner.Units.Contains(a)).ToHashSet(); if (ownUnits.Count < owner.Units.Count) { // Since units have different movement speeds, they get separated while approaching the target. // Let them regroup into tighter formation. owner.Bot.QueueOrder(new Order("Stop", leader, false)); var units = owner.Units.Where(a => !ownUnits.Contains(a)).ToArray(); owner.Bot.QueueOrder(new Order("AttackMove", null, Target.FromCell(owner.World, leader.Location), false, groupedActors: units)); } else { var enemies = owner.World.FindActorsInCircle(leader.CenterPosition, WDist.FromCells(owner.SquadManager.Info.AttackScanRadius)) .Where(owner.SquadManager.IsPreferredEnemyUnit); var target = enemies.ClosestTo(leader.CenterPosition); if (target != null) { owner.TargetActor = target; owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsAttackState(), true); } else { owner.Bot.QueueOrder(new Order("AttackMove", null, Target.FromCell(owner.World, owner.TargetActor.Location), false, groupedActors: owner.Units.ToArray())); } } if (ShouldFlee(owner)) { owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsFleeState(), true); } }
public SlaveMinerHarvesterHarvest(Actor self, CPos avoidCell) : this(self) { this.avoidCell = avoidCell; }
public override bool Tick(Actor self) { mobile.TurnToMove = false; if (IsCanceling && mobile.CanStayInCell(mobile.ToCell)) { path?.Clear(); return(true); } if (mobile.IsTraitDisabled || mobile.IsTraitPaused) { return(false); } if (destination == mobile.ToCell) { return(true); } if (path.Count == 0) { destination = mobile.ToCell; return(false); } destination = path[0]; var nextCell = PopPath(self); if (nextCell == null) { return(false); } var firstFacing = self.World.Map.FacingBetween(mobile.FromCell, nextCell.Value.Cell, mobile.Facing); if (firstFacing != mobile.Facing) { path.Add(nextCell.Value.Cell); QueueChild(new Turn(self, firstFacing)); mobile.TurnToMove = true; return(false); } mobile.SetLocation(mobile.FromCell, mobile.FromSubCell, nextCell.Value.Cell, nextCell.Value.SubCell); var map = self.World.Map; var from = (mobile.FromCell.Layer == 0 ? map.CenterOfCell(mobile.FromCell) : self.World.GetCustomMovementLayers()[mobile.FromCell.Layer].CenterOfCell(mobile.FromCell)) + map.Grid.OffsetOfSubCell(mobile.FromSubCell); var to = Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + (map.Grid.OffsetOfSubCell(mobile.FromSubCell) + map.Grid.OffsetOfSubCell(mobile.ToSubCell)) / 2; WRot?toTerrainOrientation = null; var margin = mobile.Info.TerrainOrientationAdjustmentMargin.Length; if (margin >= 0) { toTerrainOrientation = WRot.SLerp(map.TerrainOrientation(mobile.FromCell), map.TerrainOrientation(mobile.ToCell), 1, 2); } QueueChild(new MoveFirstHalf(this, from, to, mobile.Facing, mobile.Facing, null, toTerrainOrientation, margin, carryoverProgress)); carryoverProgress = 0; return(false); }
void AssignRolesToIdleUnits(Actor self) { //HACK: trim these lists -- we really shouldn't be hanging onto all this state //when it's invalidated so easily, but that's Matthew/Alli's problem. activeUnits.RemoveAll(a => a.Destroyed); unitsHangingAroundTheBase.RemoveAll(a => a.Destroyed); attackForce.RemoveAll(a => a.Destroyed); if (--assignRolesTicks > 0) { return; } else { assignRolesTicks = Info.AssignRolesInterval; } // Find idle harvesters and give them orders: foreach (var a in activeUnits) { var harv = a.TraitOrDefault <Harvester>(); if (harv == null) { continue; } if (!a.IsIdle) { Activity act = a.GetCurrentActivity(); // A Wait activity is technically idle: if ((act.GetType() != typeof(OpenRA.Mods.RA.Activities.Wait)) && (act.NextActivity == null || act.NextActivity.GetType() != typeof(OpenRA.Mods.RA.Activities.FindResources))) { continue; } } if (!harv.IsEmpty) { continue; } // Tell the idle harvester to quit slacking: world.IssueOrder(new Order("Harvest", a, false)); } var newUnits = self.World.ActorsWithTrait <IMove>() .Where(a => a.Actor.Owner == p && !a.Actor.HasTrait <BaseBuilding>() && !activeUnits.Contains(a.Actor)) .Select(a => a.Actor).ToArray(); foreach (var a in newUnits) { BotDebug("AI: Found a newly built unit"); if (a.HasTrait <Harvester>()) { world.IssueOrder(new Order("Harvest", a, false)); } else { unitsHangingAroundTheBase.Add(a); } activeUnits.Add(a); } /* Create an attack force when we have enough units around our base. */ // (don't bother leaving any behind for defense.) int randomizedSquadSize = Info.SquadSize - 4 + random.Next(200); if (unitsHangingAroundTheBase.Count >= randomizedSquadSize) { BotDebug("Launch an attack."); if (attackForce.Count == 0) { attackTarget = ChooseEnemyTarget(); if (attackTarget == null) { return; } foreach (var a in unitsHangingAroundTheBase) { if (TryToMove(a, attackTarget.Value, true)) { attackForce.Add(a); } } unitsHangingAroundTheBase.Clear(); } } // If we have any attackers, let them scan for enemy units and stop and regroup if they spot any if (attackForce.Count > 0) { bool foundEnemy = false; foreach (var a1 in attackForce) { var enemyUnits = world.FindUnitsInCircle(a1.CenterLocation, Game.CellSize * 10) .Where(unit => p.Stances[unit.Owner] == Stance.Enemy).ToList(); if (enemyUnits.Count > 0) { // Found enemy units nearby. foundEnemy = true; var enemy = enemyUnits.ClosestTo(a1.CenterLocation); // Check how many own units we have gathered nearby... var ownUnits = world.FindUnitsInCircle(a1.CenterLocation, Game.CellSize * 2) .Where(unit => unit.Owner == p).ToList(); if (ownUnits.Count < randomizedSquadSize) { // Not enough to attack. Send more units. world.IssueOrder(new Order("Stop", a1, false)); foreach (var a2 in attackForce) { if (a2 != a1) { world.IssueOrder(new Order("AttackMove", a2, false) { TargetLocation = a1.Location }); } } } else { // We have gathered sufficient units. Attack the nearest enemy unit. foreach (var a2 in attackForce) { world.IssueOrder(new Order("Attack", a2, false) { TargetActor = enemy }); } } return; } } if (!foundEnemy) { attackTarget = ChooseEnemyTarget(); if (attackTarget != null) { foreach (var a in attackForce) { TryToMove(a, attackTarget.Value, true); } } } } }
public void ResolveOrder(Actor self, Order order) { if (order.OrderString == "Harvest") { // NOTE: An explicit harvest order allows the harvester to decide which refinery to deliver to. LinkProc(self, OwnerLinkedProc = null); idleSmart = true; self.CancelActivity(); CPos?loc; if (order.TargetLocation != CPos.Zero) { // Find the nearest claimable cell to the order location (useful for group-select harvest): loc = mobile.NearestCell(order.TargetLocation, p => mobile.CanEnterCell(p) && claimLayer.TryClaimCell(self, p), 1, 6); } else { // A bot order gives us a CPos.Zero TargetLocation. loc = self.Location; } var findResources = new FindResources(self); self.QueueActivity(findResources); self.SetTargetLine(Target.FromCell(self.World, loc.Value), Color.Red); foreach (var n in notify) { n.MovingToResources(self, loc.Value, findResources); } LastOrderLocation = loc; // This prevents harvesters returning to an empty patch when the player orders them to a new patch: LastHarvestedCell = LastOrderLocation; } else if (order.OrderString == "Deliver") { // NOTE: An explicit deliver order forces the harvester to always deliver to this refinery. var iao = order.TargetActor.TraitOrDefault <IAcceptResources>(); if (iao == null || !iao.AllowDocking || !IsAcceptableProcType(order.TargetActor)) { return; } if (order.TargetActor != OwnerLinkedProc) { LinkProc(self, OwnerLinkedProc = order.TargetActor); } idleSmart = true; self.SetTargetLine(Target.FromOrder(self.World, order), Color.Green); self.CancelActivity(); var deliver = new DeliverResources(self); self.QueueActivity(deliver); foreach (var n in notify) { n.MovingToRefinery(self, order.TargetLocation, deliver); } } else if (order.OrderString == "Stop" || order.OrderString == "Move") { foreach (var n in notify) { n.MovementCancelled(self); } // Turn off idle smarts to obey the stop/move: idleSmart = false; } }
public void Tick(Actor self) { if (disabled) return; if (remainingTime > 0 && !disabled && !damageDisabled && --remainingTime <= 0) Sound.Play(Info.CloakSound, self.CenterPosition); if (self.IsDisabled()) Uncloak(); if (Info.UncloakOnMove && (lastPos == null || lastPos.Value != self.Location)) { Uncloak(); lastPos = self.Location; } }