From 43bcb749c859016d89447aee2f89ef5bb7e9be95 Mon Sep 17 00:00:00 2001 From: florianf Date: Fri, 12 Mar 2010 10:07:37 +0000 Subject: [PATCH] supertux/tile.[ch]pp: Move the unisolid solidity checks to the "Tile" object. This really didn't belong into the sector code. git-svn-id: http://supertux.lethargik.org/svn/supertux/trunk/supertux@6598 837edb03-e0f3-0310-88ca-d4d4e8b29345 --- src/supertux/sector.cpp | 337 +---------------------------------------------- src/supertux/tile.cpp | 338 ++++++++++++++++++++++++++++++++++++++++++++++++ src/supertux/tile.hpp | 28 ++++ 3 files changed, 373 insertions(+), 330 deletions(-) diff --git a/src/supertux/sector.cpp b/src/supertux/sector.cpp index f85ef49f8..08b894686 100644 --- a/src/supertux/sector.cpp +++ b/src/supertux/sector.cpp @@ -957,315 +957,6 @@ void check_collisions(collision::Constraints* constraints, } } -/* Returns zero if a unisolid tile is non-solid due to the movement direction, - * non-zero if the tile is solid due to direction. */ -int check_movement_unisolid (Vector movement, const Tile* tile) -{ - int slope_info; - double mv_x; - double mv_y; - double mv_tan; - double slope_tan; - -#define MV_NON_SOLID 0 -#define MV_SOLID 1 - - /* If the tile is not a slope, this is very easy. */ - if (!tile->is_slope ()) - { - int dir = tile->getData () & ((int) Tile::UNI_DIR_MASK); - - log_debug << "Tile data is " << tile->getData () << ", dir = " << dir << std::endl; - - if (dir != Tile::UNI_DIR_NORTH) - log_debug << "Found non-north facing unisolid tile." << std::endl; - - if (((dir == Tile::UNI_DIR_NORTH) && (movement.y >= 0)) /* moving down */ - || ((dir == Tile::UNI_DIR_SOUTH) && (movement.y < 0)) /* moving up */ - || ((dir == Tile::UNI_DIR_WEST) && (movement.x >= 0)) /* moving right */ - || ((dir == Tile::UNI_DIR_EAST) && (movement.x < 0))) /* moving left */ - return MV_SOLID; - else - return MV_NON_SOLID; - } - - /* Initialize mv_x and mv_y. Depending on the slope the axis are inverted so - * that we can always use the "SOUTHEAST" case of the slope. The southeast - * case is the following: - * . - * /! - * / ! - * +--+ - */ - mv_x = (double) movement.x; - mv_y = (double) movement.y; - - slope_info = tile->getData(); - switch (slope_info & AATriangle::DIRECTION_MASK) - { - case AATriangle::SOUTHEAST: /* . */ - /* do nothing */ /* /! */ - break; /* / ! */ - /* +--+ */ - case AATriangle::SOUTHWEST: /* . */ - mv_x *= (-1.0); /* !\ */ - break; /* ! \ */ - /* +--+ */ - case AATriangle::NORTHEAST: /* +--+ */ - mv_y *= (-1.0); /* \ ! */ - break; /* \! */ - /* ' */ - case AATriangle::NORTHWEST: /* +--+ */ - mv_x *= (-1.0); /* ! / */ - mv_y *= (-1.0); /* !/ */ - break; /* ' */ - } /* switch (slope_info & DIRECTION_MASK) */ - - /* Handle the easy cases first */ - /* If we're moving to the right and down, then the slope is solid. */ - if ((mv_x >= 0.0) && (mv_y >= 0.0)) /* 4th quadrant */ - return MV_SOLID; - /* If we're moving to the left and up, then the slope is not solid. */ - else if ((mv_x <= 0.0) && (mv_y <= 0.0)) /* 2nd quadrant */ - return MV_NON_SOLID; - - /* The pure up-down and left-right movements have already been handled. */ - assert (mv_x != 0.0); - assert (mv_y != 0.0); - - /* calculate tangent of movement */ - mv_tan = (-1.0) * mv_y / mv_x; - - /* determine tangent of the slope */ - slope_tan = 1.0; - if (((slope_info & AATriangle::DEFORM_MASK) == AATriangle::DEFORM_BOTTOM) - || ((slope_info & AATriangle::DEFORM_MASK) == AATriangle::DEFORM_TOP)) - slope_tan = 0.5; /* ~= 26.6 deg */ - else if (((slope_info & AATriangle::DEFORM_MASK) == AATriangle::DEFORM_LEFT) - || ((slope_info & AATriangle::DEFORM_MASK) == AATriangle::DEFORM_RIGHT)) - slope_tan = 2.0; /* ~= 63.4 deg */ - - /* up and right */ - if (mv_x > 0.0) /* 1st quadrant */ - { - assert (mv_y < 0.0); - if (mv_tan <= slope_tan) - return MV_SOLID; - else - return MV_NON_SOLID; - } - /* down and left */ - else if (mv_x < 0.0) /* 3rd quadrant */ - { - assert (mv_y > 0.0); - if (mv_tan >= slope_tan) - return MV_SOLID; - else - return MV_NON_SOLID; - } - - assert (1 != 1); - return (-1); - -#undef MV_NON_SOLID -#undef MV_SOLID -} /* int check_movement_unisolid */ - -int is_above_line (float l_x, float l_y, float m, - float p_x, float p_y) -{ - float interp_y = (l_y + (m * (p_x - l_x))); - if (interp_y == p_y) - return (1); - else if (interp_y > p_y) - return (1); - else - return (0); -} - -int is_below_line (float l_x, float l_y, float m, - float p_x, float p_y) -{ - if (is_above_line (l_x, l_y, m, p_x, p_y)) - return (0); - else - return (1); -} - -int check_position_unisolid (const Rectf& obj_bbox, - const Rectf& tile_bbox, - const Tile* tile) -{ - int slope_info; - float tile_x; - float tile_y; - float gradient; - float delta_x; - float delta_y; - float obj_x; - float obj_y; - -#define POS_NON_SOLID 0 -#define POS_SOLID 1 - - /* If this is not a slope, this is - again - easy */ - if (!tile->is_slope ()) - { - int dir = tile->getData () & Tile::UNI_DIR_MASK; - - if ((dir == Tile::UNI_DIR_NORTH) - && ((obj_bbox.get_bottom () - SHIFT_DELTA) <= tile_bbox.get_top ())) - return POS_SOLID; - else if ((dir == Tile::UNI_DIR_SOUTH) - && ((obj_bbox.get_top () + SHIFT_DELTA) >= tile_bbox.get_bottom ())) - return POS_SOLID; - else if ((dir == Tile::UNI_DIR_WEST) - && ((obj_bbox.get_right () - SHIFT_DELTA) <= tile_bbox.get_left ())) - return POS_SOLID; - else if ((dir == Tile::UNI_DIR_EAST) - && ((obj_bbox.get_left () + SHIFT_DELTA) >= tile_bbox.get_right ())) - return POS_SOLID; - - return POS_NON_SOLID; - } - - /* There are 20 different cases. For each case, calculate a line that - * describes the slope's surface. The line is defined by x, y, and m, the - * gradient. */ - slope_info = tile->getData(); - switch (slope_info - & (AATriangle::DIRECTION_MASK | AATriangle::DEFORM_MASK)) - { - case AATriangle::SOUTHWEST: - case AATriangle::SOUTHWEST | AATriangle::DEFORM_TOP: - case AATriangle::SOUTHWEST | AATriangle::DEFORM_LEFT: - case AATriangle::NORTHEAST: - case AATriangle::NORTHEAST | AATriangle::DEFORM_TOP: - case AATriangle::NORTHEAST | AATriangle::DEFORM_LEFT: - tile_x = tile_bbox.get_left (); - tile_y = tile_bbox.get_top (); - gradient = 1.0; - break; - - case AATriangle::SOUTHEAST: - case AATriangle::SOUTHEAST | AATriangle::DEFORM_TOP: - case AATriangle::SOUTHEAST | AATriangle::DEFORM_RIGHT: - case AATriangle::NORTHWEST: - case AATriangle::NORTHWEST | AATriangle::DEFORM_TOP: - case AATriangle::NORTHWEST | AATriangle::DEFORM_RIGHT: - tile_x = tile_bbox.get_right (); - tile_y = tile_bbox.get_top (); - gradient = -1.0; - break; - - case AATriangle::SOUTHEAST | AATriangle::DEFORM_BOTTOM: - case AATriangle::SOUTHEAST | AATriangle::DEFORM_LEFT: - case AATriangle::NORTHWEST | AATriangle::DEFORM_BOTTOM: - case AATriangle::NORTHWEST | AATriangle::DEFORM_LEFT: - tile_x = tile_bbox.get_left (); - tile_y = tile_bbox.get_bottom (); - gradient = -1.0; - break; - - case AATriangle::SOUTHWEST | AATriangle::DEFORM_BOTTOM: - case AATriangle::SOUTHWEST | AATriangle::DEFORM_RIGHT: - case AATriangle::NORTHEAST | AATriangle::DEFORM_BOTTOM: - case AATriangle::NORTHEAST | AATriangle::DEFORM_RIGHT: - tile_x = tile_bbox.get_right (); - tile_y = tile_bbox.get_bottom (); - gradient = 1.0; - break; - - default: - assert (23 == 42); - return POS_NON_SOLID; - } - - /* delta_x, delta_y: Gradient aware version of SHIFT_DELTA. Here, we set the - * sign of the values only. Also, we determine here which corner of the - * object's bounding box is the interesting one for us. */ - delta_x = 1.0 * SHIFT_DELTA; - delta_y = 1.0 * SHIFT_DELTA; - switch (slope_info & AATriangle::DIRECTION_MASK) - { - case AATriangle::SOUTHWEST: - delta_x *= 1.0; - delta_y *= -1.0; - obj_x = obj_bbox.get_left (); - obj_y = obj_bbox.get_bottom (); - break; - - case AATriangle::SOUTHEAST: - delta_x *= -1.0; - delta_y *= -1.0; - obj_x = obj_bbox.get_right (); - obj_y = obj_bbox.get_bottom (); - break; - - case AATriangle::NORTHWEST: - delta_x *= 1.0; - delta_y *= 1.0; - obj_x = obj_bbox.get_left (); - obj_y = obj_bbox.get_top (); - break; - - case AATriangle::NORTHEAST: - delta_x *= -1.0; - delta_y *= 1.0; - obj_x = obj_bbox.get_right (); - obj_y = obj_bbox.get_top (); - break; - } - - /* Adapt the delta_x, delta_y and the gradient for the 26.6 deg and 63.4 deg - * cases. */ - switch (slope_info & AATriangle::DEFORM_MASK) - { - case 0: - delta_x *= .70710678118654752440; /* 1/sqrt(2) */ - delta_y *= .70710678118654752440; /* 1/sqrt(2) */ - break; - - case AATriangle::DEFORM_BOTTOM: - case AATriangle::DEFORM_TOP: - delta_x *= .44721359549995793928; /* 1/sqrt(5) */ - delta_y *= .89442719099991587856; /* 2/sqrt(5) */ - gradient *= 0.5; - break; - - case AATriangle::DEFORM_LEFT: - case AATriangle::DEFORM_RIGHT: - delta_x *= .89442719099991587856; /* 2/sqrt(5) */ - delta_y *= .44721359549995793928; /* 1/sqrt(5) */ - gradient *= 2.0; - break; - } - - /* With a south slope, check if all points are above the line. If one point - * isn't, the slope is not solid. => You can pass through a south-slope from - * below but not from above. */ - if (((slope_info & AATriangle::DIRECTION_MASK) == AATriangle::SOUTHWEST) - || ((slope_info & AATriangle::DIRECTION_MASK) == AATriangle::SOUTHEAST)) - { - if (is_below_line (tile_x, tile_y, gradient, obj_x + delta_x, obj_y + delta_y)) - return (POS_NON_SOLID); - else - return (POS_SOLID); - } - /* northwest or northeast. Same as above, but inverted. You can pass from top - * to bottom but not vice versa. */ - else - { - if (is_above_line (tile_x, tile_y, gradient, obj_x + delta_x, obj_y + delta_y)) - return (POS_NON_SOLID); - else - return (POS_SOLID); - } - -#undef POS_NON_SOLID -#undef POS_SOLID -} /* int check_position_unisolid */ - void Sector::collision_tilemap(collision::Constraints* constraints, const Vector& movement, const Rectf& dest, @@ -1289,35 +980,21 @@ Sector::collision_tilemap(collision::Constraints* constraints, if(!tile) continue; // skip non-solid tiles - if((tile->getAttributes() & Tile::SOLID) == 0) + if(!tile->is_solid ()) continue; Rectf tile_bbox = solids->get_tile_bbox(x, y); - // only handle unisolid when the player is falling down and when he was - // above the tile before + /* If the tile is a unisolid tile, the "is_solid()" function above + * didn't do a thorough check. Calculate the position and (relative) + * movement of the object and determine whether or not the tile is + * solid with regard to those parameters. */ if(tile->is_unisolid ()) { - int status; Vector relative_movement = movement - solids->get_movement(/* actual = */ true); - /* Check if the tile is solid given the current movement. This works - * for south-slopes (which are solid when moving "down") and - * north-slopes (which are solid when moving "up". "up" and "down" is - * in quotation marks because because the slope's gradient is taken. - * Also, this uses the movement relative to the tilemaps own movement - * (if any). --octo */ - status = check_movement_unisolid (relative_movement, tile); - /* If zero is returned, the unisolid tile is non-solid. */ - if (status == 0) + if (!tile->is_solid (tile_bbox, object.get_bbox(), relative_movement)) continue; - - /* Check whether the object is already *in* the tile. If so, the tile - * is non-solid. Otherwise, if the object is "above" (south slopes) - * or "below" (north slopes), the tile will be solid. */ - status = check_position_unisolid (object.get_bbox(), tile_bbox, tile); - if (status == 0) - continue; - } + } /* if (tile->is_unisolid ()) */ if(tile->is_slope ()) { // slope tile AATriangle triangle; diff --git a/src/supertux/tile.cpp b/src/supertux/tile.cpp index fbc90716b..bdf96917c 100644 --- a/src/supertux/tile.cpp +++ b/src/supertux/tile.cpp @@ -1,6 +1,7 @@ // SuperTux // Copyright (C) 2004 Tobias Glaesser // Copyright (C) 2006 Matthias Braun +// Copyright (C) 2010 Florian Forster // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -17,7 +18,9 @@ #include "supertux/tile.hpp" +#include "supertux/constants.hpp" #include "supertux/tile_set.hpp" +#include "math/aatriangle.hpp" #include "video/drawing_context.hpp" bool Tile::draw_editor_images = false; @@ -145,4 +148,339 @@ Tile::print_debug(int id) const log_debug << " Imagespec: file " << im->file << "; rect " << im->rect << std::endl; } +/* Returns zero if a unisolid tile is non-solid due to the movement direction, + * non-zero if the tile is solid due to direction. */ +int Tile::check_movement_unisolid (const Vector movement) const +{ + int slope_info; + double mv_x; + double mv_y; + double mv_tan; + double slope_tan; + +#define MV_NON_SOLID 0 +#define MV_SOLID 1 + + /* If the tile is not a slope, this is very easy. */ + if (!this->is_slope ()) + { + int dir = this->getData () & ((int) Tile::UNI_DIR_MASK); + + if (((dir == Tile::UNI_DIR_NORTH) && (movement.y >= 0)) /* moving down */ + || ((dir == Tile::UNI_DIR_SOUTH) && (movement.y < 0)) /* moving up */ + || ((dir == Tile::UNI_DIR_WEST) && (movement.x >= 0)) /* moving right */ + || ((dir == Tile::UNI_DIR_EAST) && (movement.x < 0))) /* moving left */ + return MV_SOLID; + else + return MV_NON_SOLID; + } + + /* Initialize mv_x and mv_y. Depending on the slope the axis are inverted so + * that we can always use the "SOUTHEAST" case of the slope. The southeast + * case is the following: + * . + * /! + * / ! + * +--+ + */ + mv_x = (double) movement.x; + mv_y = (double) movement.y; + + slope_info = this->getData(); + switch (slope_info & AATriangle::DIRECTION_MASK) + { + case AATriangle::SOUTHEAST: /* . */ + /* do nothing */ /* /! */ + break; /* / ! */ + /* +--+ */ + case AATriangle::SOUTHWEST: /* . */ + mv_x *= (-1.0); /* !\ */ + break; /* ! \ */ + /* +--+ */ + case AATriangle::NORTHEAST: /* +--+ */ + mv_y *= (-1.0); /* \ ! */ + break; /* \! */ + /* ' */ + case AATriangle::NORTHWEST: /* +--+ */ + mv_x *= (-1.0); /* ! / */ + mv_y *= (-1.0); /* !/ */ + break; /* ' */ + } /* switch (slope_info & DIRECTION_MASK) */ + + /* Handle the easy cases first */ + /* If we're moving to the right and down, then the slope is solid. */ + if ((mv_x >= 0.0) && (mv_y >= 0.0)) /* 4th quadrant */ + return MV_SOLID; + /* If we're moving to the left and up, then the slope is not solid. */ + else if ((mv_x <= 0.0) && (mv_y <= 0.0)) /* 2nd quadrant */ + return MV_NON_SOLID; + + /* The pure up-down and left-right movements have already been handled. */ + assert (mv_x != 0.0); + assert (mv_y != 0.0); + + /* calculate tangent of movement */ + mv_tan = (-1.0) * mv_y / mv_x; + + /* determine tangent of the slope */ + slope_tan = 1.0; + if (((slope_info & AATriangle::DEFORM_MASK) == AATriangle::DEFORM_BOTTOM) + || ((slope_info & AATriangle::DEFORM_MASK) == AATriangle::DEFORM_TOP)) + slope_tan = 0.5; /* ~= 26.6 deg */ + else if (((slope_info & AATriangle::DEFORM_MASK) == AATriangle::DEFORM_LEFT) + || ((slope_info & AATriangle::DEFORM_MASK) == AATriangle::DEFORM_RIGHT)) + slope_tan = 2.0; /* ~= 63.4 deg */ + + /* up and right */ + if (mv_x > 0.0) /* 1st quadrant */ + { + assert (mv_y < 0.0); + if (mv_tan <= slope_tan) + return MV_SOLID; + else + return MV_NON_SOLID; + } + /* down and left */ + else if (mv_x < 0.0) /* 3rd quadrant */ + { + assert (mv_y > 0.0); + if (mv_tan >= slope_tan) + return MV_SOLID; + else + return MV_NON_SOLID; + } + + assert (1 != 1); + return (-1); + +#undef MV_NON_SOLID +#undef MV_SOLID +} /* int check_movement_unisolid */ + +int is_above_line (float l_x, float l_y, float m, + float p_x, float p_y) +{ + float interp_y = (l_y + (m * (p_x - l_x))); + if (interp_y == p_y) + return (1); + else if (interp_y > p_y) + return (1); + else + return (0); +} + +int is_below_line (float l_x, float l_y, float m, + float p_x, float p_y) +{ + if (is_above_line (l_x, l_y, m, p_x, p_y)) + return (0); + else + return (1); +} + +int Tile::check_position_unisolid (const Rectf& obj_bbox, + const Rectf& tile_bbox) const +{ + int slope_info; + float tile_x; + float tile_y; + float gradient; + float delta_x; + float delta_y; + float obj_x; + float obj_y; + +#define POS_NON_SOLID 0 +#define POS_SOLID 1 + + /* If this is not a slope, this is - again - easy */ + if (!this->is_slope ()) + { + int dir = this->getData () & Tile::UNI_DIR_MASK; + + if ((dir == Tile::UNI_DIR_NORTH) + && ((obj_bbox.get_bottom () - SHIFT_DELTA) <= tile_bbox.get_top ())) + return POS_SOLID; + else if ((dir == Tile::UNI_DIR_SOUTH) + && ((obj_bbox.get_top () + SHIFT_DELTA) >= tile_bbox.get_bottom ())) + return POS_SOLID; + else if ((dir == Tile::UNI_DIR_WEST) + && ((obj_bbox.get_right () - SHIFT_DELTA) <= tile_bbox.get_left ())) + return POS_SOLID; + else if ((dir == Tile::UNI_DIR_EAST) + && ((obj_bbox.get_left () + SHIFT_DELTA) >= tile_bbox.get_right ())) + return POS_SOLID; + + return POS_NON_SOLID; + } + + /* There are 20 different cases. For each case, calculate a line that + * describes the slope's surface. The line is defined by x, y, and m, the + * gradient. */ + slope_info = this->getData(); + switch (slope_info + & (AATriangle::DIRECTION_MASK | AATriangle::DEFORM_MASK)) + { + case AATriangle::SOUTHWEST: + case AATriangle::SOUTHWEST | AATriangle::DEFORM_TOP: + case AATriangle::SOUTHWEST | AATriangle::DEFORM_LEFT: + case AATriangle::NORTHEAST: + case AATriangle::NORTHEAST | AATriangle::DEFORM_TOP: + case AATriangle::NORTHEAST | AATriangle::DEFORM_LEFT: + tile_x = tile_bbox.get_left (); + tile_y = tile_bbox.get_top (); + gradient = 1.0; + break; + + case AATriangle::SOUTHEAST: + case AATriangle::SOUTHEAST | AATriangle::DEFORM_TOP: + case AATriangle::SOUTHEAST | AATriangle::DEFORM_RIGHT: + case AATriangle::NORTHWEST: + case AATriangle::NORTHWEST | AATriangle::DEFORM_TOP: + case AATriangle::NORTHWEST | AATriangle::DEFORM_RIGHT: + tile_x = tile_bbox.get_right (); + tile_y = tile_bbox.get_top (); + gradient = -1.0; + break; + + case AATriangle::SOUTHEAST | AATriangle::DEFORM_BOTTOM: + case AATriangle::SOUTHEAST | AATriangle::DEFORM_LEFT: + case AATriangle::NORTHWEST | AATriangle::DEFORM_BOTTOM: + case AATriangle::NORTHWEST | AATriangle::DEFORM_LEFT: + tile_x = tile_bbox.get_left (); + tile_y = tile_bbox.get_bottom (); + gradient = -1.0; + break; + + case AATriangle::SOUTHWEST | AATriangle::DEFORM_BOTTOM: + case AATriangle::SOUTHWEST | AATriangle::DEFORM_RIGHT: + case AATriangle::NORTHEAST | AATriangle::DEFORM_BOTTOM: + case AATriangle::NORTHEAST | AATriangle::DEFORM_RIGHT: + tile_x = tile_bbox.get_right (); + tile_y = tile_bbox.get_bottom (); + gradient = 1.0; + break; + + default: + assert (23 == 42); + return POS_NON_SOLID; + } + + /* delta_x, delta_y: Gradient aware version of SHIFT_DELTA. Here, we set the + * sign of the values only. Also, we determine here which corner of the + * object's bounding box is the interesting one for us. */ + delta_x = 1.0 * SHIFT_DELTA; + delta_y = 1.0 * SHIFT_DELTA; + switch (slope_info & AATriangle::DIRECTION_MASK) + { + case AATriangle::SOUTHWEST: + delta_x *= 1.0; + delta_y *= -1.0; + obj_x = obj_bbox.get_left (); + obj_y = obj_bbox.get_bottom (); + break; + + case AATriangle::SOUTHEAST: + delta_x *= -1.0; + delta_y *= -1.0; + obj_x = obj_bbox.get_right (); + obj_y = obj_bbox.get_bottom (); + break; + + case AATriangle::NORTHWEST: + delta_x *= 1.0; + delta_y *= 1.0; + obj_x = obj_bbox.get_left (); + obj_y = obj_bbox.get_top (); + break; + + case AATriangle::NORTHEAST: + delta_x *= -1.0; + delta_y *= 1.0; + obj_x = obj_bbox.get_right (); + obj_y = obj_bbox.get_top (); + break; + } + + /* Adapt the delta_x, delta_y and the gradient for the 26.6 deg and 63.4 deg + * cases. */ + switch (slope_info & AATriangle::DEFORM_MASK) + { + case 0: + delta_x *= .70710678118654752440; /* 1/sqrt(2) */ + delta_y *= .70710678118654752440; /* 1/sqrt(2) */ + break; + + case AATriangle::DEFORM_BOTTOM: + case AATriangle::DEFORM_TOP: + delta_x *= .44721359549995793928; /* 1/sqrt(5) */ + delta_y *= .89442719099991587856; /* 2/sqrt(5) */ + gradient *= 0.5; + break; + + case AATriangle::DEFORM_LEFT: + case AATriangle::DEFORM_RIGHT: + delta_x *= .89442719099991587856; /* 2/sqrt(5) */ + delta_y *= .44721359549995793928; /* 1/sqrt(5) */ + gradient *= 2.0; + break; + } + + /* With a south slope, check if all points are above the line. If one point + * isn't, the slope is not solid. => You can pass through a south-slope from + * below but not from above. */ + if (((slope_info & AATriangle::DIRECTION_MASK) == AATriangle::SOUTHWEST) + || ((slope_info & AATriangle::DIRECTION_MASK) == AATriangle::SOUTHEAST)) + { + if (is_below_line (tile_x, tile_y, gradient, obj_x + delta_x, obj_y + delta_y)) + return (POS_NON_SOLID); + else + return (POS_SOLID); + } + /* northwest or northeast. Same as above, but inverted. You can pass from top + * to bottom but not vice versa. */ + else + { + if (is_above_line (tile_x, tile_y, gradient, obj_x + delta_x, obj_y + delta_y)) + return (POS_NON_SOLID); + else + return (POS_SOLID); + } + +#undef POS_NON_SOLID +#undef POS_SOLID +} /* int check_position_unisolid */ + +bool Tile::is_solid (const Rectf& tile_bbox, const Rectf& position, const Vector& movement) const +{ + int status; + + if ((attributes & SOLID) == 0) + return (false); + + if ((attributes & UNISOLID) == 0) + return (true); + + /* Check if the tile is solid given the current movement. This works + * for south-slopes (which are solid when moving "down") and + * north-slopes (which are solid when moving "up". "up" and "down" is + * in quotation marks because because the slope's gradient is taken. + * Also, this uses the movement relative to the tilemaps own movement + * (if any). --octo */ + status = check_movement_unisolid (movement); + /* If zero is returned, the unisolid tile is non-solid. */ + if (status == 0) + return (false); + + /* Check whether the object is already *in* the tile. If so, the tile + * is non-solid. Otherwise, if the object is "above" (south slopes) + * or "below" (north slopes), the tile will be solid. */ + status = check_position_unisolid (position, tile_bbox); + if (status == 0) + return (false); + + return (true); +} /* bool Tile::is_solid */ + +/* vim: set sw=2 sts=2 et : */ /* EOF */ diff --git a/src/supertux/tile.hpp b/src/supertux/tile.hpp index 805d85be2..7c436b50f 100644 --- a/src/supertux/tile.hpp +++ b/src/supertux/tile.hpp @@ -1,6 +1,7 @@ // SuperTux // Copyright (C) 2004 Tobias Glaesser // Copyright (C) 2006 Matthias Braun +// Copyright (C) 2010 Florian Forster // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -132,11 +133,29 @@ public: int getData() const { return data; } + /** Checks the SLOPE attribute. Returns "true" if set, "false" otherwise. */ bool is_slope (void) const { return ((attributes & SLOPE) != 0); } + /** Determine the solidity of a tile. This version behaves correctly for + * unisolid tiles by taking position and movement of the object in question + * into account. Because creating the arguments for this function can be + * expensive, you should handle trivial cases using the "is_solid(void)" and + * "is_unisolid(void)" methods first. */ + bool is_solid (const Rectf& tile_bbox, const Rectf& position, const Vector& movement) const; + + /** This version only checks the SOLID flag to determine the solidity of a + * tile. This means it will always return "true" for unisolid tiles. To + * determine the *current* solidity of unisolid tiles, use the "is_solid" + * method that takes position and movement into account (see above). */ + bool is_solid (void) const + { + return ((attributes & SOLID) != 0); + } + + /** Checks the UNISOLID attribute. Returns "true" if set, "false" otherwise. */ bool is_unisolid (void) const { return ((attributes & UNISOLID) != 0); @@ -149,6 +168,15 @@ private: //might miss (and rebuke them for it) void correct_attributes(); + /** Returns zero if a unisolid tile is non-solid due to the movement + * direction, non-zero if the tile is solid due to direction. */ + int check_movement_unisolid (const Vector movement) const; + + /** Returns zero if a unisolid tile is non-solid due to the position of the + * tile and the object, non-zero if the tile is solid. */ + int check_position_unisolid (const Rectf& obj_bbox, + const Rectf& tile_bbox) const; + private: Tile(const Tile&); Tile& operator=(const Tile&); -- 2.11.0