diff --git a/src/gui/hotkeyConfig.cpp b/src/gui/hotkeyConfig.cpp index 96981248db..5b7ec2e6b4 100644 --- a/src/gui/hotkeyConfig.cpp +++ b/src/gui/hotkeyConfig.cpp @@ -317,6 +317,7 @@ Keys::Keys() : gm_delete("GM_DELETE", "Delete"), gm_clipboardcopy("GM_CLIPBOARD_COPY", "F5"), gm_show_callsigns("GM_SHOW_CALLSIGNS", "C"), + gm_show_health_bars("GM_SHOW_HEALTH_BARS", "H"), // Spectator screen spectator_show_callsigns("SPECTATOR_SHOW_CALLSIGNS", "C") @@ -508,6 +509,7 @@ void Keys::init() gm_delete.setLabel(tr("hotkey_menu", "GM screen"), tr("hotkey_GM", "Delete")); gm_clipboardcopy.setLabel(tr("hotkey_menu", "GM screen"), tr("hotkey_GM", "Copy to clipboard")); gm_show_callsigns.setLabel(tr("hotkey_menu", "GM screen"), tr("hotkey_GM", "Show callsigns (GM)")); + gm_show_health_bars.setLabel(tr("hotkey_menu", "GM screen"), tr("hotkey_GM", "Show health bars (GM)")); // Spectator screen spectator_show_callsigns.setLabel(tr("hotkey_menu", "Spectator view"), tr("hotkey_Spectator", "Show callsigns (spectator)")); diff --git a/src/gui/hotkeyConfig.h b/src/gui/hotkeyConfig.h index 5fee57b9d4..ee3d151f11 100644 --- a/src/gui/hotkeyConfig.h +++ b/src/gui/hotkeyConfig.h @@ -199,6 +199,7 @@ class Keys sp::io::Keybinding gm_delete; sp::io::Keybinding gm_clipboardcopy; sp::io::Keybinding gm_show_callsigns; + sp::io::Keybinding gm_show_health_bars; // Spectator screen binds sp::io::Keybinding spectator_show_callsigns; diff --git a/src/screens/gm/gameMasterScreen.cpp b/src/screens/gm/gameMasterScreen.cpp index d990b1b234..97d87c4142 100644 --- a/src/screens/gm/gameMasterScreen.cpp +++ b/src/screens/gm/gameMasterScreen.cpp @@ -86,6 +86,49 @@ GameMasterScreen::GameMasterScreen(RenderLayer* render_layer) ->setOverlayCallback( [this](sp::RenderTarget& renderer) { + const bool is_short_range = main_radar->getDistance() <= SHORT_RANGE_DISTANCE; + float bar_width = is_short_range ? 60.0f : 30.0f; + float bar_height = is_short_range ? 5.0f : 2.0f; + float bar_offset = bar_width * 0.5f; + + // Draw hull health bars + if (show_health_bars) + { + for (auto [entity, hull, transform, trace] : sp::ecs::Query>()) + { + const float hull_norm = hull.current / hull.max; + float bar_distance = bar_height * 4.0f; + if (trace) bar_distance = std::clamp(trace->radius * main_radar->getScale() * 2.0f, trace->min_size, trace->max_size) * 0.75f; + + if (hull_norm < 0.9f || is_short_range) + { + glm::vec2 screen_pos = main_radar->worldToScreen(transform.getPosition()); + const float health_bar_width = bar_width * hull_norm; + // Scale color from green to yellow to red with damage. + uint8_t bar_r, bar_g; + if (hull_norm >= 0.5f) + { + float t = (hull_norm - 0.5f) / 0.5f; + bar_r = static_cast(255.0f * (1.0f - t)); + bar_g = 255; + } + else if (hull_norm >= 0.2f) + { + float t = (hull_norm - 0.2f) / 0.3f; + bar_r = 255; + bar_g = static_cast(255.0f * t); + } + else + { + bar_r = 255; + bar_g = 0; + } + renderer.fillRect(sp::Rect(screen_pos.x - bar_offset, screen_pos.y + bar_distance, health_bar_width, bar_height), glm::u8vec4(bar_r, bar_g, 0, 192)); + renderer.outlineRect(sp::Rect(screen_pos.x - bar_offset, screen_pos.y + bar_distance, bar_width, bar_height), glm::u8vec4(255, 255, 255, 128)); + } + } + } + if (gm_cursor_mode != GMCursorMode::CreateEntity && gm_cursor_mode != GMCursorMode::SetDirection) return; if (!gameGlobalInfo->on_gm_preview_trace) return; const RadarTrace& trace = *gameGlobalInfo->on_gm_preview_trace; @@ -341,7 +384,7 @@ void GameMasterScreen::update(float delta) { float view_distance = std::clamp(main_radar->getDistance() * (1.0f - (key_zoom_delta * 0.1f)), MIN_ZOOM_DISTANCE, MAX_ZOOM_DISTANCE); main_radar->setDistance(view_distance); - if (view_distance < SHORT_RANGE_DISTANCE) main_radar->shortRange(); + if (view_distance <= SHORT_RANGE_DISTANCE) main_radar->shortRange(); else main_radar->longRange(); } @@ -370,6 +413,10 @@ void GameMasterScreen::update(float delta) if (keys.gm_show_callsigns.getDown()) main_radar->showCallsigns(!main_radar->getCallsigns()); + // Toggle health bars. + if (keys.gm_show_health_bars.getDown()) + show_health_bars = !show_health_bars; + bool has_object = false; has_cpu_ship = false; bool has_player_ship = false; @@ -877,6 +924,8 @@ void GameMasterScreen::onMouseWheel(float value, glm::vec2 position) // Set the new zoom level. main_radar->setDistance(view_distance); zoom_slider->setValue(view_distance); + if (view_distance <= SHORT_RANGE_DISTANCE) main_radar->shortRange(); + else main_radar->longRange(); // Adjust the radar's view position to keep the world coordinates // under the pointer consistent. diff --git a/src/screens/gm/gameMasterScreen.h b/src/screens/gm/gameMasterScreen.h index a62c75ba0b..d2da1f1ab8 100644 --- a/src/screens/gm/gameMasterScreen.h +++ b/src/screens/gm/gameMasterScreen.h @@ -106,6 +106,7 @@ class GameMasterScreen : public GuiCanvas, public Updatable { return GMCursorMode(unsigned(a) & unsigned(b)); } bool has_cpu_ship = false; + bool show_health_bars = true; GuiButton* create_button; GuiButton* cancel_action_button; diff --git a/src/systems/radar.cpp b/src/systems/radar.cpp index 47cb1d97c2..e0418b90b6 100644 --- a/src/systems/radar.cpp +++ b/src/systems/radar.cpp @@ -68,5 +68,8 @@ void BasicRadarRendering::renderOnRadar(sp::RenderTarget& renderer, sp::ecs::Ent { if (entity == my_spaceship || !(RadarRenderSystem::current_flags & RadarRenderSystem::FlagCallsigns)) return; - renderer.drawText(sp::Rect(screen_position.x, screen_position.y - 15, 0, 0), callsign.callsign, sp::Alignment::Center, 15, bold_font); + float text_distance = 15.0f; + if (auto trace = entity.getComponent()) text_distance = std::clamp(trace->radius * 1.5f * scale, trace->min_size, trace->max_size) + 15.0f * 1.5f * scale; + + renderer.drawText(sp::Rect(screen_position.x, screen_position.y - text_distance, 0, 0), callsign.callsign, sp::Alignment::Center, 15, bold_font); }