Exemplo n.º 1
0
		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);
			}
		}
Exemplo n.º 2
0
        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);
                }
            }
        }
Exemplo n.º 3
0
        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;
                    }
                });
            }
        }
Exemplo n.º 4
0
        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;
        }
Exemplo n.º 5
0
        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;
        }
Exemplo n.º 6
0
        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;
            }
        }
Exemplo n.º 7
0
        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);
        }
Exemplo n.º 8
0
        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;
            }
        }
Exemplo n.º 9
0
        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));
        }
Exemplo n.º 10
0
 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;
 }
Exemplo n.º 11
0
        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);
        }
Exemplo n.º 12
0
 public FindResources(Actor self, CPos avoidCell)
     : this(self)
 {
     this.avoidCell = avoidCell;
 }
Exemplo n.º 13
0
        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;
            }
        }
Exemplo n.º 14
0
 public FindResources(CPos avoidCell)
 {
     this.avoidCell = avoidCell;
 }
Exemplo n.º 15
0
 public DeployMiner(Actor self, CPos?location, HashSet <string> terrainTypes)
 {
     movement          = self.Trait <IMove>();
     this.location     = location;
     this.terrainTypes = terrainTypes;
 }
Exemplo n.º 16
0
        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;
            }
        }
Exemplo n.º 17
0
        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);
                }
            }
        }
Exemplo n.º 18
0
        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);
            }
        }
Exemplo n.º 19
0
        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);
        }
Exemplo n.º 20
0
 public Move(Func <List <CPos> > getPath)
 {
     this.getPath     = (_1, _2) => getPath();
     this.destination = null;
     this.nearEnough  = WRange.Zero;
 }
Exemplo n.º 21
0
        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);
            }
        }
Exemplo n.º 22
0
 public FindResources(CPos avoidCell)
 {
     this.avoidCell = avoidCell;
 }
Exemplo n.º 23
0
 public FindAndDeliverResources(Actor self, CPos orderLocation)
     : this(self, null)
 {
     this.orderLocation = orderLocation;
 }
Exemplo n.º 24
0
 void Reset()
 {
     ticksTillCheck = 0;
     destination    = null;
     nextActivity   = null;
 }
Exemplo n.º 25
0
        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);
        }
Exemplo n.º 26
0
        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);
        }
Exemplo n.º 27
0
 public FindResources(Actor self, CPos avoidCell)
     : this(self)
 {
     this.avoidCell = avoidCell;
 }
Exemplo n.º 28
0
        /// <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);
        }
Exemplo n.º 29
0
 public SpawnerRefineryHarvest(Actor self, CPos avoidCell)
     : this(self)
 {
     this.avoidCell = avoidCell;
 }
Exemplo n.º 30
0
        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);
        }
Exemplo n.º 31
0
 public void ForceMove(CPos pos)
 {
     force        = true;
     forceMovePos = pos;
     transforms.DeployTransform(true);
 }
Exemplo n.º 32
0
 public static Actor CreateActor(this World world, string name, Player owner, CPos?location, int?facing)
 {
     return(CreateActor(world, true, name, owner, location, facing));
 }
Exemplo n.º 33
0
 void Reset()
 {
     ticksTillCheck = 0;
     destination    = null;
     refinery       = null;
 }
Exemplo n.º 34
0
        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);
            }
        }
Exemplo n.º 35
0
 public SlaveMinerHarvesterHarvest(Actor self, CPos avoidCell)
     : this(self)
 {
     this.avoidCell = avoidCell;
 }
Exemplo n.º 36
0
        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);
        }
Exemplo n.º 37
0
        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);
                        }
                    }
                }
            }
        }
Exemplo n.º 38
0
        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;
            }
        }
Exemplo n.º 39
0
        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;
            }
        }