Beispiel #1
0
        private void UpdateEntity(ISceneEntity entity)
        {
            if (!(entity is IPhysical))
            {
                return;
            }
            if (entity is IScenePresence && ((IScenePresence)entity).IsChildPresence)
            {
                return;
            }

            float elapsedTime = m_elapsedTime;

            if (elapsedTime <= 0f)
            {
                return;
            }

            IPhysical physical = (IPhysical)entity;

            if (!physical.DynamicsEnabled)
            {
                return;
            }

            IPhysicalPresence presence = (physical is IPhysicalPresence) ? (IPhysicalPresence)physical : null;
            Vector3           velocity = physical.Velocity * elapsedTime;
            Vector3           position = physical.RelativePosition;

            Vector3 move        = (presence != null) ? presence.InputVelocity : Vector3.Zero;
            bool    jumping     = (presence != null) ? presence.JumpStart != 0 : false;
            float   gravity     = 0f;
            float   waterHeight = 0.0f;

            if (m_terrain != null)
            {
                waterHeight = m_terrain.WaterHeight;
            }
            float waterChestHeight        = waterHeight - (physical.Scale.Z * .33f);
            float speed                   = elapsedTime;
            float fallElapsed             = (float)(Util.TickCount() - physical.FallStart) / 1000f;
            UUID  anim                    = UUID.Zero;
            List <ISceneEntity> colliders = new List <ISceneEntity>();

            if (presence != null && presence.StunMS > 0)
            {
                move             = Vector3.Zero;
                presence.StunMS -= (int)((float)1000 * elapsedTime);
            }

            ISceneEntity collider;
            float        collisionDist;

            //if (presence != null && presence.InputDirection != Vector3.Zero)
            //{
            //    // Raycast in the direction the avatar wishes to move
            //    Ray avRay = new Ray(physical.ScenePosition, Vector3.Normalize(presence.InputDirection));
            //    if (FullSceneCollisionTest(false, avRay, presence, out collider, out collisionDist))
            //    {
            //        speed = Math.Min(speed, collisionDist - COLLISION_MARGIN);
            //        m_log.Debug("Raycasted to " + collider.Name + " (" + collider.LocalID + ")");
            //    }
            //}
            //HACK: detect both X and Y ray collisions, since we are not calculating a collision normal
            Vector3 normVel = Vector3.Normalize(velocity);

            if ((normVel.X != 0f || move.X != 0f) && FullSceneCollisionTest(false, new Ray(position, normVel.X > 0 || move.X > 0 ? Vector3.UnitX : -Vector3.UnitX), physical, out collider, out collisionDist) && collisionDist <= COLLISION_MARGIN)
            {
                move.X     = 0f;
                velocity.X = 0f;

                if (!colliders.Contains(collider))
                {
                    colliders.Add(collider);
                }
            }
            if ((normVel.Y != 0f || move.Y != 0f) && FullSceneCollisionTest(false, new Ray(position, normVel.Y > 0 || move.Y > 0 ? Vector3.UnitY : -Vector3.UnitY), physical, out collider, out collisionDist) && collisionDist <= COLLISION_MARGIN)
            {
                move.Y     = 0f;
                velocity.Y = 0f;

                if (!colliders.Contains(collider))
                {
                    colliders.Add(collider);
                }
            }

            #region Terrain Movement

            float oldFloor = 0.0f, newFloor = 0.0f;
            if (m_terrain != null)
            {
                oldFloor = m_terrain.GetTerrainHeightAt(position.X, position.Y);
            }

            position += (move * speed);

            if (m_terrain != null)
            {
                newFloor = m_terrain.GetTerrainHeightAt(position.X, position.Y);
            }

            if (presence != null)
            {
                if (presence.MovementState != MovementState.Flying && newFloor != oldFloor)
                {
                    speed /= (1f + (SQRT_TWO * Math.Abs(newFloor - oldFloor)));
                }
            }

            //HACK: distance from avatar center to the bottom of its feet
            float distanceFromFloor = physical.Scale.Z * .5f;

            // Raycast for gravity
            Ray ray = new Ray(position, -Vector3.UnitZ);
            if (FullSceneCollisionTest(false, ray, physical, out collider, out collisionDist))
            {
                if (position.Z - collisionDist > newFloor)
                {
                    newFloor = position.Z - collisionDist;

                    //FIXME
                    //if (!colliders.Contains(collider))
                    //colliders.Add(collider);
                }
            }

            float lowerLimit = newFloor + distanceFromFloor;

            #endregion Terrain Movement

            if (presence != null && presence.MovementState == MovementState.Flying)
            {
                #region Flying

                physical.FallStart = 0;
                presence.JumpStart = 0;

                //velocity falloff while flying
                velocity.X *= 0.66f;
                velocity.Y *= 0.66f;
                velocity.Z *= 0.33f;

                if (position.Z == lowerLimit)
                {
                    velocity.Z += INITIAL_HOVER_IMPULSE;
                }

                if (move.X != 0f || move.Y != 0f)
                {
                    anim = Animations.FLY;
                }
                else if (move.Z > 0f)
                {
                    anim = Animations.HOVER_UP;
                }
                else if (move.Z < 0f)
                {
                    anim = Animations.HOVER_DOWN;
                }
                else
                {
                    anim = Animations.HOVER;
                }

                #endregion Flying
            }
            else if (position.Z > lowerLimit + FALL_FORGIVENESS || position.Z <= waterChestHeight)
            {
                #region Falling/Floating/Landing

                if (position.Z > waterHeight)
                { //above water
                    //override controls while drifting
                    move = Vector3.Zero;

                    //keep most of our horizontal inertia
                    velocity.X *= 0.975f;
                    velocity.Y *= 0.975f;

                    if (physical.FallStart == 0) //|| (fallElapsed > FALL_DELAY && velocity.Z >= 0f))
                    {                            //just started falling
                        physical.FallStart = Util.TickCount();
                    }
                    else
                    {
                        gravity = GRAVITY * fallElapsed * elapsedTime; //normal gravity

                        if (!jumping)
                        {     //falling
                            if (fallElapsed > FALL_DELAY)
                            { //falling long enough to trigger the animation
                                anim = Animations.FALLDOWN;
                            }
                        }
                    }
                }
                else if (position.Z >= waterChestHeight)
                { //at the water line
                    velocity          *= 0.1f;
                    velocity.Z         = 0f;
                    physical.FallStart = 0;

                    if (move.Z < 1f)
                    {
                        position.Z = waterChestHeight;
                    }

                    if (move.Z > 0f)
                    {
                        anim = Animations.HOVER_UP;
                    }
                    else if (move.X != 0f || move.Y != 0f)
                    {
                        anim = Animations.SWIM_FORWARD;
                    }
                    else
                    {
                        anim = Animations.HOVER;
                    }
                }
                else
                {                                      //underwater
                    velocity   *= 0.1f;
                    velocity.Z += 0.75f * elapsedTime; // buoyant

                    if (move.Z < 0f)
                    {
                        anim = Animations.SWIM_DOWN;
                    }
                    else
                    {
                        anim = Animations.SWIM_FORWARD;
                    }
                }

                #endregion Falling/Floating/Landing
            }
            else
            {
                #region Ground Movement

                if (presence != null)
                {
                    if (presence.JumpStart == 0 && physical.FallStart > 0)
                    {
                        if (fallElapsed >= FALL_DELAY * 4)
                        {
                            anim            = Animations.STANDUP;
                            presence.StunMS = 2000;
                        }
                        else if (fallElapsed >= FALL_DELAY * 3)
                        {
                            anim            = Animations.MEDIUM_LAND;
                            presence.StunMS = 1000;
                        }
                        else if (fallElapsed >= FALL_DELAY * 2)
                        {
                            anim            = Animations.LAND;
                            presence.StunMS = 500;
                        }
                        if (presence.Animations.SetDefaultAnimation(anim, 1))
                        {
                            m_scene.SendPresenceAnimations(this, presence);
                        }
                    }
                }

                physical.FallStart = 0;

                //friction
                velocity  *= 0.2f;
                velocity.Z = 0f;
                position.Z = lowerLimit;

                if (presence != null)
                {
                    if (move.Z > 0f)
                    {                    //jumping
                        if (!jumping)
                        {                //begin prejump
                            move.Z = 0f; //override Z control
                            anim   = Animations.PRE_JUMP;

                            presence.JumpStart = Util.TickCount();
                        }
                        else if (Util.TickCount() - presence.JumpStart > PREJUMP_DELAY * 1000)
                        { //start actual jump
                            if (presence.JumpStart == -1)
                            {
                                //already jumping! end current jump
                                presence.JumpStart = 0;
                                return;
                            }

                            anim = Animations.JUMP;

                            Vector3 normalVel = Vector3.Normalize(velocity);

                            velocity.X += normalVel.X * JUMP_IMPULSE_HORIZONTAL * elapsedTime;
                            velocity.Y += normalVel.Y * JUMP_IMPULSE_HORIZONTAL * elapsedTime;
                            velocity.Z  = JUMP_IMPULSE_VERTICAL * elapsedTime;

                            presence.JumpStart = -1; //flag that we are currently jumping
                        }
                        else
                        {
                            move.Z = 0;  //override Z control
                        }
                    }
                    else
                    { //not jumping
                        presence.JumpStart = 0;

                        if (move.X != 0 || move.Y != 0)
                        {
                            if (move.Z < 0)
                            {
                                anim = Animations.CROUCHWALK;
                            }
                            else if (presence.MovementState == MovementState.Running)
                            {
                                anim = Animations.RUN;
                            }
                            else
                            {
                                anim = Animations.WALK;
                            }
                        }
                        else
                        {
                            if (move.Z < 0)
                            {
                                anim = Animations.CROUCH;
                            }
                            else
                            {
                                anim = Animations.STAND;
                            }
                        }
                    }
                }

                #endregion Ground Movement
            }

            if (presence != null)
            {
                if (anim != UUID.Zero && presence.StunMS <= 0 && presence.Animations.SetDefaultAnimation(anim, 1))
                {
                    m_scene.SendPresenceAnimations(this, presence);
                }
            }

            float maxVel = AVATAR_TERMINAL_VELOCITY * elapsedTime;

            #region Update Physical State

            // Calculate how far we moved this frame
            Vector3 moved = position - physical.RelativePosition;
            if (moved.Z < -maxVel)
            {
                moved.Z = -maxVel;
            }
            else if (moved.Z > maxVel)
            {
                moved.Z = maxVel;
            }

            position += velocity;

            moved.Z   = moved.Z - gravity;
            velocity += moved;
            if (velocity.Z < -maxVel)
            {
                velocity.Z = -maxVel;
            }
            else if (velocity.Z > maxVel)
            {
                velocity.Z = maxVel;
            }

            if (position.Z < lowerLimit)
            {
                position.Z = lowerLimit;
            }
            if (position.Z < lowerLimit + COLLISION_MARGIN)
            {
                velocity.Z = 0f;
            }

            physical.Velocity         = velocity / elapsedTime;
            physical.RelativePosition = position;

            #endregion Update Physical State

            EventHandler <EntityCollisionArgs> callback = OnEntityCollision;
            if (callback != null)
            {
                for (int i = 0, len = colliders.Count; i < len; i++)
                {
                    callback(this, new EntityCollisionArgs {
                        First = physical, Second = colliders[i]
                    });
                    callback(this, new EntityCollisionArgs {
                        First = colliders[i], Second = physical
                    });
                }
            }

            m_scene.EntityAddOrUpdate(this, physical, UpdateFlags.Position | UpdateFlags.Velocity, 0);
        }