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

Tux emits bubbles while being underwater #3014

Merged
merged 13 commits into from
Oct 20, 2024
Binary file added data/images/particles/air_bubble-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/particles/air_bubble-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/particles/air_bubble-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/images/particles/air_bubble-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
85 changes: 84 additions & 1 deletion src/object/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,12 @@ Player::Player(PlayerStatus& player_status, const std::string& name_, int player
SoundManager::current()->preload("sounds/invincible_start.ogg");
SoundManager::current()->preload("sounds/splash.wav");
SoundManager::current()->preload("sounds/grow.wav");
m_bubble_particles[0] = Surface::from_file("images/particles/air_bubble-1.png");
m_bubble_particles[1] = Surface::from_file("images/particles/air_bubble-2.png");
m_bubble_particles[2] = Surface::from_file("images/particles/air_bubble-3.png");
m_bubble_particles[3] = Surface::from_file("images/particles/air_bubble-4.png");
Copy link
Member

Choose a reason for hiding this comment

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

Apparently rusty sent you a sprite file? Use this in junction with the get_actions method

m_bubble_timer.start(3.0f + (rand() % 2));
Copy link
Member

Choose a reason for hiding this comment

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

For usage of rng in graphics effects, use graphicsRandom

Suggested change
m_bubble_timer.start(3.0f + (rand() % 2));
m_bubble_timer.start(3.0f + graphicsRandom.randf(2));


m_col.set_size(TUX_WIDTH, is_big() ? BIG_TUX_HEIGHT : SMALL_TUX_HEIGHT);

m_sprite->set_angle(0.0f);
Expand Down Expand Up @@ -412,6 +418,22 @@ Player::update(float dt_sec)
}
}

if (!m_active_bubbles.empty())
{
for (auto& bubble : m_active_bubbles)
{
bubble.second.y -= dt_sec * 30.0f;
bubble.second.x += std::sin(bubble.second.y * 0.1f) * dt_sec * 5.0f;
}

m_active_bubbles.remove_if([&](const std::pair<SurfacePtr, glm::vec2>& bubble)
{
Rectf bubble_box(bubble.second.x, bubble.second.y, bubble.second.x + 16.f, bubble.second.y + 16.f);
bool is_out_of_water = Sector::get().is_free_of_tiles(bubble_box, true, Tile::WATER);
bruhmoent marked this conversation as resolved.
Show resolved Hide resolved
return is_out_of_water;
});
}

// Skip if in multiplayer respawn
if (is_dead() && m_target && Sector::get().get_object_count<Player>([this](const Player& p) { return !p.is_dead() && !p.is_dying() && !p.is_winning() && &p != this; }))
{
Expand Down Expand Up @@ -514,6 +536,64 @@ Player::update(float dt_sec)
if (m_physic.get_velocity_y() > -350.f && m_controller->hold(Control::UP))
m_physic.set_velocity_y(-350.f);
}

if (m_bubble_timer.check())
{
glm::vec2 beak_local_offset(30.f, 0.0f);
float big_offset_x = is_big() ? 4.0f : 0.0f;

// Calculate the offsets based on the sprite angle
float offset_x = std::cos(m_swimming_angle) * 10.0f;
float offset_y = std::sin(m_swimming_angle) * 10.0f;

// Rotate the beak offset based on the sprite's angle
float rotated_beak_offset_x = beak_local_offset.x * std::cos(m_swimming_angle) - beak_local_offset.y * std::sin(m_swimming_angle);
float rotated_beak_offset_y = beak_local_offset.x * std::sin(m_swimming_angle) + beak_local_offset.y * std::cos(m_swimming_angle);

glm::vec2 player_center = m_col.m_bbox.get_middle();
glm::vec2 beak_position;

// Determine direction based on the radians
if (m_swimming_angle > static_cast<float>(M_PI_2) && m_swimming_angle < 3.0f * static_cast<float>(M_PI_2)) // Facing left
Copy link
Member

Choose a reason for hiding this comment

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

We use math::PI_2

Suggested change
if (m_swimming_angle > static_cast<float>(M_PI_2) && m_swimming_angle < 3.0f * static_cast<float>(M_PI_2)) // Facing left
if (m_swimming_angle > static_cast<float>(math::PI_2)) && m_swimming_angle < 3.0f * static_cast<float>(math::PI_2)) // Facing left

