feat(gameplay): detect underwater state and play transition/bubble sounds

- Add is_underwater() and set_underwater() to ClientPlayer.
- Change get_world() to return non-const reference for audio access.
- Update Camera::update_move_camera() to detect water block changes and trigger transition sound.
- Add repeating timer in ClientWorld to play random bubble ambient sounds while underwater.
- Include new ambient sound assets for water transitions and bubbles.
- Also add sand walk sound asset (unused in this commit).
This commit is contained in:
2026-07-07 17:19:47 +08:00
parent f36b05e2fd
commit 85dadc17c9
8 changed files with 26 additions and 4 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -55,7 +55,7 @@ public:
void set_gait(Gait gait);
GameMode& game_mode();
const ClientWorld& get_world() const;
ClientWorld& get_world();
void set_uuid(std::string_view uuid);
std::string get_uuid() const;
@@ -70,6 +70,8 @@ public:
bool ray_cast(const glm::vec3& start, const glm::vec3& dir,
glm::ivec3& block_pos, glm::vec3& normal,
float distance = 4.0f);
bool is_underwater() const;
void set_underwater(bool u);
private:
using enum GameMode;
@@ -100,6 +102,7 @@ private:
bool m_moving = false;
bool m_sprinting = false;
bool m_underwater = false;
glm::vec3 direction = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 move_distance{0.0f, 0.0f, 0.0f};

View File

@@ -40,10 +40,17 @@ void Camera::update_move_camera() {
}
glm::ivec3 block_pos = glm::floor(m_camera_pos);
auto& world = m_player->get_world();
bool change;
if (world.get_block_tpye(block_pos) == 7) {
m_under_water = true;
change = true;
} else {
m_under_water = false;
change = false;
}
if (m_under_water != change) {
m_under_water = change;
m_player->set_underwater(m_under_water);
m_player->get_world().get_audio().play_2d(
"ambient/water/in_and_out_of_water.flac", true);
}
}

View File

@@ -580,7 +580,7 @@ float& ClientPlayer::fly_y_speed() { return m_fly_y_speed; }
unsigned ClientPlayer::place_block() const { return m_place_block; };
void ClientPlayer::set_gait(Gait gait) { m_gait = gait; }
GameMode& ClientPlayer::game_mode() { return m_game_mode; }
const ClientWorld& ClientPlayer::get_world() const { return m_world; }
ClientWorld& ClientPlayer::get_world() { return m_world; }
void ClientPlayer::set_uuid(std::string_view uuid) {
std::lock_guard lock(m_uuid_mutex);
@@ -615,6 +615,9 @@ void ClientPlayer::init(std::string_view name) {
});
}
bool ClientPlayer::is_underwater() const { return m_underwater; }
void ClientPlayer::set_underwater(bool u) { m_underwater = u; }
float ClientPlayer::yaw() const { return m_yaw; }
float ClientPlayer::pitch() const { return m_pitch; }
float& ClientPlayer::angle() { return m_angle; }

View File

@@ -398,6 +398,15 @@ void ClientWorld::init(std::string_view player_name,
}
});
m_timers.try_emplace("under water bubble", 1.0f, [this]() {
if (m_player.is_underwater()) {
auto ans = m_random.random_int(1, 2);
std::string sound =
"ambient/water/bubble00" + std::to_string(ans) + ".ogg";
m_audio.play_2d(sound, true);
}
});
LoginReq req;
req.set_name(m_player.get_name());
while (!client->is_connected()) {