Example #1
0
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            if (IsCanceling)
            {
                return(NextActivity);
            }

            var target = targets.ClosestTo(self);

            if (target == null)
            {
                return(this);
            }

            QueueChild(self, new AttackMoveActivity(self, () => move.MoveTo(target.Location, 2)), true);
            QueueChild(self, new Wait(25));
            return(this);
        }
        public override Activity Tick(Actor self)
        {
            // Do turn first, if needed.
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                return(this);
            }

            // Without this, turn for facing deploy angle will be canceled and immediately deploy!
            if (IsCanceling)
            {
                return(NextActivity);
            }

            if (IsInterruptible)
            {
                IsInterruptible = false;                 // must DEPLOY from now.
                deploy.Deploy();
                return(this);
            }

            // Wait for deployment
            if (deploy.DeployState == DeployState.Deploying)
            {
                return(this);
            }

            // Failed or success, we are going to NextActivity.
            // Deploy() at the first run would have put DeployState == Deploying so
            // if we are back to DeployState.Undeployed, it means deploy failure.
            // Parent activity will see the status and will take appropriate action.
            return(NextActivity);
        }
Example #3
0
            public override Activity Tick(Actor self)
            {
                if (ChildActivity != null)
                {
                    ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                    if (ChildActivity != null)
                    {
                        return(this);
                    }
                }

                if (IsCanceling || !attack.CanAttack(self, target))
                {
                    return(NextActivity);
                }

                if (attack.charges == 0)
                {
                    return(this);
                }

                foreach (var notify in self.TraitsImplementing <INotifyTeslaCharging>())
                {
                    notify.Charging(self, target);
                }

                if (!string.IsNullOrEmpty(attack.info.ChargeAudio))
                {
                    Game.Sound.Play(SoundType.World, attack.info.ChargeAudio, self.CenterPosition);
                }

                QueueChild(self, new Wait(attack.info.InitialChargeDelay), true);
                QueueChild(self, new ChargeFire(attack, target));
                return(this);
            }
Example #4
0
        public void Tick()
        {
            var wasIdle = IsIdle;

            CurrentActivity = ActivityUtils.RunActivityTick(this, CurrentActivity);
            if (this.Info.Name == "asteroid")
            {
            }
            if (!wasIdle && IsIdle)
            {
                foreach (var n in becomingIdles)
                {
                    n.OnBecomingIdle(this);
                }

                // If IsIdle is true, it means the last CurrentActivity.Tick returned null.
                // If a next activity has been queued via OnBecomingIdle, we need to start running it now,
                // to avoid an 'empty' null tick where the actor will (visibly, if moving) do nothing.
                CurrentActivity = ActivityUtils.RunActivityTick(this, CurrentActivity);
            }
            else if (wasIdle)
            {
                foreach (var tickIdle in tickIdles)
                {
                    tickIdle.TickIdle(this);
                }
            }
        }
Example #5
0
            public override Activity Tick(Actor self)
            {
                if (ChildActivity != null)
                {
                    ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                    if (ChildActivity != null)
                    {
                        return(this);
                    }
                }

                if (IsCanceling || !attack.CanAttack(self, target))
                {
                    return(NextActivity);
                }

                if (attack.charges == 0)
                {
                    return(NextActivity);
                }

                attack.DoAttack(self, target);

                QueueChild(self, new Wait(attack.info.ChargeDelay), true);
                return(this);
            }
Example #6
0
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            if (IsCanceling || target.Type == TargetType.Invalid)
            {
                return(NextActivity);
            }

            if (mobile.IsTraitDisabled || mobile.IsTraitPaused)
            {
                return(this);
            }

            var currentPos = self.CenterPosition;
            var targetPos  = target.Positions.PositionClosestTo(currentPos);

            // Give up if the target has moved too far
            if (targetMovementThreshold > WDist.Zero && (targetPos - targetStartPos).LengthSquared > targetMovementThreshold.LengthSquared)
            {
                return(NextActivity);
            }

            // Turn if required
            var delta  = targetPos - currentPos;
            var facing = delta.HorizontalLengthSquared != 0 ? delta.Yaw.Facing : mobile.Facing;

            if (facing != mobile.Facing)
            {
                var turn = ActivityUtils.RunActivityTick(self, new Turn(self, facing));
                if (turn != null)
                {
                    QueueChild(self, turn);
                }

                return(this);
            }

            // Can complete the move in this step
            var speed = mobile.MovementSpeedForCell(self, self.Location);

            if (delta.LengthSquared <= speed * speed)
            {
                mobile.SetVisualPosition(self, targetPos);
                return(NextActivity);
            }

            // Move towards the target
            mobile.SetVisualPosition(self, currentPos + delta * speed / delta.Length);

            return(this);
        }
Example #7
0
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                return(this);
            }

            if (IsCanceling)
            {
                return(NextActivity);
            }

            if (rearmableInfo != null && ammoPools.Any(p => p.Info.Name == info.AmmoPoolName && !p.HasAmmo()))
            {
                // Rearm (and possibly repair) at rearm building, then back out here to refill the minefield some more
                var rearmTarget = self.World.Actors.Where(a => self.Owner.Stances[a.Owner] == Stance.Ally &&
                                                          rearmableInfo.RearmActors.Contains(a.Info.Name))
                                  .ClosestTo(self);

                if (rearmTarget == null)
                {
                    return(NextActivity);
                }

                // Add a CloseEnough range of 512 to the Rearm/Repair activities in order to ensure that we're at the host actor
                QueueChild(self, new MoveAdjacentTo(self, Target.FromActor(rearmTarget)), true);
                QueueChild(self, movement.MoveTo(self.World.Map.CellContaining(rearmTarget.CenterPosition), rearmTarget));
                QueueChild(self, new Resupply(self, rearmTarget, new WDist(512)));
                return(this);
            }

            if ((minefield == null || minefield.Contains(self.Location)) && ShouldLayMine(self, self.Location))
            {
                LayMine(self);
                QueueChild(self, new Wait(20), true);                 // A little wait after placing each mine, for show
                return(this);
            }

            if (minefield != null && minefield.Length > 0)
            {
                // Don't get stuck forever here
                for (var n = 0; n < 20; n++)
                {
                    var p = minefield.Random(self.World.SharedRandom);
                    if (ShouldLayMine(self, p))
                    {
                        QueueChild(self, movement.MoveTo(p, 0), true);
                        return(this);
                    }
                }
            }

            // TODO: Return somewhere likely to be safe (near rearm building) so we're not sitting out in the minefield.
            return(NextActivity);
        }
Example #8
0
 public virtual void QueueChild(Actor self, Activity activity, bool pretick = false)
 {
     if (ChildActivity != null)
     {
         ChildActivity.Queue(self, activity);
     }
     else
     {
         ChildActivity = pretick ? ActivityUtils.RunActivityTick(self, activity) : activity;
     }
 }