{
beak_position = player_center + glm::vec2(rotated_beak_offset_x - big_offset_x * 2, rotated_beak_offset_y);
}
else // Facing right (including straight up or down)
{
beak_position = player_center + glm::vec2(rotated_beak_offset_x - 4.0f + big_offset_x, rotated_beak_offset_y);
}

int num_bubbles = graphicsRandom.rand(1, 3);

for (int i = 0; i < num_bubbles; ++i)
{
int random_index = graphicsRandom.rand(0, 3);
SurfacePtr bubble_surface = m_bubble_particles[random_index];

glm::vec2 bubble_pos;
if (m_swimming_angle > static_cast<float>(M_PI_2) && m_swimming_angle < 3.0f * static_cast<float>(M_PI_2)) // Facing left
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (m_swimming_angle > static_cast<float>(M_PI_2) && m_swimming_angle < 3.0f * static_cast<float>(M_PI_2)) // Facing left
if (m_swimming_angle > static_cast<float>(math::PI_2) && m_swimming_angle < 3.0f * static_cast<float>(math::PI_2)) // Facing left

{
bubble_pos = beak_position + glm::vec2(offset_x - big_offset_x * 2, offset_y);
}
else // Facing right (including straight up or down)
{
bubble_pos = beak_position + glm::vec2(offset_x - 4.0f + big_offset_x, offset_y);
}

if (num_bubbles > 1)
{
float burst_offset_x = graphicsRandom.randf(-5.0f, 5.0f);
float burst_offset_y = graphicsRandom.randf(-5.0f, 5.0f);
bubble_pos.x += burst_offset_x;
bubble_pos.y += burst_offset_y;
}

m_active_bubbles.push_back({ bubble_surface, bubble_pos });
}

// Restart the timer for the next wave of bubbles
m_bubble_timer.start(0.8f + graphicsRandom.randf(0.0f, 0.6f));
}
}
else
{
Expand Down Expand Up @@ -2245,10 +2325,13 @@ Player::draw(DrawingContext& context)
get_bonus() == EARTH_BONUS ? Color(1.f, 0.9f, 0.6f) :
Color(1.f, 1.f, 1.f));

for (auto bubble_sprite : m_active_bubbles)
{
context.color().draw_surface(bubble_sprite.first, bubble_sprite.second, LAYER_TILES - 5);
}
m_sprite->set_color(m_stone ? Color(1.f, 1.f, 1.f) : power_color);
}


void
Player::collision_tile(uint32_t tile_attributes)
{
Expand Down
7 changes: 7 additions & 0 deletions src/object/player.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
#include "video/layer.hpp"
#include "video/surface_ptr.hpp"

#include <array>
bruhmoent marked this conversation as resolved.
Show resolved Hide resolved
#include <list>

class BadGuy;
class Climbable;
class Controller;
Expand Down Expand Up @@ -552,6 +555,10 @@ class Player final : public MovingObject

SurfacePtr m_airarrow; /**< arrow indicating Tux' position when he's above the camera */

std::array<SurfacePtr, 4> m_bubble_particles; /**< bubble particles for swimming */
Timer m_bubble_timer; /**< timer for spawning bubble particles */
std::list<std::pair<SurfacePtr, glm::vec2>> m_active_bubbles; /**< active bubble particles */
MatusGuy marked this conversation as resolved.
Show resolved Hide resolved

Vector m_floor_normal;

bool m_ghost_mode; /**< indicates if Tux should float around and through solid objects */
Expand Down
Loading