diff --git a/CMakeLists.txt b/CMakeLists.txt index 716c645024..568148172a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -270,6 +270,7 @@ set(MAIN_SOURCES src/screens/extra/powerManagement.cpp src/screens/extra/databaseScreen.cpp src/screens/extra/commsScreen.cpp + src/screens/extra/scannerScreen.cpp src/screens/extra/shipLogScreen.cpp src/screens/gm/gameMasterScreen.cpp src/screens/gm/objectCreationView.cpp @@ -625,6 +626,7 @@ set(MAIN_SOURCES src/screens/crew6/weaponsScreen.h src/screens/crewStationScreen.h src/screens/extra/commsScreen.h + src/screens/extra/scannerScreen.h src/screens/extra/damcon.h src/screens/extra/databaseScreen.h src/screens/extra/powerManagement.h diff --git a/src/crewPosition.cpp b/src/crewPosition.cpp index 05479f0258..cb37b6fb78 100644 --- a/src/crewPosition.cpp +++ b/src/crewPosition.cpp @@ -13,6 +13,8 @@ string crewPositionToString(CrewPosition value) { case CrewPosition::singlePilot: return "singlepilot"; case CrewPosition::damageControl: return "damagecontrol"; case CrewPosition::powerManagement: return "powermanagement"; + case CrewPosition::altScience: return "altscience"; + case CrewPosition::scanOnly: return "scanonly"; case CrewPosition::databaseView: return "database"; case CrewPosition::altRelay: return "altrelay"; case CrewPosition::commsOnly: return "commsonly"; @@ -51,6 +53,10 @@ std::optional tryParseCrewPosition(string value) { return CrewPosition::damageControl; else if (value == "powermanagement") return CrewPosition::powerManagement; + else if (value == "altscience" || value == "tacticalmap") + return CrewPosition::altScience; + else if (value == "scanonly") + return CrewPosition::scanOnly; else if (value == "database" || value == "databaseview") return CrewPosition::databaseView; else if (value == "altrelay") diff --git a/src/crewPosition.h b/src/crewPosition.h index e6f4588e35..c816859d09 100644 --- a/src/crewPosition.h +++ b/src/crewPosition.h @@ -22,9 +22,11 @@ enum class CrewPosition //extras damageControl, powerManagement, - databaseView, - altRelay, - commsOnly, + altScience, // Science without scanning or database + scanOnly, // Science scanner only + databaseView, // Science database only + altRelay, // Relay without comms + commsOnly, // Relay comms only shipLog, MAX diff --git a/src/menus/shipSelectionScreen.cpp b/src/menus/shipSelectionScreen.cpp index 1ae41074f2..c59a720c48 100644 --- a/src/menus/shipSelectionScreen.cpp +++ b/src/menus/shipSelectionScreen.cpp @@ -748,13 +748,13 @@ CrewPositionSelection::CrewPositionSelection(GuiContainer* owner, string id, int // 6/5 player crew panel (new GuiLabel(standard_crew_panel, "CREW_POSITION_SELECT_LABEL", tr("6/5 player crew"), 30.0f))->addBackground()->setSize(GuiElement::GuiSizeMax, 50)->setAttribute("margin", "0, 0, 0, 10"); - auto create_crew_position_button = [this](GuiElement* standard_crew_panel, int n) { + auto createCrewPositionButton = [this](GuiElement* standard_crew_panel, int n) { auto cp = CrewPosition(n); auto button = new GuiToggleButton(standard_crew_panel, "", getCrewPositionName(cp), [this, cp](bool value){ my_player_info->commandSetCrewPosition(window_index, cp, value); unselectSingleOptions(); }); - button->setSize(GuiElement::GuiSizeMax, 50); + button->setSize(GuiElement::GuiSizeMax, button_height); button->setIcon(getCrewPositionIcon(cp)); button->setValue(size_t(window_index) < my_player_info->crew_positions.size() && my_player_info->crew_positions[window_index].has(cp)); crew_position_button[n] = button; @@ -762,26 +762,26 @@ CrewPositionSelection::CrewPositionSelection(GuiContainer* owner, string id, int }; for (int n = 0; n <= int(CrewPosition::relayOfficer); n++) { - create_crew_position_button(standard_crew_panel, n); - standard_crew_panel->setSize(standard_crew_panel->getSize() + glm::vec2(0.0f, 50.0f)); + createCrewPositionButton(standard_crew_panel, n); + standard_crew_panel->setSize(standard_crew_panel->getSize() + glm::vec2(0.0f, button_height)); } // 4/3/1 player crew panel for (int n = int(CrewPosition::tacticalOfficer); n <= int(CrewPosition::singlePilot); n++) { - create_crew_position_button(limited_crew_panel, n); - limited_crew_panel->setSize(limited_crew_panel->getSize() + glm::vec2(0.0f, 50.0f)); + createCrewPositionButton(limited_crew_panel, n); + limited_crew_panel->setSize(limited_crew_panel->getSize() + glm::vec2(0.0f, button_height)); } // Center column + // 3D screens panel auto space_screens_panel = new GuiPanel(center_container, ""); - space_screens_panel->setSize(GuiElement::GuiSizeMax, 180.0f); + space_screens_panel->setSize(GuiElement::GuiSizeMax, 170.0f); space_screens_panel->setAttribute("margin", "0, 0, 0, 20"); space_screens_panel->setAttribute("padding", "20, 20, 0, 20"); space_screens_panel->setAttribute("layout", "vertical"); (new GuiLabel(space_screens_panel, "CREW_POSITION_SELECT_LABEL", tr("3D screens"), 30.0f))->addBackground()->setSize(GuiElement::GuiSizeMax, 50.0f)->setAttribute("margin", "0, 0, 0, 10"); - // 3D screens panel // Main screen button main_screen_button = new GuiToggleButton(space_screens_panel, "", tr("Main screen"), [this](bool value) { my_player_info->commandSetMainScreen(window_index, value); @@ -789,18 +789,18 @@ CrewPositionSelection::CrewPositionSelection(GuiContainer* owner, string id, int }); main_screen_button ->setValue(my_player_info->main_screen & (1 << window_index)) - ->setSize(GuiElement::GuiSizeMax, 50.0f); + ->setSize(GuiElement::GuiSizeMax, button_height); // Window button auto window_button_row = new GuiElement(space_screens_panel, ""); - window_button_row->setSize(GuiElement::GuiSizeMax, 50.0f)->setAttribute("layout", "horizontal"); + window_button_row->setSize(GuiElement::GuiSizeMax, button_height)->setAttribute("layout", "horizontal"); window_button = new GuiToggleButton(window_button_row, "WINDOW_BUTTON", tr("Ship window"), [this](bool value) { disableAllExcept(window_button); }); - window_button->setSize(GuiElement::GuiSizeMax, 50.0f); + window_button->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); window_angle = new GuiTextEntry(window_button_row, "WINDOW_ANGLE","0"); - window_angle->setSize(75.0f, 50.0f); + window_angle->setSize(75.0f, button_height); window_angle->setSelectOnFocus(); window_angle->callback([this](string text) { // Check validity: Only allow numbers and no more than 3 digits. Angles above 360 are fine though. if (text !="" && text !="-") window_angle->setText(text.toInt()); @@ -823,12 +823,12 @@ CrewPositionSelection::CrewPositionSelection(GuiContainer* owner, string id, int main_screen_controls_button = new GuiToggleButton(alternative_options_panel, "MAIN_SCREEN_CONTROLS_ENABLE", tr("Main screen controls"), [this](bool value) { my_player_info->commandSetMainScreenControl(window_index, value); }); - main_screen_controls_button->setValue(my_player_info->main_screen_control)->setSize(GuiElement::GuiSizeMax, 50); + main_screen_controls_button->setValue(my_player_info->main_screen_control)->setSize(GuiElement::GuiSizeMax, button_height); for (int n = int(CrewPosition::singlePilot) + 1; n < int(CrewPosition::MAX); n++) { - create_crew_position_button(alternative_options_panel, n); - alternative_options_panel->setSize(alternative_options_panel->getSize() + glm::vec2(0.0f, 50.0f)); + createCrewPositionButton(alternative_options_panel, n); + alternative_options_panel->setSize(alternative_options_panel->getSize() + glm::vec2(0.0f, button_height)); } // Right column diff --git a/src/menus/shipSelectionScreen.h b/src/menus/shipSelectionScreen.h index 258209f007..ba9013eec1 100644 --- a/src/menus/shipSelectionScreen.h +++ b/src/menus/shipSelectionScreen.h @@ -67,7 +67,9 @@ class CrewPositionSelection : public GuiPanel void disableAllExcept(GuiToggleButton* button); void unselectSingleOptions(); + const float button_height = 45.0f; int window_index; + GuiButton* ready_button; GuiToggleButton* main_screen_button; GuiToggleButton* crew_position_button[static_cast(CrewPosition::MAX)]; diff --git a/src/playerInfo.cpp b/src/playerInfo.cpp index ac57decafe..2e566e0d85 100644 --- a/src/playerInfo.cpp +++ b/src/playerInfo.cpp @@ -22,6 +22,7 @@ #include "screens/extra/powerManagement.h" #include "screens/extra/databaseScreen.h" #include "screens/extra/commsScreen.h" +#include "screens/extra/scannerScreen.h" #include "screens/extra/shipLogScreen.h" #include "screenComponents/mainScreenControls.h" @@ -1099,6 +1100,10 @@ void PlayerInfo::spawnUI(int monitor_index, RenderLayer* render_layer) screen->addStationTab(new DamageControlScreen(container), CrewPosition::damageControl, getCrewPositionName(CrewPosition::damageControl), getCrewPositionIcon(CrewPosition::damageControl)); if (cps.has(CrewPosition::powerManagement)) screen->addStationTab(new PowerManagementScreen(container), CrewPosition::powerManagement, getCrewPositionName(CrewPosition::powerManagement), getCrewPositionIcon(CrewPosition::powerManagement)); + if (cps.has(CrewPosition::altScience)) + screen->addStationTab(new ScienceScreen(container, false), CrewPosition::altScience, getCrewPositionName(CrewPosition::altScience), getCrewPositionIcon(CrewPosition::altScience)); + if (cps.has(CrewPosition::scanOnly)) + screen->addStationTab(new ScannerScreen(container), CrewPosition::scanOnly, getCrewPositionName(CrewPosition::scanOnly), getCrewPositionIcon(CrewPosition::scanOnly)); if (cps.has(CrewPosition::databaseView)) screen->addStationTab(new DatabaseScreen(container), CrewPosition::databaseView, getCrewPositionName(CrewPosition::databaseView), getCrewPositionIcon(CrewPosition::databaseView)); if (cps.has(CrewPosition::altRelay)) @@ -1149,6 +1154,8 @@ string getCrewPositionName(CrewPosition position) case CrewPosition::singlePilot: return tr("station","Single Pilot"); case CrewPosition::damageControl: return tr("station","Damage Control"); case CrewPosition::powerManagement: return tr("station","Power Management"); + case CrewPosition::altScience: return tr("station","Long-range Radar"); + case CrewPosition::scanOnly: return tr("station","Scanner"); case CrewPosition::databaseView: return tr("station","Database"); case CrewPosition::altRelay: return tr("station","Strategic Map"); case CrewPosition::commsOnly: return tr("station","Comms"); @@ -1172,6 +1179,8 @@ string getCrewPositionIcon(CrewPosition position) case CrewPosition::singlePilot: return ""; case CrewPosition::damageControl: return ""; case CrewPosition::powerManagement: return ""; + case CrewPosition::altScience: return ""; + case CrewPosition::scanOnly: return ""; case CrewPosition::databaseView: return ""; case CrewPosition::altRelay: return ""; case CrewPosition::commsOnly: return ""; diff --git a/src/screenComponents/frequencyCurve.cpp b/src/screenComponents/frequencyCurve.cpp index 0dc67daf4f..d9e1cb2ec9 100644 --- a/src/screenComponents/frequencyCurve.cpp +++ b/src/screenComponents/frequencyCurve.cpp @@ -70,9 +70,9 @@ void GuiFrequencyCurve::onDraw(sp::RenderTarget& renderer) } // end if enemy_has_equipment else { if (frequency_is_beam) - renderer.drawText(rect, tr("scienceFrequencyGraph", "No enemy beams"), sp::Alignment::Center, 35); + renderer.drawText(rect, tr("scienceFrequencyGraph", "No beams"), sp::Alignment::Center, 35); else - renderer.drawText(rect, tr("scienceFrequencyGraph", "No enemy shields"), sp::Alignment::Center, 35); + renderer.drawText(rect, tr("scienceFrequencyGraph", "No shields"), sp::Alignment::Center, 35); } }else{ renderer.drawText(rect, tr("scienceFrequencyGraph", "No data"), sp::Alignment::Center, 35); diff --git a/src/screenComponents/scanTargetButton.cpp b/src/screenComponents/scanTargetButton.cpp index d2ea8be91d..09e58c3098 100644 --- a/src/screenComponents/scanTargetButton.cpp +++ b/src/screenComponents/scanTargetButton.cpp @@ -1,4 +1,5 @@ #include "scanTargetButton.h" +#include "gameGlobalInfo.h" #include "playerInfo.h" #include "targetsContainer.h" #include "gui/gui2_button.h" @@ -8,8 +9,8 @@ #include "i18n.h" -GuiScanTargetButton::GuiScanTargetButton(GuiContainer* owner, string id, TargetsContainer* targets) -: GuiElement(owner, id), targets(targets) +GuiScanTargetButton::GuiScanTargetButton(GuiContainer* owner, string id, TargetsContainer* targets, bool allow_scanning) +: GuiElement(owner, id), targets(targets), allow_scanning(allow_scanning) { button = new GuiButton(this, id + "_BUTTON", tr("scienceButton", "Scan"), [this]() { if (my_spaceship && this->targets && this->targets->get()) @@ -28,28 +29,47 @@ void GuiScanTargetButton::onUpdate() void GuiScanTargetButton::onDraw(sp::RenderTarget& target) { auto ss = my_spaceship.getComponent(); - if (!ss) - return; + if (!ss) return; if (ss->delay > 0.0f) { - progress->show(); - progress->setRange(0, ss->max_scanning_delay); - progress->setValue(ss->delay); - button->hide(); + if (allow_scanning && gameGlobalInfo->scanning_complexity == EScanningComplexity::SC_None) + { + progress + ->setText(tr("scienceButton", "Scanning...")) + ->setRange(0.0f, ss->max_scanning_delay) + ->setValue(ss->delay) + ->show(); + button->hide(); + } + else + { + progress->hide(); + button + ->setText(tr("scienceButton", "Scan initiated...")) + ->disable(); + } } else { + button->show(); + progress->hide(); + sp::ecs::Entity obj; - if (targets) - obj = targets->get(); + if (targets) obj = targets->get(); - button->show(); auto scanstate = obj.getComponent(); if (scanstate && scanstate->getStateFor(my_spaceship) != ScanState::State::FullScan) - button->enable(); + { + button + ->setText(allow_scanning ? tr("sciencebutton", "Scan"): tr("sciencebutton", "Link to scanner")) + ->enable(); + } else - button->disable(); - progress->hide(); + { + button + ->setText(tr("sciencebutton", "No scanner target")) + ->disable(); + } } } diff --git a/src/screenComponents/scanTargetButton.h b/src/screenComponents/scanTargetButton.h index 9dec4d2cd9..8d5d4e7e38 100644 --- a/src/screenComponents/scanTargetButton.h +++ b/src/screenComponents/scanTargetButton.h @@ -1,5 +1,4 @@ -#ifndef SCAN_TARGET_BUTTON_H -#define SCAN_TARGET_BUTTON_H +#pragma once #include "gui/gui2_element.h" @@ -13,11 +12,10 @@ class GuiScanTargetButton : public GuiElement TargetsContainer* targets; GuiButton* button; GuiProgressbar* progress; + bool allow_scanning = true; public: - GuiScanTargetButton(GuiContainer* owner, string id, TargetsContainer* targets); + GuiScanTargetButton(GuiContainer* owner, string id, TargetsContainer* targets, bool allow_scanning=true); virtual void onUpdate() override; virtual void onDraw(sp::RenderTarget& target) override; }; - -#endif//SCAN_TARGET_BUTTON_H diff --git a/src/screenComponents/scanningDialog.cpp b/src/screenComponents/scanningDialog.cpp index ad10f47dc4..04fe48ad1f 100644 --- a/src/screenComponents/scanningDialog.cpp +++ b/src/screenComponents/scanningDialog.cpp @@ -12,10 +12,6 @@ GuiScanningDialog::GuiScanningDialog(GuiContainer* owner, string id) : GuiElement(owner, id) { - locked = false; - lock_start_time = 0; - scan_depth = 0; - setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); box = new GuiPanel(this, id + "_BOX"); @@ -247,3 +243,8 @@ std::pair GuiScanningDialog::getScanComplexityDepth() } return {complexity, depth}; } + +bool GuiScanningDialog::isBoxVisible() +{ + return isVisible() && box->isVisible(); +} diff --git a/src/screenComponents/scanningDialog.h b/src/screenComponents/scanningDialog.h index 7db5d40b70..b244097fd4 100644 --- a/src/screenComponents/scanningDialog.h +++ b/src/screenComponents/scanningDialog.h @@ -1,5 +1,4 @@ -#ifndef SCANNING_DIALOG_H -#define SCANNING_DIALOG_H +#pragma once #include "gui/gui2_element.h" #include "signalQualityIndicator.h" @@ -37,6 +36,5 @@ class GuiScanningDialog : public GuiElement void setupParameters(); void updateSignal(); + bool isBoxVisible(); }; - -#endif//SCANNING_DIALOG_H diff --git a/src/screens/crew4/operationsScreen.cpp b/src/screens/crew4/operationsScreen.cpp index d9fd39da1b..4ecae83b50 100644 --- a/src/screens/crew4/operationsScreen.cpp +++ b/src/screens/crew4/operationsScreen.cpp @@ -20,7 +20,7 @@ OperationScreen::OperationScreen(GuiContainer* owner) : GuiOverlay(owner, "", colorConfig.background) { - science = new ScienceScreen(this, CrewPosition::operationsOfficer); + science = new ScienceScreen(this, true, CrewPosition::operationsOfficer); science->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax)->setMargins(0, 0, 0, 50); science->science_radar->setCallbacks( [this](sp::io::Pointer::Button button, glm::vec2 position) { // Down diff --git a/src/screens/crew6/scienceScreen.cpp b/src/screens/crew6/scienceScreen.cpp index 94d628c5da..55068c458a 100644 --- a/src/screens/crew6/scienceScreen.cpp +++ b/src/screens/crew6/scienceScreen.cpp @@ -33,8 +33,8 @@ #include "gui/gui2_slider.h" #include "gui/gui2_image.h" -ScienceScreen::ScienceScreen(GuiContainer* owner, CrewPosition crew_position) -: GuiOverlay(owner, "SCIENCE_SCREEN", colorConfig.background) +ScienceScreen::ScienceScreen(GuiContainer* owner, bool allow_scanning, CrewPosition crew_position) +: GuiOverlay(owner, "SCIENCE_SCREEN", colorConfig.background), allow_scanning(allow_scanning) { auto lrr = my_spaceship.getComponent(); targets.setAllowWaypointSelection(); @@ -101,8 +101,8 @@ ScienceScreen::ScienceScreen(GuiContainer* owner, CrewPosition crew_position) custom_function_sidebar = new GuiCustomShipFunctions(radar_view, crew_position, ""); custom_function_sidebar->setPosition(-15, 210, sp::Alignment::TopRight)->setSize(250, GuiElement::GuiSizeMax)->hide(); - // Scan button. - scan_button = new GuiScanTargetButton(info_sidebar, "SCAN_BUTTON", &targets); + // Scan button (link to scanner on altScience). + scan_button = new GuiScanTargetButton(info_sidebar, "SCAN_BUTTON", &targets, allow_scanning); scan_button->setSize(GuiElement::GuiSizeMax, 50)->setVisible(my_spaceship.hasComponent()); // Simple scan data. @@ -118,7 +118,9 @@ ScienceScreen::ScienceScreen(GuiContainer* owner, CrewPosition crew_position) info_faction->setSize(GuiElement::GuiSizeMax, 30); info_type = new GuiKeyValueDisplay(info_sidebar, "SCIENCE_TYPE", 0.4, tr("science", "Type"), ""); info_type->setSize(GuiElement::GuiSizeMax, 30); - info_type_button = new GuiButton(info_type, "SCIENCE_TYPE_BUTTON", tr("scienceButton", "DB"), [this]() { + info_type_button = new GuiButton(info_type, "SCIENCE_TYPE_BUTTON", tr("scienceButton", "DB"), [this, allow_scanning]() { + if (!allow_scanning) return; + auto ship = targets.get(); if (auto tn = ship.getComponent()) { @@ -131,7 +133,7 @@ ScienceScreen::ScienceScreen(GuiContainer* owner, CrewPosition crew_position) } } }); - info_type_button->setTextSize(20)->setPosition(0, 1, sp::Alignment::TopLeft)->setSize(50, 28); + info_type_button->setTextSize(20)->setPosition(0, 1, sp::Alignment::TopLeft)->setSize(50, 28)->setVisible(allow_scanning); info_shields = new GuiKeyValueDisplay(info_sidebar, "SCIENCE_SHIELDS", 0.4, tr("science", "Shields"), ""); info_shields->setSize(GuiElement::GuiSizeMax, 30); info_hull = new GuiKeyValueDisplay(info_sidebar, "SCIENCE_HULL", 0.4, tr("science", "Hull"), ""); @@ -187,25 +189,6 @@ ScienceScreen::ScienceScreen(GuiContainer* owner, CrewPosition crew_position) database_view = new DatabaseViewComponent(this); database_view->hide()->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); - // Probe view button - probe_view_button = new GuiToggleButton(radar_view, "PROBE_VIEW", tr("scienceButton", "Probe View"), [this](bool value){ - auto rl = my_spaceship.getComponent(); - if (value && rl && rl->linked_entity) - { - auto transform = rl->linked_entity.getComponent(); - if (transform) { - science_radar->hide(); - probe_radar->show(); - probe_radar->setViewPosition(transform->getPosition())->show(); - } - }else{ - probe_view_button->setValue(false); - science_radar->show(); - probe_radar->hide(); - } - }); - probe_view_button->setPosition(20, -120, sp::Alignment::BottomLeft)->setSize(200, 50)->disable(); - // Draw the zoom slider. zoom_slider = new GuiSlider(radar_view, "", lrr ? lrr->long_range : 30000.0f, lrr ? lrr->short_range : 5000.0f, lrr ? lrr->long_range : 30000.0f, [this](float value) { @@ -217,16 +200,54 @@ ScienceScreen::ScienceScreen(GuiContainer* owner, CrewPosition crew_position) zoom_label = new GuiLabel(zoom_slider, "", "Zoom: 1.0x", 30); zoom_label->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); - // Radar/database view toggle. - view_mode_selection = new GuiListbox(this, "VIEW_SELECTION", [this](int index, string value) { - radar_view->setVisible(index == 0); - background_gradient->setVisible(index == 0); - database_view->setVisible(index == 1); - }); - view_mode_selection->setOptions({tr("scienceButton", "Radar"), tr("scienceButton", "Database")})->setSelectionIndex(0)->setPosition(20, -20, sp::Alignment::BottomLeft)->setSize(200, 100); + // View controls. + GuiElement* view_controls = new GuiElement(radar_view, "VIEW_CONTROLS"); + view_controls + ->setPosition(20.0f, -20.0f, sp::Alignment::BottomLeft) + ->setSize(200.0f, 150.0f) + ->setAttribute("layout", "verticalbottom"); - // Scanning dialog. - new GuiScanningDialog(this, "SCANNING_DIALOG"); + // Probe view button. + probe_view_button = new GuiToggleButton(view_controls, "PROBE_VIEW", tr("scienceButton", "Probe View"), + [this](bool value) + { + auto rl = my_spaceship.getComponent(); + if (value && rl && rl->linked_entity) + { + if (auto transform = rl->linked_entity.getComponent()) + { + science_radar->hide(); + probe_radar->show(); + probe_radar->setViewPosition(transform->getPosition())->show(); + } + } + else + { + probe_view_button->setValue(false); + science_radar->show(); + probe_radar->hide(); + } + } + ); + probe_view_button->setSize(200.0f, 50.0f)->disable(); + + // Radar/database view toggle, create only on Science/Ops (allow_scanning). + view_mode_selection = new GuiListbox(view_controls, "VIEW_SELECTION", + [this](int index, string value) + { + radar_view->setVisible(index == 0); + background_gradient->setVisible(index == 0); + database_view->setVisible(index == 1); + } + ); + view_mode_selection + ->setOptions({tr("scienceButton", "Radar"), tr("scienceButton", "Database")}) + ->setSelectionIndex(0) + ->setSize(200.0f, 100.0f) + ->setVisible(allow_scanning); + + // Scanning dialog, create only on Science/Ops. + if (allow_scanning) new GuiScanningDialog(this, "SCANNING_DIALOG"); } void ScienceScreen::onDraw(sp::RenderTarget& renderer) @@ -348,7 +369,7 @@ void ScienceScreen::onDraw(sp::RenderTarget& renderer) if (std::abs(rel_velocity) < 0.01f) rel_velocity = 0.0f; - info_relspeed->setValue(string(rel_velocity / 1000.0f * 60.0f, 1) + DISTANCE_UNIT_1K + "/min"); + info_relspeed->setValue(tr("science", "{relative_speed}/min").format({{"relative_speed", string(rel_velocity / 1000.0f * 60.0f, 1) + DISTANCE_UNIT_1K}})); } } if (auto cs = target.getComponent()) @@ -506,7 +527,7 @@ void ScienceScreen::onDraw(sp::RenderTarget& renderer) info_distance->setValue(string(distance / 1000.0f, 1) + DISTANCE_UNIT_1K); info_heading->setValue(string(int(heading))); - info_relspeed->setValue(string(rel_velocity / 1000.0f * 60.0f, 1) + DISTANCE_UNIT_1K + "/min"); + info_relspeed->setValue(tr("science", "{relative_speed}/min").format({{"relative_speed", string(rel_velocity / 1000.0f * 60.0f, 1) + DISTANCE_UNIT_1K}})); } } } @@ -515,12 +536,12 @@ void ScienceScreen::onDraw(sp::RenderTarget& renderer) void ScienceScreen::onUpdate() { - if (my_spaceship) + if (my_spaceship + && my_spaceship.hasComponent() + && (!allow_scanning || my_spaceship.getComponent()->delay == 0.0f)) { // Initiate a scan on scannable objects. - if (keys.science_scan_object.getDown() && - my_spaceship.hasComponent() && - my_spaceship.getComponent()->delay == 0.0f) + if (keys.science_scan_object.getDown()) { auto obj = targets.get(); @@ -535,11 +556,10 @@ void ScienceScreen::onUpdate() } // Cycle selection through scannable objects. - if (keys.science_select_next_scannable.getDown() && - my_spaceship.hasComponent() && - my_spaceship.getComponent()->delay == 0.0f) + if (keys.science_select_next_scannable.getDown()) { - if (auto transform = my_spaceship.getComponent()) { + if (auto transform = my_spaceship.getComponent()) + { auto lrr = my_spaceship.getComponent(); targets.setNext(transform->getPosition(), lrr ? lrr->long_range : 25000.0f, TargetsContainer::ESelectionType::Scannable); } diff --git a/src/screens/crew6/scienceScreen.h b/src/screens/crew6/scienceScreen.h index 47bc855006..3be29dbe7a 100644 --- a/src/screens/crew6/scienceScreen.h +++ b/src/screens/crew6/scienceScreen.h @@ -59,11 +59,12 @@ class ScienceScreen : public GuiOverlay sp::ecs::Entity observation_point; GuiListbox* view_mode_selection; public: - ScienceScreen(GuiContainer* owner, CrewPosition crew_position=CrewPosition::scienceOfficer); + ScienceScreen(GuiContainer* owner, bool allow_scanning=true, CrewPosition crew_position=CrewPosition::scienceOfficer); virtual void onDraw(sp::RenderTarget& target) override; virtual void onUpdate() override; private: + bool allow_scanning; //used to judge when to update the UI label and zoom float previous_long_range_radar=0; float previous_short_range_radar=0; diff --git a/src/screens/extra/scannerScreen.cpp b/src/screens/extra/scannerScreen.cpp new file mode 100644 index 0000000000..a5532a7b40 --- /dev/null +++ b/src/screens/extra/scannerScreen.cpp @@ -0,0 +1,299 @@ +#include "scannerScreen.h" +#include +#include "playerInfo.h" +#include "gameGlobalInfo.h" +#include "featureDefs.h" + +#include "components/collision.h" +#include "components/name.h" +#include "components/hull.h" +#include "components/radar.h" +#include "components/shields.h" +#include "components/beamweapon.h" +#include "components/scanning.h" + +#include "systems/radarblock.h" + +#include "screenComponents/frequencyCurve.h" +#include "screenComponents/scanningDialog.h" +#include "gui/gui2_button.h" +#include "gui/gui2_keyvaluedisplay.h" +#include "gui/gui2_label.h" +#include "gui/gui2_progressbar.h" +#include "gui/gui2_scrolltext.h" +#include "gui/gui2_selector.h" +#include "screenComponents/rotatingModelView.h" + +ScannerScreen::ScannerScreen(GuiContainer* owner) +: GuiOverlay(owner, "SCANNER_SCREEN", colorConfig.background) +{ + (new GuiOverlay(this, "BACKGROUND_CROSSES", glm::u8vec4{255,255,255,255})) + ->setTextureTiled("gui/background/crosses.png"); + + label = new GuiLabel(this, "SCANNER_LABEL", tr("scienceButton", "No previous scan contact"), 40.0f); + label + ->setSize(GuiElement::GuiSizeMax, 40.0f) + ->setPosition(0.0f, -250.0f, sp::Alignment::Center); + dialog = new GuiScanningDialog(this, "SCANNER"); + progress = new GuiProgressbar(this, "SCANNER_PROGRESS", 0.0f, 6.0f, 0.0f); + progress + ->setPosition(0.0f, 0.0f, sp::Alignment::Center) + ->setSize(500.0f, 50.0f) + ->hide(); + + // Scanner target info. + right_sidebar = new GuiElement(this, "SCANNER_RIGHT_SIDEBAR"); + right_sidebar + ->setPosition(-20.0f, 170.0f, sp::Alignment::TopRight) + ->setSize(300.0f, GuiElement::GuiSizeMax) + ->setMargins(0, 0, 0, 75) + ->setAttribute("layout", "vertical"); + + left_sidebar = new GuiElement(this, "SCANNER_LEFT_SIDEBAR"); + left_sidebar + ->setPosition(20.0f, 170.0f, sp::Alignment::TopLeft) + ->setSize(300.0f, GuiElement::GuiSizeMax) + ->setMargins(0, 0, 0, 75) + ->setAttribute("layout", "vertical"); + + // Simple scan data. + info_target = new GuiLabel(right_sidebar, "SCANNER_TARGET_LABEL", tr("scanner", "No previous target"), 30.0f); + info_target + ->addBackground() + ->setSize(GuiElement::GuiSizeMax, 50.0f); + info_callsign = new GuiKeyValueDisplay(right_sidebar, "SCIENCE_CALLSIGN", 0.4, tr("science", "Callsign"), ""); + info_callsign->setSize(GuiElement::GuiSizeMax, 30); + info_distance = new GuiKeyValueDisplay(right_sidebar, "SCIENCE_DISTANCE", 0.4, tr("science", "Distance"), ""); + info_distance->setSize(GuiElement::GuiSizeMax, 30); + info_heading = new GuiKeyValueDisplay(right_sidebar, "SCIENCE_HEADING", 0.4, tr("science", "Bearing"), ""); + info_heading->setSize(GuiElement::GuiSizeMax, 30); + info_relspeed = new GuiKeyValueDisplay(right_sidebar, "SCIENCE_REL_SPEED", 0.4, tr("science", "Rel. Speed"), ""); + info_relspeed->setSize(GuiElement::GuiSizeMax, 30); + info_faction = new GuiKeyValueDisplay(right_sidebar, "SCIENCE_FACTION", 0.4, tr("science", "Faction"), ""); + info_faction->setSize(GuiElement::GuiSizeMax, 30); + info_type = new GuiKeyValueDisplay(right_sidebar, "SCIENCE_TYPE", 0.4, tr("science", "Type"), ""); + info_type->setSize(GuiElement::GuiSizeMax, 30); + info_shields = new GuiKeyValueDisplay(right_sidebar, "SCIENCE_SHIELDS", 0.4, tr("science", "Shields"), ""); + info_shields->setSize(GuiElement::GuiSizeMax, 30); + info_hull = new GuiKeyValueDisplay(right_sidebar, "SCIENCE_HULL", 0.4, tr("science", "Hull"), ""); + info_hull->setSize(GuiElement::GuiSizeMax, 30); + + // Full scan data + // List each system's status. + info_systems = new GuiLabel(left_sidebar, "SCANNER_SYSTEMS", tr("Scanned systems"), 30.0f); + info_systems + ->addBackground() + ->setSize(GuiElement::GuiSizeMax, 50.0f); + for(int n = 0; n < ShipSystem::COUNT; n++) + { + info_system[n] = new GuiKeyValueDisplay(left_sidebar, "SCIENCE_SYSTEM_" + string(n), 0.75, getLocaleSystemName(ShipSystem::Type(n)), "-"); + info_system[n]->setSize(GuiElement::GuiSizeMax, 30); + info_system[n]->hide(); + } + + // Prep and hide the frequency graphs. + info_shield_frequency = new GuiFrequencyCurve(left_sidebar, "SCIENCE_SHIELD_FREQUENCY", false, true); + info_shield_frequency->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); + info_beam_frequency = new GuiFrequencyCurve(left_sidebar, "SCIENCE_BEAM_FREQUENCY", true, false); + info_beam_frequency->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); + + // Show shield and beam frequencies only if enabled by the server. + if (!gameGlobalInfo->use_beam_shield_frequencies) + { + info_shield_frequency->hide(); + info_beam_frequency->hide(); + } + + // Prep and hide the description text area. + info_description = new GuiScrollFormattedText(right_sidebar, "SCIENCE_DESC", ""); + info_description->setTextSize(28)->setMargins(20, 0, 0, 0)->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax)->hide(); +} + +void ScannerScreen::onDraw(sp::RenderTarget& target) +{ + GuiOverlay::onDraw(target); + auto ss = my_spaceship.getComponent(); + if (!ss) return; + if (last_target != ss->target && ss->target != sp::ecs::Entity()) last_target = ss->target; + + // If the cached target entity exists, is scanned, is within long-range radar range and is radar visible to the player ship, show its current information up to its scan level, its database entry?, and its mesh. + // If the cached target isn't radar visible to the player ship or out of range, stop updating information about it and indicate that it is out of sensor contact. + // If the cached target entity isn't scanned, don't show any information about it. + // If the cached target entity no longer exists, don't show any information about it. + // Don't update the cache until a scan of another target is in progress. + // When/if the science radar target is replicated, maybe update this to the current science target instead of a cached scan target. + if (ss->delay > 0.0f) + { + if (gameGlobalInfo->scanning_complexity == EScanningComplexity::SC_None) + { + progress + ->setText(tr("scienceButton", "Scanning...")) + ->setRange(0.0f, ss->max_scanning_delay) + ->setValue(ss->delay) + ->show(); + label->hide(); + dialog->hide(); + } + else + { + progress->hide(); + label + ->setText(tr("scienceButton", "Scanning...")) + ->show(); + dialog->show(); + } + } + else + { + progress->hide(); + label->show(); + if (last_target) + label->setText(tr("scanner", "Tracking most recent scan")); + else + label->setText(tr("scanner", "Most recent scan contact lost")); + dialog->show(); + } + + // Display and update information on cached last target if still visible to + // the player's radar. + auto lrr = my_spaceship.getComponent(); + bool in_range = false; + if (last_target && lrr) + { + info_target->setText(tr("scanner", "Most recent scan")); + + auto my_transform = my_spaceship.getComponent(); + auto target_transform = last_target.getComponent(); + + if (my_transform && target_transform) + { + auto position_diff = target_transform->getPosition() - my_transform->getPosition(); + float distance = glm::length(position_diff); + + if (distance <= lrr->long_range) + { + float heading = vec2ToAngle(position_diff) - 270.0f; + while (heading < 0) heading += 360.0f; + + info_distance->setValue(string(distance / 1000.0f, 1) + DISTANCE_UNIT_1K); + info_heading->setValue(string(static_cast(heading))); + + auto my_physics = my_spaceship.getComponent(); + auto target_physics = last_target.getComponent(); + + if (my_physics && target_physics) + { + float rel_velocity = dot(target_physics->getVelocity(), position_diff / distance) - dot(my_physics->getVelocity(), position_diff / distance); + + if (std::abs(rel_velocity) < 0.01f) rel_velocity = 0.0f; + info_relspeed->setValue(tr("science", "{relative_speed}/min").format({{"relative_speed", string(rel_velocity / 1000.0f * 60.0f, 1) + DISTANCE_UNIT_1K}})); + } + + // Not in range if radar blocked. + in_range = !(RadarBlockSystem::isRadarBlockedFrom(my_transform->getPosition(), last_target, lrr->short_range)); + } + } + + // Update data only if still in range. + if (in_range) + { + if (auto cs = last_target.getComponent()) + info_callsign->setValue(cs->callsign); + + auto scanstate_component = last_target.getComponent(); + auto scanstate = scanstate_component ? scanstate_component->getStateFor(my_spaceship) : ScanState::State::FullScan; + + auto sd = last_target.getComponent(); + string description = ""; + if (sd) + { + switch (scanstate) + { + case ScanState::State::NotScanned: description = sd->not_scanned; break; + case ScanState::State::FriendOrFoeIdentified: description = sd->friend_or_foe_identified; break; + case ScanState::State::SimpleScan: description = sd->simple_scan; break; + case ScanState::State::FullScan: description = sd->full_scan; break; + } + } + if (description.empty()) + info_description->hide(); + else + info_description->setText(description)->show(); + + // On a simple scan or deeper, show the faction, ship type, shields, + // hull integrity, and database reference button. + if (scanstate >= ScanState::State::SimpleScan) + { + auto faction = Faction::getInfo(last_target); + info_faction->setValue(faction.locale_name); + + if (auto tn = last_target.getComponent()) + info_type->setValue(tn->localized); + + if (auto shields = last_target.getComponent()) + { + string str = ""; + for (size_t n = 0; n < shields->entries.size(); n++) + { + if (n > 0) str += ":"; + str += string(int(shields->entries[n].level)); + } + info_shields->setValue(str); + } + + if (auto hull = last_target.getComponent()) + info_hull->setValue(int(ceil(hull->current))); + } + else + { + info_faction->setValue(""); + info_type->setValue(""); + info_shields->setValue(""); + info_hull->setValue(""); + } + + // On a full scan, show tactical and systems data (if any), and its + // description (if one is set). + if (scanstate >= ScanState::State::FullScan) + { + left_sidebar->show(); + info_shield_frequency->show(); + info_beam_frequency->show(); + + for (int n = 0; n < ShipSystem::COUNT; n++) + info_system[n]->show(); + + info_description->show(); + + // If beam and shield frequencies are enabled on the server, + // populate their graphs. + if (gameGlobalInfo->use_beam_shield_frequencies) + { + auto shieldsystem = last_target.getComponent(); + info_shield_frequency->setFrequency(shieldsystem ? shieldsystem->frequency : -1); + auto beamsystem = last_target.getComponent(); + info_beam_frequency->setFrequency(beamsystem ? beamsystem->frequency : -1); + + // Show on graph information that target has no shields instead of frequencies. + info_shield_frequency->setEnemyHasEquipment(shieldsystem); + + // Show on graph information that target has no beams instad of frequencies. + info_beam_frequency->setEnemyHasEquipment(beamsystem); + } + + // Show the status of each subsystem. + for (int n = 0; n < ShipSystem::COUNT; n++) + { + if (auto sys = ShipSystem::get(last_target, ShipSystem::Type(n))) + { + const float system_health = sys->health; + info_system[n]->setValue(string(int(system_health * 100.0f)) + "%")->setColor(glm::u8vec4(255, 127.5f * (system_health + 1), 127.5f * (system_health + 1), 255)); + } + } + } + else left_sidebar->hide(); + } + else label->setText(tr("scanner", "Most recent scan contact lost")); + } + else info_target->setText(tr("scanner", "No recent scan")); +} diff --git a/src/screens/extra/scannerScreen.h b/src/screens/extra/scannerScreen.h new file mode 100644 index 0000000000..e59bf44927 --- /dev/null +++ b/src/screens/extra/scannerScreen.h @@ -0,0 +1,46 @@ +#pragma once + +#include "gui/gui2_overlay.h" + +class GuiFrequencyCurve; +class GuiKeyValueDisplay; +class GuiLabel; +class GuiRotatingModelView; +class GuiProgressbar; +class GuiScanningDialog; +class GuiSelector; +class GuiScrollText; + +class ScannerScreen : public GuiOverlay +{ +private: + GuiLabel* label; + GuiRotatingModelView* mesh_viewer; + GuiProgressbar* progress; + GuiScanningDialog* dialog; + + GuiSelector* sidebar_selector; + GuiElement* right_sidebar; + GuiElement* left_sidebar; + GuiLabel* info_target; + GuiKeyValueDisplay* info_callsign; + GuiKeyValueDisplay* info_distance; + GuiKeyValueDisplay* info_heading; + GuiKeyValueDisplay* info_relspeed; + GuiKeyValueDisplay* info_faction; + GuiKeyValueDisplay* info_type; + GuiKeyValueDisplay* info_shields; + GuiKeyValueDisplay* info_hull; + GuiScrollText* info_description; + + GuiLabel* info_systems; + GuiKeyValueDisplay* info_system[ShipSystem::COUNT]; + GuiFrequencyCurve* info_shield_frequency; + GuiFrequencyCurve* info_beam_frequency; + + sp::ecs::Entity last_target; +public: + ScannerScreen(GuiContainer* owner); + + virtual void onDraw(sp::RenderTarget& renderer) override; +};