Example #9
0
        public override Activity Tick(Actor self)
        {
            if (canceled)
            {
                return(NextActivity);
            }

            // Correct the visual position after we jumped
            if (jumpComplete)
            {
                if (ChildActivity == null)
                {
                    return(NextActivity);
                }

                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                return(this);
            }

            if (target.Type != TargetType.Invalid)
            {
                targetPosition = target.CenterPosition;
            }

            var position = length > 1 ? WPos.Lerp(origin, targetPosition, ticks, length - 1) : targetPosition;

            mobile.SetVisualPosition(self, position);

            // We are at the destination
            if (++ticks >= length)
            {
                // Revoke the run condition
                attack.IsAiming = false;

                // Move to the correct subcells, if our target actor uses subcells
                // (This does not update the visual position!)
                mobile.SetLocation(destinationCell, destinationSubCell, destinationCell, destinationSubCell);

                // Revoke the condition before attacking, as it is usually used to pause the attack trait
                attack.RevokeLeapCondition(self);
                attack.DoAttack(self, target);

                jumpComplete = true;
                QueueChild(self, mobile.VisualMove(self, position, self.World.Map.CenterOfSubCell(destinationCell, destinationSubCell)), true);

                return(this);
            }

            return(this);
        }
Example #10
0
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            if (transportable != null)
            {
                transportable.MovementCancelled(self);
            }

            return(NextActivity);
        }
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            // Try and find a new transport nearby
            if (IsCanceling || string.IsNullOrEmpty(type))
            {
                return(NextActivity);
            }

            Func <Actor, bool> isValidTransport = a =>
            {
                var c = a.TraitOrDefault <Cargo>();
                return(c != null && c.Info.Types.Contains(passenger.Info.CargoType) &&
                       (c.Unloading || c.CanLoad(a, self)));
            };

            var candidates = self.World.FindActorsInCircle(self.CenterPosition, passenger.Info.AlternateTransportScanRange)
                             .Where(isValidTransport)
                             .ToList();

            // Prefer transports of the same type as the primary
            var transport = candidates.Where(a => a.Info.Name == type).ClosestTo(self);

            if (transport == null)
            {
                transport = candidates.ClosestTo(self);
            }

            if (transport != null)
            {
                QueueChild(self, ActivityUtils.RunActivityTick(self, new EnterTransport(self, Target.FromActor(transport))), true);
                return(this);
            }

            return(NextActivity);
        }
Example #12
0
        public override Activity Tick(Actor self)
        {
            if (IsCanceling)
            {
                return(NextActivity);
            }

            if (ChildActivity != null)
            {
                ActivityUtils.RunActivityTick(self, ChildActivity);
                return(this);
            }

            // Prevent deployment in bogus locations
            var transforms = self.TraitOrDefault <Transforms>();

            if (transforms != null && !transforms.CanDeploy())
            {
                Cancel(self, true);
                return(NextActivity);
            }

            foreach (var nt in self.TraitsImplementing <INotifyTransform>())
            {
                nt.BeforeTransform(self);
            }

            var makeAnimation = self.TraitOrDefault <WithMakeAnimation>();

            if (!SkipMakeAnims && makeAnimation != null)
            {
                // Once the make animation starts the activity must not be stopped anymore.
                IsInterruptible = false;

                // Wait forever
                QueueChild(self, new WaitFor(() => false));
                makeAnimation.Reverse(self, () => DoTransform(self));
                return(this);
            }

            return(NextActivity);
        }
        public override Activity Tick(Actor self)
        {
            // We are not currently attacking a target, so scan for new targets.
            if (!IsCanceling && ChildActivity != null && ChildActivity.NextActivity == null && autoTarget != null)
            {
                // ScanForTarget already limits the scanning rate for performance so we don't need to do that here.
                var target = autoTarget.ScanForTarget(self, true, true);
                if (target.Type != TargetType.Invalid)
                {
                    // We have found a target so cancel the current move activity and queue attack activities.
                    ChildActivity.Cancel(self);
                    var attackBases = autoTarget.ActiveAttackBases;
                    foreach (var ab in attackBases)
                    {
                        QueueChild(self, ab.GetAttackActivity(self, target, true, false));
                        ab.OnQueueAttackActivity(self, target, false, true, false);
                    }

                    // Make sure to continue moving when the attack activities have finished.
                    QueueChild(self, getInner());
                }
            }

            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            // The last queued childactivity is guaranteed to be the inner move, so if we get here it means
            // we have reached our destination and there are no more enemies on our path.
            return(NextActivity);
        }
Example #14
0
        public override Activity Tick(Actor self)
        {
            // Update our view of the target
            bool targetIsHiddenActor;

            target = target.Recalculate(self.Owner, out targetIsHiddenActor);
            if (!targetIsHiddenActor && target.Type == TargetType.Actor)
            {
                lastVisibleTarget = Target.FromTargetPositions(target);
            }

            var oldUseLastVisibleTarget = useLastVisibleTarget;

            useLastVisibleTarget = targetIsHiddenActor || !target.IsValidFor(self);

            // Cancel immediately if the target died while we were entering it
            if (!IsCanceling && useLastVisibleTarget && lastState == EnterState.Entering)
            {
                Cancel(self, true);
            }

            TickInner(self, target, useLastVisibleTarget);

            // Update target lines if required
            if (useLastVisibleTarget != oldUseLastVisibleTarget && targetLineColor.HasValue)
            {
                self.SetTargetLine(useLastVisibleTarget ? lastVisibleTarget : target, targetLineColor.Value, false);
            }

            // We need to wait for movement to finish before transitioning to
            // the next state or next activity
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            // Note that lastState refers to what we have just *finished* doing
            switch (lastState)
            {
            case EnterState.Approaching:
            {
                // NOTE: We can safely cancel in this case because we know the
                // actor has finished any in-progress move activities
                if (IsCanceling)
                {
                    return(NextActivity);
                }

                // Lost track of the target
                if (useLastVisibleTarget && lastVisibleTarget.Type == TargetType.Invalid)
                {
                    return(NextActivity);
                }

                // We are not next to the target - lets fix that
                if (target.Type != TargetType.Invalid && !move.CanEnterTargetNow(self, target))
                {
                    // Target lines are managed by this trait, so we do not pass targetLineColor
                    var initialTargetPosition = (useLastVisibleTarget ? lastVisibleTarget : target).CenterPosition;
                    QueueChild(self, move.MoveToTarget(self, target, initialTargetPosition), true);
                    return(this);
                }

                // We are next to where we thought the target should be, but it isn't here
                // There's not much more we can do here
                if (useLastVisibleTarget || target.Type != TargetType.Actor)
                {
                    return(NextActivity);
                }

                // Are we ready to move into the target?
                if (TryStartEnter(self, target.Actor))
                {
                    lastState = EnterState.Entering;
                    QueueChild(self, move.MoveIntoTarget(self, target), true);
                    return(this);
                }

                // Subclasses can cancel the activity during TryStartEnter
                // Return immediately to avoid an extra tick's delay
                if (IsCanceling)
                {
                    return(NextActivity);
                }

                return(this);
            }

            case EnterState.Entering:
            {
                // Check that we reached the requested position
                var targetPos = target.Positions.PositionClosestTo(self.CenterPosition);
                if (!IsCanceling && self.CenterPosition == targetPos && target.Type == TargetType.Actor)
                {
                    OnEnterComplete(self, target.Actor);
                }

                lastState = EnterState.Exiting;
                QueueChild(self, move.MoveIntoWorld(self, self.Location), true);
                return(this);
            }

            case EnterState.Exiting:
                return(NextActivity);
            }

            return(this);
        }
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            //далее все return NextActivity означают, завершение текущей Activity,так как она выполнила свои задачи.
            //а return this означает, что остаемся внутри этой Activity

            //if (IsCanceling)
            //	return NextActivity;

            //if (NextActivity != null)
            //{
            //	// Interrupt automated harvesting after clearing the first cell.
            //	if (!harvInfo.QueueFullLoad && (hasHarvestedCell || harv.LastSearchFailed))
            //		return NextActivity;

            //	// Interrupt automated harvesting after first complete harvest cycle.
            //	if (hasDeliveredLoad || harv.IsFull)
            //		return NextActivity;
            //}

            if (!hasWaited)
            {
                //var moveTo = mobile.NearestMoveableCell(unblockCell, 1, 5);
                //QueueChild(self, mobile.MoveTo(moveTo, 1), true);
                //QueueChild(self, new Wait(200), true);
                hasWaited = true;
                return(this);
            }

            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 no harvestable position could be found and we are at the refinery, get out of the way
            // of the refinery entrance.
            //if (harv.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);
            //			self.SetTargetLine(Target.FromCell(self.World, moveTo), Color.Green, false);
            //			QueueChild(self, mobile.MoveTo(moveTo, 1), true);
            //		}
            //	}

            //	return this;
            //}
            if (closestHarvestableCell == null)
            {
                return(this);
            }
            // If we get here, our search for resources was successful. Commence harvesting.
            QueueChild(self, new EatResource(self, closestHarvestableCell.Value), true);
            lastHarvestedCell = closestHarvestableCell.Value;
            hasHarvestedCell  = true;
            return(this);
        }
