Esempio n. 1
0
        private void CheckForCollisions()
        {
            contactX           = true;
            contactYbottom     = true;
            contactYtop        = true;
            contactLeft        = true;
            contactRight       = true;
            contactFoliage     = false;
            isInClimbingArea   = false;
            insideClimbingArea = null;
            IsInAir            = true;
            bool ray1Intersect = false, ray2Intersect = false;
            bool rayLeftIntersect = false, rayRightIntersect = false;

            TrapPlacementPossible = false;
            //Multiple iterations are used for collision detection, since one correction could cause another collision
            Collidable trapPlacementRay = new Ray(new Vector2(TrapPlacementPos.X, TrapPlacementPos.Y - 50), new Vector2(TrapPlacementPos.X, TrapPlacementPos.Y + 50));

            for (var iteration = 0;
                 iteration < collisionTestIterations && (contactX || contactYbottom || contactYtop);
                 iteration++)
            {
                // Calculate the amount of X and Y movement expected by the player this frame
                var nextMove = speed * delta;

                // Store the original final expected movement of the player so we can
                // see if it has been modified due to a collision or potential collision later
                var originalMove = nextMove;

                // No collisions found yet
                contactX = contactYbottom = contactYtop = contactLeft = contactRight = false;

                insideClimbingArea = new CollidableResults()
                {
                    IntersectLeft  = false,
                    IntersectRight = false,
                    IntersectUp    = false,
                    IntersectDown  = false,
                };

                rayCastResult = new CollidableResults()
                {
                    Intersect = false
                };

                trapPlacementCastResult = new CollidableResults()
                {
                    Intersect = false
                };
                //check for potential collisions with polygons
                float tempX, tempY, tempWidth, tempHeight;
                tempX      = (float)Math.Round(position.X + hitboxOffset.X - 1, MidpointRounding.AwayFromZero);
                tempY      = (float)Math.Round(position.Y + hitboxOffset.Y - 1, MidpointRounding.AwayFromZero);
                tempWidth  = (float)Math.Round(tempX + BoundingBox.Width + 1, MidpointRounding.AwayFromZero);
                tempHeight = (float)Math.Round(tempY + BoundingBox.Height + 1, MidpointRounding.AwayFromZero);

                //Calculate the bounding polygon of the player at the current position
                var playerNextBounds = new Polygon(new List <Vector2>
                {
                    new Vector2(tempX, tempY),
                    new Vector2(tempWidth, tempY),
                    new Vector2(tempWidth, tempHeight),
                    new Vector2(tempX, tempHeight)
                }, false, false);

                var rayHeight = (tempHeight + tempY) / 2f;

                Collidable ray1 = new Ray(new Vector2(tempX, tempHeight - 10),
                                          new Vector2(tempX, tempHeight + 10));
                Collidable ray2 = new Ray(new Vector2(tempWidth, tempHeight - 10),
                                          new Vector2(tempWidth, tempHeight + 10));

                Collidable rayLeft = new Ray(new Vector2(tempX + 10, rayHeight),
                                             new Vector2(tempX - 10, rayHeight));
                Collidable rayRight = new Ray(new Vector2(tempWidth - 10, rayHeight),
                                              new Vector2(tempWidth + 10, rayHeight));

                climableYCenter = -1f;

                for (var i = 0; i < scene.GetCollidables().Count; i++)
                {
                    var currentCollidable = scene.GetCollidables()[i];

                    if (!currentCollidable.IsClimbable)
                    {
                        //----------------- Ray - Slope detection ---------------------------//
                        //The following code section shoots rays from the bottom of the player's bounding box to the ground.
                        //The results of the rays are used to check, whether the player is standing on a slope. If yes, the ..
                        //.. movement of the players is adjusted according to the steepness of the slope.
                        //!contactX && !contactYbottom && !contactYtop

                        if (!rayCastResult.Intersect)
                        {
                            rayCastResult = RayCast.DetectSlope(ray1, ray2, currentCollidable);
                        }
                        if (!ray1Intersect)
                        {
                            ray1Intersect = RayCast.CheckIfInsideClimbingArea(ray1, currentCollidable);
                        }
                        if (!ray2Intersect)
                        {
                            ray2Intersect = RayCast.CheckIfInsideClimbingArea(ray2, currentCollidable);
                        }
                        if (ray1Intersect || ray2Intersect)
                        {
                            IsInAir = false;
                        }

                        //Check if player collides left or right using rays
                        if (!rayLeftIntersect)
                        {
                            rayLeftIntersect = RayCast.CheckIfInsideClimbingArea(rayLeft, currentCollidable);
                        }
                        if (!rayRightIntersect)
                        {
                            rayRightIntersect = RayCast.CheckIfInsideClimbingArea(rayRight, currentCollidable);
                        }


                        //----------------- Ray - Slope detection -END---------------------------//
                    }
                    //------------------- Trap Placement Check -------------------------------//

                    if (!trapPlacementCastResult.Intersect)
                    {
                        trapPlacementCastResult.Intersect = RayCast.CheckIfInsideClimbingArea(trapPlacementRay, currentCollidable);
                    }

                    //------------------- Trap Placement Check  - END -------------------------------//

                    var polygonCollisionResult = currentCollidable.CheckForCollision(playerNextBounds,
                                                                                     speed * delta);


                    //Only adjust speed of the player if the polygon can't be climbed up or down( => IsClimbable)
                    if (polygonCollisionResult.WillIntersect && !currentCollidable.IsClimbable)
                    {
                        nextMove = polygonCollisionResult.MinimumTranslationVector + speed * delta;
                    }
                    else if ((polygonCollisionResult.WillIntersect || polygonCollisionResult.Intersect) &&
                             currentCollidable.IsClimbable)
                    {
                        isInClimbingArea = true;

                        bool atTop = false;

                        //--------- Ray- Climbing area detection ----------- //
                        //The following code section shoots ray left and right out of the players bounding box, in order
                        //to verify, whether the player is still inside the bounds of the climbing area and limit his movement otherwise
                        if (IsClimbing)
                        {
                            //check if player is still inside the climbable area
                            rayHeight = (tempHeight + tempY) / 2f;
                            var   rayWidth       = (tempWidth + tempX) / 2f;
                            var   deltaHeight    = (tempHeight - tempY) / 2f;
                            float collidableXMin = currentCollidable.MinWidth;
                            float collidableXMax = currentCollidable.MaxWidth;
                            float collidableYMax = currentCollidable.MaxHeight;
                            float collidableYMin = currentCollidable.ClimbGroupMinY;


                            if (rayHeight - currentCollidable.CenterYClimbGroup < 0)
                            {
                                atTop = true;
                            }


                            if (tempX >= collidableXMin)
                            {
                                Collidable leftRay = new Ray(new Vector2(collidableXMin - 10, rayHeight),
                                                             new Vector2(tempX + 10, rayHeight));
                                if (RayCast.CheckIfInsideClimbingArea(leftRay, currentCollidable))
                                {
                                    insideClimbingArea.IntersectLeft = true;
                                }
                            }

                            if (tempWidth <= collidableXMax)
                            {
                                Collidable rightRay = new Ray(new Vector2(tempWidth - 10, rayHeight),
                                                              new Vector2(collidableXMax + 10, rayHeight));
                                if (RayCast.CheckIfInsideClimbingArea(rightRay, currentCollidable))
                                {
                                    insideClimbingArea.IntersectRight = true;
                                }
                            }

                            if (tempHeight <= collidableYMax)
                            {
                                Collidable downRayLeft = new Ray(new Vector2(tempX, tempHeight + 20),
                                                                 new Vector2(tempX, collidableYMax + 30));
                                Collidable downRayRight = new Ray(new Vector2(tempWidth, tempHeight + 20),
                                                                  new Vector2(tempWidth, collidableYMax + 30));
                                Collidable downRayMiddle = new Ray(new Vector2(rayWidth, tempHeight - 2),
                                                                   new Vector2(rayWidth, collidableYMax + 10));

                                if (atTop)
                                {
                                    if (RayCast.CheckIfInsideClimbingArea(downRayMiddle, currentCollidable))
                                    {
                                        insideClimbingArea.IntersectDown = true;
                                    }
                                }
                                else
                                {
                                    if (RayCast.CheckIfInsideClimbingArea(downRayLeft, currentCollidable) && RayCast.CheckIfInsideClimbingArea(downRayRight, currentCollidable))
                                    {
                                        insideClimbingArea.IntersectDown = true;
                                    }
                                }
                            }

                            if (atTop)
                            {
                                insideClimbingArea.IntersectUp = !(tempY + deltaHeight < collidableYMin);
                            }
                            else
                            {
                                insideClimbingArea.IntersectUp = true;
                            }

                            climbableYMax   = currentCollidable.ClimbGroupMaxY;
                            climableYCenter = currentCollidable.CenterYClimbGroup;
                        }

                        //--------- Ray- Climbing area detection -END----------- //
                    }


                    // Detect what type of contact has occurred based on a comparison of
                    // the original expected movement vector and the new one

                    if (nextMove.Y > originalMove.Y)
                    {
                        contactYtop = true;
                    }

                    if (nextMove.Y < originalMove.Y)
                    {
                        contactYbottom = true;
                    }

                    if (nextMove.X < originalMove.X)
                    {
                        contactRight = true;
                    }

                    if (nextMove.X > originalMove.X)
                    {
                        contactLeft = true;
                    }

                    contactX = contactLeft || contactRight;

                    if (contactX && contactYtop && speed.Y < 0)
                    {
                        speed.Y = nextMove.Y = 0;
                    }

                    if (contactX && contactYbottom && speed.Y > 0)
                    {
                        speed.Y = nextMove.Y = 0;
                    }
                }

                if (!isInClimbingArea)
                {
                    IsClimbing = false;
                }

                if (IsClimbing)
                {
                    nextMove = originalMove;
                    var anyContact = contactYbottom || contactYtop || contactX;
                    if (anyContact && Math.Abs(originalMove.X) > 0.001)
                    {
                        if (!insideClimbingArea.IntersectLeft && originalMove.X < 0 && contactLeft)
                        {
                            nextMove.X = 0;
                            speed.X    = 0;
                        }
                        if (!insideClimbingArea.IntersectRight && originalMove.X > 0 && contactRight)
                        {
                            nextMove.X = 0;
                            speed.X    = 0;
                        }
                    }

                    if (anyContact && originalMove.Y > 0.001)
                    {
                        if ((insideClimbingArea.IntersectLeft || insideClimbingArea.IntersectRight) &&
                            !insideClimbingArea.IntersectDown)
                        {
                            nextMove.Y = 0;
                            speed.Y    = 0;
                            IsClimbing = false;
                        }
                    }

                    if (originalMove.Y < 0f && !insideClimbingArea.IntersectUp)
                    {
                        nextMove.Y -= CharacterConfig.JUMP_START_SPEED_Y / 10f;
                        IsClimbing  = false;
                    }
                }
                // If a contact has been detected, apply the re-calculated movement vector
                // and disable any further movement this frame (in either X or Y as appropriate)
                if (contactYtop || contactYbottom)
                {
                    position.Y += nextMove.Y;

                    speed.Y = 0;

                    if (contactYbottom)
                    {
                        jumping = false;
                        IsInAir = false;
                    }
                }


                if (contactX)
                {
                    if (rayCastResult.Intersect && !contactYtop && !(rayLeftIntersect && originalMove.X < 0) && !(rayRightIntersect && originalMove.X > 0))
                    {
                        position.X += nextMove.X * 0.5f * Math.Abs(rayCastResult.CollisionNormal.X);

                        if (Math.Abs(nextMove.X) > 0.0001f)
                        {
                            float newSpeed = speed.X * (1 - 0.3f * Math.Abs(rayCastResult.CollisionNormal.X));
                            speed.X = 0.6f * speed.X + 0.4f * newSpeed;
                        }
                        else
                        {
                            speed.X = 0;
                        }
                    }
                    else
                    {
                        if (!(rayLeftIntersect && nextMove.X < 0) && !(rayRightIntersect && nextMove.X > 0))
                        {
                            position.X += nextMove.X;
                        }

                        speed.X = 0;
                    }
                }
                else if (insideClimbingArea != null)
                {
                    position.X += nextMove.X;
                }

                if (jumping && IsClimbing)
                {
                    jumping = false;
                    speed.X = 0;
                }

                if (((contactX || contactYtop) && (IsInAir) && !IsClimbing))
                {
                    speed.X    = nextMove.X = 0;
                    speed.Y   += 3 * accY * delta;
                    nextMove.Y = speed.Y * delta;
                }

                if (IsInAir && !jumping)
                {
                    speed.X *= 0.9f;
                }

                if (trapPlacementCastResult.Intersect && !(rayLeftIntersect && !IsFacingRight()) && !(rayRightIntersect && IsFacingRight()))
                {
                    TrapPlacementPossible = true;
                }
            }

            //Check if the player intersects any of the other actors (like undergrowth, treasure, base etc.)
            IsTouchingShopkeeper = false;
            contactFoliage       = false;
            touchedActors.Clear();

            foreach (var actor in scene.GetActors())
            {
                Rectangle boundingBoxActor;
                if (actor.GetType() == typeof(ShopKeeper))
                {
                    ShopKeeper shopKeeper = actor as ShopKeeper;
                    boundingBoxActor = shopKeeper.BoundingBox;
                    if (!BoundingBox.Intersects(boundingBoxActor))
                    {
                        continue;
                    }
                    IsTouchingShopkeeper = true;
                    if (character.InvTreasure != null)
                    {
                        shopKeeper.ContactPlayer = true;
                    }
                }
                else
                {
                    boundingBoxActor = new Rectangle((int)actor.GetPosition().X,
                                                     (int)(actor.GetPosition().Y - actor.GetSize().Y),
                                                     (int)actor.GetSize().X, (int)actor.GetSize().Y);

                    if (!BoundingBox.Intersects(boundingBoxActor))
                    {
                        continue;
                    }

                    if (actor.GetType() == typeof(Projectile) && character.GetPosession().GetType() != typeof(AiIndigenController))
                    {
                        scene.MatchSoundManager.PlaySoundEffect(SoundEffectEnumeration.Hit);
                        ((Projectile)actor).DestroyProjectile();
                        IsStunned       = true;
                        CurrentStunTime = ProjectileConfig.BULLET_STUN_TIME;
                    }
                }

                touchedActors.Add(actor);
            }

            foreach (Foliage foliage in scene.GetFoliage())
            {
                Rectangle boundingBoxActor = new Rectangle((int)foliage.GetPosition().X,
                                                           (int)(foliage.GetPosition().Y - foliage.GetSize().Y),
                                                           (int)foliage.GetSize().X, (int)foliage.GetSize().Y);

                if (!BoundingBox.Intersects(boundingBoxActor))
                {
                    continue;
                }

                if (foliage.Active)
                {
                    contactFoliage = true;
                }
                touchedActors.Add(foliage);
            }
        }