diff --git a/src/badguy/badguy.cpp b/src/badguy/badguy.cpp index 6a82c21486..7d9eff2ec8 100644 --- a/src/badguy/badguy.cpp +++ b/src/badguy/badguy.cpp @@ -47,6 +47,10 @@ static const float FADEOUT_TIME = 0.2f; static const float X_OFFSCREEN_DISTANCE = 1280; static const float Y_OFFSCREEN_DISTANCE = 800; +/** Ice physics constants (identical to player ice physics) */ +static const float BADGUY_ICE_FRICTION_MULTIPLIER = 0.1f; // Same as player +static const float BADGUY_ICE_ACCELERATION_MULTIPLIER = 0.25f; // Same as player + BadGuy::BadGuy(const Vector& pos, const std::string& sprite_name, int layer, const std::string& light_sprite_name, const std::string& ice_sprite_name, const std::string& fire_sprite_name) : @@ -67,6 +71,8 @@ BadGuy::BadGuy(const Vector& pos, Direction direction, const std::string& sprite m_frozen(false), m_ignited(false), m_in_water(false), + m_on_ice(false), + m_ice_this_frame(false), m_dead_script(), m_melting_time(0), m_lightsprite(SpriteManager::current()->create(light_sprite_name)), @@ -120,6 +126,8 @@ BadGuy::BadGuy(const ReaderMapping& reader, const std::string& sprite_name, m_frozen(false), m_ignited(false), m_in_water(false), + m_on_ice(false), + m_ice_this_frame(false), m_dead_script(), m_melting_time(0), m_lightsprite(SpriteManager::current()->create(light_sprite_name)), @@ -440,6 +448,12 @@ BadGuy::get_allowed_directions() const void BadGuy::active_update(float dt_sec) { + // Manage ice state each frame + if (!m_ice_this_frame && on_ground()) { + m_on_ice = false; + } + m_ice_this_frame = false; + if (!is_grabbed()) { if (is_in_water() && m_water_affected) @@ -456,6 +470,9 @@ BadGuy::active_update(float dt_sec) } } + // Apply ice physics if on ice + apply_ice_physics(); + if (m_frozen) { m_sprite->stop_animation(); } @@ -478,6 +495,11 @@ BadGuy::collision_tile(uint32_t tile_attributes) SoundManager::current()->play("sounds/splash.ogg", get_pos()); } + if (tile_attributes & Tile::ICE) { + m_ice_this_frame = true; + m_on_ice = true; + } + if (tile_attributes & Tile::HURTS && is_hurtable()) { Rectf hurtbox = get_bbox().grown(-6.f); @@ -713,6 +735,23 @@ BadGuy::collision_bullet(Bullet& bullet, const CollisionHit& hit) } } +void +BadGuy::apply_ice_physics() +{ + if (!m_on_ice || !on_ground()) return; + + float velx = m_physic.get_velocity_x(); + // No artificial velocity threshold - let natural physics handle sliding + + // Use same friction base as player (WALK_ACCELERATION_X = 300) + float friction = 300.0f * BADGUY_ICE_FRICTION_MULTIPLIER; // Base friction value + if (velx < 0) { + m_physic.set_acceleration_x(friction); + } else if (velx > 0) { + m_physic.set_acceleration_x(-friction); + } +} + void BadGuy::kill_squished(GameObject& object) { diff --git a/src/badguy/badguy.hpp b/src/badguy/badguy.hpp index 86358d1eee..dd56172ef0 100644 --- a/src/badguy/badguy.hpp +++ b/src/badguy/badguy.hpp @@ -258,6 +258,9 @@ class BadGuy : public MovingSprite, collision_solid. */ bool on_ground() const; + /** Apply ice physics to reduce friction when on ice */ + void apply_ice_physics(); + /** Returns floor normal stored the last time when update_on_ground_flag was called and we touched something solid from above. */ @@ -299,6 +302,8 @@ class BadGuy : public MovingSprite, bool m_frozen; bool m_ignited; /**< true if this badguy is currently on fire */ bool m_in_water; /** < true if the badguy is currently in water */ + bool m_on_ice; /**< true if the badguy is currently on ice */ + bool m_ice_this_frame; /**< true if the badguy touched ice this frame */ std::string m_dead_script; /**< script to execute when badguy is killed */ diff --git a/src/badguy/walking_badguy.cpp b/src/badguy/walking_badguy.cpp index efa6b74334..1262e7b670 100644 --- a/src/badguy/walking_badguy.cpp +++ b/src/badguy/walking_badguy.cpp @@ -20,6 +20,9 @@ #include "sprite/sprite.hpp" +// Ice physics constant (identical to player ice physics) +static const float BADGUY_ICE_ACCELERATION_MULTIPLIER = 0.25f; + WalkingBadguy::WalkingBadguy(const Vector& pos, const std::string& sprite_name_, const std::string& walk_left_action_, @@ -145,7 +148,8 @@ WalkingBadguy::active_update(float dt_sec, float dest_x_velocity, float modifier { /* acceleration == walk-speed => it will take one second to get from zero * to full speed. */ - m_physic.set_acceleration_x (dest_x_velocity * modifier); + float ice_multiplier = (m_on_ice && on_ground()) ? BADGUY_ICE_ACCELERATION_MULTIPLIER : 1.0f; + m_physic.set_acceleration_x (dest_x_velocity * modifier * ice_multiplier); } /* Check if we're going too fast */ else if (((dest_x_velocity <= 0.0f) && (current_x_velocity < dest_x_velocity)) || @@ -153,7 +157,8 @@ WalkingBadguy::active_update(float dt_sec, float dest_x_velocity, float modifier { /* acceleration == walk-speed => it will take one second to get twice the * speed to normal speed. */ - m_physic.set_acceleration_x ((-1.f) * dest_x_velocity); + float ice_multiplier = (m_on_ice && on_ground()) ? BADGUY_ICE_ACCELERATION_MULTIPLIER : 1.0f; + m_physic.set_acceleration_x ((-1.f) * dest_x_velocity * ice_multiplier); } else {