From 9d4bc85674689eaee7e419d2542263fa3c05db7a Mon Sep 17 00:00:00 2001 From: fnpratto Date: Sun, 22 Jun 2025 16:03:19 -0300 Subject: [PATCH 1/7] feat: enhance keyboard handling for bomb actions and add world interaction methods --- client/gui/controllers/keyboardhandler.cpp | 28 ++++++++++++++++++++-- client/gui/controllers/keyboardhandler.h | 4 +++- client/gui/map_view/sdl_world.cpp | 25 +++++++++++++++++++ client/gui/map_view/sdl_world.h | 4 ++++ 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/client/gui/controllers/keyboardhandler.cpp b/client/gui/controllers/keyboardhandler.cpp index 1fa4ca34..1c43ff6a 100644 --- a/client/gui/controllers/keyboardhandler.cpp +++ b/client/gui/controllers/keyboardhandler.cpp @@ -7,25 +7,30 @@ KeyboardHandler::KeyboardHandler(Queue& output_queue, shopDisplay& shopRef, ScoreDisplay& score_displayRef, SoundManager& sound_managerRef, - SdlHud& hudDisplayRef): + SdlHud& hudDisplayRef, SdlWorld& wordRef): output_queue(output_queue), shopRef(shopRef), score_displayRef(score_displayRef), sound_manager(sound_managerRef), - hudDisplayRef(hudDisplayRef) {} + hudDisplayRef(hudDisplayRef), + worldRef(wordRef), + active_b(false) {} void KeyboardHandler::handleEvent(const SDL_Event& event) { if (event.type != SDL_KEYDOWN) { switch (event.key.keysym.sym) { case SDLK_ESCAPE: shopRef.updateShopState(false); + active_b = false; break; case SDLK_b: output_queue.push(Message(GetShopPricesCommand())); + active_b = false; break; case SDLK_m: sound_manager.toggle_mute(); hudDisplayRef.update_mute_icon(); + active_b = false; break; case SDLK_TAB: if (!score_displayRef.isActive()) { @@ -33,34 +38,53 @@ void KeyboardHandler::handleEvent(const SDL_Event& event) { } else { score_displayRef.updateState(); } + active_b = false; break; case SDLK_1: std::cout << "Switching to primary item" << std::endl; output_queue.push(Message(SwitchItemCommand(ItemSlot::Primary))); + active_b = false; break; case SDLK_2: std::cout << "Switching to 2 item" << std::endl; output_queue.push(Message(SwitchItemCommand(ItemSlot::Secondary))); + active_b = false; break; case SDLK_3: std::cout << "Switching to 3 item" << std::endl; output_queue.push(Message(SwitchItemCommand(ItemSlot::Melee))); + active_b = false; break; case SDLK_4: std::cout << "Switching to 4 item" << std::endl; output_queue.push(Message(SwitchItemCommand(ItemSlot::Bomb))); + active_b = false; break; case SDLK_r: output_queue.push(Message(ReloadCommand())); sound_manager.play("reload"); + active_b = false; break; case SDLK_g: sound_manager.play("item_pick"); output_queue.push(Message(PickUpItemCommand())); + active_b = false; + break; + case SDLK_e: + std::optional maybe_message = worldRef.getStartBombMessage(sound_manager); + if (maybe_message.has_value()) { + active_b = true; + } break; } } update_direction(); + if (active_b) { + std::optional maybe_message = worldRef.getStopBombMessage(sound_manager); + if (maybe_message.has_value()) { + active_b = false; + } + } } void KeyboardHandler::update_direction() { diff --git a/client/gui/controllers/keyboardhandler.h b/client/gui/controllers/keyboardhandler.h index efc193ed..7572a0d2 100644 --- a/client/gui/controllers/keyboardhandler.h +++ b/client/gui/controllers/keyboardhandler.h @@ -18,7 +18,7 @@ class KeyboardHandler { public: explicit KeyboardHandler(Queue& output_queue, shopDisplay& shopRef, ScoreDisplay& score_displayRef, SoundManager& sound_manager, - SdlHud& hudDisplayRef); + SdlHud& hudDisplayRef, SdlWorld& wordRef); void handleEvent(const SDL_Event& event); private: @@ -27,7 +27,9 @@ class KeyboardHandler { ScoreDisplay& score_displayRef; SoundManager& sound_manager; SdlHud& hudDisplayRef; + SdlWorld& worldRef; void update_direction(); + bool active_b; }; #endif // KEYBOARDHANDLER_H diff --git a/client/gui/map_view/sdl_world.cpp b/client/gui/map_view/sdl_world.cpp index 5909a91b..214137d2 100644 --- a/client/gui/map_view/sdl_world.cpp +++ b/client/gui/map_view/sdl_world.cpp @@ -78,3 +78,28 @@ void SdlWorld::render() { } bullets_info.clear(); } + + +std::optional SdlWorld::getStartBombMessage(SoundManager& sound_manager) { + Team player_team = game_state.get_players().at(player_name).get_team(); + if (player_team == Team::CT && game_state.get_phase().get_type() == PhaseType::InRound) { + sound_manager.play("defuse_bomb"); + return Message(StartDefusingBombCommand()); + } else if (player_team == Team::TT && game_state.get_phase().get_type() == PhaseType::InRound) { + sound_manager.play("plant_bomb"); + return Message(StartPlantingBombCommand()); + } + return std::nullopt; +} + +std::optional SdlWorld::getStopBombMessage(SoundManager& sound_manager) { + Team player_team = game_state.get_players().at(player_name).get_team(); + if (player_team == Team::CT && game_state.get_phase().get_type() == PhaseType::InRound) { + sound_manager.play("stop_defuse_bomb"); + return Message(StopDefusingBombCommand()); + } else if (player_team == Team::TT && game_state.get_phase().get_type() == PhaseType::InRound) { + sound_manager.play("stop_plant_bomb"); + return Message(StopPlantingBombCommand()); + } + return std::nullopt; +} diff --git a/client/gui/map_view/sdl_world.h b/client/gui/map_view/sdl_world.h index 9e4de99a..f10ea53b 100644 --- a/client/gui/map_view/sdl_world.h +++ b/client/gui/map_view/sdl_world.h @@ -14,7 +14,9 @@ #include "../window_elements/area.h" #include "../window_elements/sdl_texture.h" #include "../window_elements/sdl_window.h" +#include "client/sound_manager.h" #include "common/map/map.h" +#include "common/message.h" #include "common/updates/game_update.h" #include "sdl_bullet.h" @@ -57,4 +59,6 @@ class SdlWorld { void render(); void handleHit(Vector2D get_origin, Vector2D get_hit_pos, Vector2D get_hit_dir, bool is_hit); + std::optional getStartBombMessage(SoundManager& sound_manager); + std::optional getStopBombMessage(SoundManager& sound_manager); }; From 2eebb79e277b5b6118ef4e63f6fcdd549f229a9d Mon Sep 17 00:00:00 2001 From: fnpratto Date: Sun, 22 Jun 2025 17:14:31 -0300 Subject: [PATCH 2/7] feat: add bomb planted rendering and update bomb phase handling --- client/game_config.h | 2 +- client/gui/map_view/sdl_item.cpp | 13 +++++++++++++ client/gui/map_view/sdl_item.h | 1 + client/gui/map_view/sdl_world.cpp | 4 +++- client/sdl_display.cpp | 4 ++-- 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/client/game_config.h b/client/game_config.h index e5c036ae..164d36dc 100644 --- a/client/game_config.h +++ b/client/game_config.h @@ -82,7 +82,7 @@ constexpr std::string_view AWP_ITEM_PATH = "../assets/gfx/map/awp_d.bmp"; constexpr std::string_view M3_ITEM_PATH = "../assets/gfx/map/m3_d.bmp"; constexpr std::string_view AK_ITEM_PATH = "../assets/gfx/map/ak47_d.bmp"; constexpr std::string_view BOMB_ITEM_PATH = "../assets/gfx/guns/bomb.bmp"; -constexpr std::string_view BOMB_PLANTED_ITEM_PATH = "../assets/gfx/map/bomb_b.bmp"; +constexpr std::string_view BOMB_PLANTED_ITEM_PATH = "../assets/gfx/map/bomb_d.bmp"; } // namespace Paths // --- Gun Type Offsets --- diff --git a/client/gui/map_view/sdl_item.cpp b/client/gui/map_view/sdl_item.cpp index 9acd47c5..328c0602 100644 --- a/client/gui/map_view/sdl_item.cpp +++ b/client/gui/map_view/sdl_item.cpp @@ -7,10 +7,12 @@ SdlItem::SdlItem(SdlWindow& window, const GameUpdate& game_state, const SdlCamer m3_t(std::string(GameConfig::Paths::M3_ITEM_PATH), window), ak_t(std::string(GameConfig::Paths::AK_ITEM_PATH), window), bomb_t(std::string(GameConfig::Paths::BOMB_ITEM_PATH), window), + bomb_planted_t(std::string(GameConfig::Paths::BOMB_PLANTED_ITEM_PATH), window), game_state(game_state), camera(camera) {} void SdlItem::render() { + if (game_state.get_bomb().has_value()) { const auto& bomb = game_state.get_bomb().value(); Vector2D pos = bomb.hitbox.get_pos(); @@ -20,6 +22,17 @@ void SdlItem::render() { if (camera.can_see(pos_cam)) bomb_t.render(origin, dest); } + + if (game_state.get_bomb().has_value() && + game_state.get_bomb().value().item.get_bomb_phase() == BombPhaseType::Planted) { + const auto& bomb = game_state.get_bomb().value(); + Vector2D pos = bomb.hitbox.get_pos(); + Area origin(0, 0, 24, 24); + Vector2D pos_cam = camera.get_screen_pos(pos); + Area dest(pos_cam.get_x(), pos_cam.get_y(), 32, 32); + if (camera.can_see(pos_cam)) + bomb_planted_t.render(origin, dest); + } for (const auto& [gun_type, gun_item]: game_state.get_dropped_guns()) { Vector2D pos = gun_item.get_pos(); if (!camera.can_see(pos)) diff --git a/client/gui/map_view/sdl_item.h b/client/gui/map_view/sdl_item.h index 9878ed1f..35bee8f3 100644 --- a/client/gui/map_view/sdl_item.h +++ b/client/gui/map_view/sdl_item.h @@ -20,6 +20,7 @@ class SdlItem { SdlTexture m3_t; SdlTexture ak_t; SdlTexture bomb_t; + SdlTexture bomb_planted_t; const GameUpdate& game_state; const SdlCamera& camera; diff --git a/client/gui/map_view/sdl_world.cpp b/client/gui/map_view/sdl_world.cpp index 214137d2..c76e0e08 100644 --- a/client/gui/map_view/sdl_world.cpp +++ b/client/gui/map_view/sdl_world.cpp @@ -81,8 +81,10 @@ void SdlWorld::render() { std::optional SdlWorld::getStartBombMessage(SoundManager& sound_manager) { + std::cout << "getStartBombMessage called" << std::endl; Team player_team = game_state.get_players().at(player_name).get_team(); - if (player_team == Team::CT && game_state.get_phase().get_type() == PhaseType::InRound) { + if (player_team == Team::CT && game_state.get_phase().get_type() == PhaseType::InRound && + game_state.get_bomb().value().item.get_bomb_phase() == BombPhaseType::Planted) { sound_manager.play("defuse_bomb"); return Message(StartDefusingBombCommand()); } else if (player_team == Team::TT && game_state.get_phase().get_type() == PhaseType::InRound) { diff --git a/client/sdl_display.cpp b/client/sdl_display.cpp index 8ee77b3a..49ffdd61 100644 --- a/client/sdl_display.cpp +++ b/client/sdl_display.cpp @@ -89,8 +89,8 @@ void SDLDisplay::run() { quit_flag, MouseHandler(output_queue, SCREEN_WIDTH, SCREEN_HEIGHT, list_teams, *shop_display, hud_display, list_skins), - KeyboardHandler(output_queue, *shop_display, *score_display, sound_manager, - hud_display)); + KeyboardHandler(output_queue, *shop_display, *score_display, sound_manager, hud_display, + *world)); end_round_display = std::make_unique(window, state); input_handler->start(); From 9bd1d77f9fd6ae6d2f08f27e5447848f31e31d3a Mon Sep 17 00:00:00 2001 From: fnpratto Date: Tue, 24 Jun 2025 06:32:33 -0300 Subject: [PATCH 3/7] working: remove unused active_b variable and simplify event handling feat(sdl_display): handle bomb planted state in rendering logic feat(sdl_world): add bomb action validation and improve bomb message handling --- client/gui/controllers/keyboardhandler.cpp | 37 ++- client/gui/controllers/keyboardhandler.h | 1 - client/gui/map_view/sdl_world.cpp | 33 +- client/sdl_display.cpp | 13 + maps/map.yaml | 350 ++++++++++----------- 5 files changed, 233 insertions(+), 201 deletions(-) diff --git a/client/gui/controllers/keyboardhandler.cpp b/client/gui/controllers/keyboardhandler.cpp index 1c43ff6a..d5c5c1b3 100644 --- a/client/gui/controllers/keyboardhandler.cpp +++ b/client/gui/controllers/keyboardhandler.cpp @@ -1,6 +1,7 @@ #include "keyboardhandler.h" #include +#include #include @@ -13,24 +14,22 @@ KeyboardHandler::KeyboardHandler(Queue& output_queue, shopDisplay& shop score_displayRef(score_displayRef), sound_manager(sound_managerRef), hudDisplayRef(hudDisplayRef), - worldRef(wordRef), - active_b(false) {} + worldRef(wordRef) {} void KeyboardHandler::handleEvent(const SDL_Event& event) { if (event.type != SDL_KEYDOWN) { switch (event.key.keysym.sym) { case SDLK_ESCAPE: shopRef.updateShopState(false); - active_b = false; break; case SDLK_b: output_queue.push(Message(GetShopPricesCommand())); - active_b = false; + break; case SDLK_m: sound_manager.toggle_mute(); hudDisplayRef.update_mute_icon(); - active_b = false; + break; case SDLK_TAB: if (!score_displayRef.isActive()) { @@ -38,53 +37,53 @@ void KeyboardHandler::handleEvent(const SDL_Event& event) { } else { score_displayRef.updateState(); } - active_b = false; + break; case SDLK_1: std::cout << "Switching to primary item" << std::endl; output_queue.push(Message(SwitchItemCommand(ItemSlot::Primary))); - active_b = false; + break; case SDLK_2: std::cout << "Switching to 2 item" << std::endl; output_queue.push(Message(SwitchItemCommand(ItemSlot::Secondary))); - active_b = false; + break; case SDLK_3: std::cout << "Switching to 3 item" << std::endl; output_queue.push(Message(SwitchItemCommand(ItemSlot::Melee))); - active_b = false; + break; case SDLK_4: std::cout << "Switching to 4 item" << std::endl; output_queue.push(Message(SwitchItemCommand(ItemSlot::Bomb))); - active_b = false; + break; case SDLK_r: output_queue.push(Message(ReloadCommand())); sound_manager.play("reload"); - active_b = false; + break; case SDLK_g: sound_manager.play("item_pick"); output_queue.push(Message(PickUpItemCommand())); - active_b = false; break; case SDLK_e: std::optional maybe_message = worldRef.getStartBombMessage(sound_manager); if (maybe_message.has_value()) { - active_b = true; + output_queue.push(std::move(*maybe_message)); + return; } + return; break; } } - update_direction(); - if (active_b) { - std::optional maybe_message = worldRef.getStopBombMessage(sound_manager); - if (maybe_message.has_value()) { - active_b = false; - } + + std::optional maybe_message = worldRef.getStopBombMessage(sound_manager); + if (maybe_message.has_value()) { + output_queue.push(std::move(*maybe_message)); } + update_direction(); } void KeyboardHandler::update_direction() { diff --git a/client/gui/controllers/keyboardhandler.h b/client/gui/controllers/keyboardhandler.h index 7572a0d2..0b7037d1 100644 --- a/client/gui/controllers/keyboardhandler.h +++ b/client/gui/controllers/keyboardhandler.h @@ -29,7 +29,6 @@ class KeyboardHandler { SdlHud& hudDisplayRef; SdlWorld& worldRef; void update_direction(); - bool active_b; }; #endif // KEYBOARDHANDLER_H diff --git a/client/gui/map_view/sdl_world.cpp b/client/gui/map_view/sdl_world.cpp index bfc48e90..b98a8b73 100644 --- a/client/gui/map_view/sdl_world.cpp +++ b/client/gui/map_view/sdl_world.cpp @@ -68,29 +68,50 @@ void SdlWorld::render() { knife_slashes.end()); } - +// enum class BombPhaseType { NotPlanted, Planted, Exploded, Defused, Planting, Defusing }; std::optional SdlWorld::getStartBombMessage(SoundManager& sound_manager) { + if (game_state.get_phase().get_type() != PhaseType::InRound) { + std::cout << "Cannot start bomb action outside of InRound phase." << std::endl; + return std::nullopt; + } std::cout << "getStartBombMessage called" << std::endl; Team player_team = game_state.get_players().at(player_name).get_team(); - if (player_team == Team::CT && game_state.get_phase().get_type() == PhaseType::InRound && - game_state.get_bomb().value().item.get_bomb_phase() == BombPhaseType::Planted) { + if (player_team == Team::CT) { + /*if (game_state.get_bomb().value().item.get_bomb_phase() == BombPhaseType::Planted) {*/ + std::cout << "Starting bomb defusing" << std::endl; sound_manager.play("defuse_bomb"); return Message(StartDefusingBombCommand()); - } else if (player_team == Team::TT && game_state.get_phase().get_type() == PhaseType::InRound) { + //} + } else if (player_team == Team::TT) { + // if (game_state.get_bomb().value().item.get_bomb_phase() == BombPhaseType::NotPlanted) { sound_manager.play("plant_bomb"); + std::cout << "Starting bomb planting" << std::endl; return Message(StartPlantingBombCommand()); + // } } return std::nullopt; } std::optional SdlWorld::getStopBombMessage(SoundManager& sound_manager) { + if (game_state.get_phase().get_type() != PhaseType::InRound) { + std::cout << "Cannot start bomb action outside of InRound phase." << std::endl; + return std::nullopt; + } Team player_team = game_state.get_players().at(player_name).get_team(); - if (player_team == Team::CT && game_state.get_phase().get_type() == PhaseType::InRound) { + if (player_team == Team::CT) { + // if (game_state.get_bomb().value().item.get_bomb_phase() == BombPhaseType::Defusing) { + std::cout << "Stop defusing bomb" << std::endl; sound_manager.play("stop_defuse_bomb"); return Message(StopDefusingBombCommand()); - } else if (player_team == Team::TT && game_state.get_phase().get_type() == PhaseType::InRound) { + //} + + } else if (player_team == Team::TT) { + // if (game_state.get_bomb().value().item.get_bomb_phase() == BombPhaseType::Planting) { + std::cout << "Stop plant bomb" << std::endl; sound_manager.play("stop_plant_bomb"); return Message(StopPlantingBombCommand()); + // } } + return std::nullopt; } diff --git a/client/sdl_display.cpp b/client/sdl_display.cpp index 075ea79c..19fe3ed9 100644 --- a/client/sdl_display.cpp +++ b/client/sdl_display.cpp @@ -124,6 +124,9 @@ void SDLDisplay::run() { world->render(); hud_display.render(); end_round_display->render(); + } else if (state.get_phase().get_type() == PhaseType::BombPlanted) { + world->render(); + hud_display.render(); } if (score_display->isActive()) { @@ -286,6 +289,16 @@ void SDLDisplay::update_state() { sound_manager.play("error"); break; } + case MessageType::BOMB_EXPLODED_RESP: { + std::cout << "Received BombExplodedResponse" << std::endl; + auto bomb_exploded_resp = msg.get_content(); + /*GameUpdate updates = game.get_full_update(); + EXPECT_TRUE(bomb_exploded_resp.get_explosion_center() == + updates.get_bomb().value().hitbox.get_center()); + EXPECT_EQ(bomb_exploded_resp.get_explosion_radius(), BombConfig::max_range);*/ + sound_manager.play("bomb_exploded"); + break; + } default: { std::cerr << "SDLDisplay::update_state: Received unexpected message type: " << msg.get_type() << std::endl; diff --git a/maps/map.yaml b/maps/map.yaml index 3bdb0dd1..47fefad9 100644 --- a/maps/map.yaml +++ b/maps/map.yaml @@ -3,178 +3,178 @@ max_players: 10 height: 5 width: 5 tiles: - - x: 1 - y: 1 - id: 9 - is_collidable: false - is_spawn_tt: true - is_spawn_ct: false - is_bomb_site: false - - x: 3 - y: 3 - id: 9 - is_collidable: false - is_spawn_tt: false - is_spawn_ct: true - is_bomb_site: false - - x: 1 - y: 2 - id: 9 - is_collidable: false - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 2 - y: 1 - id: 9 - is_collidable: false - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 2 - y: 2 - id: 9 - is_collidable: false - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: true - - x: 3 - y: 2 - id: 9 - is_collidable: false - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 1 - y: 3 - id: 20 - is_collidable: true - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 2 - y: 3 - id: 9 - is_collidable: false - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 0 - y: 0 - id: 20 - is_collidable: true - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 1 - y: 0 - id: 20 - is_collidable: true - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 2 - y: 0 - id: 20 - is_collidable: true - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 3 - y: 0 - id: 20 - is_collidable: true - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 4 - y: 0 - id: 20 - is_collidable: true - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 0 - y: 1 - id: 20 - is_collidable: true - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 4 - y: 1 - id: 20 - is_collidable: true - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 0 - y: 2 - id: 20 - is_collidable: true - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 4 - y: 2 - id: 20 - is_collidable: true - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 0 - y: 3 - id: 20 - is_collidable: true - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 4 - y: 3 - id: 20 - is_collidable: true - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 0 - y: 4 - id: 20 - is_collidable: true - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 1 - y: 4 - id: 20 - is_collidable: true - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 2 - y: 4 - id: 20 - is_collidable: true - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 3 - y: 4 - id: 20 - is_collidable: true - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 4 - y: 4 - id: 20 - is_collidable: true - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false - - x: 3 - y: 1 - id: 9 - is_collidable: false - is_spawn_tt: false - is_spawn_ct: false - is_bomb_site: false + - x: 1 + y: 1 + id: 9 + is_collidable: false + is_spawn_tt: true + is_spawn_ct: false + is_bomb_site: true + - x: 3 + y: 3 + id: 9 + is_collidable: false + is_spawn_tt: false + is_spawn_ct: true + is_bomb_site: true + - x: 1 + y: 2 + id: 9 + is_collidable: false + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: true + - x: 2 + y: 1 + id: 9 + is_collidable: false + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: true + - x: 2 + y: 2 + id: 9 + is_collidable: false + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: true + - x: 3 + y: 2 + id: 9 + is_collidable: false + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: true + - x: 1 + y: 3 + id: 20 + is_collidable: true + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: true + - x: 2 + y: 3 + id: 9 + is_collidable: false + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: true + - x: 0 + y: 0 + id: 20 + is_collidable: true + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: false + - x: 1 + y: 0 + id: 20 + is_collidable: true + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: false + - x: 2 + y: 0 + id: 20 + is_collidable: true + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: false + - x: 3 + y: 0 + id: 20 + is_collidable: true + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: false + - x: 4 + y: 0 + id: 20 + is_collidable: true + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: false + - x: 0 + y: 1 + id: 20 + is_collidable: true + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: false + - x: 4 + y: 1 + id: 20 + is_collidable: true + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: false + - x: 0 + y: 2 + id: 20 + is_collidable: true + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: false + - x: 4 + y: 2 + id: 20 + is_collidable: true + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: false + - x: 0 + y: 3 + id: 20 + is_collidable: true + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: false + - x: 4 + y: 3 + id: 20 + is_collidable: true + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: false + - x: 0 + y: 4 + id: 20 + is_collidable: true + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: false + - x: 1 + y: 4 + id: 20 + is_collidable: true + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: false + - x: 2 + y: 4 + id: 20 + is_collidable: true + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: false + - x: 3 + y: 4 + id: 20 + is_collidable: true + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: false + - x: 4 + y: 4 + id: 20 + is_collidable: true + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: false + - x: 3 + y: 1 + id: 9 + is_collidable: false + is_spawn_tt: false + is_spawn_ct: false + is_bomb_site: true From 3136d2be13a8fcac96e0bcaa6baff6905d9d6963 Mon Sep 17 00:00:00 2001 From: Jesabel Pugliese Date: Tue, 24 Jun 2025 10:55:11 -0300 Subject: [PATCH 4/7] fix(bomb): save plant_time correctly when bomb planted --- server/items/bomb.cpp | 16 ++++++++++------ server/items/bomb.h | 1 + 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/server/items/bomb.cpp b/server/items/bomb.cpp index f6360995..9ea2f081 100644 --- a/server/items/bomb.cpp +++ b/server/items/bomb.cpp @@ -48,16 +48,19 @@ void Bomb::start_defusing(TimePoint now) { change_bomb_phase(BombPhaseType::Defusing, now); } -void Bomb::stop_defusing(TimePoint now) { +void Bomb::stop_defusing([[maybe_unused]] TimePoint now) { if (state.get_bomb_phase() != BombPhaseType::Defusing) return; - change_bomb_phase(BombPhaseType::Planted, now); + state.set_bomb_phase(BombPhaseType::Planted); + phase_start_time = plant_time; } void Bomb::advance(TimePoint now) { if (is_planting()) { - if (now - phase_start_time >= std::chrono::seconds(BombConfig::secs_to_plant)) + if (now - phase_start_time >= std::chrono::seconds(BombConfig::secs_to_plant)) { change_bomb_phase(BombPhaseType::Planted, now); + plant_time = now; + } return; } @@ -69,8 +72,8 @@ void Bomb::advance(TimePoint now) { if (!is_planted()) return; - float new_secs_to_explode = state.get_secs_to_explode() - - std::chrono::duration(now - phase_start_time).count(); + float new_secs_to_explode = + state.get_secs_to_explode() - std::chrono::duration(now - plant_time).count(); state.set_secs_to_explode(std::max(0.0f, new_secs_to_explode)); } @@ -81,6 +84,7 @@ Effect Bomb::explode(const Vector2D& origin) { } void Bomb::reset() { - change_bomb_phase(BombPhaseType::NotPlanted, TimePoint::min()); + change_bomb_phase(BombPhaseType::NotPlanted, TimePoint()); + plant_time = TimePoint(); state.set_secs_to_explode(BombConfig::secs_to_explode); } diff --git a/server/items/bomb.h b/server/items/bomb.h index 67529e20..efe142c4 100644 --- a/server/items/bomb.h +++ b/server/items/bomb.h @@ -7,6 +7,7 @@ class Bomb: public Logic { TimePoint phase_start_time; + TimePoint plant_time; void change_bomb_phase(BombPhaseType new_phase, TimePoint now); From 2b6b157cc4d097819d3e03e61a73baea26029597 Mon Sep 17 00:00:00 2001 From: Jesabel Pugliese Date: Tue, 24 Jun 2025 11:14:20 -0300 Subject: [PATCH 5/7] fix(bomb): calculate seconds to explode correctly --- server/items/bomb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/items/bomb.cpp b/server/items/bomb.cpp index 9ea2f081..16f5a783 100644 --- a/server/items/bomb.cpp +++ b/server/items/bomb.cpp @@ -73,7 +73,7 @@ void Bomb::advance(TimePoint now) { return; float new_secs_to_explode = - state.get_secs_to_explode() - std::chrono::duration(now - plant_time).count(); + BombConfig::secs_to_explode - std::chrono::duration(now - plant_time).count(); state.set_secs_to_explode(std::max(0.0f, new_secs_to_explode)); } From 2cb75eb5cb4b590d07a20c56fcdadffd95cefaf3 Mon Sep 17 00:00:00 2001 From: Jesabel Pugliese Date: Tue, 24 Jun 2025 11:18:49 -0300 Subject: [PATCH 6/7] fix(game): handle player stop planting when bomb is planted --- server/game/game.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/server/game/game.cpp b/server/game/game.cpp index c504e7ad..e9c5f3cf 100644 --- a/server/game/game.cpp +++ b/server/game/game.cpp @@ -92,6 +92,7 @@ void Game::advance_bomb_logic() { bomb.advance(state.get_phase().get_time_now()); if (!bomb.is_planted()) return; + player->handle_stop_planting(state.get_phase().get_time_now()); state.add_bomb(std::move(player->drop_bomb().value()), player->get_hitbox().center); state.get_phase().start_bomb_planted_phase(); } From 52ff5523d1d91feae9b92bbd8c81da75bed842cd Mon Sep 17 00:00:00 2001 From: Jesabel Pugliese Date: Tue, 24 Jun 2025 11:35:09 -0300 Subject: [PATCH 7/7] fix(bomb): allow starting bomb action only during InRound or BombPlanted phases --- client/gui/map_view/sdl_world.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/gui/map_view/sdl_world.cpp b/client/gui/map_view/sdl_world.cpp index 68e62ba9..daabfb4d 100644 --- a/client/gui/map_view/sdl_world.cpp +++ b/client/gui/map_view/sdl_world.cpp @@ -97,7 +97,8 @@ void SdlWorld::render() { // enum class BombPhaseType { NotPlanted, Planted, Exploded, Defused, Planting, Defusing }; std::optional SdlWorld::getStartBombMessage(SoundManager& sound_manager) { - if (game_state.get_phase().get_type() != PhaseType::InRound) { + if (game_state.get_phase().get_type() != PhaseType::InRound && + game_state.get_phase().get_type() != PhaseType::BombPlanted) { std::cout << "Cannot start bomb action outside of InRound phase." << std::endl; return std::nullopt; }