Example #16
0
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            if (IsCanceling || harv.IsFull)
            {
                return(NextActivity);
            }

            // Move towards the target cell
            if (self.Location != targetCell)
            {
                foreach (var n in self.TraitsImplementing <INotifyHarvesterAction>())
                {
                    n.MovingToResources(self, targetCell, new FindAndDeliverResources(self));
                }

                self.SetTargetLine(Target.FromCell(self.World, targetCell), Color.Red, false);
                QueueChild(self, move.MoveTo(targetCell, 2), true);
                return(this);
            }

            if (!harv.CanHarvestCell(self, self.Location))
            {
                return(NextActivity);
            }

            // Turn to one of the harvestable facings
            if (harvInfo.HarvestFacings != 0)
            {
                var current = facing.Facing;
                var desired = body.QuantizeFacing(current, harvInfo.HarvestFacings);
                if (desired != current)
                {
                    QueueChild(self, new Turn(self, desired), true);
                    return(this);
                }
            }

            var resource = resLayer.Harvest(self.Location);

            if (resource == null)
            {
                return(NextActivity);
            }

            harv.AcceptResource(self, resource);

            foreach (var t in self.TraitsImplementing <INotifyHarvesterAction>())
            {
                t.Harvested(self, resource);
            }

            QueueChild(self, new Wait(harvInfo.BaleLoadDelay), true);
            return(this);
        }
Example #17
0
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            cargo.Unloading = false;
            if (IsCanceling || cargo.IsEmpty(self))
            {
                return(NextActivity);
            }

            if (!cargo.CanUnload())
            {
                Cancel(self, true);
                return(NextActivity);
            }

            foreach (var inu in notifiers)
            {
                inu.Unloading(self);
            }

            var actor = cargo.Peek(self);
            var spawn = self.CenterPosition;

            var exitSubCell = ChooseExitSubCell(actor);

            if (exitSubCell == null)
            {
                self.NotifyBlocker(BlockedExitCells(actor));
                QueueChild(self, new Wait(10), true);
                return(this);
            }

            cargo.Unload(self);
            self.World.AddFrameEndTask(w =>
            {
                if (actor.Disposed)
                {
                    return;
                }

                var move = actor.Trait <IMove>();
                var pos  = actor.Trait <IPositionable>();

                actor.CancelActivity();
                pos.SetVisualPosition(actor, spawn);
                actor.QueueActivity(move.MoveIntoWorld(actor, exitSubCell.Value.First, exitSubCell.Value.Second));
                actor.SetTargetLine(Target.FromCell(w, exitSubCell.Value.First, exitSubCell.Value.Second), Color.Green, false);
                w.Add(actor);
            });

            if (!unloadAll || cargo.IsEmpty(self))
            {
                return(NextActivity);
            }

            cargo.Unloading = true;
            return(this);
        }
Example #18
0
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            if (IsCanceling)
            {
                return(NextActivity);
            }

            if (NextActivity != null)
            {
                // Interrupt automated harvesting after clearing the first cell.
                if (!harvInfo.QueueFullLoad && (hasHarvestedCell || harv.LastSearchFailed))
                {
                    return(NextActivity);
                }

                // Interrupt automated harvesting after first complete harvest cycle.
                if (hasDeliveredLoad || harv.IsFull)
                {
                    return(NextActivity);
                }
            }

            // Are we full or have nothing more to gather? Deliver resources.
            if (harv.IsFull || (!harv.IsEmpty && harv.LastSearchFailed))
            {
                QueueChild(self, new DeliverResources(self), true);
                hasDeliveredLoad = true;
                return(this);
            }

            // After a failed search, wait and sit still for a bit before searching again.
            if (harv.LastSearchFailed && !hasWaited)
            {
                QueueChild(self, new Wait(harv.Info.WaitDuration), true);
                hasWaited = true;
                return(this);
            }

            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);
                    harv.LastSearchFailed  = !closestHarvestableCell.HasValue;
                }
                else
                {
                    harv.LastSearchFailed = true;
                }
            }
            else
            {
                harv.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 (harv.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);
                        self.SetTargetLine(Target.FromCell(self.World, moveTo), Color.Green, false);
                        QueueChild(self, mobile.MoveTo(moveTo, 1), true);
                    }
                }

                return(this);
            }

            // If we get here, our search for resources was successful. Commence harvesting.
            QueueChild(self, new HarvestResource(self, closestHarvestableCell.Value), true);
            lastHarvestedCell = closestHarvestableCell.Value;
            hasHarvestedCell  = true;
            return(this);
        }
