Ejemplo n.º 1
0
        State FindAndTransitionToNextState(Actor self)
        {
            switch (nextState)
            {
            case State.ApproachingOrEntering:

                // Reserve to enter or approach
                isEnteringOrInside = false;
                switch (TryReserveElseTryAlternateReserve(self))
                {
                case ReserveStatus.None:
                    return(State.Done);                                    // No available target -> abort to next activity

                case ReserveStatus.TooFar:
                    inner = move.MoveToTarget(self, targetCenter ? Target.FromPos(target.CenterPosition) : target);                                     // Approach
                    return(State.ApproachingOrEntering);

                case ReserveStatus.Pending:
                    return(State.ApproachingOrEntering);                                    // Retry next tick

                case ReserveStatus.Ready:
                    break;                                     // Reserved target -> start entering target
                }

                // Entering
                isEnteringOrInside = true;
                savedPos           = self.CenterPosition;               // Save position of self, before entering, for returning on exit

                inner = move.MoveIntoTarget(self, target);              // Enter

                if (inner != null)
                {
                    nextState = State.Inside;                             // Should be inside once inner activity is null
                    return(State.ApproachingOrEntering);
                }

                // Can enter but there is no activity for it, so go inside without one
                goto case State.Inside;

            case State.Inside:
                // Might as well teleport into target if there is no MoveIntoTarget activity
                if (nextState == State.ApproachingOrEntering)
                {
                    nextState = State.Inside;
                }

                // Otherwise, try to recover from moving target
                else if (target.CenterPosition != self.CenterPosition)
                {
                    nextState = State.ApproachingOrEntering;
                    Unreserve(self, false);
                    if (Reserve(self) == ReserveStatus.Ready)
                    {
                        inner = move.MoveIntoTarget(self, target);                                 // Enter
                        if (inner != null)
                        {
                            return(State.ApproachingOrEntering);
                        }

                        nextState = State.ApproachingOrEntering;
                        goto case State.ApproachingOrEntering;
                    }

                    nextState          = State.ApproachingOrEntering;
                    isEnteringOrInside = false;
                    inner = move.MoveIntoWorld(self, self.World.Map.CellContaining(savedPos));

                    return(State.ApproachingOrEntering);
                }

                OnInside(self);

                // Return if Abort(Actor) or Done(self) was called from OnInside.
                if (nextState >= State.Exiting)
                {
                    return(State.Inside);
                }

                inner     = this;                     // Start inside activity
                nextState = State.Exiting;            // Exit once inner activity is null (unless Done(self) is called)
                return(State.Inside);

            // TODO: Handle target moved while inside or always call done for movable targets and use a separate exit activity
            case State.Exiting:
                inner = move.MoveIntoWorld(self, self.World.Map.CellContaining(savedPos));

                // If not successfully exiting, retry on next tick
                if (inner == null)
                {
                    return(State.Exiting);
                }
                isEnteringOrInside = false;
                nextState          = State.Done;
                return(State.Exiting);

            case State.Done:
                return(State.Done);
            }

            return(State.Done);            // dummy to quiet dumb compiler
        }
Ejemplo n.º 2
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);
        }