diff --git a/src/screens/crew6/engineeringScreen.cpp b/src/screens/crew6/engineeringScreen.cpp index db3d4fb4d1..c660014262 100644 --- a/src/screens/crew6/engineeringScreen.cpp +++ b/src/screens/crew6/engineeringScreen.cpp @@ -33,6 +33,16 @@ EngineeringScreen::EngineeringScreen(GuiContainer* owner, CrewPosition crew_position) : GuiOverlay(owner, "ENGINEERING_SCREEN", GuiTheme::getColor("background")) { + bool has_coolant = false; + bool has_reactor = false; + float power_max = 3.0f; + if (my_spaceship) + { + has_coolant = my_spaceship.hasComponent(); + has_reactor = my_spaceship.hasComponent(); + if (!has_coolant && !has_reactor) power_max = 1.0f; + } + slider_tick_style = theme->getStyle("slider.tick"); overlay_damaged_style = theme->getStyle("overlay.damaged"); overlay_overheating_style = theme->getStyle("overlay.overheating"); @@ -47,7 +57,11 @@ EngineeringScreen::EngineeringScreen(GuiContainer* owner, CrewPosition crew_posi stats->setPosition(20, 100, sp::Alignment::TopLeft)->setSize(240, 200)->setAttribute("layout", "vertical"); auto energy_display = new EnergyInfoDisplay(stats, "ENERGY_DISPLAY", 0.45, true); - energy_display->setIcon("gui/icons/energy")->setTextSize(20)->setSize(240, 40); + energy_display + ->setIcon("gui/icons/energy") + ->setTextSize(20.0f) + ->setSize(240.0f, 40.0f) + ->setVisible(has_reactor); auto hull_display = new HullInfoDisplay(stats, "HULL_DISPLAY", 0.45); hull_display->setTextSize(20)->setSize(240, 40); auto front_shield_display = new ShieldsInfoDisplay(stats, "SHIELDS_DISPLAY", 0.45, 0); @@ -55,7 +69,9 @@ EngineeringScreen::EngineeringScreen(GuiContainer* owner, CrewPosition crew_posi auto rear_shield_display = new ShieldsInfoDisplay(stats, "SHIELDS_DISPLAY", 0.45, 1); rear_shield_display->setSize(240, 40); auto coolant_display = new CoolantInfoDisplay(stats, "COOLANT_DISPLAY", 0.45); - coolant_display->setSize(240, 40); + coolant_display + ->setSize(240.0f, 40.0f) + ->setVisible(has_coolant); self_destruct_button = new GuiSelfDestructButton(this, "SELF_DESTRUCT"); self_destruct_button->setPosition(20, 20, sp::Alignment::TopLeft)->setSize(240, 100)->setVisible(my_spaceship && my_spaceship.hasComponent()); @@ -65,91 +81,143 @@ EngineeringScreen::EngineeringScreen(GuiContainer* owner, CrewPosition crew_posi GuiElement* system_row_layouts = new GuiElement(system_config_container, "SYSTEM_ROWS"); system_row_layouts->setPosition(0, 0, sp::Alignment::BottomLeft)->setAttribute("layout", "verticalbottom"); system_row_layouts->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); - float column_width = gameGlobalInfo->use_system_damage ? 100 : 150; - for(int n=0; nuse_system_damage ? 100.0f : 150.0f; + + for (int n = 0; n < ShipSystem::COUNT; n++) { string id = "SYSTEM_ROW_" + getSystemName(ShipSystem::Type(n)); SystemRow info; info.row = new GuiElement(system_row_layouts, id); - info.row->setAttribute("layout", "horizontal"); - info.row->setSize(GuiElement::GuiSizeMax, 50); + info.row + ->setSize(GuiElement::GuiSizeMax, 50.0f) + ->setAttribute("layout", "horizontal"); - info.button = new GuiToggleButton(info.row, id + "_SELECT", getLocaleSystemName(ShipSystem::Type(n)), [this, n](bool value){ - selectSystem(ShipSystem::Type(n)); - }); - info.button->setSize(300, GuiElement::GuiSizeMax); + info.button = new GuiToggleButton(info.row, id + "_SELECT", getLocaleSystemName(ShipSystem::Type(n)), + [this, n](bool value) + { + selectSystem(ShipSystem::Type(n)); + } + ); + info.button->setSize(300.0f, GuiElement::GuiSizeMax); info.damage_bar = new GuiProgressbar(info.row, id + "_DAMAGE", 0.0f, 1.0f, 0.0f); - info.damage_bar->setSize(150, GuiElement::GuiSizeMax); + info.damage_bar + ->setSize(150.0f, GuiElement::GuiSizeMax) + ->setVisible(gameGlobalInfo->use_system_damage); info.damage_icon = new GuiImage(info.damage_bar, "", "gui/icons/system_health"); - info.damage_icon->setColor(overlay_damaged_style->get(getState()).color)->setPosition(0, 0, sp::Alignment::CenterRight)->setSize(GuiElement::GuiSizeMatchHeight, GuiElement::GuiSizeMax); - info.damage_label = new GuiLabel(info.damage_bar, id + "_DAMAGE_LABEL", "...", 20); + info.damage_icon + ->setColor(overlay_damaged_style->get(getState()).color) + ->setPosition(0.0f, 0.0f, sp::Alignment::CenterRight) + ->setSize(GuiElement::GuiSizeMatchHeight, GuiElement::GuiSizeMax); + info.damage_label = new GuiLabel(info.damage_bar, id + "_DAMAGE_LABEL", "...", 20.0f); info.damage_label->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); info.heat_bar = new GuiProgressbar(info.row, id + "_HEAT", 0.0f, 1.0f, 0.0f); - info.heat_bar->setSize(column_width, GuiElement::GuiSizeMax); - info.heat_arrow = new GuiArrow(info.heat_bar, id + "_HEAT_ARROW", 0); - info.heat_arrow->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); + info.heat_bar + ->setSize(column_width, GuiElement::GuiSizeMax) + ->setVisible(has_coolant); + info.heat_arrow = new GuiArrow(info.heat_bar, id + "_HEAT_ARROW", 0.0f); + info.heat_arrow + ->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); info.heat_icon = new GuiImage(info.heat_bar, "", "gui/icons/status_overheat"); - info.heat_icon->setColor(overlay_overheating_style->get(getState()).color)->setPosition(0, 0, sp::Alignment::Center)->setSize(GuiElement::GuiSizeMatchHeight, GuiElement::GuiSizeMax); - info.power_bar = new GuiProgressSlider(info.row, id + "_POWER", 0.0f, 3.0f, 0.0f, [this,n](float value){ - if (my_spaceship) - my_player_info->commandSetSystemPowerRequest(ShipSystem::Type(n), value); - }); + info.heat_icon + ->setColor(overlay_overheating_style->get(getState()).color) + ->setPosition(0.0f, 0.0f, sp::Alignment::Center) + ->setSize(GuiElement::GuiSizeMatchHeight, GuiElement::GuiSizeMax); + info.power_bar = new GuiProgressSlider(info.row, id + "_POWER", 0.0f, power_max, 0.0f, + [this,n](float value) + { + if (my_spaceship) + my_player_info->commandSetSystemPowerRequest(ShipSystem::Type(n), value); + } + ); info.power_bar->setColor(glm::u8vec4(192, 192, 32, 128))->setSize(column_width, GuiElement::GuiSizeMax); - info.coolant_bar = new GuiProgressSlider(info.row, id + "_COOLANT", 0.0f, 10.0f, 0.0f, [this,n](float value){ - if (my_spaceship) - my_player_info->commandSetSystemCoolantRequest(ShipSystem::Type(n), value); - }); - info.coolant_bar->setColor(glm::u8vec4(32, 128, 128, 128))->setSize(column_width, GuiElement::GuiSizeMax); - if (!gameGlobalInfo->use_system_damage) - info.damage_bar->hide(); + info.coolant_bar = new GuiProgressSlider(info.row, id + "_COOLANT", 0.0f, 10.0f, 0.0f, + [this,n](float value) + { + if (my_spaceship) + my_player_info->commandSetSystemCoolantRequest(ShipSystem::Type(n), value); + } + ); + info.coolant_bar + ->setColor(glm::u8vec4(32, 128, 128, 128)) + ->setSize(column_width, GuiElement::GuiSizeMax) + ->setVisible(has_coolant); info.coolant_max_indicator = new GuiImage(info.coolant_bar, "", slider_tick_style->get(getState()).texture); - info.coolant_max_indicator->setSize(40, 40); - info.coolant_max_indicator->setAngle(90); - info.coolant_max_indicator->setColor({255,255,255,0}); + info.coolant_max_indicator + ->setAngle(90.0f) + ->setColor({255,255,255,0}) + ->setSize(40.0f, 40.0f); info.row->moveToBack(); system_rows.push_back(info); } GuiElement* icon_layout = new GuiElement(system_row_layouts, ""); - icon_layout->setSize(GuiElement::GuiSizeMax, 48)->setAttribute("layout", "horizontal"); - (new GuiElement(icon_layout, "FILLER"))->setSize(300, GuiElement::GuiSizeMax); - if (gameGlobalInfo->use_system_damage) - (new GuiImage(icon_layout, "SYSTEM_HEALTH_ICON", "gui/icons/system_health"))->setSize(150, GuiElement::GuiSizeMax); - (new GuiImage(icon_layout, "HEAT_ICON", "gui/icons/status_overheat"))->setSize(column_width, GuiElement::GuiSizeMax); - (new GuiImage(icon_layout, "POWER_ICON", "gui/icons/energy"))->setSize(column_width, GuiElement::GuiSizeMax); - coolant_remaining_bar = new GuiProgressSlider(icon_layout, "", 0, 10.0, 10.0, [](float requested_unused_coolant) - { - float total_requested = 0.0f; - auto coolant = my_spaceship.getComponent(); - if (!coolant) return; - float new_max_total = coolant->max - requested_unused_coolant; - for(int n=0; ncoolant_request; - } - if (new_max_total < total_requested) { // Drain systems - for(int n=0; ncommandSetSystemCoolantRequest(ShipSystem::Type(n), sys->coolant_request * new_max_total / total_requested); + icon_layout + ->setSize(GuiElement::GuiSizeMax, 48.0f) + ->setAttribute("layout", "horizontal"); + (new GuiElement(icon_layout, "FILLER")) + ->setSize(300.0f, GuiElement::GuiSizeMax); + system_health_icon = new GuiImage(icon_layout, "SYSTEM_HEALTH_ICON", "gui/icons/system_health"); + system_health_icon + ->setSize(150.0f, GuiElement::GuiSizeMax) + ->setVisible(gameGlobalInfo->use_system_damage); + heat_icon = new GuiImage(icon_layout, "HEAT_ICON", "gui/icons/status_overheat"); + heat_icon + ->setSize(column_width, GuiElement::GuiSizeMax) + ->setVisible(has_coolant); + (new GuiImage(icon_layout, "POWER_ICON", "gui/icons/energy")) + ->setSize(column_width, GuiElement::GuiSizeMax); + + coolant_remaining_bar = new GuiProgressSlider(icon_layout, "", 0, 10.0, 10.0, + [](float requested_unused_coolant) + { + auto coolant = my_spaceship.getComponent(); + if (!coolant) return; + float total_requested = 0.0f; + float new_max_total = coolant->max - requested_unused_coolant; + + for (int n = 0; n < ShipSystem::COUNT; n++) + { + if (auto sys = ShipSystem::get(my_spaceship, ShipSystem::Type(n))) + total_requested += sys->coolant_request; } - } else { // Put coolant into systems - int system_count = 0; - for(int n=0; ncommandSetSystemCoolantRequest(ShipSystem::Type(n), std::min(sys->coolant_request + add, 10.0f)); + + // Drain systems + if (new_max_total < total_requested) + { + for (int n = 0; n < ShipSystem::COUNT; n++) + { + if (auto sys = ShipSystem::get(my_spaceship, ShipSystem::Type(n))) + my_player_info->commandSetSystemCoolantRequest(ShipSystem::Type(n), sys->coolant_request * new_max_total / total_requested); + } + } + // Put coolant into systems + else + { + int system_count = 0; + for (int n = 0; n < ShipSystem::COUNT; n++) + { + if (ShipSystem::get(my_spaceship, ShipSystem::Type(n))) + system_count++; + } + + float add = (new_max_total - total_requested) / static_cast(system_count); + + for (int n = 0; n < ShipSystem::COUNT; n++) + { + if (auto sys = ShipSystem::get(my_spaceship, ShipSystem::Type(n))) + my_player_info->commandSetSystemCoolantRequest(ShipSystem::Type(n), std::min(sys->coolant_request + add, 10.0f)); + } } } - }); - coolant_remaining_bar->setColor(glm::u8vec4(32, 128, 128, 128))->setDrawBackground(false)->setSize(column_width, GuiElement::GuiSizeMax); - (new GuiImage(coolant_remaining_bar, "COOLANT_ICON", "gui/icons/coolant"))->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); + ); + coolant_remaining_bar + ->setColor(glm::u8vec4(32, 128, 128, 128)) + ->setDrawBackground(false) + ->setSize(column_width, GuiElement::GuiSizeMax) + ->setVisible(has_coolant); + (new GuiImage(coolant_remaining_bar, "COOLANT_ICON", "gui/icons/coolant")) + ->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); system_rows[int(ShipSystem::Type::Reactor)].button->setIcon("gui/icons/system_reactor"); system_rows[int(ShipSystem::Type::BeamWeapons)].button->setIcon("gui/icons/system_beam"); @@ -162,30 +230,56 @@ EngineeringScreen::EngineeringScreen(GuiContainer* owner, CrewPosition crew_posi system_rows[int(ShipSystem::Type::RearShield)].button->setIcon("gui/icons/shields-aft"); system_effects_container = new GuiElement(system_config_container, ""); - system_effects_container->setPosition(0, -400, sp::Alignment::BottomRight)->setSize(270, 400)->setAttribute("layout", "verticalbottom"); + system_effects_container + ->setPosition(0.0f, -400.0f, sp::Alignment::BottomRight) + ->setSize(270.0f, 400.0f) + ->setAttribute("layout", "verticalbottom"); + GuiPanel* box = new GuiPanel(system_config_container, "POWER_COOLANT_BOX"); - box->setPosition(0, 0, sp::Alignment::BottomRight)->setSize(270, 400); - power_label = new GuiLabel(box, "POWER_LABEL", tr("slider", "Power"), 30); - power_label->setVertical()->setAlignment(sp::Alignment::Center)->setPosition(20, 20, sp::Alignment::TopLeft)->setSize(30, 360); + box + ->setPosition(0.0f, 0.0f, sp::Alignment::BottomRight) + ->setSize(270.0f, 400.0f); + power_label = new GuiLabel(box, "POWER_LABEL", tr("slider", "Power"), 30.0f); + power_label + ->setVertical() + ->setAlignment(sp::Alignment::Center) + ->setPosition(20.0f, 20.0f, sp::Alignment::TopLeft) + ->setSize(30.0f, 360.0f); coolant_label = new GuiLabel(box, "COOLANT_LABEL", tr("slider", "Coolant"), 30); - coolant_label->setVertical()->setAlignment(sp::Alignment::Center)->setPosition(110, 20, sp::Alignment::TopLeft)->setSize(30, 360); - - power_slider = new GuiSlider(box, "POWER_SLIDER", 3.0, 0.0, 1.0, [this](float value) { - if (my_spaceship && selected_system != ShipSystem::Type::None) - my_player_info->commandSetSystemPowerRequest(selected_system, value); - }); - power_slider->setPosition(50, 20, sp::Alignment::TopLeft)->setSize(60, 360); - for(float snap_point = 0.0f; snap_point <= 3.0f; snap_point += 0.5f) + coolant_label + ->setVertical() + ->setAlignment(sp::Alignment::Center) + ->setPosition(110.0f, 20.0f, sp::Alignment::TopLeft) + ->setSize(30.0f, 360.0f) + ->setVisible(has_coolant); + + power_slider = new GuiSlider(box, "POWER_SLIDER", power_max, 0.0f, 1.0f, + [this](float value) + { + if (my_spaceship && selected_system != ShipSystem::Type::None) + my_player_info->commandSetSystemPowerRequest(selected_system, value); + } + ); + power_slider + ->setPosition(50.0f, 20.0f, sp::Alignment::TopLeft) + ->setSize(60.0f, 360.0f) + ->disable(); + for (float snap_point = 0.0f; snap_point <= power_max; snap_point += 0.5f) power_slider->addSnapValue(snap_point, snap_point == 1.0f ? 0.1f : 0.01f); - power_slider->disable(); - coolant_slider = new GuiSlider(box, "COOLANT_SLIDER", 10.0, 0.0, 0.0, [this](float value) { - if (my_spaceship && selected_system != ShipSystem::Type::None) - my_player_info->commandSetSystemCoolantRequest(selected_system, value); - }); - coolant_slider->setPosition(140, 20, sp::Alignment::TopLeft)->setSize(60, 360); - for(float snap_point = 0.0f; snap_point <= 10.0f; snap_point += 2.5f) + coolant_slider = new GuiSlider(box, "COOLANT_SLIDER", 10.0, 0.0, 0.0, + [this](float value) + { + if (my_spaceship && selected_system != ShipSystem::Type::None) + my_player_info->commandSetSystemCoolantRequest(selected_system, value); + } + ); + coolant_slider + ->setPosition(140.0f, 20.0f, sp::Alignment::TopLeft) + ->setSize(60.0f, 360.0f) + ->disable() + ->setVisible(has_coolant); + for (float snap_point = 0.0f; snap_point <= 10.0f; snap_point += 2.5f) coolant_slider->addSnapValue(snap_point, 0.1f); - coolant_slider->disable(); (new GuiShipInternalView(system_row_layouts, "SHIP_INTERNAL_VIEW", 48.0f))->setShip(my_spaceship)->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); @@ -197,98 +291,148 @@ void EngineeringScreen::onDraw(sp::RenderTarget& renderer) if (my_spaceship) { float total_coolant_used = 0.0f; + auto reactor = my_spaceship.getComponent(); auto coolant = my_spaceship.getComponent(); + float power_max = (reactor || coolant) ? 3.0f : 1.0f; + + system_health_icon->setVisible(gameGlobalInfo->use_system_damage); + heat_icon->setVisible(coolant); - for(int n=0; nsetVisible(system); - if (!system) - continue; + if (!system) continue; - float health = system->health; - if (health < 0.0f) - info.damage_bar->setValue(-health)->setColor(glm::u8vec4(128, 32, 32, 192)); - else - info.damage_bar->setValue(health)->setColor(glm::u8vec4(64, 128 * health, 64 * health, 192)); - info.damage_label->setText(toNearbyIntString(health * 100) + "%"); - float health_max = system->health_max; - if (health_max < 1.0f) - info.damage_icon->show(); - else - info.damage_icon->hide(); + if (gameGlobalInfo->use_system_damage) + { + float health = system->health; + if (health < 0.0f) + { + info.damage_bar + ->setValue(-health) + ->setColor(glm::u8vec4(128, 32, 32, 192)); + } + else + { + info.damage_bar + ->setValue(health) + ->setColor(glm::u8vec4(64, static_cast(128.0f * health), static_cast(64.0f * health), 192)); + } + info.damage_label->setText(toNearbyIntString(health * 100.0f) + "%"); + float health_max = system->health_max; + info.damage_icon->setVisible(health_max < 1.0f); + } - float heat = system->heat_level; - info.heat_bar->setValue(heat)->setColor(glm::u8vec4(128, 32 + 96 * (1.0f - heat), 32, 192)); - float heating_diff = system->getHeatingDelta(); - if (heating_diff > 0) - info.heat_arrow->setAngle(90); - else - info.heat_arrow->setAngle(-90); - info.heat_arrow->setVisible(heat > 0); - info.heat_arrow->setColor(glm::u8vec4(255, 255, 255, std::min(255, int(255.0f * fabs(heating_diff))))); - if (heat > 0.9f && fmod(engine->getElapsedTime(), 0.5f) < 0.25f) - info.heat_icon->show(); - else - info.heat_icon->hide(); + info.power_bar + ->setRange(0.0f, power_max) + ->setValue(system->power_level); - info.power_bar->setValue(system->power_level); - info.coolant_bar->setValue(system->coolant_level); - if (coolant) info.coolant_bar->setEnable(!coolant->auto_levels); + info.heat_bar->setVisible(coolant); + info.coolant_bar->setVisible(coolant); - auto slider_tick_color = slider_tick_style->get(getState()).color; - if (system->coolant_request > 0.0f) + if (coolant) { - float f = system->coolant_request * 0.1f; - info.coolant_max_indicator->setPosition(-20.0f + info.coolant_bar->getSize().x * f, 5.0f); - info.coolant_max_indicator->setColor({slider_tick_color.r, slider_tick_color.g, slider_tick_color.b, 255}); - } - else - info.coolant_max_indicator->setColor({slider_tick_color.r, slider_tick_color.g, slider_tick_color.b, 0}); + // Render heat + const float heat = system->heat_level; + const float heating_diff = system->getHeatingDelta(); - total_coolant_used += system->coolant_level; + info.heat_bar + ->setValue(heat) + ->setColor(glm::u8vec4(128, 32 + static_cast(96.0f * (1.0f - heat)), 32, 192)); + + info.heat_arrow + ->setAngle((heating_diff > 0) ? 90.0f : -90.0f) + ->setColor(glm::u8vec4(255, 255, 255, std::min(255, static_cast(255.0f * fabs(heating_diff))))) + ->setVisible(heat > 0.0f); + + info.heat_icon->setVisible(heat > 0.9f && fmod(engine->getElapsedTime(), 0.5f) < 0.25f); + + // Render coolant + info.coolant_bar + ->setValue(system->coolant_level) + ->setEnable(!coolant->auto_levels); + + auto slider_tick_color = slider_tick_style->get(getState()).color; + if (system->coolant_request > 0.0f) + { + info.coolant_max_indicator + ->setColor({slider_tick_color.r, slider_tick_color.g, slider_tick_color.b, 255}) + ->setPosition(-20.0f + info.coolant_bar->getSize().x * (system->coolant_request * 0.1f), 5.0f); + } + else + info.coolant_max_indicator->setColor({slider_tick_color.r, slider_tick_color.g, slider_tick_color.b, 0}); + + total_coolant_used += system->coolant_level; + } } + // Render total remaining coolant coolant_remaining_bar->setVisible(coolant); - if (coolant) { - coolant_remaining_bar->setRange(0, coolant->max); - coolant_remaining_bar->setValue(coolant->max - total_coolant_used); + if (coolant) + { + coolant_remaining_bar + ->setRange(0.0f, coolant->max) + ->setValue(coolant->max - total_coolant_used); } if (selected_system != ShipSystem::Type::None) { auto system = ShipSystem::get(my_spaceship, selected_system); - if (system) { - power_label->setText(tr("slider", "Power: {current_level}% / {requested}%").format({{"current_level", toNearbyIntString(system->power_level * 100)}, {"requested", toNearbyIntString(system->power_request * 100)}})); - power_slider->setValue(system->power_request); + if (system) + { + // Render power slider + power_label->setText(tr("slider", "Power: {current_level}% / {requested}%").format({ + {"current_level", toNearbyIntString(system->power_level * 100.0f)}, + {"requested", toNearbyIntString(system->power_request * 100.0f)} + })); + + // Limit max power to 100% if lacking both Coolant and Reactor. + // Rotated bar takes the max value first. + power_slider + ->setRange((coolant || reactor) ? 3.0f : 1.0f, 0.0f) + ->setValue(system->power_request); + + // Render coolant slider coolant_label->setVisible(coolant); coolant_slider->setVisible(coolant); - if (coolant) { - coolant_label->setText(tr("slider", "Coolant: {current_level}% / {requested}%").format({{"current_level", toNearbyIntString(system->coolant_level / coolant->max_coolant_per_system * 100.0f)}, {"requested", toNearbyIntString(std::min(system->coolant_request, coolant->max) / coolant->max_coolant_per_system * 100)}})); - coolant_slider->setEnable(!coolant->auto_levels); - coolant_slider->setValue(std::min(system->coolant_request, coolant->max)); + if (coolant) + { + coolant_label->setText(tr("slider", "Coolant: {current_level}% / {requested}%").format({ + {"current_level", toNearbyIntString(system->coolant_level / coolant->max_coolant_per_system * 100.0f)}, + {"requested", toNearbyIntString(std::min(system->coolant_request, coolant->max) / coolant->max_coolant_per_system * 100.0f)} + })); + coolant_slider + ->setValue(std::min(system->coolant_request, coolant->max)) + ->setEnable(!coolant->auto_levels); } system_effects_index = 0; float effectiveness = system->getSystemEffectiveness(); + float health_max = system->health_max; if (health_max < 1.0f) - addSystemEffect(tr("Engineer", "Maximal health"), toNearbyIntString(health_max * 100) + "%"); - switch(selected_system) + addSystemEffect(tr("Engineer", "Maximal health"), toNearbyIntString(health_max * 100.0f) + "%"); + switch (selected_system) { case ShipSystem::Type::Reactor: if (effectiveness > 1.0f) - effectiveness = (1.0f + effectiveness) / 2.0f; - addSystemEffect(tr("Energy production"), tr("{energy}/min").format({{"energy", string(effectiveness * -system->power_factor * system->power_factor_rate * 60.0f, 1)}})); + effectiveness = (1.0f + effectiveness) * 0.5f; + addSystemEffect(tr("Energy production"), tr("{energy}/min").format({ + {"energy", string(effectiveness * -system->power_factor * system->power_factor_rate * 60.0f, 1)} + })); break; case ShipSystem::Type::BeamWeapons: - addSystemEffect(tr("Firing rate"), toNearbyIntString(effectiveness * 100) + "%"); + addSystemEffect(tr("Firing rate"), toNearbyIntString(effectiveness * 100.0f) + "%"); // If the ship has a turret, also note that the rotation rate // is affected. - if (auto beamweapons = my_spaceship.getComponent()) { - for(auto& mount : beamweapons->mounts) { - if (mount.turret_arc > 0) { + if (auto beamweapons = my_spaceship.getComponent()) + { + for (auto& mount : beamweapons->mounts) + { + if (mount.turret_arc > 0) + { addSystemEffect(tr("Engineer", "Turret rotation rate"), toNearbyIntString(effectiveness * 100) + "%"); break; } @@ -296,78 +440,79 @@ void EngineeringScreen::onDraw(sp::RenderTarget& renderer) } break; case ShipSystem::Type::MissileSystem: - addSystemEffect(tr("missile","Reload rate"), toNearbyIntString(effectiveness * 100) + "%"); + addSystemEffect(tr("missile", "Reload rate"), toNearbyIntString(effectiveness * 100.0f) + "%"); break; - case ShipSystem::Type::Maneuver:{ - addSystemEffect(tr("Turning speed"), toNearbyIntString(effectiveness * 100) + "%"); - auto combat = my_spaceship.getComponent(); - if (combat) { - auto impulse = my_spaceship.getComponent(); - auto thrusters = my_spaceship.getComponent(); - if (impulse && thrusters) - addSystemEffect(tr("Combat recharge rate"), toNearbyIntString(((impulse->getSystemEffectiveness() + thrusters->getSystemEffectiveness()) / 2.0f) * 100) + "%"); + case ShipSystem::Type::Maneuver: + { + addSystemEffect(tr("Turning speed"), toNearbyIntString(effectiveness * 100.0f) + "%"); + if (my_spaceship.hasComponent()) + { + auto impulse = my_spaceship.getComponent(); + auto thrusters = my_spaceship.getComponent(); + if (impulse && thrusters) + addSystemEffect(tr("Combat recharge rate"), toNearbyIntString(((impulse->getSystemEffectiveness() + thrusters->getSystemEffectiveness()) * 0.5f) * 100.0f) + "%"); + } } - }break; - case ShipSystem::Type::Impulse:{ - addSystemEffect(tr("Impulse speed"), toNearbyIntString(effectiveness * 100) + "%"); - auto combat = my_spaceship.getComponent(); - if (combat) { - auto impulse = my_spaceship.getComponent(); - auto thrusters = my_spaceship.getComponent(); - if (impulse && thrusters) - addSystemEffect(tr("Combat recharge rate"), toNearbyIntString(((impulse->getSystemEffectiveness() + thrusters->getSystemEffectiveness()) / 2.0f) * 100) + "%"); + break; + case ShipSystem::Type::Impulse: + { + addSystemEffect(tr("Impulse speed"), toNearbyIntString(effectiveness * 100) + "%"); + if (my_spaceship.hasComponent()) + { + auto impulse = my_spaceship.getComponent(); + auto thrusters = my_spaceship.getComponent(); + if (impulse && thrusters) + addSystemEffect(tr("Combat recharge rate"), toNearbyIntString(((impulse->getSystemEffectiveness() + thrusters->getSystemEffectiveness()) * 0.5f) * 100.0f) + "%"); + } } - }break; + break; case ShipSystem::Type::Warp: - addSystemEffect(tr("Warp drive speed"), toNearbyIntString(effectiveness * 100) + "%"); + addSystemEffect(tr("Warp drive speed"), toNearbyIntString(effectiveness * 100.0f) + "%"); break; case ShipSystem::Type::JumpDrive:{ - if (auto jump = my_spaceship.getComponent()) { + if (auto jump = my_spaceship.getComponent()) + { if (jump->get_seconds_to_jump() == std::numeric_limits::max()) - addSystemEffect(tr("Time to jump activation"), "∞ sec."); - else - addSystemEffect(tr("Time to jump activation"), tr("jumpcontrol", "{delay} sec.").format({{"delay", string(jump->get_seconds_to_jump())}})); - addSystemEffect(tr("Jump drive recharge rate"), toNearbyIntString(jump->get_recharge_rate() * 100) + "%"); - } - }break; - case ShipSystem::Type::FrontShield:{ - auto shields = my_spaceship.getComponent(); - if (shields) { - if (gameGlobalInfo->use_beam_shield_frequencies) - addSystemEffect(tr("shields","Calibration speed"), toNearbyIntString((shields->front_system.getSystemEffectiveness() + shields->rear_system.getSystemEffectiveness()) / 2.0f * 100) + "%"); - addSystemEffect(tr("shields","Charge rate"), toNearbyIntString(effectiveness * 100) + "%"); { - DamageInfo di; - di.type = DamageType::Kinetic; - float damage_negate = 1.0f - shields->getDamageFactor(0); - if (damage_negate < 0.0f) - addSystemEffect(tr("Extra damage"), toNearbyIntString(-damage_negate * 100) + "%"); - else - addSystemEffect(tr("Damage negate"), toNearbyIntString(damage_negate * 100) + "%"); + addSystemEffect(tr("Time to jump activation"), tr("jumpcontrol", "{delay} sec.").format({ + {"delay", (jump->get_seconds_to_jump() == std::numeric_limits::max()) ? "∞" : string(jump->get_seconds_to_jump())} + })); } + addSystemEffect(tr("Jump drive recharge rate"), toNearbyIntString(jump->get_recharge_rate() * 100.0f) + "%"); } }break; - case ShipSystem::Type::RearShield:{ - auto shields = my_spaceship.getComponent(); - if (shields) { - if (gameGlobalInfo->use_beam_shield_frequencies) - addSystemEffect(tr("shields","Calibration speed"), toNearbyIntString((shields->front_system.getSystemEffectiveness() + shields->rear_system.getSystemEffectiveness()) / 2.0f * 100) + "%"); - addSystemEffect(tr("shields","Charge rate"), toNearbyIntString(effectiveness * 100) + "%"); + case ShipSystem::Type::FrontShield: + case ShipSystem::Type::RearShield: + { + if (auto shields = my_spaceship.getComponent()) { - DamageInfo di; - di.type = DamageType::Kinetic; - float damage_negate = 1.0f - shields->getDamageFactor(shields->entries.size() - 1); + // Determine front or rear shields. + const std::size_t shield_index = (selected_system == ShipSystem::Type::FrontShield) + ? 0 + : shields->entries.size() - 1; + + // Add frequency calibration speed effect, if relevant. + if (gameGlobalInfo->use_beam_shield_frequencies) + addSystemEffect(tr("shields", "Calibration speed"), toNearbyIntString((shields->front_system.getSystemEffectiveness() + shields->rear_system.getSystemEffectiveness()) * 0.5f * 100.0f) + "%"); + + // Add charge rate effect. + addSystemEffect(tr("shields", "Charge rate"), toNearbyIntString(effectiveness * 100.0f) + "%"); + + // Add damage negation/vulnerability rate effect. + const float damage_negate = 1.0f - shields->getDamageFactor(shield_index); if (damage_negate < 0.0f) - addSystemEffect(tr("Extra damage"), toNearbyIntString(-damage_negate * 100) + "%"); + addSystemEffect(tr("Extra damage"), toNearbyIntString(-damage_negate * 100.0f) + "%"); else - addSystemEffect(tr("Damage negate"), toNearbyIntString(damage_negate * 100) + "%"); + addSystemEffect(tr("Damage negate"), toNearbyIntString(damage_negate * 100.0f) + "%"); } } - }break; + break; default: break; } - for(unsigned int idx=system_effects_index; idxhide(); } } @@ -379,23 +524,40 @@ void EngineeringScreen::onUpdate() { if (my_spaceship && isVisible()) { + auto reactor = my_spaceship.getComponent(); auto coolant = my_spaceship.getComponent(); - for(unsigned int n=0; n(n)); float set_value = keys.engineering_set_power_for_system[n].getValue() * 3.0f; auto sys = ShipSystem::get(my_spaceship, static_cast(n)); + + // Set system power request. if (sys && set_value != sys->power_request && (set_value != 0.0f || set_power_active[n])) { + // Cap set_value to 100% if the ship lacks both Reactor and + // Coolant components. Otherwise, overpowering the system is + // free. In this situation there's also no benefit to + // underpowering the system, but some scenarios still use power + // assignment as a feature. + if (!reactor && !coolant) set_value = std::clamp(set_value, 0.0f, 1.0f); + + // Set the system power request. my_player_info->commandSetSystemPowerRequest(static_cast(n), set_value); - set_power_active[n] = set_value != 0.0f; //Make sure the next update is send, even if it is back to zero. + // Make sure the next update is sent, even if it is back to zero. + set_power_active[n] = set_value != 0.0f; } - if (coolant) { + + if (coolant) + { set_value = keys.engineering_set_coolant_for_system[n].getValue() * coolant->max_coolant_per_system; if (sys && set_value != sys->coolant_request && (set_value != 0.0f || set_coolant_active[n])) { my_player_info->commandSetSystemCoolantRequest(static_cast(n), set_value); - set_coolant_active[n] = set_value != 0.0f; //Make sure the next update is send, even if it is back to zero. + // Make sure the next update is sent, even if it is back to zero. + set_coolant_active[n] = set_value != 0.0f; } } } diff --git a/src/screens/crew6/engineeringScreen.h b/src/screens/crew6/engineeringScreen.h index ec42e3a045..1055f8d8d1 100644 --- a/src/screens/crew6/engineeringScreen.h +++ b/src/screens/crew6/engineeringScreen.h @@ -28,6 +28,8 @@ class EngineeringScreen : public GuiOverlay GuiSlider* power_slider; GuiLabel* coolant_label; GuiSlider* coolant_slider; + GuiImage* system_health_icon; + GuiImage* heat_icon; GuiProgressbar* coolant_remaining_bar; class SystemRow diff --git a/src/screens/extra/powerManagement.cpp b/src/screens/extra/powerManagement.cpp index 8d55775157..7bc2851d71 100644 --- a/src/screens/extra/powerManagement.cpp +++ b/src/screens/extra/powerManagement.cpp @@ -25,7 +25,7 @@ PowerManagementScreen::PowerManagementScreen(GuiContainer* owner) coolant_display->setIcon("gui/icons/coolant")->setTextSize(20)->setPosition(315, 20, sp::Alignment::TopLeft)->setSize(280, 40); GuiElement* layout = new GuiElement(this, ""); layout->setPosition(20, 60, sp::Alignment::TopLeft)->setSize(GuiElement::GuiSizeMax, 400)->setAttribute("layout", "horizontal"); - for(int n=0; naddBackground()->setAlignment(sp::Alignment::Center)->setPosition(0, 0, sp::Alignment::TopLeft)->setSize(290, 50); (new GuiLabel(box, "", tr("button", "Power"), 30))->setVertical()->setAlignment(sp::Alignment::CenterLeft)->setPosition(20, 50, sp::Alignment::TopLeft)->setSize(30, 340); - (new GuiLabel(box, "", tr("button", "Coolant"), 30))->setVertical()->setAlignment(sp::Alignment::CenterLeft)->setPosition(100, 50, sp::Alignment::TopLeft)->setSize(30, 340); - (new GuiLabel(box, "", tr("button", "Heat"), 30))->setVertical()->setAlignment(sp::Alignment::CenterLeft)->setPosition(180, 50, sp::Alignment::TopLeft)->setSize(30, 340); + systems[n].coolant_label = new GuiLabel(box, "", tr("button", "Coolant"), 30.0f); + systems[n].coolant_label + ->setVertical() + ->setAlignment(sp::Alignment::CenterLeft) + ->setPosition(100.0f, 50.0f, sp::Alignment::TopLeft) + ->setSize(30.0f, 340.0f); + systems[n].heat_label = new GuiLabel(box, "", tr("button", "Heat"), 30.0f); + systems[n].heat_label + ->setVertical() + ->setAlignment(sp::Alignment::CenterLeft) + ->setPosition(180.0f, 50.0f, sp::Alignment::TopLeft) + ->setSize(30.0f, 340.0f); systems[n].power_bar = new GuiProgressbar(box, "", 0.0, 3.0, 1.0); systems[n].power_bar->setDrawBackground(false)->setPosition(52.5, 60, sp::Alignment::TopLeft)->setSize(50, 320); @@ -79,6 +89,8 @@ void PowerManagementScreen::onDraw(sp::RenderTarget& renderer) { auto reactor = my_spaceship.getComponent(); auto coolant = my_spaceship.getComponent(); + float power_max = (reactor || coolant) ? 3.0f : 1.0f; + if (reactor) { //Update the energy usage. if (previous_energy_measurement == 0.0f) @@ -107,20 +119,33 @@ void PowerManagementScreen::onDraw(sp::RenderTarget& renderer) { auto sys = ShipSystem::get(my_spaceship, ShipSystem::Type(n)); systems[n].box->setVisible(sys); - if (sys) { - systems[n].power_slider->setValue(sys->power_request); + if (sys) + { + // Power + systems[n].power_slider + ->setRange(power_max, 0.0f) // Backward order for rotated slider + ->setValue(sys->power_request); + systems[n].power_bar + ->setRange(0.0f, power_max) + ->setValue(sys->power_level) + ->setColor(glm::u8vec4(255, 255, 0, 255)); + + // Heat and coolant systems[n].coolant_slider->setVisible(coolant); - if (coolant) { + systems[n].coolant_bar->setVisible(coolant); + systems[n].coolant_label->setVisible(coolant); + systems[n].heat_bar->setVisible(coolant); + systems[n].heat_label->setVisible(coolant); + if (coolant) + { systems[n].coolant_slider->setValue(std::min(sys->coolant_request, coolant->max)); systems[n].coolant_slider->setEnable(!coolant->auto_levels); - } - float heat = sys->heat_level; - float power = sys->power_level; - float coolant = sys->coolant_level; - systems[n].heat_bar->setValue(heat)->setColor(glm::u8vec4(128, 128 * (1.0f - heat), 0, 255)); - systems[n].power_bar->setValue(power)->setColor(glm::u8vec4(255, 255, 0, 255)); - systems[n].coolant_bar->setValue(coolant)->setColor(glm::u8vec4(0, 128, 255, 255)); + float heat = sys->heat_level; + float coolant = sys->coolant_level; + systems[n].heat_bar->setValue(heat)->setColor(glm::u8vec4(128, 128 * (1.0f - heat), 0, 255)); + systems[n].coolant_bar->setValue(coolant)->setColor(glm::u8vec4(0, 128, 255, 255)); + } } } } diff --git a/src/screens/extra/powerManagement.h b/src/screens/extra/powerManagement.h index ee1d5975a7..ae3881104e 100644 --- a/src/screens/extra/powerManagement.h +++ b/src/screens/extra/powerManagement.h @@ -1,9 +1,9 @@ -#ifndef POWER_MANAGEMENT_H -#define POWER_MANAGEMENT_H +#pragma once #include "gui/gui2_overlay.h" #include "components/shipsystem.h" +class GuiLabel; class GuiPanel; class GuiSlider; class GuiProgressbar; @@ -27,8 +27,10 @@ class PowerManagementScreen : public GuiOverlay GuiSlider* power_slider; GuiSlider* coolant_slider; GuiProgressbar* heat_bar; + GuiLabel* heat_label; GuiProgressbar* power_bar; GuiProgressbar* coolant_bar; + GuiLabel* coolant_label; }; SystemRow systems[ShipSystem::COUNT]; bool set_power_active[ShipSystem::COUNT] = {false}; @@ -39,5 +41,3 @@ class PowerManagementScreen : public GuiOverlay void onDraw(sp::RenderTarget& target) override; virtual void onUpdate() override; }; - -#endif//POWER_MANAGEMENT_H diff --git a/src/systems/shipsystemssystem.cpp b/src/systems/shipsystemssystem.cpp index fb78bf646c..2861c75ad7 100644 --- a/src/systems/shipsystemssystem.cpp +++ b/src/systems/shipsystemssystem.cpp @@ -15,28 +15,35 @@ void ShipSystemsSystem::update(float delta) { if (!game_server) return; for(auto [entity, system] : sp::ecs::Query()) - updateSystem(system, delta, entity.hasComponent()); + updateSystem(entity, system, delta); for(auto [entity, system] : sp::ecs::Query()) - updateSystem(system, delta, entity.hasComponent()); + updateSystem(entity, system, delta); for(auto [entity, system] : sp::ecs::Query()) - updateSystem(system, delta, entity.hasComponent()); + updateSystem(entity, system, delta); for(auto [entity, system] : sp::ecs::Query()) - updateSystem(system, delta, entity.hasComponent()); + updateSystem(entity, system, delta); for(auto [entity, system] : sp::ecs::Query()) - updateSystem(system, delta, entity.hasComponent()); + updateSystem(entity, system, delta); for(auto [entity, system] : sp::ecs::Query()) - updateSystem(system, delta, entity.hasComponent()); + updateSystem(entity, system, delta); for(auto [entity, system] : sp::ecs::Query()) - updateSystem(system, delta, entity.hasComponent()); + updateSystem(entity, system, delta); for(auto [entity, system] : sp::ecs::Query()) { - updateSystem(system.front_system, delta, entity.hasComponent()); + updateSystem(entity, system.front_system, delta); if (system.entries.size() > 1) - updateSystem(system.rear_system, delta, entity.hasComponent()); + updateSystem(entity, system.rear_system, delta); } } -void ShipSystemsSystem::updateSystem(ShipSystem& system, float delta, bool has_coolant) +void ShipSystemsSystem::updateSystem(sp::ecs::Entity entity, ShipSystem& system, float delta) { + const bool has_coolant = entity.hasComponent(); + // Cap system power request to 100% if a ship lacks both Coolant (no heat + // generation) and a Reactor (no energy consumption). Otherwise, + // overpowering the system is free of consequences. + if (!has_coolant && !entity.hasComponent()) + system.power_request = std::min(system.power_request, 1.0f); + system.health = std::min(1.0f, system.health + delta * system.auto_repair_per_second); system.hacked_level = std::max(0.0f, system.hacked_level - delta / unhack_time); diff --git a/src/systems/shipsystemssystem.h b/src/systems/shipsystemssystem.h index 40c28eb779..b6e46ad1a7 100644 --- a/src/systems/shipsystemssystem.h +++ b/src/systems/shipsystemssystem.h @@ -2,6 +2,7 @@ #include "ecs/system.h" #include "ecs/query.h" +#include "ecs/entity.h" #include "components/shipsystem.h" @@ -12,5 +13,5 @@ class ShipSystemsSystem : public sp::ecs::System void update(float delta) override; private: - void updateSystem(ShipSystem& system, float delta, bool has_coolant); + void updateSystem(sp::ecs::Entity entity, ShipSystem& system, float delta); };