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 (part 2) #3060

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a55dfe8
wip
MatusGuy Mar 2, 2024
0c646b6
wip again
MatusGuy Mar 20, 2024
318beb6
this is tiring to work on
MatusGuy Mar 23, 2024
d161231
Merge branch 'SuperTux:master' into maxdropheight-slopes
MatusGuy Mar 23, 2024
441feec
something somthing is free of tiles
MatusGuy Mar 23, 2024
3c27073
fix
MatusGuy Apr 4, 2024
bd7a371
Fix this stupid function once and for all
MatusGuy Apr 4, 2024
5b7dd3d
Merge branch 'master' into maxdropheight-slopes
MatusGuy Apr 4, 2024
0dacfcc
HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
MatusGuy Apr 4, 2024
d533060
just another tale from the streets of america
MatusGuy Apr 4, 2024
54cfa58
haa
MatusGuy Jul 15, 2024
e0a6ede
Merge branch 'refs/heads/real-master' into maxdropheight-slopes
MatusGuy Jul 15, 2024
9d74250
it works??
MatusGuy Jul 17, 2024
f0eaf04
wip
MatusGuy Jul 29, 2024
9c94ca7
"just close your eyes and count to 3 (one, two, three)"
MatusGuy Aug 12, 2024
b75fb3e
fix stuff
MatusGuy Aug 15, 2024
79f845e
Merge branch 'real-master' into maxdropheight-slopes
MatusGuy Sep 13, 2024
14c1b6e
fix objects and that weird slope fall thing
MatusGuy Sep 13, 2024
f7ac61d
ahaeiu
MatusGuy Sep 15, 2024
80195af
offsets n' shit
MatusGuy Sep 15, 2024
71d206b
bgah
MatusGuy Sep 15, 2024
a5b2417
fix this weird thing
MatusGuy Sep 16, 2024
47ded8e
Merge branch 'real-master' into maxdropheight-slopes
MatusGuy Sep 16, 2024
47c535a
del
MatusGuy Sep 16, 2024
80807fd
fix bitchy ci
MatusGuy Oct 23, 2024
7206342
Merge branch 'real-master' into maxdropheight-slopes
MatusGuy Oct 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 88 additions & 17 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 @@ -70,11 +71,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 @@ -113,11 +115,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 Down Expand Up @@ -288,7 +291,7 @@ BadGuy::update(float dt_sec)
m_freezesprite->set_action(get_overlay_size(), 1);
else
m_freezesprite->set_action("default", 1);

active_update(dt_sec);
break;

Expand Down Expand Up @@ -837,25 +840,93 @@ 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;

float fh = static_cast<float>(height);

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

Vector end(eye.x, eye.y + fh + 2.f);

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

if (!result.is_valid)
{
// The ground is deeper than max drop height. Turn around.
return true;
}

auto tile_p = std::get_if<const Tile*>(&result.hit);
if (tile_p && (*tile_p) && (*tile_p)->is_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
// by returning false at the end of this function.
}
}

if (m_detected_slope != 0)
{
float dirmult = (m_dir == Direction::LEFT ? 1.f : -1.f);

// X position of the opposite face of the hitbox relative to m_dir.
float rearx = (m_dir == Direction::LEFT ? get_bbox().get_right() : get_bbox().get_left());

// X Offset from rearx used for determining the start of the raycast.
float startoff = (get_width() / 5.f) * dirmult;
Vector eye(rearx - startoff, oy);

// X Offset from eye's X used for determining the end of the raycast.
float endoff = startoff - (2.f * dirmult);
Vector end(eye.x + endoff, eye.y + 80.f);

// The resulting line segment (eye, end) should result in a downwards facing diagonal direction.

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

if (!result.is_valid)
{
// Turn around and climb the slope.
m_detected_slope = 0;
return true;
}

if (result.box.get_top() - oy > fh + 1.f)
{
// Result is not within reach.
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.
return false;
}

// No longer going down a slope. Switch off slope mode.
m_detected_slope = 0;
}
const Rectf rect = Rectf(x1, y1, x2, y2);

return Sector::get().is_free_of_statics(rect) && Sector::get().is_free_of_specifically_movingstatics(rect);
return false;
}

Player*
Expand Down
16 changes: 12 additions & 4 deletions src/badguy/badguy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,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);