Example #19
0
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            // Refuse to take off if it would land immediately again.
            // Special case: Don't kill other deploy hotkey activities.
            if (aircraft.ForceLanding)
            {
                return(NextActivity);
            }

            // If a Cancel was triggered at this point, it's unlikely that previously queued child activities finished,
            // so 'resupplied' needs to be set to false, else it + abortOnResupply might cause another Cancel
            // that would cancel any other activities that were queued after the first Cancel was triggered.
            // TODO: This is a mess, we need to somehow make the activity cancelling a bit less tricky.
            if (resupplied && IsCanceling)
            {
                resupplied = false;
            }

            if (resupplied && abortOnResupply)
            {
                Cancel(self);
            }

            if (resupplied || IsCanceling || self.IsDead)
            {
                return(NextActivity);
            }

            if (dest == null || dest.IsDead || !Reservable.IsAvailableFor(dest, self))
            {
                dest = ReturnToBase.ChooseResupplier(self, true);
            }

            if (!isCalculated)
            {
                Calculate(self);
            }

            if (dest == null)
            {
                var nearestResupplier = ChooseResupplier(self, false);

                if (nearestResupplier != null)
                {
                    if (aircraft.Info.CanHover)
                    {
                        var distanceFromResupplier = (nearestResupplier.CenterPosition - self.CenterPosition).HorizontalLength;
                        var distanceLength         = aircraft.Info.WaitDistanceFromResupplyBase.Length;

                        // If no pad is available, move near one and wait
                        if (distanceFromResupplier > distanceLength)
                        {
                            var randomPosition = WVec.FromPDF(self.World.SharedRandom, 2) * distanceLength / 1024;
                            var target         = Target.FromPos(nearestResupplier.CenterPosition + randomPosition);

                            QueueChild(self, new HeliFly(self, target, WDist.Zero, aircraft.Info.WaitDistanceFromResupplyBase, targetLineColor: Color.Green), true);
                        }

                        return(this);
                    }
                    else
                    {
                        QueueChild(self,
                                   new Fly(self, Target.FromActor(nearestResupplier), WDist.Zero, aircraft.Info.WaitDistanceFromResupplyBase, targetLineColor: Color.Green),
                                   true);

                        QueueChild(self, new FlyCircle(self, aircraft.Info.NumberOfTicksToVerifyAvailableAirport), true);
                        return(this);
                    }
                }
                else if (nearestResupplier == null && aircraft.Info.VTOL && aircraft.Info.LandWhenIdle)
                {
                    // Using Queue instead of QueueChild here is intentional, as we want VTOLs with LandWhenIdle to land and stay there in this situation
                    Cancel(self);
                    if (aircraft.Info.TurnToLand)
                    {
                        Queue(self, new Turn(self, aircraft.Info.InitialFacing));
                    }

                    Queue(self, new Land(self));
                    return(NextActivity);
                }
                else
                {
                    // Prevent an infinite loop in case we'd return to the activity that called ReturnToBase in the first place. Go idle instead.
                    Cancel(self);
                    return(NextActivity);
                }
            }

            var exit   = dest.FirstExitOrDefault(null);
            var offset = exit != null ? exit.Info.SpawnOffset : WVec.Zero;

            if (aircraft.Info.CanHover)
            {
                QueueChild(self, new HeliFly(self, Target.FromPos(dest.CenterPosition + offset)), true);
            }
            else if (aircraft.Info.VTOL)
            {
                QueueChild(self, new Fly(self, Target.FromPos(dest.CenterPosition + offset)), true);
            }
            else
            {
                var turnRadius = Fly.CalculateTurnRadius(aircraft.Info.Speed, aircraft.Info.TurnSpeed);

                QueueChild(self, new Fly(self, Target.FromPos(w1), WDist.Zero, new WDist(turnRadius * 3)), true);
                QueueChild(self, new Fly(self, Target.FromPos(w2)), true);

                // Fix a problem when the airplane is sent to resupply near the airport
                QueueChild(self, new Fly(self, Target.FromPos(w3), WDist.Zero, new WDist(turnRadius / 2)), true);
            }

            if (ShouldLandAtBuilding(self, dest))
            {
                aircraft.MakeReservation(dest);

                if (aircraft.Info.VTOL && aircraft.Info.TurnToDock)
                {
                    QueueChild(self, new Turn(self, aircraft.Info.InitialFacing), true);
                }

                QueueChild(self, new Land(self, Target.FromActor(dest), offset), true);
                QueueChild(self, new Resupply(self, dest, WDist.Zero), true);
                resupplied = true;
            }

            return(this);
        }
Example #20
0
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            //if (IsCanceling || harv.IsFull)
            //	return NextActivity;

            // Move towards the target cell
            if (self.Location != targetCell)
            {
                //foreach (var n in self.TraitsImplementing<INotifyHarvesterAction>())
                //	n.MovingToResources(self, targetCell, new FindAndDeliverResources(self));

                self.SetTargetLine(Target.FromCell(self.World, targetCell), Color.Red, false);
                QueueChild(self, move.MoveTo(targetCell, 2), true);
                return(this);
            }

            if (!harv.CanHarvestCell(self, self.Location))
            {
                return(NextActivity);
            }

            // Turn to one of the harvestable facings
            //if (harvInfo.HarvestFacings != 0)
            //{
            //	var current = facing.Facing;
            //	var desired = body.QuantizeFacing(current, harvInfo.HarvestFacings);
            //	if (desired != current)
            //	{
            //		QueueChild(self, new Turn(self, desired), true);
            //		return this;
            //	}
            //}

            var resource = resLayer.Harvest(self.Location);

            if (resource == null)
            {
                return(NextActivity);
            }

            harv.AcceptResource(self, resource);


            // это событие ловится WithHarverAnimation классом, чтобы 1 раз проиграть анимацию сборки спайса.
            //foreach (var t in self.TraitsImplementing<INotifyHarvesterAction>())
            //	t.Harvested(self, resource);

            foreach (var t in self.TraitsImplementing <INotifyAttack>())
            {
                if (t is OpenRA.Mods.Common.Traits.Render.WithAttackOverlay)                 //возьмем анимацию для поедания из анимации атаки.
                {
                    t.PreparingAttack(self, new Target(), null, null);
                }
            }
            //этой Activity делается пауза после одного съедания спайса, чтобы не сосать как пылесосом и подождать анимацию глотания спайса

            //QueueChild(self, new Wait(500), true);
            return(this);
        }
