/** * \brief Updates the position. */ void RelativeMovement::update() { if (entity_followed == nullptr) { finished = true; return; } if (entity_followed->is_being_removed()) { finished = true; entity_followed = nullptr; } else { Point next = entity_followed->get_xy() + entity_offset; Point dnext = next - get_xy(); if (!are_obstacles_ignored()) { if (!finished && (dnext.x != 0 || dnext.y != 0)) { if (!test_collision_with_obstacles(dnext)) { set_xy(next); } else { finished = true; notify_obstacle_reached(); } } } else { set_xy(next); } } }
/** * \brief Tests whether a point collides with the map obstacles. * \param layer Layer of point to check. * \param point Point to check. * \param entity_to_check The entity to check (used to decide what is * considered as obstacle) * \return \c true if the point is overlapping an obstacle. */ bool Map::test_collision_with_obstacles( Layer layer, const Point& point, MapEntity& entity_to_check) const { return test_collision_with_obstacles(layer, point.x, point.y, entity_to_check); }
void StraightMovement::update_y() { uint32_t next_move_time_y = delay_y; if(move_y != 0) { //entity wants to move in y direction. next_move_time_y = delay_y; if(!test_collision_with_obstacles(0, move_y) && !test_collision_with_borders(0, move_y)) { translate_y(move_y); //make the move on y if(move_x != 0 && test_collision_with_obstacles(move_x, 0)) { // if there is also a y move and this move is stopped by an obstacles. next_move_time_y = (int) (1000 / get_speed()); update_x(); } } } else { stop(); } next_move_date_y += next_move_time_y; }
void StraightMovement::update_x() { uint32_t next_move_time_x; if(move_x != 0) { // entity wants to move in x direction. next_move_time_x = delay_x; if(!test_collision_with_obstacles(move_x, 0) && !test_collision_with_borders(move_x, 0)) { translate_x(move_x); // no collision in x direction if(move_y != 0 && test_collision_with_obstacles(0, move_y)) { // if there is also a y move and this move is stopped by an obstacles. next_move_time_x = (int) (1000 / get_speed()); update_y(); } } } else { //entity does not want to move in x direction so we stop the movement. stop(); } next_move_date_x += next_move_time_x; }
/** * @brief Updates the y position of the entity if it wants to move * (non-smooth version). */ void StraightMovement::update_non_smooth_y() { if (y_move != 0) { // if it's time to try a move // make the move only if there is no collision if (!test_collision_with_obstacles(x_move, y_move)) { translate_y(y_move); } else { stop(); // also stop on x } next_move_date_y += y_delay; } }
/** * @brief Updates the x position of the entity if it wants to move * (non-smooth version). */ void StraightMovement::update_non_smooth_x() { if (x_move != 0) { // make the move only if there is no collision if (!test_collision_with_obstacles(x_move, y_move)) { translate_x(x_move); } else { stop(); // also stop on y } next_move_date_x += x_delay; } }
/** * @brief Computes the position of the object controlled by this movement. * * This function should be called whenever the angle, the radius or the center changes. */ void CircleMovement::recompute_position() { Rectangle center = this->center_point; if (center_entity != NULL) { center.add_xy(center_entity->get_xy()); } const Rectangle &xy = Geometry::get_xy(center, Geometry::degrees_to_radians(current_angle), current_radius); if (get_entity() == NULL || !test_collision_with_obstacles(xy.get_x() - get_entity()->get_x(), xy.get_y() - get_entity()->get_y())) { set_xy(xy); notify_position_changed(); } else { notify_obstacle_reached(); } }
/** * \brief Computes the position of the object controlled by this movement. * * This function should be called whenever the angle, the radius or the center changes. */ void CircleMovement::recompute_position() { Point center = this->center_point; if (center_entity != nullptr) { center += center_entity->get_xy(); } Point xy = Geometry::get_xy(center, Geometry::degrees_to_radians(current_angle), current_radius); if (get_entity() == nullptr || !test_collision_with_obstacles(xy - get_entity()->get_xy())) { set_xy(xy); notify_position_changed(); } else { notify_obstacle_reached(); } }
/** * @brief Updates the movement. */ void TargetMovement::update() { if (System::now() >= next_recomputation_date) { recompute_movement(); next_recomputation_date += recomputation_delay; } // see if the target is reached int dx = target_x - get_x(); int dy = target_y - get_y(); if (dx * sign_x <= 0 && dy * sign_y <= 0) { if (!test_collision_with_obstacles(dx, dy)) { set_xy(target_x, target_y); // because the target movement may have not been very precise stop(); finished = true; } } StraightMovement::update(); }
/** * \brief Updates the position. */ void FollowMovement::update() { if (entity_followed == NULL) { finished = true; return; } if (entity_followed->is_being_removed()) { finished = true; RefCountable::unref(entity_followed); entity_followed = NULL; } else { int next_x = entity_followed->get_x() + x; int next_y = entity_followed->get_y() + y; int dx = next_x - get_x(); int dy = next_y - get_y(); if (!are_obstacles_ignored()) { if (!finished && (dx != 0 || dy != 0)) { if (!test_collision_with_obstacles(dx, dy)) { set_x(next_x); set_y(next_y); } else { finished = true; notify_obstacle_reached(); } } } else { set_x(next_x); set_y(next_y); } } }
/** * @brief Updates the y position of the entity if it wants to move * (smooth version). */ void StraightMovement::update_smooth_y() { if (y_move != 0) { // The entity wants to move on y. // By default, next_move_date_y will be incremented by y_delay, // unless we modify the movement in such a way that the // y speed needs to be fixed. uint32_t next_move_date_y_increment = y_delay; if (!test_collision_with_obstacles(0, y_move)) { translate_y(y_move); // Make the move. if (x_move != 0 && test_collision_with_obstacles(x_move, 0)) { // If there is also an x move, and if this x move is illegal, // we still allow the y move and we give it all the speed. next_move_date_y_increment = (int) (1000.0 / get_speed()); } } else { if (x_move == 0) { // The move on y is not possible and there is no x move: // let's try to add a move on x to make a diagonal move, // but only if the wall is really diagonal: otherwise, the hero // could bypass sensors. if (!test_collision_with_obstacles(1, y_move) // Can move diagonally and: && (test_collision_with_obstacles(-1, 0) || // the wall is really diagonal test_collision_with_obstacles(1, 0)) // or we don't have a choice anyway. ) { translate_xy(1, y_move); next_move_date_y_increment = (int) (y_delay * Geometry::SQRT_2); // Fix the speed. } else if (!test_collision_with_obstacles(-1, y_move) && (test_collision_with_obstacles(1, 0) || test_collision_with_obstacles(-1, 0)) ) { translate_xy(-1, y_move); next_move_date_y_increment = (int) (y_delay * Geometry::SQRT_2); } else { // The diagonal moves didn't work either. // So we look for a place (up to 8 pixels on the left and on the right) // where the required move would be allowed. // If we find a such place, then we move towards this place. bool moved = false; for (int i = 1; i <= 8 && !moved; i++) { if (!test_collision_with_obstacles(i, y_move) && !test_collision_with_obstacles(1, 0)) { translate_x(1); moved = true; } else if (!test_collision_with_obstacles(-i, y_move) && !test_collision_with_obstacles(-1, 0)) { translate_x(-1); moved = true; } } } } else { // The move on y is not possible, but there is also a horizontal move. if (!test_collision_with_obstacles(x_move, y_move)) { // This case is only necessary in narrow diagonal passages. translate_xy(x_move, y_move); next_move_date_x += x_delay; // Delay the next update_smooth_x() since we just replaced it. } else if (!test_collision_with_obstacles(x_move, 0)) { // Do the horizontal move right now, don't wait uselessly. update_x(); } } } next_move_date_y += next_move_date_y_increment; } }
/** * \brief Returns whether the entity would collide with the map * if it was moved a few pixels from its position. * * If the movement is not attached to an entity of a map, * or if obstacles are ignored, false is always returned. * * \param dxy distance between the current position and the position to check * \return true if the entity would overlap the map obstacles in this position */ bool Movement::test_collision_with_obstacles(const Point& dxy) const { return test_collision_with_obstacles(dxy.x, dxy.y); }
/** * @brief Returns whether the entity would collide with the map * if it was moved a few pixels from its position. * * If the movement is not attached to an entity of a map, * or if obstacles are ignored, false is always returned. * * @param dxy distance between the current position and the position to check * @return true if the entity would overlap the map obstacles in this position */ bool Movement::test_collision_with_obstacles(const Rectangle& dxy) { return test_collision_with_obstacles(dxy.get_x(), dxy.get_y()); }
/** * \brief Updates the x position of the entity if it wants to move * (smooth version). */ void StraightMovement::update_smooth_x() { if (x_move != 0) { // The entity wants to move on x. // By default, next_move_date_x will be incremented by x_delay, // unless we modify below the movement in such a way that the // x speed needs to be fixed. uint32_t next_move_date_x_increment = x_delay; if (!test_collision_with_obstacles(x_move, 0)) { translate_x(x_move); // Make the move. if (y_move != 0 && test_collision_with_obstacles(0, y_move)) { // If there is also a y move, and if this y move is illegal, // we still allow the x move and we give it all the speed. next_move_date_x_increment = (int) (1000.0 / get_speed()); } } else { if (y_move == 0) { // The move on x is not possible and there is no y move: // let's try to add a move on y to make a diagonal move, // but only if the wall is really diagonal: otherwise, the hero // could bypass sensors. if (!test_collision_with_obstacles(x_move, 1) // Can move diagonally and: && (test_collision_with_obstacles(0, -1) || // the wall is really diagonal test_collision_with_obstacles(0, 1)) // or we don't have a choice anyway. ) { translate_xy(x_move, 1); next_move_date_x_increment = (int) (x_delay * Geometry::SQRT_2); // Fix the speed. } else if (!test_collision_with_obstacles(x_move, -1) && (test_collision_with_obstacles(0, 1) || test_collision_with_obstacles(0, -1)) ) { translate_xy(x_move, -1); next_move_date_x_increment = (int) (x_delay * Geometry::SQRT_2); } else { // The diagonal moves didn't work either. // So we look for a place (up to 8 pixels up and down) // where the required move would be allowed. // If we find a such place, then we move towards this place. bool moved = false; for (int i = 1; i <= 8 && !moved; i++) { if (!test_collision_with_obstacles(x_move, i) && !test_collision_with_obstacles(0, 1)) { translate_y(1); moved = true; } else if (!test_collision_with_obstacles(x_move, -i) && !test_collision_with_obstacles(0, -1)) { translate_y(-1); moved = true; } } } } else { // The move on x is not possible, but there is also a vertical move. if (!test_collision_with_obstacles(0, y_move)) { // Do the vertical move right now, don't wait uselessly. update_y(); } else { // The x move is not possible and neither is the y move. // Last chance: do both x and y moves in one step. // This case is only necessary in narrow diagonal passages. // We do it as a last resort, because we want separate x and y // steps whenever possible: otherwise, the hero could bypass sensors. if (!test_collision_with_obstacles(x_move, y_move)) { translate_xy(x_move, y_move); next_move_date_y += y_delay; // Delay the next update_smooth_y() since we just replaced it. } } } } next_move_date_x += next_move_date_x_increment; } }
/** * @brief Updates the y position of the entity if it wants to move * (smooth version). */ void StraightMovement::update_smooth_y() { if (y_move != 0) { // the entity wants to move on y // by default, next_move_date_y will be incremented by y_delay, // unless we modify the movement in such a way that the // y speed needs to be fixed uint32_t next_move_date_y_increment = y_delay; if (!test_collision_with_obstacles(0, y_move)) { translate_y(y_move); // make the move if (x_move != 0 && test_collision_with_obstacles(x_move, 0)) { // if there is also an x move, and if this x move is illegal, // we still allow the y move and we give it all the speed next_move_date_y_increment = (int) (1000.0 / get_speed()); } } else { if (x_move == 0) { // The move on y is not possible: let's try // to add a move on x to make a diagonal move. if (!test_collision_with_obstacles(1, y_move) && test_collision_with_obstacles(-1, 0)) { translate_xy(1, y_move); next_move_date_y_increment = (int) (y_delay * Geometry::SQRT_2); // fix the speed } else if (!test_collision_with_obstacles(-1, y_move) && test_collision_with_obstacles(1, 0)) { translate_xy(-1, y_move); next_move_date_y_increment = (int) (y_delay * Geometry::SQRT_2); } else { /* The diagonal moves didn't work either. * So we look for a place (up to 8 pixels on the left and on the right) * where the required move would be allowed. * If we find a such place, then we move towards this place. */ bool moved = false; for (int i = 1; i <= 8 && !moved; i++) { if (!test_collision_with_obstacles(i, y_move) && !test_collision_with_obstacles(1, 0)) { translate_x(1); moved = true; } else if (!test_collision_with_obstacles(-i, y_move) && !test_collision_with_obstacles(-1, 0)) { translate_x(-1); moved = true; } } } } else { // no attractive place was found, but there is a horizontal move if (!test_collision_with_obstacles(x_move, 0)) { // do the horizontal move right now, don't wait uselessly update_x(); } } } next_move_date_y += next_move_date_y_increment; } }