/** 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 @@ -259,6 +259,9 @@ class BadGuy : public MovingSprite,
private:
void try_activate();

protected:
static const int s_normal_max_drop_height = 600;

protected:
Physic m_physic;

Expand Down Expand Up @@ -294,6 +297,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 @@ -307,9 +318,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;
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
3 changes: 0 additions & 3 deletions src/badguy/walking_badguy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,6 @@ class WalkingBadguy : public BadGuy
protected:
void turn_around();

protected:
static const int s_normal_max_drop_height = 600;

protected:
std::string walk_left_action;
std::string walk_right_action;
Expand Down
97 changes: 58 additions & 39 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 Expand Up @@ -764,46 +764,49 @@ CollisionSystem::is_free_of_specifically_movingstatics(const Rectf& rect, const
CollisionSystem::RaycastResult
CollisionSystem::get_first_line_intersection(const Vector& line_start,
const Vector& line_end,
bool ignore_objects,
RaycastIgnore ignore,
const CollisionObject* ignore_object) const
{
using namespace collision;
RaycastResult result{};

// Check if no tile is in the way.
const float lsx = std::min(line_start.x, line_end.x);
const float lex = std::max(line_start.x, line_end.x);
const float lsy = std::min(line_start.y, line_end.y);
const float ley = std::max(line_start.y, line_end.y);

for (float test_x = lsx; test_x <= lex; test_x += 16) { // NOLINT.
for (float test_y = lsy; test_y <= ley; test_y += 16) { // NOLINT.
for (const auto& solids : m_sector.get_solid_tilemaps()) {
const auto& test_vector = Vector(test_x, test_y);
if(solids->is_outside_bounds(test_vector))
{
continue;
}
RaycastResult tileresult;

if (ignore != IGNORE_TILES)
{
// Check if no tile is in the way.
const float lsx = std::min(line_start.x, line_end.x);
const float lex = std::max(line_start.x, line_end.x);
const float lsy = std::min(line_start.y, line_end.y);
const float ley = std::max(line_start.y, line_end.y);

for (float test_x = lsx; test_x <= lex; test_x += 16) { // NOLINT.
for (float test_y = lsy; test_y <= ley; test_y += 16) { // NOLINT.
for (const auto& solids : m_sector.get_solid_tilemaps()) {
const auto& test_vector = Vector(test_x, test_y);
if(solids->is_outside_bounds(test_vector))
{
continue;
}

const Tile* tile = &solids->get_tile_at(test_vector);
const Tile* tile = &solids->get_tile_at(test_vector);

// FIXME: check collision with slope tiles
if ((tile->get_attributes() & Tile::SOLID))
{
result.is_valid = true;
result.hit = tile;
result.box = solids->get_tile_bbox(static_cast<int>(test_vector.x / 32.f), static_cast<int>(test_vector.y / 32.f));
return result;
// FIXME: check collision with slope tiles
if ((tile->get_attributes() & Tile::SOLID))
{
tileresult.is_valid = true;
tileresult.hit = tile;
tileresult.box = solids->get_tile_bbox(static_cast<int>(test_vector.x / 32.f), static_cast<int>(test_vector.y / 32.f));
goto finish_tiles;
}
}
}
}
}

if (ignore_objects)
{
result.is_valid = false;
return result;
}
finish_tiles:
if (ignore == IGNORE_OBJECTS)
return tileresult;

RaycastResult objresult;

// Check if no object is in the way.
for (const auto& object : m_objects) {
Expand All @@ -815,22 +818,38 @@ CollisionSystem::get_first_line_intersection(const Vector& line_start,
{
if (intersects_line(object->get_bbox(), line_start, line_end))
{
result.is_valid = true;
result.hit = object;
result.box = object->get_bbox();
return result;
objresult.is_valid = true;
objresult.hit = object;
objresult.box = object->get_bbox();
break;
}
}
}

result.is_valid = false;
return result;
if (ignore == IGNORE_TILES)
return objresult;

if (tileresult.is_valid && objresult.is_valid)
{
float tiledist = glm::distance(tileresult.box.get_middle(), line_start);
float objdist = glm::distance(objresult.box.get_middle(), line_start);
return tiledist < objdist ? tileresult : objresult;
}
else if (tileresult.is_valid)
return tileresult;
else if (objresult.is_valid)
return objresult;
else
{
return RaycastResult();
}
}

bool
CollisionSystem::free_line_of_sight(const Vector& line_start, const Vector& line_end, bool ignore_objects, const CollisionObject* ignore_object) const
{
return !get_first_line_intersection(line_start, line_end, ignore_objects, ignore_object).is_valid;
auto ignore = (ignore_objects ? IGNORE_OBJECTS : IGNORE_NONE);
return !get_first_line_intersection(line_start, line_end, ignore, ignore_object).is_valid;
}

std::vector<CollisionObject*>
Expand Down
Loading
Loading