Example #21
0
            public override Activity Tick(Actor self)
            {
                if (ChildActivity != null)
                {
                    ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                    if (ChildActivity != null)
                    {
                        return(this);
                    }
                }

                if (IsCanceling)
                {
                    // Cancel the requested target, but keep firing on it while in range
                    if (attack.Info.PersistentTargeting)
                    {
                        attack.OpportunityTarget      = attack.RequestedTarget;
                        attack.OpportunityForceAttack = attack.RequestedForceAttack;
                        attack.OpportunityTargetIsPersistentTarget = true;
                    }

                    attack.RequestedTarget = Target.Invalid;
                    return(NextActivity);
                }

                // Check that AttackFollow hasn't cancelled the target by modifying attack.Target
                // Having both this and AttackFollow modify that field is a horrible hack.
                if (hasTicked && attack.RequestedTarget.Type == TargetType.Invalid)
                {
                    return(NextActivity);
                }

                if (attack.IsTraitPaused)
                {
                    return(this);
                }

                bool targetIsHiddenActor;

                attack.RequestedForceAttack    = forceAttack;
                attack.RequestedTarget         = target = target.Recalculate(self.Owner, out targetIsHiddenActor);
                attack.RequestedTargetLastTick = self.World.WorldTick;
                hasTicked = true;

                if (!targetIsHiddenActor && target.Type == TargetType.Actor)
                {
                    lastVisibleTarget       = Target.FromTargetPositions(target);
                    lastVisibleMaximumRange = attack.GetMaximumRangeVersusTarget(target);
                    lastVisibleMinimumRange = attack.GetMinimumRange();
                    lastVisibleOwner        = target.Actor.Owner;
                    lastVisibleTargetTypes  = target.Actor.GetEnabledTargetTypes();

                    // Try and sit at least one cell away from the min or max ranges to give some leeway if the target starts moving.
                    if (move != null && target.Actor.Info.HasTraitInfo <IMoveInfo>())
                    {
                        var preferMinRange = Math.Min(lastVisibleMinimumRange.Length + 1024, lastVisibleMaximumRange.Length);
                        var preferMaxRange = Math.Max(lastVisibleMaximumRange.Length - 1024, lastVisibleMinimumRange.Length);
                        lastVisibleMaximumRange = new WDist((lastVisibleMaximumRange.Length - 1024).Clamp(preferMinRange, preferMaxRange));
                    }
                }

                var oldUseLastVisibleTarget = useLastVisibleTarget;
                var maxRange = lastVisibleMaximumRange;
                var minRange = lastVisibleMinimumRange;

                useLastVisibleTarget = targetIsHiddenActor || !target.IsValidFor(self);

                // Most actors want to be able to see their target before shooting
                if (target.Type == TargetType.FrozenActor && !attack.Info.TargetFrozenActors && !forceAttack)
                {
                    var rs = revealsShroud
                             .Where(Exts.IsTraitEnabled)
                             .MaxByOrDefault(s => s.Range);

                    // Default to 2 cells if there are no active traits
                    var sightRange = rs != null ? rs.Range : WDist.FromCells(2);
                    if (sightRange < maxRange)
                    {
                        maxRange = sightRange;
                    }
                }

                // If we are ticking again after previously sequencing a MoveWithRange then that move must have completed
                // Either we are in range and can see the target, or we've lost track of it and should give up
                if (wasMovingWithinRange && targetIsHiddenActor)
                {
                    attack.RequestedTarget = Target.Invalid;
                    return(NextActivity);
                }

                // Update target lines if required
                if (useLastVisibleTarget != oldUseLastVisibleTarget)
                {
                    self.SetTargetLine(useLastVisibleTarget ? lastVisibleTarget : target, Color.Red, false);
                }

                // Target is hidden or dead, and we don't have a fallback position to move towards
                if (useLastVisibleTarget && !lastVisibleTarget.IsValidFor(self))
                {
                    attack.RequestedTarget = Target.Invalid;
                    return(NextActivity);
                }

                var pos         = self.CenterPosition;
                var checkTarget = useLastVisibleTarget ? lastVisibleTarget : target;

                // We've reached the required range - if the target is visible and valid then we wait
                // otherwise if it is hidden or dead we give up
                if (checkTarget.IsInRange(pos, maxRange) && !checkTarget.IsInRange(pos, minRange))
                {
                    if (useLastVisibleTarget)
                    {
                        attack.RequestedTarget = Target.Invalid;
                        return(NextActivity);
                    }

                    return(this);
                }

                // We can't move into range, so give up
                if (move == null || maxRange == WDist.Zero || maxRange < minRange)
                {
                    attack.RequestedTarget = Target.Invalid;
                    return(NextActivity);
                }

                wasMovingWithinRange = true;
                QueueChild(self, move.MoveWithinRange(target, minRange, maxRange, checkTarget.CenterPosition, Color.Red), true);
                return(this);
            }
Example #22
0
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                return(this);
            }

            if (IsCanceling)
            {
                return(NextActivity);
            }

            bool targetIsHiddenActor;

            target = RecalculateTarget(self, out targetIsHiddenActor);
            if (!targetIsHiddenActor && target.Type == TargetType.Actor)
            {
                lastVisibleTarget       = Target.FromTargetPositions(target);
                lastVisibleMaximumRange = attackTraits.Where(x => !x.IsTraitDisabled)
                                          .Min(x => x.GetMaximumRangeVersusTarget(target));

                lastVisibleOwner       = target.Actor.Owner;
                lastVisibleTargetTypes = target.Actor.GetEnabledTargetTypes();
            }

            var oldUseLastVisibleTarget = useLastVisibleTarget;

            useLastVisibleTarget = targetIsHiddenActor || !target.IsValidFor(self);

            // If we are ticking again after previously sequencing a MoveWithRange then that move must have completed
            // Either we are in range and can see the target, or we've lost track of it and should give up
            if (wasMovingWithinRange && targetIsHiddenActor)
            {
                return(NextActivity);
            }

            // Update target lines if required
            if (useLastVisibleTarget != oldUseLastVisibleTarget)
            {
                self.SetTargetLine(useLastVisibleTarget ? lastVisibleTarget : target, Color.Red, false);
            }

            // Target is hidden or dead, and we don't have a fallback position to move towards
            if (useLastVisibleTarget && !lastVisibleTarget.IsValidFor(self))
            {
                return(NextActivity);
            }

            wasMovingWithinRange = false;
            var pos         = self.CenterPosition;
            var checkTarget = useLastVisibleTarget ? lastVisibleTarget : target;

            // We don't know where the target actually is, so move to where we last saw it
            if (useLastVisibleTarget)
            {
                // We've reached the assumed position but it is not there or we can't move any further - give up
                if (checkTarget.IsInRange(pos, lastVisibleMaximumRange) || move == null || lastVisibleMaximumRange == WDist.Zero)
                {
                    return(NextActivity);
                }

                // Move towards the last known position
                wasMovingWithinRange = true;
                QueueChild(self, move.MoveWithinRange(target, WDist.Zero, lastVisibleMaximumRange, checkTarget.CenterPosition, Color.Red), true);
                return(this);
            }

            attackStatus = AttackStatus.UnableToAttack;

            foreach (var attack in attackTraits.Where(x => !x.IsTraitDisabled))
            {
                var status = TickAttack(self, attack);
                attack.IsAiming = status == AttackStatus.Attacking || status == AttackStatus.NeedsToTurn;
            }

            if (attackStatus.HasFlag(AttackStatus.NeedsToMove))
            {
                wasMovingWithinRange = true;
            }

            if (attackStatus >= AttackStatus.NeedsToTurn)
            {
                return(this);
            }

            return(NextActivity);
        }
