Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix max drop height on slopes #2812

Merged
merged 16 commits into from
Sep 9, 2024
Merged
130 changes: 114 additions & 16 deletions src/badguy/badguy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "audio/sound_manager.hpp"
#include "badguy/dispenser.hpp"
#include "editor/editor.hpp"
#include "math/aatriangle.hpp"
#include "math/random.hpp"
#include "object/bullet.hpp"
#include "object/camera.hpp"
Expand Down Expand Up @@ -69,11 +70,12 @@ BadGuy::BadGuy(const Vector& pos, Direction direction, const std::string& sprite
m_glowing(false),
m_water_affected(true),
m_unfreeze_timer(),
m_floor_normal(0.0f, 0.0f),
m_detected_slope(0),
m_state(STATE_INIT),
m_is_active_flag(),
m_state_timer(),
m_on_ground_flag(false),
m_floor_normal(0.0f, 0.0f),
m_colgroup_active(COLGROUP_MOVING)
{
SoundManager::current()->preload("sounds/squish.wav");
Expand Down Expand Up @@ -112,11 +114,12 @@ BadGuy::BadGuy(const ReaderMapping& reader, const std::string& sprite_name,
m_glowing(false),
m_water_affected(true),
m_unfreeze_timer(),
m_floor_normal(0.0f, 0.0f),
m_detected_slope(0),
m_state(STATE_INIT),
m_is_active_flag(),
m_state_timer(),
m_on_ground_flag(false),
m_floor_normal(0.0f, 0.0f),
m_colgroup_active(COLGROUP_MOVING)
{
std::string dir_str;
Expand All @@ -139,6 +142,22 @@ BadGuy::BadGuy(const ReaderMapping& reader, const std::string& sprite_name,
void
BadGuy::draw(DrawingContext& context)
{
Vector eye(0, get_bbox().get_bottom() + 1.f);
eye.x = (m_dir == Direction::LEFT ? get_bbox().get_left() : get_bbox().get_right());

Vector end(eye.x, eye.y + 256.f);
context.color().draw_line(eye, end, Color::GREEN, LAYER_GUI);

float dirmult = (m_dir == Direction::LEFT ? 1.f : -1.f);
float rearx = (m_dir == Direction::LEFT ? get_bbox().get_right() : get_bbox().get_left());

float soff = (get_width() / 5.f) * dirmult;
MatusGuy marked this conversation as resolved.
Show resolved Hide resolved
Vector seye(rearx - soff, eye.y);

float eoff = soff - (2.f * dirmult);
MatusGuy marked this conversation as resolved.
Show resolved Hide resolved
Vector send(seye.x + eoff, seye.y + 80.f);
context.color().draw_line(seye, send, Color::GREEN, LAYER_GUI);

if (!m_sprite.get()) return;

if (m_state == STATE_INIT || m_state == STATE_INACTIVE)
Expand Down Expand Up @@ -833,25 +852,104 @@ BadGuy::try_activate()
}

bool
BadGuy::might_fall(int height) const
BadGuy::might_fall(int height)
{
// Make sure we check for at least a 1-pixel fall.
using RaycastResult = CollisionSystem::RaycastResult;

assert(height > 0);

float x1;
float x2;
float y1 = m_col.m_bbox.get_bottom() + 1;
float y2 = m_col.m_bbox.get_bottom() + 1 + static_cast<float>(height);
if (m_dir == Direction::LEFT) {
x1 = m_col.m_bbox.get_left() - 1;
x2 = m_col.m_bbox.get_left();
} else {
x1 = m_col.m_bbox.get_right();
x2 = m_col.m_bbox.get_right() + 1;
// Origin in Y coord used for raycasting.
float oy = get_bbox().get_bottom() + 1.f;

if (m_detected_slope == 0)
{
Vector eye(0, oy);
eye.x = (m_dir == Direction::LEFT ? get_bbox().get_left() : get_bbox().get_right());

// TODO: change to max possible drop height
Vector end(eye.x, eye.y + 256.f);
//std::cout << eye << " " << end << std::endl;
MatusGuy marked this conversation as resolved.
Show resolved Hide resolved

RaycastResult result = Sector::get().get_first_line_intersection(eye, end, false, nullptr);
//bool slope = false;

auto tile_p = std::get_if<const Tile*>(&result.hit);
if (result.is_valid && result.box.get_top() - eye.y < static_cast<float>(height))
{
// The ground is within max drop height. Continue.
return false;
}
else if (tile_p && (*tile_p) && (*tile_p)->is_slope())
MatusGuy marked this conversation as resolved.
Show resolved Hide resolved
{
// Check if we are about to go down a slope.
AATriangle tri((*tile_p)->get_data());
if (tri.is_south() && (m_dir == Direction::LEFT ? tri.is_east() : !tri.is_east()))
{
// Switch to slope mode.
m_detected_slope = tri.dir;
}

// Otherwise, climb the slope like normal.
return false;
MatusGuy marked this conversation as resolved.
Show resolved Hide resolved
}
else
{
// The ground is no longer within reach. Turn around.
return true;
}
}
const Rectf rect = Rectf(x1, y1, x2, y2);

return Sector::get().is_free_of_statics(rect) && Sector::get().is_free_of_specifically_movingstatics(rect);
if (m_detected_slope != 0)
{
//AATriangle tri(m_detected_slope);
std::cout << "SLOPE " << m_detected_slope << std::endl;
//slope = true;

float dirmult = (m_dir == Direction::LEFT ? 1.f : -1.f);
float rearx = (m_dir == Direction::LEFT ? get_bbox().get_right() : get_bbox().get_left());

float soff = (get_width() / 5.f) * dirmult;
MatusGuy marked this conversation as resolved.
Show resolved Hide resolved
Vector eye(rearx - soff, oy);

float eoff = soff - (2.f * dirmult);
MatusGuy marked this conversation as resolved.
Show resolved Hide resolved
Vector end(eye.x + eoff, eye.y + 80.f);

RaycastResult result = Sector::get().get_first_line_intersection(eye, end, false, nullptr);

std::cout << result.is_valid << std::endl;

if (!result.is_valid)
{
// Turn around and climb the slope.
m_detected_slope = 0;
return true;
}
else
MatusGuy marked this conversation as resolved.
Show resolved Hide resolved
{
if (result.box.get_top() - eye.y > static_cast<float>(height) + 1.f)
{
// Result is not within reach.
std::cout << "gahh " << result.box.get_top() - eye.y << std::endl;
m_detected_slope = 0;
return true;
}

auto tile_p = std::get_if<const Tile*>(&result.hit);
if (tile_p && (*tile_p) && (*tile_p)->is_slope())
{
// Still going down a slope. Continue.
//m_detected_slope = (*tile_p)->get_data();
return false;
}

// No longer going down a slope. Switch off slope mode.
m_detected_slope = 0;
std::cout << "aggh" << std::endl;
MatusGuy marked this conversation as resolved.
Show resolved Hide resolved
return false;
}
}

return false;
}

Player*
Expand Down
13 changes: 9 additions & 4 deletions src/badguy/badguy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ class BadGuy : public MovingSprite,

/** Returns true if we might soon fall at least @c height
pixels. Minimum value for height is 1 pixel */
bool might_fall(int height = 1) const;
bool might_fall(int height = 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you tell me why this method is no longer const? Do we modify the object state with the changes? And if so: Could we prevent that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It changes m_detected_slope. I do this because I want to prevent having two raycasts at a time. So to fix that I alternate between them by checking if m_detected_slope is 0 or not.


/** Update on_ground_flag judging by solid collision @c hit. This
gets called from the base implementation of collision_solid, so
Expand Down Expand Up @@ -293,6 +293,14 @@ class BadGuy : public MovingSprite,

Timer m_unfreeze_timer;

/** floor normal stored the last time when update_on_ground_flag was
called and we touched something solid from above */
Vector m_floor_normal;

/** Used for the might_fall function.
Represents the tile data of the detected slope. */
int m_detected_slope;

private:
State m_state;

Expand All @@ -306,9 +314,6 @@ class BadGuy : public MovingSprite,
update_on_ground_flag was called last frame */
bool m_on_ground_flag;

/** floor normal stored the last time when update_on_ground_flag was
called and we touched something solid from above */
Vector m_floor_normal;

/** CollisionGroup the badguy should be in while active */
CollisionGroup m_colgroup_active;
Expand Down
7 changes: 6 additions & 1 deletion src/badguy/walking_badguy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ void WalkingBadguy::set_ledge_behavior(LedgeBehavior behavior)
break;

case LedgeBehavior::SMART:
max_drop_height = static_cast<int>(get_bbox().get_width()) / 2;
max_drop_height = 16.f;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this number have any significance? Seems like a magic number. Just curious.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's half a tile

break;

case LedgeBehavior::NORMAL:
Expand All @@ -123,6 +123,11 @@ WalkingBadguy::active_update(float dt_sec, float dest_x_velocity, float modifier
{
BadGuy::active_update(dt_sec);

// Walk down slopes smoothly.
if (on_ground() && m_floor_normal.y != 0 && (m_floor_normal.x * m_physic.get_velocity_x()) >= 0) {
m_physic.set_velocity_y((std::abs(m_physic.get_velocity_x()) * std::abs(m_floor_normal.x)) + 100.f);
}

float current_x_velocity = m_physic.get_velocity_x ();

if (m_frozen)
Expand Down
4 changes: 2 additions & 2 deletions src/collision/collision_system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -708,11 +708,11 @@ CollisionSystem::is_free_of_tiles(const Rectf& rect, const bool ignoreUnisolid,
}

bool
CollisionSystem::is_free_of_statics(const Rectf& rect, const CollisionObject* ignore_object, const bool ignoreUnisolid) const
CollisionSystem::is_free_of_statics(const Rectf& rect, const CollisionObject* ignore_object, const bool ignoreUnisolid, uint32_t tiletype) const
{
using namespace collision;

if (!is_free_of_tiles(rect, ignoreUnisolid)) return false;
if (!is_free_of_tiles(rect, ignoreUnisolid, tiletype)) return false;

for (const auto& object : m_objects) {
if (object == ignore_object) continue;
Expand Down
2 changes: 1 addition & 1 deletion src/collision/collision_system.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class CollisionSystem final
}

bool is_free_of_tiles(const Rectf& rect, const bool ignoreUnisolid = false, uint32_t tiletype = Tile::SOLID) const;
bool is_free_of_statics(const Rectf& rect, const CollisionObject* ignore_object, const bool ignoreUnisolid) const;
bool is_free_of_statics(const Rectf& rect, const CollisionObject* ignore_object, const bool ignoreUnisolid, uint32_t tiletype = Tile::SOLID) const;
bool is_free_of_movingstatics(const Rectf& rect, const CollisionObject* ignore_object) const;
bool is_free_of_specifically_movingstatics(const Rectf& rect, const CollisionObject* ignore_object) const;

Expand Down
11 changes: 11 additions & 0 deletions src/math/aatriangle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ class AATriangle final
{
}

// Meant for checking directions
AATriangle(int newdir) :
bbox(),
dir(newdir)
{
}

inline int get_dir() const { return dir; }
inline bool is_south() const { return (dir & DIRECTION_MASK) == SOUTHWEST || (dir & DIRECTION_MASK) == SOUTHEAST; }
inline bool is_east() const { return (dir & DIRECTION_MASK) == NORTHEAST || (dir & DIRECTION_MASK) == SOUTHEAST; }

public:
Rectf bbox;
int dir;
Expand Down
5 changes: 3 additions & 2 deletions src/supertux/sector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -550,11 +550,12 @@ Sector::is_free_of_solid_tiles(float left, float top, float right, float bottom,
}

bool
Sector::is_free_of_statics(const Rectf& rect, const MovingObject* ignore_object, const bool ignoreUnisolid) const
Sector::is_free_of_statics(const Rectf& rect, const MovingObject* ignore_object, const bool ignoreUnisolid, uint32_t tiletype) const
{
return m_collision_system->is_free_of_statics(rect,
ignore_object ? ignore_object->get_collision_object() : nullptr,
ignoreUnisolid);
ignoreUnisolid,
tiletype);
}

bool
Expand Down
3 changes: 2 additions & 1 deletion src/supertux/sector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ class Sector final : public Base::Sector
1.) solid tiles and
2.) MovingObjects in COLGROUP_STATIC.
Note that this does not include badguys or players. */
bool is_free_of_statics(const Rectf& rect, const MovingObject* ignore_object = nullptr, const bool ignoreUnisolid = false) const;
bool is_free_of_statics(const Rectf& rect, const MovingObject* ignore_object = nullptr,
const bool ignoreUnisolid = false, uint32_t tiletype = Tile::SOLID) const;
/**
* @scripting
* @description Checks if the specified sector-relative rectangle is free of both:
Expand Down
Loading