Example #23
0
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            if (IsCanceling)
            {
                return(NextActivity);
            }

            bool targetIsHiddenActor;

            target = target.Recalculate(self.Owner, out targetIsHiddenActor);
            if (!targetIsHiddenActor && target.Type == TargetType.Actor)
            {
                lastVisibleTarget = Target.FromTargetPositions(target);
            }

            var oldUseLastVisibleTarget = useLastVisibleTarget;

            useLastVisibleTarget = targetIsHiddenActor || !target.IsValidFor(self);

            // If we are ticking again after previously sequencing a MoveWithRange then that move must have completed
            // Either we are in range and can see the target, or we've lost track of it and should give up
            if (wasMovingWithinRange && targetIsHiddenActor)
            {
                return(NextActivity);
            }

            wasMovingWithinRange = false;

            // Update target lines if required
            if (useLastVisibleTarget != oldUseLastVisibleTarget && targetLineColor.HasValue)
            {
                self.SetTargetLine(useLastVisibleTarget ? lastVisibleTarget : target, targetLineColor.Value, false);
            }

            // Target is hidden or dead, and we don't have a fallback position to move towards
            if (useLastVisibleTarget && !lastVisibleTarget.IsValidFor(self))
            {
                return(NextActivity);
            }

            var pos         = self.CenterPosition;
            var checkTarget = useLastVisibleTarget ? lastVisibleTarget : target;

            // We've reached the required range - if the target is visible and valid then we wait
            // otherwise if it is hidden or dead we give up
            if (checkTarget.IsInRange(pos, maxRange) && !checkTarget.IsInRange(pos, minRange))
            {
                return(useLastVisibleTarget ? NextActivity : this);
            }

            // Move into range
            wasMovingWithinRange = true;
            QueueChild(self, move.MoveWithinRange(target, minRange, maxRange, checkTarget.CenterPosition, targetLineColor), true);
            return(this);
        }
Example #24
0
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            if (cargo != carryall.Carryable)
            {
                return(NextActivity);
            }

            if (cargo.IsDead || IsCanceling || carryable.IsTraitDisabled || !cargo.AppearsFriendlyTo(self))
            {
                carryall.UnreserveCarryable(self);
                return(NextActivity);
            }

            if (carryall.State != Carryall.CarryallState.Reserved)
            {
                return(NextActivity);
            }

            switch (state)
            {
            case PickupState.Intercept:
                QueueChild(self, movement.MoveWithinRange(Target.FromActor(cargo), WDist.FromCells(4), targetLineColor: Color.Yellow), true);
                state = PickupState.LockCarryable;
                return(this);

            case PickupState.LockCarryable:
                if (!carryable.LockForPickup(cargo, self))
                {
                    Cancel(self);
                }

                state = PickupState.MoveToCarryable;
                return(this);

            case PickupState.MoveToCarryable:
            {
                // Line up with the attachment point
                var localOffset    = carryall.OffsetForCarryable(self, cargo).Rotate(carryableBody.QuantizeOrientation(self, cargo.Orientation));
                var targetPosition = cargo.CenterPosition - carryableBody.LocalToWorld(localOffset);
                if ((self.CenterPosition - targetPosition).HorizontalLengthSquared != 0)
                {
                    QueueChild(self, new HeliFly(self, Target.FromPos(targetPosition)), true);
                    return(this);
                }

                state = PickupState.Turn;
                return(this);
            }

            case PickupState.Turn:
                if (carryallFacing.Facing != carryableFacing.Facing)
                {
                    QueueChild(self, new Turn(self, carryableFacing.Facing), true);
                    return(this);
                }

                state = PickupState.Land;
                return(this);

            case PickupState.Land:
            {
                var localOffset    = carryall.OffsetForCarryable(self, cargo).Rotate(carryableBody.QuantizeOrientation(self, cargo.Orientation));
                var targetPosition = cargo.CenterPosition - carryableBody.LocalToWorld(localOffset);
                if ((self.CenterPosition - targetPosition).HorizontalLengthSquared != 0 || carryallFacing.Facing != carryableFacing.Facing)
                {
                    state = PickupState.MoveToCarryable;
                    return(this);
                }

                if (targetPosition.Z != self.CenterPosition.Z)
                {
                    QueueChild(self, new Land(self, Target.FromActor(cargo), -carryableBody.LocalToWorld(localOffset)));
                    return(this);
                }

                state = delay > 0 ? PickupState.Wait : PickupState.Pickup;
                return(this);
            }

            case PickupState.Wait:
                QueueChild(self, new Wait(delay, false), true);
                state = PickupState.Pickup;
                return(this);

            case PickupState.Pickup:
                // Remove our carryable from world
                Attach(self);
                return(this);
            }

            return(NextActivity);
        }
Example #25
0
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            if (IsCanceling || carryall.State != Carryall.CarryallState.Carrying || carryall.Carryable.IsDead)
            {
                return(NextActivity);
            }

            // Drop the actor at the current position
            if (destination.Type == TargetType.Invalid)
            {
                destination = Target.FromCell(self.World, self.Location);
            }

            var target = FindDropLocation(destination, carryall.Info.DropRange);

            // Can't land, so wait at the target until something changes
            if (target.Type == TargetType.Invalid)
            {
                QueueChild(self, new HeliFly(self, destination), true);
                QueueChild(self, new Wait(25));
                return(this);
            }

            // Move to drop-off location
            var localOffset       = carryall.CarryableOffset.Rotate(body.QuantizeOrientation(self, self.Orientation));
            var carryablePosition = self.CenterPosition + body.LocalToWorld(localOffset);

            if ((carryablePosition - target.CenterPosition).HorizontalLengthSquared != 0)
            {
                // For non-zero offsets the drop position depends on the carryall facing
                // We therefore need to predict/correct for the facing *at the drop point*
                if (carryall.CarryableOffset.HorizontalLengthSquared != 0)
                {
                    var dropFacing = (target.CenterPosition - self.CenterPosition).Yaw.Facing;
                    localOffset = carryall.CarryableOffset.Rotate(body.QuantizeOrientation(self, WRot.FromFacing(dropFacing)));
                    QueueChild(self, new HeliFly(self, Target.FromPos(target.CenterPosition - body.LocalToWorld(localOffset))), true);
                    QueueChild(self, new Turn(self, dropFacing));
                    return(this);
                }

                QueueChild(self, new HeliFly(self, target), true);
                return(this);
            }

            // Make sure that the carried actor is on the ground before releasing it
            if (self.World.Map.DistanceAboveTerrain(carryablePosition) != WDist.Zero)
            {
                QueueChild(self, new Land(self), true);
            }

            // Pause briefly before releasing for visual effect
            if (carryall.Info.UnloadingDelay > 0)
            {
                QueueChild(self, new Wait(carryall.Info.UnloadingDelay, false), true);
            }

            // Release carried actor
            QueueChild(self, new ReleaseUnit(self));
            QueueChild(self, new HeliFly(self, Target.FromPos(self.CenterPosition)));
            return(this);
        }
Example #26
0
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            // Refuse to take off if it would land immediately again.
            if (aircraft.ForceLanding)
            {
                Cancel(self);
            }

            if (IsCanceling)
            {
                // Cancel the requested target, but keep firing on it while in range
                if (attackAircraft.Info.PersistentTargeting)
                {
                    attackAircraft.OpportunityTarget      = attackAircraft.RequestedTarget;
                    attackAircraft.OpportunityForceAttack = attackAircraft.RequestedForceAttack;
                    attackAircraft.OpportunityTargetIsPersistentTarget = true;
                }

                attackAircraft.RequestedTarget = Target.Invalid;
                return(NextActivity);
            }

            // Check that AttackFollow hasn't cancelled the target by modifying attack.Target
            // Having both this and AttackFollow modify that field is a horrible hack.
            if (hasTicked && attackAircraft.RequestedTarget.Type == TargetType.Invalid)
            {
                return(NextActivity);
            }

            if (attackAircraft.IsTraitPaused)
            {
                return(this);
            }

            bool targetIsHiddenActor;

            attackAircraft.RequestedTarget         = target = target.Recalculate(self.Owner, out targetIsHiddenActor);
            attackAircraft.RequestedTargetLastTick = self.World.WorldTick;
            hasTicked = true;

            if (!targetIsHiddenActor && target.Type == TargetType.Actor)
            {
                lastVisibleTarget       = Target.FromTargetPositions(target);
                lastVisibleMaximumRange = attackAircraft.GetMaximumRangeVersusTarget(target);
                lastVisibleOwner        = target.Actor.Owner;
                lastVisibleTargetTypes  = target.Actor.GetEnabledTargetTypes();
            }

            var oldUseLastVisibleTarget = useLastVisibleTarget;

            useLastVisibleTarget = targetIsHiddenActor || !target.IsValidFor(self);

            // Update target lines if required
            if (useLastVisibleTarget != oldUseLastVisibleTarget)
            {
                self.SetTargetLine(useLastVisibleTarget ? lastVisibleTarget : target, Color.Red, false);
            }

            // Target is hidden or dead, and we don't have a fallback position to move towards
            if (useLastVisibleTarget && !lastVisibleTarget.IsValidFor(self))
            {
                attackAircraft.RequestedTarget = Target.Invalid;
                return(NextActivity);
            }

            // If all valid weapons have depleted their ammo and Rearmable trait exists, return to RearmActor to reload and then resume the activity
            if (rearmable != null && !useLastVisibleTarget && attackAircraft.Armaments.All(x => x.IsTraitPaused || !x.Weapon.IsValidAgainst(target, self.World, self)))
            {
                QueueChild(self, new ReturnToBase(self, aircraft.Info.AbortOnResupply), true);
                return(this);
            }

            var pos         = self.CenterPosition;
            var checkTarget = useLastVisibleTarget ? lastVisibleTarget : target;

            // We don't know where the target actually is, so move to where we last saw it
            if (useLastVisibleTarget)
            {
                // We've reached the assumed position but it is not there - give up
                if (checkTarget.IsInRange(pos, lastVisibleMaximumRange))
                {
                    attackAircraft.RequestedTarget = Target.Invalid;
                    return(NextActivity);
                }

                // Fly towards the last known position
                QueueChild(self, new Fly(self, target, WDist.Zero, lastVisibleMaximumRange, checkTarget.CenterPosition, Color.Red), true);
                return(this);
            }

            if (self.World.Map.DistanceAboveTerrain(self.CenterPosition).Length < aircraft.Info.MinAirborneAltitude)
            {
                QueueChild(self, new TakeOff(self), true);
            }

            if (attackAircraft != null && target.IsInRange(self.CenterPosition, attackAircraft.GetMinimumRange()))
            {
                QueueChild(self, new FlyTimed(ticksUntilTurn, self), true);
            }

            QueueChild(self, new Fly(self, target, checkTarget.CenterPosition, Color.Red), true);
            QueueChild(self, new FlyTimed(ticksUntilTurn, self));

            return(this);
        }
        public override Activity Tick(Actor self)
        {
            bool targetIsHiddenActor;
            var  oldTargetLocation = lastVisibleTargetLocation;

            target = target.Recalculate(self.Owner, out targetIsHiddenActor);
            if (!targetIsHiddenActor && target.Type == TargetType.Actor)
            {
                lastVisibleTarget         = Target.FromTargetPositions(target);
                lastVisibleTargetLocation = self.World.Map.CellContaining(target.CenterPosition);
            }

            // Target is equivalent to checkTarget variable in other activities
            // value is either lastVisibleTarget or target based on visibility and validity
            var targetIsValid           = Target.IsValidFor(self);
            var oldUseLastVisibleTarget = useLastVisibleTarget;

            useLastVisibleTarget = targetIsHiddenActor || !targetIsValid;

            // Update target lines if required
            if (useLastVisibleTarget != oldUseLastVisibleTarget && targetLineColor.HasValue)
            {
                self.SetTargetLine(useLastVisibleTarget ? lastVisibleTarget : target, targetLineColor.Value, false);
            }

            // Target is hidden or dead, and we don't have a fallback position to move towards
            var noTarget = useLastVisibleTarget && !lastVisibleTarget.IsValidFor(self);

            // Inner move order has completed.
            if (ChildActivity == null)
            {
                // We are done here if the order was cancelled for any
                // reason except the target moving.
                if (IsCanceling || !repath || !targetIsValid)
                {
                    return(NextActivity);
                }

                // Target has moved, and MoveAdjacentTo is still valid.
                ChildActivity = Mobile.MoveTo(() => CalculatePathToTarget(self));
                repath        = false;
            }

            // Cancel the current path if the activity asks to stop, or asks to repath
            // The repath happens once the move activity stops in the next cell
            var shouldStop   = ShouldStop(self);
            var shouldRepath = targetIsValid && !repath && ShouldRepath(self, oldTargetLocation);

            if (shouldStop || shouldRepath || noTarget)
            {
                if (ChildActivity != null)
                {
                    ChildActivity.Cancel(self);
                }

                repath = shouldRepath;
            }

            // Ticks the inner move activity to actually move the actor.
            ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);

            return(this);
        }
Example #28
0
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            switch (dockingState)
            {
            case DockingState.Wait:
                return(this);

            case DockingState.Turn:
                dockingState = DockingState.Dock;
                QueueChild(self, new Turn(self, DockAngle), true);
                if (IsDragRequired)
                {
                    QueueChild(self, new Drag(self, StartDrag, EndDrag, DragLength));
                }
                return(this);

            case DockingState.Dock:
                if (Refinery.IsInWorld && !Refinery.IsDead)
                {
                    foreach (var nd in Refinery.TraitsImplementing <INotifyDocking>())
                    {
                        nd.Docked(Refinery, self);
                    }
                }

                return(OnStateDock(self));

            case DockingState.Loop:
                if (!Refinery.IsInWorld || Refinery.IsDead || Harv.TickUnload(self, Refinery))
                {
                    dockingState = DockingState.Undock;
                }

                return(this);

            case DockingState.Undock:
                return(OnStateUndock(self));

            case DockingState.Complete:
                if (Refinery.IsInWorld && !Refinery.IsDead)
                {
                    foreach (var nd in Refinery.TraitsImplementing <INotifyDocking>())
                    {
                        nd.Undocked(Refinery, self);
                    }
                }

                Harv.LastLinkedProc = Harv.LinkedProc;
                Harv.LinkProc(self, null);
                if (IsDragRequired)
                {
                    QueueChild(self, new Drag(self, EndDrag, StartDrag, DragLength), true);
                }

                dockingState = DockingState.Finished;
                return(this);

            case DockingState.Finished:
                return(NextActivity);
            }

            throw new InvalidOperationException("Invalid harvester dock state");
        }
Example #29
0
        public override Activity Tick(Actor self)
        {
            // Run this even if the target became invalid to avoid visual glitches
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                return(this);
            }

            if (IsCanceling)
            {
                return(NextActivity);
            }

            bool targetIsHiddenActor;

            target = target.Recalculate(self.Owner, out targetIsHiddenActor);
            if (!targetIsHiddenActor && target.Type == TargetType.Actor)
            {
                lastVisibleTarget      = Target.FromTargetPositions(target);
                lastVisibleMinRange    = attack.GetMinimumRangeVersusTarget(target);
                lastVisibleMaxRange    = attack.GetMaximumRangeVersusTarget(target);
                lastVisibleOwner       = target.Actor.Owner;
                lastVisibleTargetTypes = target.Actor.GetEnabledTargetTypes();
            }

            var oldUseLastVisibleTarget = useLastVisibleTarget;

            useLastVisibleTarget = targetIsHiddenActor || !target.IsValidFor(self);

            // Update target lines if required
            if (useLastVisibleTarget != oldUseLastVisibleTarget)
            {
                self.SetTargetLine(useLastVisibleTarget ? lastVisibleTarget : target, Color.Red, false);
            }

            // Target is hidden or dead, and we don't have a fallback position to move towards
            if (useLastVisibleTarget && !lastVisibleTarget.IsValidFor(self))
            {
                return(NextActivity);
            }

            var pos         = self.CenterPosition;
            var checkTarget = useLastVisibleTarget ? lastVisibleTarget : target;

            if (!checkTarget.IsInRange(pos, lastVisibleMaxRange) || checkTarget.IsInRange(pos, lastVisibleMinRange))
            {
                if (!allowMovement || lastVisibleMaxRange == WDist.Zero || lastVisibleMaxRange < lastVisibleMinRange)
                {
                    return(NextActivity);
                }

                QueueChild(self, mobile.MoveWithinRange(target, lastVisibleMinRange, lastVisibleMaxRange, checkTarget.CenterPosition, Color.Red), true);
                return(this);
            }

            // Ready to leap, but target isn't visible
            if (targetIsHiddenActor || target.Type != TargetType.Actor)
            {
                return(NextActivity);
            }

            // Target is not valid
            if (!target.IsValidFor(self) || !attack.HasAnyValidWeapons(target))
            {
                return(NextActivity);
            }

            var edible = target.Actor.TraitOrDefault <EdibleByLeap>();

            if (edible == null || !edible.CanLeap(self))
            {
                return(NextActivity);
            }

            // Can't leap yet
            if (attack.Armaments.All(a => a.IsReloading))
            {
                return(this);
            }

            // Use CenterOfSubCell with ToSubCell instead of target.Centerposition
            // to avoid continuous facing adjustments as the target moves
            var targetMobile  = target.Actor.TraitOrDefault <Mobile>();
            var targetSubcell = targetMobile != null ? targetMobile.ToSubCell : SubCell.Any;

            var destination   = self.World.Map.CenterOfSubCell(target.Actor.Location, targetSubcell);
            var origin        = self.World.Map.CenterOfSubCell(self.Location, mobile.FromSubCell);
            var desiredFacing = (destination - origin).Yaw.Facing;

            if (mobile.Facing != desiredFacing)
            {
                QueueChild(self, new Turn(self, desiredFacing), true);
                return(this);
            }

            QueueChild(self, new Leap(self, target, mobile, targetMobile, info.Speed.Length, attack, edible), true);

            // Re-queue the child activities to kill the target if it didn't die in one go
            return(this);
        }
Example #30
0
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            // HACK: If the activity is cancelled while we're already resupplying (or about to start resupplying),
            // move actor outside the resupplier footprint
            // TODO: This check is nowhere near robust enough, and should be rewritten
            if (IsCanceling && host.IsInRange(self.CenterPosition, closeEnough))
            {
                QueueChild(self, self.Trait <IMove>().MoveToTarget(self, host), true);
                foreach (var notifyResupply in notifyResupplies)
                {
                    notifyResupply.ResupplyTick(host.Actor, self, ResupplyType.None);
                }

                return(this);
            }

            if (IsCanceling || host.Type == TargetType.Invalid ||
                (closeEnough.LengthSquared > 0 && !host.IsInRange(self.CenterPosition, closeEnough)))
            {
                // This is necessary to ensure host resupply actions (like animations) finish properly
                foreach (var notifyResupply in notifyResupplies)
                {
                    notifyResupply.ResupplyTick(host.Actor, self, ResupplyType.None);
                }

                return(NextActivity);
            }

            if (activeResupplyTypes.HasFlag(ResupplyType.Repair))
            {
                RepairTick(self);
            }

            if (activeResupplyTypes.HasFlag(ResupplyType.Rearm))
            {
                RearmTick(self);
            }

            foreach (var notifyResupply in notifyResupplies)
            {
                notifyResupply.ResupplyTick(host.Actor, self, activeResupplyTypes);
            }

            if (activeResupplyTypes == 0)
            {
                var aircraft = self.TraitOrDefault <Aircraft>();
                if (aircraft != null)
                {
                    aircraft.AllowYieldingReservation();
                    if (aircraft.Info.TakeOffOnResupply)
                    {
                        Queue(self, new TakeOff(self, (a, b, c) => NextActivity == null && b.NextActivity == null));
                    }
                }

                return(NextActivity);
            }

            return(this);
        }