From 1c0cad5a68c1a8a1196afd6a30022a213c519bf7 Mon Sep 17 00:00:00 2001 From: Oznogon Date: Sun, 5 Apr 2026 16:03:07 -0700 Subject: [PATCH 1/7] Add options for waypoint routes, sets Add options to allow waypoints to be connected by lines to form routes, and for a player ship to manage up to 4 sets of waypoints. Waypoint sets provide both additional waypoints and means to differentiate waypoints. Each set has a theme-defined color and waypoint icon, defaulting to the EE default icon and the default blue-gray (set 1), red (2), green (3), and cyan blue (4). Waypoint routes connect all waypoints in a set with a line, in ascending ID order. If these options are enabled, this adds controls to Relay and Operations to select a waypoint set when adding or selecting waypoints, and to toggle a waypoint set's route visibility. Existing Lua functions for waypoint management can add, remove, and reposition waypoints in each set by passing the set ID number as an optional last argument. This also adds a Lua function to enable or disable route behavior (commandSetWaypointRoute(bool)). These options are NOT ENABLED by default. This PR should NOT CHANGE default behaviors for players or scenario writers. This PR adds toggles for these options to the "Extra settings" menu available from the ship selection screen, making them OPT-IN at the server level. --- resources/gui/default.theme.txt | 27 +++ scripts/api/entity/playerspaceship.lua | 98 +++++++---- src/components/player.cpp | 47 +++-- src/components/player.h | 14 +- src/gameGlobalInfo.cpp | 4 + src/gameGlobalInfo.h | 2 + src/gui/hotkeyConfig.cpp | 2 + src/gui/hotkeyConfig.h | 1 + src/menus/shipSelectionScreen.cpp | 29 +++- src/playerInfo.cpp | 45 +++-- src/playerInfo.h | 7 +- src/screenComponents/radarView.cpp | 150 ++++++++++++++-- src/screenComponents/radarView.h | 13 ++ src/screenComponents/targetsContainer.cpp | 19 +- src/screenComponents/targetsContainer.h | 4 +- src/screens/crew4/operationsScreen.cpp | 52 +++++- src/screens/crew4/operationsScreen.h | 8 + src/screens/crew6/relayScreen.cpp | 201 ++++++++++++++-------- src/screens/crew6/relayScreen.h | 8 + src/script.cpp | 35 ++-- src/script/components.cpp | 1 + 21 files changed, 574 insertions(+), 193 deletions(-) diff --git a/resources/gui/default.theme.txt b/resources/gui/default.theme.txt index e1823bc164..74f7690227 100644 --- a/resources/gui/default.theme.txt +++ b/resources/gui/default.theme.txt @@ -58,6 +58,33 @@ color: #000000D0 } } + [ship_waypoint_set2] { + image: waypoint.png + [ship_waypoint_set2.background] { + color: #FF4040D0 + } + [ship_waypoint_set2.text] { + color: #000000D0 + } + } + [ship_waypoint_set3] { + image: waypoint.png + [ship_waypoint_set3.background] { + color: #40FF40D0 + } + [ship_waypoint_set3.text] { + color: #000000D0 + } + } + [ship_waypoint_set4] { + image: waypoint.png + [ship_waypoint_set4.background] { + color: #40FFFFD0 + } + [ship_waypoint_set4.text] { + color: #000000D0 + } + } [radar] { font: gui/fonts/BigShouldersDisplay-ExtraBold.ttf diff --git a/scripts/api/entity/playerspaceship.lua b/scripts/api/entity/playerspaceship.lua index bc3a2c910f..da613131fd 100644 --- a/scripts/api/entity/playerspaceship.lua +++ b/scripts/api/entity/playerspaceship.lua @@ -34,33 +34,54 @@ function PlayerSpaceship() end --- Returns the coordinates of a waypoint with the given index that's been set by this player ship. ---- Waypoints are 1-indexed. ---- Example: ---- x, y = ship:getWaypoint(1) -function Entity:getWaypoint(index) - if self.components.waypoints and index > 0 and index <= #self.components.waypoints then - local wp = self.components.waypoints[index] - return wp.x, wp.y +--- Waypoints are 1-indexed. Optional set_id (1-4, default 1) selects the waypoint set. +--- Example: +--- x, y = ship:getWaypoint(1) -- wp 1 from set 1 +--- x, y = ship:getWaypoint(1, 2) -- wp 1 from set 2 +function Entity:getWaypoint(index, set_id) + set_id = set_id or 1 + if self.components.waypoints then + local n = 0 + for _, wp in ipairs(self.components.waypoints) do + if wp.set_id == set_id then + n = n + 1 + if n == index then return wp.x, wp.y end + end + end end return 0, 0 end --- Returns the numeric label for the given waypoint index. ---- Waypoints are 1-indexed. ---- Example: ---- id = ship:getWaypointID(1) -function Entity:getWaypointID(index) - if self.components.waypoints and index > 0 and index <= #self.components.waypoints then - local wp = self.components.waypoints[index] - return wp.id +--- Waypoints are 1-indexed. Optional set_id (1-4, default 1) selects the waypoint set. +--- Example: +--- id = ship:getWaypointID(1) -- wp 1 from set 1 +--- id = ship:getWaypointID(1, 2) -- wp 1 from set 2 +function Entity:getWaypointID(index, set_id) + set_id = set_id or 1 + if self.components.waypoints then + local n = 0 + for _, wp in ipairs(self.components.waypoints) do + if wp.set_id == set_id then + n = n + 1 + if n == index then return wp.id end + end + end end return 0 end --- Returns the total number of active waypoints owned by this player ship. +--- Optional set_id (1-4, default 1) selects the waypoint set. --- Example: ---- ship:getWaypointCount() -function Entity:getWaypointCount() - if self.components.waypoints then return #self.components.waypoints end - return 0 +--- ship:getWaypointCount() -- count in set 1 +--- ship:getWaypointCount(2) -- count in set 2 +function Entity:getWaypointCount(set_id) + set_id = set_id or 1 + if not self.components.waypoints then return 0 end + local count = 0 + for _, wp in ipairs(self.components.waypoints) do + if wp.set_id == set_id then count = count + 1 end + end + return count end --- Returns this player ship's EAlertLevel. --- Returns "Normal", "YELLOW ALERT", "RED ALERT", which differ from the valid values for commandSetAlertLevel(). @@ -610,27 +631,40 @@ function Entity:commandSetShieldFrequency(index) return self end --- Commands this player ship to add a waypoint at the given coordinates. ---- This respects the 9-waypoint limit and won't add more waypoints if 9 already exist. +--- This respects the 9-waypoint limit per set and won't add more waypoints if 9 already exist. +--- Optional set_id (1-4, default 1) selects the waypoint set. +--- Example: +--- ship:commandAddWaypoint(1000, 2000) -- add wp to set 1 +--- ship:commandAddWaypoint(1000, 2000, 2) -- add wp to set 2 +function Entity:commandAddWaypoint(x, y, set_id) + commandAddWaypoint(self, x, y, set_id or 1) + return self +end +--- Commands this player ship to remove the waypoint with the given ID. +--- Optional set_id (1-4, default 1) selects the waypoint set. --- Example: ---- ship:commandAddWaypoint(1000, 2000) -function Entity:commandAddWaypoint(x, y) - commandAddWaypoint(self, x, y) +--- ship:commandRemoveWaypoint(1) -- remove wp 1 from set 1 +--- ship:commandRemoveWaypoint(1, 2) -- remove wp 1 from set 2 +function Entity:commandRemoveWaypoint(index, set_id) + commandRemoveWaypoint(self, index, set_id or 1) return self end ---- Commands this player ship to remove the waypoint with the given index. ---- This uses a 0-index, while waypoints are numbered on player screens with a 1-index. +--- Commands this player ship to move the waypoint with the given ID to the given coordinates. +--- Optional set_id (1-4, default 1) selects the waypoint set. --- Example: ---- ship:commandRemoveWaypoint(0) -- removes waypoint 1 -function Entity:commandRemoveWaypoint(index) - commandRemoveWaypoint(self, index) +--- ship:commandMoveWaypoint(1, -1000, -2000) -- move wp 1 from set 1 +--- ship:commandMoveWaypoint(1, -1000, -2000, 2) -- move wp 1 from set 2 +function Entity:commandMoveWaypoint(index, x, y, set_id) + commandMoveWaypoint(self, index, x, y, set_id or 1) return self end ---- Commands this player ship to move the waypoint with the given index to the given coordinates. ---- This uses a 0-index, while waypoints are numbered on player screens with a 1-index. +--- Commands this player ship to set or clear the route flag for a waypoint set. +--- Optional set_id (1-4, default 1) selects the waypoint set. --- Example: ---- ship:commandMoveWaypoint(0,-1000,-2000) -- moves waypoint 1 to -1000,-2000 -function Entity:commandMoveWaypoint(index, x, y) - commandMoveWaypoint(self, index, x, y) +--- ship:commandSetWaypointRoute(true) -- make set 1 a route +--- ship:commandSetWaypointRoute(false, 2) -- remove routes from set 2 +function Entity:commandSetWaypointRoute(is_route, set_id) + commandSetWaypointRoute(self, is_route, set_id or 1) return self end --- Commands this player ship to activate its self-destruct sequence. diff --git a/src/components/player.cpp b/src/components/player.cpp index 7bc6346eeb..6e5a1273bc 100644 --- a/src/components/player.cpp +++ b/src/components/player.cpp @@ -28,17 +28,22 @@ string alertLevelToLocaleString(AlertLevel level) } } -int Waypoints::addNew(glm::vec2 position) +int Waypoints::addNew(glm::vec2 position, int set_id) { - if (waypoints.size() == 9) + if (set_id < 1 || set_id > MAX_SETS) return -1; + int count = 0; + for (auto& p : waypoints) + if (p.set_id == set_id) count++; + if (count >= 9) return -1; - for(int id=1; id<10; id++) { + for (int id = 1; id < 10; id++) + { bool used = false; - for(auto& p : waypoints) - if (p.id == id) - used = true; - if (!used) { - waypoints.push_back({id, position}); + for (auto& p : waypoints) + if (p.id == id && p.set_id == set_id) used = true; + if (!used) + { + waypoints.push_back({id, set_id, position}); dirty = true; return id; } @@ -46,10 +51,12 @@ int Waypoints::addNew(glm::vec2 position) return -1; } -void Waypoints::move(int id, glm::vec2 position) +void Waypoints::move(int id, glm::vec2 position, int set_id) { - for(auto& p : waypoints) { - if (p.id == id) { + for (auto& p : waypoints) + { + if (p.id == id && p.set_id == set_id) + { p.position = position; dirty = true; return; @@ -57,16 +64,22 @@ void Waypoints::move(int id, glm::vec2 position) } } -void Waypoints::remove(int id) +void Waypoints::remove(int id, int set_id) { - waypoints.erase(std::remove_if(waypoints.begin(), waypoints.end(), [id](auto& p) { return p.id == id; }), waypoints.end()); + waypoints.erase(std::remove_if(waypoints.begin(), waypoints.end(), [id, set_id](auto& p) { return p.id == id && p.set_id == set_id; }), waypoints.end()); dirty = true; } -std::optional Waypoints::get(int id) +std::optional Waypoints::get(int id, int set_id) { - for(auto& p : waypoints) - if (p.id == id) - return p.position; + for (auto& p : waypoints) + if (p.id == id && p.set_id == set_id) return p.position; return {}; +} + +void Waypoints::setRoute(bool value, int set_id) +{ + if (set_id < 1 || set_id > MAX_SETS) return; + is_route[set_id - 1] = value; + dirty = true; } \ No newline at end of file diff --git a/src/components/player.h b/src/components/player.h index a389272fb0..2540e2c949 100644 --- a/src/components/player.h +++ b/src/components/player.h @@ -1,5 +1,6 @@ #pragma once +#include #include "stringImproved.h" #include "crewPosition.h" @@ -50,17 +51,22 @@ class PlayerControl class Waypoints { public: + static constexpr int MAX_SETS = 4; bool dirty = true; + // IDs are 1-9 within sets 1-4 struct Point { int id; + int set_id; glm::vec2 position; }; std::vector waypoints; + std::array is_route{}; - int addNew(glm::vec2 position); - void move(int id, glm::vec2 position); - void remove(int id); - std::optional get(int id); + int addNew(glm::vec2 position, int set_id = 1); + void move(int id, glm::vec2 position, int set_id = 1); + void remove(int id, int set_id = 1); + std::optional get(int id, int set_id = 1); + void setRoute(bool value, int set_id = 1); }; string alertLevelToString(AlertLevel level); diff --git a/src/gameGlobalInfo.cpp b/src/gameGlobalInfo.cpp index cdf517710f..d156a007ae 100644 --- a/src/gameGlobalInfo.cpp +++ b/src/gameGlobalInfo.cpp @@ -31,6 +31,8 @@ GameGlobalInfo::GameGlobalInfo() hacking_games = HG_All; use_beam_shield_frequencies = true; use_system_damage = true; + enable_multiple_waypoint_sets = false; + enable_waypoint_routes = false; allow_main_screen_tactical_radar = true; allow_main_screen_long_range_radar = true; allow_main_screen_strategic_map = true; @@ -48,6 +50,8 @@ GameGlobalInfo::GameGlobalInfo() registerMemberReplication(&victory_faction); registerMemberReplication(&use_beam_shield_frequencies); registerMemberReplication(&use_system_damage); + registerMemberReplication(&enable_multiple_waypoint_sets); + registerMemberReplication(&enable_waypoint_routes); registerMemberReplication(&allow_main_screen_tactical_radar); registerMemberReplication(&allow_main_screen_long_range_radar); registerMemberReplication(&allow_main_screen_strategic_map); diff --git a/src/gameGlobalInfo.h b/src/gameGlobalInfo.h index 62b02343f8..b5b46a0c09 100644 --- a/src/gameGlobalInfo.h +++ b/src/gameGlobalInfo.h @@ -52,6 +52,8 @@ class GameGlobalInfo : public MultiplayerObject, public Updatable EHackingGames hacking_games; bool use_beam_shield_frequencies; bool use_system_damage; + bool enable_multiple_waypoint_sets; + bool enable_waypoint_routes; bool allow_main_screen_tactical_radar; bool allow_main_screen_long_range_radar; bool allow_main_screen_strategic_map; diff --git a/src/gui/hotkeyConfig.cpp b/src/gui/hotkeyConfig.cpp index 253cfbf83f..4ef1e75501 100644 --- a/src/gui/hotkeyConfig.cpp +++ b/src/gui/hotkeyConfig.cpp @@ -318,6 +318,7 @@ Keys::Keys() : gm_delete("GM_DELETE", "Delete"), gm_clipboardcopy("GM_CLIPBOARD_COPY", "F5"), gm_show_callsigns("GM_SHOW_CALLSIGNS", "C"), + gm_show_waypoints("GM_SHOW_WAYPOINTS", "W"), // Spectator screen spectator_show_callsigns("SPECTATOR_SHOW_CALLSIGNS", "C") @@ -509,6 +510,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_waypoints.setLabel(tr("hotkey_menu", "GM screen"), tr("hotkey_GM", "Show waypoints (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 67d2172338..591df870a6 100644 --- a/src/gui/hotkeyConfig.h +++ b/src/gui/hotkeyConfig.h @@ -200,6 +200,7 @@ class Keys sp::io::Keybinding gm_delete; sp::io::Keybinding gm_clipboardcopy; sp::io::Keybinding gm_show_callsigns; + sp::io::Keybinding gm_show_waypoints; // Spectator screen binds sp::io::Keybinding spectator_show_callsigns; diff --git a/src/menus/shipSelectionScreen.cpp b/src/menus/shipSelectionScreen.cpp index a3325a8fc2..9345615eff 100644 --- a/src/menus/shipSelectionScreen.cpp +++ b/src/menus/shipSelectionScreen.cpp @@ -338,7 +338,10 @@ ShipSelectionScreen::ShipSelectionScreen() if (game_server) { auto extra_settings_panel = new GuiPanel(this, ""); - extra_settings_panel->setSize(600, 325)->setPosition(0, 0, sp::Alignment::Center)->hide(); + extra_settings_panel + ->setSize(600.0f, 375.0f) + ->setPosition(0.0f, 0.0f, sp::Alignment::Center) + ->hide(); auto extra_settings = new GuiElement(extra_settings_panel, ""); extra_settings->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax)->setMargins(25)->setAttribute("layout", "vertical"); // Science scan complexity selector. @@ -376,6 +379,30 @@ ShipSelectionScreen::ShipSelectionScreen() gameGlobalInfo->use_system_damage = value == 1; }))->setValue(gameGlobalInfo->use_system_damage)->setSize(275, GuiElement::GuiSizeMax)->setPosition(0, 0, sp::Alignment::CenterRight); + // Waypoint settings row. + row = new GuiElement(extra_settings, ""); + row + ->setSize(GuiElement::GuiSizeMax, 50.0f) + ->setAttribute("layout", "horizontal"); + + (new GuiToggleButton(row, "GAME_MULTI_WP_SETS_TOGGLE", tr("Multiple waypoint sets"), + [](bool value) + { + gameGlobalInfo->enable_multiple_waypoint_sets = value == 1; + } + ))->setValue(gameGlobalInfo->enable_multiple_waypoint_sets) + ->setSize(275.0f, GuiElement::GuiSizeMax) + ->setPosition(0.0f, 0.0f, sp::Alignment::CenterLeft); + + (new GuiToggleButton(row, "GAME_WP_ROUTES_TOGGLE", tr("Waypoint routes"), + [](bool value) + { + gameGlobalInfo->enable_waypoint_routes = value == 1; + } + ))->setValue(gameGlobalInfo->enable_waypoint_routes) + ->setSize(275.0f, GuiElement::GuiSizeMax) + ->setPosition(0.0f, 0.0f, sp::Alignment::CenterRight); + auto close_button = new GuiButton(extra_settings_panel, "", tr("Close"), [this, extra_settings_panel](){ extra_settings_panel->hide(); container->show(); diff --git a/src/playerInfo.cpp b/src/playerInfo.cpp index c615eb4a32..807e47ece9 100644 --- a/src/playerInfo.cpp +++ b/src/playerInfo.cpp @@ -104,6 +104,7 @@ static const uint16_t CMD_CUSTOM_FUNCTION = 0x0029; static const uint16_t CMD_TURN_SPEED = 0x002A; static const uint16_t CMD_CREW_SET_TARGET = 0x002B; static const uint16_t CMD_ABORT_JUMP = 0x002C; +static const uint16_t CMD_SET_WAYPOINT_ROUTE = 0x002D; //Pre-ship commands static const uint16_t CMD_UPDATE_CREW_POSITION = 0x0101; @@ -406,24 +407,31 @@ void PlayerInfo::commandSetShieldFrequency(int32_t frequency) sendClientCommand(packet); } -void PlayerInfo::commandAddWaypoint(glm::vec2 position) +void PlayerInfo::commandAddWaypoint(glm::vec2 position, int32_t set_id) { sp::io::DataBuffer packet; - packet << CMD_ADD_WAYPOINT << position; + packet << CMD_ADD_WAYPOINT << position << set_id; sendClientCommand(packet); } -void PlayerInfo::commandRemoveWaypoint(int32_t index) +void PlayerInfo::commandRemoveWaypoint(int32_t index, int32_t set_id) { sp::io::DataBuffer packet; - packet << CMD_REMOVE_WAYPOINT << index; + packet << CMD_REMOVE_WAYPOINT << index << set_id; sendClientCommand(packet); } -void PlayerInfo::commandMoveWaypoint(int32_t index, glm::vec2 position) +void PlayerInfo::commandMoveWaypoint(int32_t index, glm::vec2 position, int32_t set_id) { sp::io::DataBuffer packet; - packet << CMD_MOVE_WAYPOINT << index << position; + packet << CMD_MOVE_WAYPOINT << index << position << set_id; + sendClientCommand(packet); +} + +void PlayerInfo::commandSetWaypointRoute(bool is_route, int32_t set_id) +{ + sp::io::DataBuffer packet; + packet << CMD_SET_WAYPOINT_ROUTE << is_route << set_id; sendClientCommand(packet); } @@ -861,19 +869,21 @@ void PlayerInfo::onReceiveClientCommand(int32_t client_id, sp::io::DataBuffer& p case CMD_ADD_WAYPOINT: { glm::vec2 position{}; - packet >> position; + int32_t set_id = 1; + packet >> position >> set_id; auto wp = ship.getComponent(); if (wp) { - wp->addNew(position); + wp->addNew(position, set_id); } } break; case CMD_REMOVE_WAYPOINT: { int32_t id; - packet >> id; + int32_t set_id = 1; + packet >> id >> set_id; if (auto wp = ship.getComponent()) { - wp->remove(id); + wp->remove(id, set_id); } } break; @@ -881,9 +891,20 @@ void PlayerInfo::onReceiveClientCommand(int32_t client_id, sp::io::DataBuffer& p { int32_t id; glm::vec2 position{}; - packet >> id >> position; + int32_t set_id = 1; + packet >> id >> position >> set_id; + if (auto wp = ship.getComponent()) { + wp->move(id, position, set_id); + } + } + break; + case CMD_SET_WAYPOINT_ROUTE: + { + bool is_route; + int32_t set_id = 1; + packet >> is_route >> set_id; if (auto wp = ship.getComponent()) { - wp->move(id, position); + wp->setRoute(is_route, set_id); } } break; diff --git a/src/playerInfo.h b/src/playerInfo.h index 633e0e01fc..c3fed2700c 100644 --- a/src/playerInfo.h +++ b/src/playerInfo.h @@ -66,9 +66,10 @@ class PlayerInfo : public MultiplayerObject void commandSetBeamFrequency(int32_t frequency); void commandSetBeamSystemTarget(ShipSystem::Type system); void commandSetShieldFrequency(int32_t frequency); - void commandAddWaypoint(glm::vec2 position); - void commandRemoveWaypoint(int32_t index); - void commandMoveWaypoint(int32_t index, glm::vec2 position); + void commandAddWaypoint(glm::vec2 position, int32_t set_id = 1); + void commandRemoveWaypoint(int32_t index, int32_t set_id = 1); + void commandMoveWaypoint(int32_t index, glm::vec2 position, int32_t set_id = 1); + void commandSetWaypointRoute(bool is_route, int32_t set_id); void commandActivateSelfDestruct(); void commandCancelSelfDestruct(); void commandConfirmDestructCode(int8_t index, uint32_t code); diff --git a/src/screenComponents/radarView.cpp b/src/screenComponents/radarView.cpp index def1c8912d..1d966c6696 100644 --- a/src/screenComponents/radarView.cpp +++ b/src/screenComponents/radarView.cpp @@ -28,6 +28,7 @@ #include "radarView.h" #include "missileTubeControls.h" #include "targetsContainer.h" +#include "components/name.h" #include "gui/theme.h" @@ -81,7 +82,16 @@ GuiRadarView::GuiRadarView(GuiContainer* owner, string id, TargetsContainer* tar radar_range_indicators_style(theme->getStyle("radar.range_indicators")), ship_waypoint_style(theme->getStyle("ship_waypoint")), ship_waypoint_background_style(theme->getStyle("ship_waypoint.background")), - ship_waypoint_text_style(theme->getStyle("ship_waypoint.text")) + ship_waypoint_text_style(theme->getStyle("ship_waypoint.text")), + ship_waypoint_set2_style(theme->getStyle("ship_waypoint_set2")), + ship_waypoint_set2_background_style(theme->getStyle("ship_waypoint_set2.background")), + ship_waypoint_set2_text_style(theme->getStyle("ship_waypoint_set2.text")), + ship_waypoint_set3_style(theme->getStyle("ship_waypoint_set3")), + ship_waypoint_set3_background_style(theme->getStyle("ship_waypoint_set3.background")), + ship_waypoint_set3_text_style(theme->getStyle("ship_waypoint_set3.text")), + ship_waypoint_set4_style(theme->getStyle("ship_waypoint_set4")), + ship_waypoint_set4_background_style(theme->getStyle("ship_waypoint_set4.background")), + ship_waypoint_set4_text_style(theme->getStyle("ship_waypoint_set4.text")) { } @@ -117,7 +127,16 @@ GuiRadarView::GuiRadarView(GuiContainer* owner, string id, float distance, Targe radar_range_indicators_style(theme->getStyle("radar.range_indicators")), ship_waypoint_style(theme->getStyle("ship_waypoint")), ship_waypoint_background_style(theme->getStyle("ship_waypoint.background")), - ship_waypoint_text_style(theme->getStyle("ship_waypoint.text")) + ship_waypoint_text_style(theme->getStyle("ship_waypoint.text")), + ship_waypoint_set2_style(theme->getStyle("ship_waypoint_set2")), + ship_waypoint_set2_background_style(theme->getStyle("ship_waypoint_set2.background")), + ship_waypoint_set2_text_style(theme->getStyle("ship_waypoint_set2.text")), + ship_waypoint_set3_style(theme->getStyle("ship_waypoint_set3")), + ship_waypoint_set3_background_style(theme->getStyle("ship_waypoint_set3.background")), + ship_waypoint_set3_text_style(theme->getStyle("ship_waypoint_set3.text")), + ship_waypoint_set4_style(theme->getStyle("ship_waypoint_set4")), + ship_waypoint_set4_background_style(theme->getStyle("ship_waypoint_set4.background")), + ship_waypoint_set4_text_style(theme->getStyle("ship_waypoint_set4.text")) { } @@ -264,7 +283,11 @@ void GuiRadarView::onDraw(sp::RenderTarget& renderer) glStencilFunc(GL_EQUAL, as_mask(RadarStencil::RadarBounds), as_mask(RadarStencil::RadarBounds)); // Always draw waypoints, if enabled. - if (show_waypoints) drawWaypoints(renderer); + if (show_waypoints) + { + if (show_game_master_data) drawAllPlayerWaypoints(renderer); + else drawWaypoints(renderer); + } // Always draw heading indicators, if enabled. if (show_heading_indicators) drawHeadingIndicators(renderer); @@ -485,38 +508,127 @@ void GuiRadarView::drawGhostDots(sp::RenderTarget& renderer) } } -void GuiRadarView::drawWaypoints(sp::RenderTarget& renderer) +void GuiRadarView::drawWaypointSetForShip(sp::RenderTarget& renderer, Waypoints* waypoints, int set_id, + const GuiThemeStyle* sprite_style, const GuiThemeStyle* bg_style, const GuiThemeStyle* text_style, + bool show_route, const string& owner_label, glm::u8vec4 owner_label_color) { - auto waypoints = my_spaceship.getComponent(); - if (!waypoints) - return; + const auto& waypoint_sprite = sprite_style->get(getState()).texture; + const auto& waypoint_color = bg_style->get(getState()).color; + const auto& waypoint_text_style = text_style->get(getState()); + auto font = waypoint_text_style.font; + if (!font) font = bold_font; glm::vec2 radar_screen_center(rect.position.x + rect.size.x / 2.0f, rect.position.y + rect.size.y / 2.0f); - for(unsigned int n=0; nwaypoints.size(); n++) + // Draw route line if enabled + if (show_route && waypoints->is_route[set_id - 1]) { - auto screen_position = worldToScreen(waypoints->waypoints[n].position); + std::vector set_points; - const auto& waypoint_sprite = ship_waypoint_style->get(getState()).texture; - const auto& waypoint_color = ship_waypoint_background_style->get(getState()).color; - const auto& waypoint_text_style = ship_waypoint_text_style->get(getState()); - auto font = waypoint_text_style.font; - // Fallback to bold font - if (!font) font = bold_font; + for (auto& p : waypoints->waypoints) + if (p.set_id == set_id) set_points.push_back(&p); + + std::sort(set_points.begin(), set_points.end(), + [](auto* a, auto* b) + { + return a->id < b->id; + } + ); + glm::u8vec4 route_color(waypoint_color.r, waypoint_color.g, waypoint_color.b, waypoint_color.a / 2); + for (size_t i = 1; i < set_points.size(); i++) + { + auto p0 = worldToScreen(set_points[i - 1]->position); + auto p1 = worldToScreen(set_points[i]->position); + renderer.drawLine(p0, p1, route_color); + } + } + + // Draw waypoint sprites + for (auto& wp : waypoints->waypoints) + { + if (wp.set_id != set_id) continue; + auto screen_position = worldToScreen(wp.position); + glm::u8vec4 label_color = (owner_label_color.a != 0) ? owner_label_color : waypoint_text_style.color; renderer.drawSprite(waypoint_sprite, screen_position - glm::vec2(0, 10), 20, waypoint_color); - renderer.drawText(sp::Rect(screen_position.x, screen_position.y - 10, 0, 0), string(waypoints->waypoints[n].id), sp::Alignment::Center, 14, font, waypoint_text_style.color); + renderer.drawText(sp::Rect(screen_position.x, screen_position.y - 10, 0, 0), string(wp.id), sp::Alignment::Center, 14, font, waypoint_text_style.color); + if (!owner_label.empty()) + renderer.drawText(sp::Rect(screen_position.x, screen_position.y + 4.0f, 0.0f, 0.0f), owner_label, sp::Alignment::Center, 14, font, label_color); if (style != Rectangular && glm::length(screen_position - radar_screen_center) > std::min(rect.size.x, rect.size.y) * 0.5f) { screen_position = radar_screen_center + ((screen_position - radar_screen_center) / glm::length(screen_position - radar_screen_center) * std::min(rect.size.x, rect.size.y) * 0.4f); - renderer.drawRotatedSprite(waypoint_sprite, screen_position, 20, vec2ToAngle(screen_position - radar_screen_center) - 90, waypoint_color); - renderer.drawText(sp::Rect(screen_position.x, screen_position.y, 0, 0), string(waypoints->waypoints[n].id), sp::Alignment::Center, 14, font, waypoint_text_style.color); + renderer.drawText(sp::Rect(screen_position.x, screen_position.y, 0, 0), string(wp.id), sp::Alignment::Center, 14, font, waypoint_text_style.color); + if (!owner_label.empty()) + renderer.drawText(sp::Rect(screen_position.x, screen_position.y + 14.0f, 0.0f, 0.0f), owner_label, sp::Alignment::Center, 14, font, label_color); } } } +void GuiRadarView::drawWaypoints(sp::RenderTarget& renderer) +{ + auto waypoints = my_spaceship.getComponent(); + if (!waypoints) return; + + bool show_route = gameGlobalInfo && gameGlobalInfo->enable_waypoint_routes; + int max_sets = (gameGlobalInfo && gameGlobalInfo->enable_multiple_waypoint_sets) ? Waypoints::MAX_SETS : 1; + + const GuiThemeStyle* bg_styles[Waypoints::MAX_SETS] = { + ship_waypoint_background_style, + ship_waypoint_set2_background_style, + ship_waypoint_set3_background_style, + ship_waypoint_set4_background_style, + }; + const GuiThemeStyle* sprite_styles[Waypoints::MAX_SETS] = { + ship_waypoint_style, + ship_waypoint_set2_style, + ship_waypoint_set3_style, + ship_waypoint_set4_style, + }; + const GuiThemeStyle* text_styles[Waypoints::MAX_SETS] = { + ship_waypoint_text_style, + ship_waypoint_set2_text_style, + ship_waypoint_set3_text_style, + ship_waypoint_set4_text_style, + }; + + for (int s = 1; s <= max_sets; s++) + drawWaypointSetForShip(renderer, waypoints, s, sprite_styles[s - 1], bg_styles[s - 1], text_styles[s - 1], show_route, ""); +} + +void GuiRadarView::drawAllPlayerWaypoints(sp::RenderTarget& renderer) +{ + bool show_route = gameGlobalInfo && gameGlobalInfo->enable_waypoint_routes; + int max_sets = (gameGlobalInfo && gameGlobalInfo->enable_multiple_waypoint_sets) ? Waypoints::MAX_SETS : 1; + + const GuiThemeStyle* bg_styles[Waypoints::MAX_SETS] = { + ship_waypoint_background_style, + ship_waypoint_set2_background_style, + ship_waypoint_set3_background_style, + ship_waypoint_set4_background_style, + }; + const GuiThemeStyle* sprite_styles[Waypoints::MAX_SETS] = { + ship_waypoint_style, + ship_waypoint_set2_style, + ship_waypoint_set3_style, + ship_waypoint_set4_style, + }; + const GuiThemeStyle* text_styles[Waypoints::MAX_SETS] = { + ship_waypoint_text_style, + ship_waypoint_set2_text_style, + ship_waypoint_set3_text_style, + ship_waypoint_set4_text_style, + }; + + constexpr glm::u8vec4 gm_owner_label_color{255, 255, 255, 208}; + for (auto [entity, waypoints, cs] : sp::ecs::Query()) + { + for (int s = 1; s <= max_sets; s++) + drawWaypointSetForShip(renderer, &waypoints, s, sprite_styles[s - 1], bg_styles[s - 1], text_styles[s - 1], show_route, cs.callsign, gm_owner_label_color); + } +} + void GuiRadarView::drawRangeIndicators(sp::RenderTarget& renderer) { if (range_indicator_step_size < 1.0f) @@ -782,7 +894,7 @@ void GuiRadarView::drawTargets(sp::RenderTarget& renderer) auto waypoints = my_spaceship.getComponent(); if (my_spaceship && waypoints && targets->getWaypointIndex() > -1) { - if (auto waypoint_position = waypoints->get(targets->getWaypointIndex())) { + if (auto waypoint_position = waypoints->get(targets->getWaypointIndex(), targets->getWaypointSetId())) { auto object_position_on_screen = worldToScreen(waypoint_position.value()); renderer.drawSprite("redicule.png", object_position_on_screen - glm::vec2{0, 10}, 48); diff --git a/src/screenComponents/radarView.h b/src/screenComponents/radarView.h index 28cb3afa83..e7c4841d0c 100644 --- a/src/screenComponents/radarView.h +++ b/src/screenComponents/radarView.h @@ -6,6 +6,7 @@ class GuiMissileTubeControls; class TargetsContainer; class GuiThemeStyle; +class Waypoints; class GuiRadarView : public GuiElement { @@ -76,6 +77,15 @@ class GuiRadarView : public GuiElement const GuiThemeStyle* ship_waypoint_style; const GuiThemeStyle* ship_waypoint_background_style; const GuiThemeStyle* ship_waypoint_text_style; + const GuiThemeStyle* ship_waypoint_set2_style; + const GuiThemeStyle* ship_waypoint_set2_background_style; + const GuiThemeStyle* ship_waypoint_set2_text_style; + const GuiThemeStyle* ship_waypoint_set3_style; + const GuiThemeStyle* ship_waypoint_set3_background_style; + const GuiThemeStyle* ship_waypoint_set3_text_style; + const GuiThemeStyle* ship_waypoint_set4_style; + const GuiThemeStyle* ship_waypoint_set4_background_style; + const GuiThemeStyle* ship_waypoint_set4_text_style; public: GuiRadarView(GuiContainer* owner, string id, TargetsContainer* targets); GuiRadarView(GuiContainer* owner, string id, float distance, TargetsContainer* targets); @@ -91,6 +101,7 @@ class GuiRadarView : public GuiElement GuiRadarView* disableGhostDots() { show_ghost_dots = false; return this; } GuiRadarView* enableWaypoints() { show_waypoints = true; return this; } GuiRadarView* disableWaypoints() { show_waypoints = false; return this; } + bool getWaypoints() { return show_waypoints; } GuiRadarView* enableTargetProjections(GuiMissileTubeControls* missile_tube_controls) { show_target_projection = true; this->missile_tube_controls = missile_tube_controls; return this; } GuiRadarView* disableTargetProjections() { show_target_projection = false; return this; } GuiRadarView* enableMissileTubeIndicators() { show_missile_tubes = true; return this; } @@ -138,6 +149,8 @@ class GuiRadarView : public GuiElement void drawFriendlyNotVisibleAreas(sp::RenderTarget& target); void drawGhostDots(sp::RenderTarget& target); void drawWaypoints(sp::RenderTarget& target); + void drawAllPlayerWaypoints(sp::RenderTarget& target); + void drawWaypointSetForShip(sp::RenderTarget& renderer, Waypoints* waypoints, int set_id, const GuiThemeStyle* sprite_style, const GuiThemeStyle* bg_style, const GuiThemeStyle* text_style, bool show_route, const string& owner_label = "", glm::u8vec4 owner_label_color = {0,0,0,0}); void drawRangeIndicators(sp::RenderTarget& target); void drawTargetProjections(sp::RenderTarget& target); void drawMissileTubes(sp::RenderTarget& target); diff --git a/src/screenComponents/targetsContainer.cpp b/src/screenComponents/targetsContainer.cpp index d5c0608764..7bd6a8d9aa 100644 --- a/src/screenComponents/targetsContainer.cpp +++ b/src/screenComponents/targetsContainer.cpp @@ -11,12 +11,14 @@ TargetsContainer::TargetsContainer() { waypoint_selection_index = -1; + waypoint_selection_set_id = 1; allow_waypoint_selection = false; } void TargetsContainer::clear() { waypoint_selection_index = -1; + waypoint_selection_set_id = 1; entries.clear(); } @@ -41,11 +43,13 @@ void TargetsContainer::set(sp::ecs::Entity obj) clear(); } waypoint_selection_index = -1; + waypoint_selection_set_id = 1; } void TargetsContainer::set(const std::vector& objs) { waypoint_selection_index = -1; + waypoint_selection_set_id = 1; entries = objs; } @@ -89,6 +93,7 @@ void TargetsContainer::setToClosestTo(glm::vec2 position, float max_range, ESele { clear(); waypoint_selection_index = waypoints->waypoints[n].id; + waypoint_selection_set_id = waypoints->waypoints[n].set_id; return; } } @@ -101,16 +106,24 @@ void TargetsContainer::setToClosestTo(glm::vec2 position, float max_range, ESele int TargetsContainer::getWaypointIndex() { auto waypoints = my_spaceship.getComponent(); - if (!waypoints || waypoint_selection_index < 0 || !waypoints->get(waypoint_selection_index)) + if (!waypoints || waypoint_selection_index < 0 || !waypoints->get(waypoint_selection_index, waypoint_selection_set_id)) waypoint_selection_index = -1; return waypoint_selection_index; } -void TargetsContainer::setWaypointIndex(int index) +int TargetsContainer::getWaypointSetId() +{ + return waypoint_selection_set_id; +} + +void TargetsContainer::setWaypointIndex(int index, int set_id) { auto waypoints = my_spaceship.getComponent(); - if (waypoints && waypoints->get(index)) + if (waypoints && waypoints->get(index, set_id)) + { waypoint_selection_index = index; + waypoint_selection_set_id = set_id; + } } void TargetsContainer::setNext(glm::vec2 position, float max_range, ESelectionType selection_type) diff --git a/src/screenComponents/targetsContainer.h b/src/screenComponents/targetsContainer.h index 3e792e1037..3945474883 100644 --- a/src/screenComponents/targetsContainer.h +++ b/src/screenComponents/targetsContainer.h @@ -25,7 +25,8 @@ class TargetsContainer std::vector getTargets(); sp::ecs::Entity get(); int getWaypointIndex(); - void setWaypointIndex(int index); + int getWaypointSetId(); + void setWaypointIndex(int index, int set_id = 1); void setToClosestTo(glm::vec2 position, float max_range, ESelectionType selection_type); void setNext(glm::vec2 position, float max_range, ESelectionType selection_type); @@ -35,6 +36,7 @@ class TargetsContainer std::vector entries; bool allow_waypoint_selection; int waypoint_selection_index; + int waypoint_selection_set_id; void setNext(glm::vec2 position, float max_range, std::vector& entities); void sortByDistance(glm::vec2 position, std::vector& entities); diff --git a/src/screens/crew4/operationsScreen.cpp b/src/screens/crew4/operationsScreen.cpp index e29ec69d78..e390341c08 100644 --- a/src/screens/crew4/operationsScreen.cpp +++ b/src/screens/crew4/operationsScreen.cpp @@ -6,6 +6,7 @@ #include "gui/gui2_keyvaluedisplay.h" #include "gui/gui2_togglebutton.h" +#include "gui/gui2_selector.h" #include "components/scanning.h" #include "components/radar.h" @@ -27,21 +28,23 @@ OperationScreen::OperationScreen(GuiContainer* owner) [this](sp::io::Pointer::Button button, glm::vec2 position) { // Down // If not our ship, or if we're scanning, ignore clicks. auto science_scanner = my_spaceship.getComponent(); - if (science_scanner && science_scanner->delay > 0.0f) - return; + if (science_scanner && science_scanner->delay > 0.0f) return; // If we're in target selection mode, there's a waypoint, and this // is our ship... if (mode == TargetSelection && science->targets.getWaypointIndex() > -1 && my_spaceship) { - if (auto waypoints = my_spaceship.getComponent()) { + if (auto waypoints = my_spaceship.getComponent()) + { // ... and we select something near a waypoint, switch to move // waypoint mode. - if (auto waypoint_position = waypoints->get(science->targets.getWaypointIndex())) { + if (auto waypoint_position = waypoints->get(science->targets.getWaypointIndex(), science->targets.getWaypointSetId())) + { if (glm::length(waypoint_position.value() - position) < 1000.0f) { mode = MoveWaypoint; drag_waypoint_index = science->targets.getWaypointIndex(); + drag_waypoint_set = science->targets.getWaypointSetId(); } } } @@ -51,7 +54,7 @@ OperationScreen::OperationScreen(GuiContainer* owner) [this](glm::vec2 position) { // Drag // If we're dragging a waypoint, move it. if (mode == MoveWaypoint && my_spaceship) - my_player_info->commandMoveWaypoint(drag_waypoint_index, position); + my_player_info->commandMoveWaypoint(drag_waypoint_index, position, drag_waypoint_set); }, [this](glm::vec2 position) { // Up switch(mode) @@ -61,13 +64,13 @@ OperationScreen::OperationScreen(GuiContainer* owner) break; case WaypointPlacement: if (my_spaceship) - my_player_info->commandAddWaypoint(position); + my_player_info->commandAddWaypoint(position, active_waypoint_set); mode = TargetSelection; place_waypoint_button->setValue(false); break; case MoveWaypoint: mode = TargetSelection; - science->targets.setWaypointIndex(drag_waypoint_index); + science->targets.setWaypointIndex(drag_waypoint_index, drag_waypoint_set); break; } }, @@ -104,11 +107,32 @@ OperationScreen::OperationScreen(GuiContainer* owner) [this]() { if (my_spaceship && science->targets.getWaypointIndex() >= 0) - my_player_info->commandRemoveWaypoint(science->targets.getWaypointIndex()); + my_player_info->commandRemoveWaypoint(science->targets.getWaypointIndex(), science->targets.getWaypointSetId()); } ); delete_waypoint_button->setSize(200.0f, 50.0f); + // Waypoint set selector, shown only when multiple sets are enabled. + waypoint_set_selector = new GuiSelector(relay_functions, "WAYPOINT_SET_SELECTOR", + [this](int index, string value) + { + active_waypoint_set = index + 1; + } + ); + waypoint_set_selector + ->setOptions({tr("Waypoint set 1"), tr("Waypoint set 2"), tr("Waypoint set 3"), tr("Waypoint set 4")}) + ->setSelectionIndex(0) + ->setSize(GuiElement::GuiSizeMax, 50.0f); + + // Route toggle. + route_toggle = new GuiToggleButton(relay_functions, "WAYPOINT_ROUTE_TOGGLE", tr("Route"), + [this](bool value) + { + if (my_spaceship) my_player_info->commandSetWaypointRoute(value, active_waypoint_set); + } + ); + route_toggle->setSize(200.0f, 50.0f); + auto stats = new GuiElement(this, "OPERATIONS_STATS"); stats->setPosition(20, 60, sp::Alignment::TopLeft)->setSize(240, 80)->setAttribute("layout", "vertical"); @@ -117,7 +141,6 @@ OperationScreen::OperationScreen(GuiContainer* owner) info_reputation->setTextSize(20)->setSize(200, 40); // Scenario clock display. - info_clock = new GuiKeyValueDisplay(stats, "INFO_CLOCK", 0.55f, tr("Clock") + ":", ""); info_clock->setTextSize(20)->setSize(200, 40); @@ -144,4 +167,15 @@ void OperationScreen::onDraw(sp::RenderTarget& target) info_reputation->hide(); info_clock->hide(); } + + // Show/hide waypoint set selector and route toggle + waypoint_set_selector->setVisible(gameGlobalInfo->enable_multiple_waypoint_sets); + route_toggle->setVisible(gameGlobalInfo->enable_waypoint_routes); + + // Sync route toggle + if (gameGlobalInfo->enable_waypoint_routes) + { + if (auto wp = my_spaceship.getComponent()) + route_toggle->setValue(wp->is_route[active_waypoint_set - 1]); + } } diff --git a/src/screens/crew4/operationsScreen.h b/src/screens/crew4/operationsScreen.h index 55243b217d..241074835e 100644 --- a/src/screens/crew4/operationsScreen.h +++ b/src/screens/crew4/operationsScreen.h @@ -1,10 +1,13 @@ #pragma once +#include #include "gui/gui2_overlay.h" +class GuiElement; class GuiOverlay; class GuiKeyValueDisplay; class GuiButton; +class GuiSelector; class GuiToggleButton; class ScienceScreen; @@ -20,6 +23,8 @@ class OperationScreen : public GuiOverlay EMode mode; int drag_waypoint_index; + int drag_waypoint_set; + int active_waypoint_set = 1; ScienceScreen* science; @@ -28,6 +33,9 @@ class OperationScreen : public GuiOverlay GuiToggleButton* place_waypoint_button; GuiButton* delete_waypoint_button; + GuiSelector* waypoint_set_selector; + std::array waypoint_set_buttons{}; + GuiToggleButton* route_toggle; glm::vec2 mouse_down_position{0, 0}; public: diff --git a/src/screens/crew6/relayScreen.cpp b/src/screens/crew6/relayScreen.cpp index d7bd665eac..dc2a73812b 100644 --- a/src/screens/crew6/relayScreen.cpp +++ b/src/screens/crew6/relayScreen.cpp @@ -43,75 +43,88 @@ RelayScreen::RelayScreen(GuiContainer* owner, bool allow_comms) { targets.setAllowWaypointSelection(); radar = new GuiRadarView(this, "RELAY_RADAR", MAX_ZOOM_DISTANCE, &targets); - radar->longRange()->enableWaypoints()->enableCallsigns()->setStyle(GuiRadarView::Rectangular)->setFogOfWarStyle(GuiRadarView::FriendlysShortRangeFogOfWar); - radar->setAutoCentering(false); - radar->setPosition(0, 0, sp::Alignment::TopLeft)->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); - radar->setCallbacks( - [this](sp::io::Pointer::Button button, glm::vec2 position) { //down - if (mode == TargetSelection && targets.getWaypointIndex() > -1) { - if (auto waypoints = my_spaceship.getComponent()) { - if (auto waypoint_position = waypoints->get(targets.getWaypointIndex())) { - if (glm::length(waypoint_position.value() - position) < 1000.0f) { - mode = MoveWaypoint; - drag_waypoint_index = targets.getWaypointIndex(); + radar + ->longRange() + ->enableWaypoints() + ->enableCallsigns() + ->setStyle(GuiRadarView::Rectangular) + ->setFogOfWarStyle(GuiRadarView::FriendlysShortRangeFogOfWar) + ->setAutoCentering(false) + ->setCallbacks( + [this](sp::io::Pointer::Button button, glm::vec2 position) + { // Down + if (mode == TargetSelection && targets.getWaypointIndex() > -1) + { + if (auto waypoints = my_spaceship.getComponent()) + { + if (auto waypoint_position = waypoints->get(targets.getWaypointIndex(), targets.getWaypointSetId())) + { + if (glm::length(waypoint_position.value() - position) < 1000.0f) + { + mode = MoveWaypoint; + drag_waypoint_index = targets.getWaypointIndex(); + drag_waypoint_set = targets.getWaypointSetId(); + } } } } + mouse_down_position = position; + }, + [this](glm::vec2 position) + { // Drag + if (mode == TargetSelection) + radar->setViewPosition(radar->getViewPosition() - (position - mouse_down_position)); + if (mode == MoveWaypoint && my_spaceship) + my_player_info->commandMoveWaypoint(drag_waypoint_index, position, drag_waypoint_set); + }, + [this](glm::vec2 position) + { // Up + switch(mode) + { + case TargetSelection: + targets.setToClosestTo(position, 1000, TargetsContainer::Targetable); + break; + case WaypointPlacement: + if (my_spaceship) my_player_info->commandAddWaypoint(position, active_waypoint_set); + mode = TargetSelection; + option_buttons->show(); + cancel_button->hide(); + break; + case MoveWaypoint: + mode = TargetSelection; + targets.setWaypointIndex(drag_waypoint_index, drag_waypoint_set); + break; + case LaunchProbe: + if (my_spaceship) my_player_info->commandLaunchProbe(position); + mode = TargetSelection; + option_buttons->show(); + cancel_button->hide(); + break; + } + }, + [this](float value, glm::vec2 position) + { // Wheel + // Calculate the new zoom level. + const float view_distance = std::clamp( + radar->getDistance() * (1.0f - value * 0.1f), + MIN_ZOOM_DISTANCE, + MAX_ZOOM_DISTANCE + ); + + // Get the world coordinates under the pointer before zooming. + const glm::vec2 world_position_before_zoom = radar->screenToWorld(position); + + // Set the new zoom level. + radar->setDistance(view_distance); + zoom_slider->setValue(view_distance); + + // Adjust the radar's view position to keep the world coordinates + // under the pointer consistent. + radar->setViewPosition(radar->getViewPosition() + world_position_before_zoom - radar->screenToWorld(position)); } - mouse_down_position = position; - }, - [this](glm::vec2 position) { //drag - if (mode == TargetSelection) - radar->setViewPosition(radar->getViewPosition() - (position - mouse_down_position)); - if (mode == MoveWaypoint && my_spaceship) - my_player_info->commandMoveWaypoint(drag_waypoint_index, position); - }, - [this](glm::vec2 position) { //up - switch(mode) - { - case TargetSelection: - targets.setToClosestTo(position, 1000, TargetsContainer::Targetable); - break; - case WaypointPlacement: - if (my_spaceship) - my_player_info->commandAddWaypoint(position); - mode = TargetSelection; - option_buttons->show(); - cancel_button->hide(); - break; - case MoveWaypoint: - mode = TargetSelection; - targets.setWaypointIndex(drag_waypoint_index); - break; - case LaunchProbe: - if (my_spaceship) - my_player_info->commandLaunchProbe(position); - mode = TargetSelection; - option_buttons->show(); - cancel_button->hide(); - break; - } - }, - [this](float value, glm::vec2 position) { // wheel - // Calculate the new zoom level. - const float view_distance = std::clamp( - radar->getDistance() * (1.0f - value * 0.1f), - MIN_ZOOM_DISTANCE, - MAX_ZOOM_DISTANCE - ); - - // Get the world coordinates under the pointer before zooming. - const glm::vec2 world_position_before_zoom = radar->screenToWorld(position); - - // Set the new zoom level. - radar->setDistance(view_distance); - zoom_slider->setValue(view_distance); - - // Adjust the radar's view position to keep the world coordinates - // under the pointer consistent. - radar->setViewPosition(radar->getViewPosition() + world_position_before_zoom - radar->screenToWorld(position)); - } - ); + ) + ->setPosition(0.0f, 0.0f, sp::Alignment::TopLeft) + ->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); if (auto transform = my_spaceship.getComponent()) radar->setViewPosition(transform->getPosition()); @@ -171,19 +184,46 @@ RelayScreen::RelayScreen(GuiContainer* owner, bool allow_comms) link_to_science_button->setSize(GuiElement::GuiSizeMax, 50)->setVisible(my_spaceship.hasComponent() && my_spaceship.hasComponent() && my_spaceship.hasComponent()); // Manage waypoints. - (new GuiButton(option_buttons, "WAYPOINT_PLACE_BUTTON", tr("Place waypoint"), [this]() { - mode = WaypointPlacement; - option_buttons->hide(); - cancel_button->setText(tr("Cancel waypoint"))->show(); - }))->setSize(GuiElement::GuiSizeMax, 50); + (new GuiButton(option_buttons, "WAYPOINT_PLACE_BUTTON", tr("Place waypoint"), + [this]() + { + mode = WaypointPlacement; + option_buttons->hide(); + cancel_button + ->setText(tr("Cancel waypoint")) + ->show(); + } + ))->setSize(GuiElement::GuiSizeMax, 50.0f); - delete_waypoint_button = new GuiButton(option_buttons, "WAYPOINT_DELETE_BUTTON", tr("Delete waypoint"), [this]() { - if (my_spaceship && targets.getWaypointIndex() >= 0) + delete_waypoint_button = new GuiButton(option_buttons, "WAYPOINT_DELETE_BUTTON", tr("Delete waypoint"), + [this]() { - my_player_info->commandRemoveWaypoint(targets.getWaypointIndex()); + if (my_spaceship && targets.getWaypointIndex() >= 0) + my_player_info->commandRemoveWaypoint(targets.getWaypointIndex(), targets.getWaypointSetId()); } - }); - delete_waypoint_button->setSize(GuiElement::GuiSizeMax, 50); + ); + delete_waypoint_button->setSize(GuiElement::GuiSizeMax, 50.0f); + + // Waypoint set selector, shown only when multiple sets are enabled. + waypoint_set_selector = new GuiSelector(option_buttons, "WAYPOINT_SET_SELECTOR", + [this](int index, string value) + { + active_waypoint_set = index + 1; + } + ); + waypoint_set_selector + ->setOptions({tr("Waypoint set 1"), tr("Waypoint set 2"), tr("Waypoint set 3"), tr("Waypoint set 4")}) + ->setSelectionIndex(0) + ->setSize(GuiElement::GuiSizeMax, 50.0f); + + // Route toggle, shown only when server allows routes. + route_toggle = new GuiToggleButton(option_buttons, "WAYPOINT_ROUTE_TOGGLE", tr("Route"), + [this](bool value) + { + if (my_spaceship) my_player_info->commandSetWaypointRoute(value, active_waypoint_set); + } + ); + route_toggle->setSize(GuiElement::GuiSizeMax, 50.0f); // Launch probe button. launch_probe_button = new GuiButton(option_buttons, "LAUNCH_PROBE_BUTTON", tr("Launch probe"), [this]() { @@ -341,4 +381,15 @@ void RelayScreen::onDraw(sp::RenderTarget& renderer) } delete_waypoint_button->setEnable(targets.getWaypointIndex() >= 0); + + // Show/hide waypoint set selector and route toggle based on global settings + waypoint_set_selector->setVisible(gameGlobalInfo->enable_multiple_waypoint_sets); + route_toggle->setVisible(gameGlobalInfo->enable_waypoint_routes); + + // Sync route toggle from current ship state + if (my_spaceship && gameGlobalInfo->enable_waypoint_routes) + { + if (auto wp = my_spaceship.getComponent()) + route_toggle->setValue(wp->is_route[active_waypoint_set - 1]); + } } diff --git a/src/screens/crew6/relayScreen.h b/src/screens/crew6/relayScreen.h index 60c86f1e1b..47c6164185 100644 --- a/src/screens/crew6/relayScreen.h +++ b/src/screens/crew6/relayScreen.h @@ -1,14 +1,17 @@ #pragma once +#include #include "screenComponents/targetsContainer.h" #include "gui/gui2_overlay.h" class GuiButton; +class GuiElement; class GuiHackingDialog; class GuiKeyValueDisplay; class GuiLabel; class GuiRadarView; class GuiRadarZoomSlider; +class GuiSelector; class GuiSlider; class GuiToggleButton; @@ -28,6 +31,9 @@ class RelayScreen : public GuiOverlay TargetsContainer targets; int drag_waypoint_index; + int drag_waypoint_set; + int active_waypoint_set = 1; + GuiRadarView* radar; GuiKeyValueDisplay* info_callsign; @@ -42,6 +48,8 @@ class RelayScreen : public GuiOverlay GuiButton* delete_waypoint_button; GuiButton* launch_probe_button; GuiToggleButton* center_button; + GuiSelector* waypoint_set_selector; + GuiToggleButton* route_toggle; GuiRadarZoomSlider* zoom_slider; GuiLabel* zoom_label; diff --git a/src/script.cpp b/src/script.cpp index 1d153a9e65..6011059a9d 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1067,26 +1067,26 @@ void luaCommandSetShieldFrequency(sp::ecs::Entity ship, int frequency) { } } -static void luaCommandAddWaypoint(sp::ecs::Entity ship, float x, float y) { - if (my_player_info && my_player_info->ship == ship) { my_player_info->commandAddWaypoint({x, y}); return; } - if (auto wp = ship.getComponent()) { - wp->addNew({x, y}); - } +static void luaCommandAddWaypoint(sp::ecs::Entity ship, float x, float y, int set_id = 1) { + if (my_player_info && my_player_info->ship == ship) { my_player_info->commandAddWaypoint({x, y}, set_id); return; } + if (auto wp = ship.getComponent()) + wp->addNew({x, y}, set_id); } -static void luaCommandRemoveWaypoint(sp::ecs::Entity ship, int index) { - if (my_player_info && my_player_info->ship == ship) { my_player_info->commandRemoveWaypoint(index); return; } - auto wp = ship.getComponent(); - if (wp && index >= 0 && index < int(wp->waypoints.size())) { - wp->waypoints.erase(wp->waypoints.begin() + index); - wp->dirty = true; - } +static void luaCommandRemoveWaypoint(sp::ecs::Entity ship, int index, int set_id = 1) { + if (my_player_info && my_player_info->ship == ship) { my_player_info->commandRemoveWaypoint(index, set_id); return; } + if (auto wp = ship.getComponent()) + wp->remove(index, set_id); } -static void luaCommandMoveWaypoint(sp::ecs::Entity ship, int index, float x, float y) { - if (my_player_info && my_player_info->ship == ship) { my_player_info->commandMoveWaypoint(index, {x, y}); return; } - if (auto wp = ship.getComponent()) { - wp->move(index, {x, y}); - } +static void luaCommandMoveWaypoint(sp::ecs::Entity ship, int index, float x, float y, int set_id = 1) { + if (my_player_info && my_player_info->ship == ship) { my_player_info->commandMoveWaypoint(index, {x, y}, set_id); return; } + if (auto wp = ship.getComponent()) + wp->move(index, {x, y}, set_id); +} +static void luaCommandSetWaypointRoute(sp::ecs::Entity ship, bool is_route, int set_id = 1) { + if (my_player_info && my_player_info->ship == ship) { my_player_info->commandSetWaypointRoute(is_route, set_id); return; } + if (auto wp = ship.getComponent()) + wp->setRoute(is_route, set_id); } static void luaCommandActivateSelfDestruct(sp::ecs::Entity ship) { if (my_player_info && my_player_info->ship == ship) { my_player_info->commandActivateSelfDestruct(); return; } @@ -1321,6 +1321,7 @@ bool setupScriptEnvironment(sp::script::Environment& env) env.setGlobal("commandAddWaypoint", &luaCommandAddWaypoint); env.setGlobal("commandRemoveWaypoint", &luaCommandRemoveWaypoint); env.setGlobal("commandMoveWaypoint", &luaCommandMoveWaypoint); + env.setGlobal("commandSetWaypointRoute", &luaCommandSetWaypointRoute); env.setGlobal("commandActivateSelfDestruct", &luaCommandActivateSelfDestruct); env.setGlobal("commandCancelSelfDestruct", &luaCommandCancelSelfDestruct); env.setGlobal("commandConfirmDestructCode", &luaCommandConfirmDestructCode); diff --git a/src/script/components.cpp b/src/script/components.cpp index c11385cfec..951e7ed5d6 100644 --- a/src/script/components.cpp +++ b/src/script/components.cpp @@ -366,6 +366,7 @@ void initComponentScriptBindings() sp::script::ComponentHandler::name("waypoints"); BIND_ARRAY_DIRTY_FLAG(Waypoints, waypoints, dirty); BIND_ARRAY_DIRTY_FLAG_MEMBER_NAMED(Waypoints, waypoints, "id", id, dirty); + BIND_ARRAY_DIRTY_FLAG_MEMBER_NAMED(Waypoints, waypoints, "set_id", set_id, dirty); BIND_ARRAY_DIRTY_FLAG_MEMBER_NAMED(Waypoints, waypoints, "x", position.x, dirty); BIND_ARRAY_DIRTY_FLAG_MEMBER_NAMED(Waypoints, waypoints, "y", position.y, dirty); sp::script::ComponentHandler::name("share_short_range_radar"); From ed9e42d38ff44dfbf463640685a64bc9a2e24b7d Mon Sep 17 00:00:00 2001 From: Oznogon Date: Sun, 5 Apr 2026 16:15:05 -0700 Subject: [PATCH 2/7] Add waypoint management to GM screen Add waypoint visibility, creation, and deletion controls visible when a player ship is selected. This includes a hotkey (default `W`) to toggle waypoint visibility. If visible, waypoints list the callsign of their associated entity beneath their icon, and can be repositioned by clicking and dragging them. If waypoint sets and routes are visible, controls for selecting a set and toggling route visibility are also available. Waypoints are NOT VISIBLE by default. The only waypoint control visible by default is the visibility toggle. This should NOT change default behaviors. Waypoints cannot be selected as the GM screen target. --- src/screens/gm/gameMasterScreen.cpp | 230 +++++++++++++++++++++++++++- src/screens/gm/gameMasterScreen.h | 18 +++ 2 files changed, 247 insertions(+), 1 deletion(-) diff --git a/src/screens/gm/gameMasterScreen.cpp b/src/screens/gm/gameMasterScreen.cpp index d990b1b234..cfb5accd76 100644 --- a/src/screens/gm/gameMasterScreen.cpp +++ b/src/screens/gm/gameMasterScreen.cpp @@ -283,6 +283,125 @@ GameMasterScreen::GameMasterScreen(RenderLayer* render_layer) }))->setTextSize(20)->setSize(GuiElement::GuiSizeMax, 30); (new GuiLabel(order_layout, "ORDERS_LABEL", tr("Orders:"), 20))->addBackground()->setSize(GuiElement::GuiSizeMax, 30); + // Player ship waypoint controls (shown when a player ship is selected) + gm_player_waypoint_layout = new GuiElement(this, "GM_WP_LAYOUT"); + gm_player_waypoint_layout + ->setPosition(-20.0f, -240.0f, sp::Alignment::BottomRight) + ->setSize(300.0f, GuiElement::GuiSizeMax) + ->hide() + ->setAttribute("layout", "verticalbottom"); + + gm_delete_waypoint_button = new GuiToggleButton(gm_player_waypoint_layout, "GM_WP_DELETE", tr("button", "Delete waypoint"), + [this](bool value) + { + // Cancel add mode when entering delete mode. + gm_delete_waypoint_mode = value; + if (value && gm_add_waypoint_mode) + { + gm_add_waypoint_mode = false; + gm_waypoint_target_ship = {}; + gm_add_waypoint_button->setValue(false); + } + } + ); + gm_delete_waypoint_button + ->setTextSize(20.0f) + ->setSize(GuiElement::GuiSizeMax, 30.0f); + + // Toggle button to enter/exit waypoint placement mode for the selected player ship. + gm_add_waypoint_button = new GuiToggleButton(gm_player_waypoint_layout, "GM_WP_ADD", tr("button", "Add waypoint"), + [this](bool value) + { + if (value) + { + // Enter waypoint placement mode for the selected player ship. + for (auto entity : targets.getTargets()) + { + if (entity.hasComponent()) + { + gm_waypoint_target_ship = entity; + gm_add_waypoint_mode = true; + // Cancel delete mode if active. + if (gm_delete_waypoint_mode) + { + gm_delete_waypoint_mode = false; + gm_delete_waypoint_button->setValue(false); + } + return; + } + } + // No player ship found; revert toggle. + gm_add_waypoint_button->setValue(false); + } + else + { + gm_add_waypoint_mode = false; + gm_waypoint_target_ship = {}; + } + } + ); + gm_add_waypoint_button + ->setTextSize(20.0f) + ->setSize(GuiElement::GuiSizeMax, 30.0f); + + gm_route_toggle = new GuiToggleButton(gm_player_waypoint_layout, "GM_WP_ROUTE", tr("Draw route for this waypoint set"), + [this](bool value) + { + for (auto entity : targets.getTargets()) + { + if (entity.hasComponent()) + { + if (auto wp = entity.getComponent()) + wp->setRoute(value, gm_waypoint_set); + break; + } + } + } + ); + gm_route_toggle + ->setTextSize(20.0f) + ->setSize(GuiElement::GuiSizeMax, 30.0f); + + gm_waypoint_set_selector = new GuiSelector(gm_player_waypoint_layout, "GM_WP_SET", + [this](int index, string value) + { + gm_waypoint_set = index + 1; + // Sync route toggle when set changes + for (auto entity : targets.getTargets()) + { + if (entity.hasComponent()) + { + if (auto wp = entity.getComponent()) + gm_route_toggle->setValue(wp->is_route[gm_waypoint_set - 1]); + break; + } + } + } + ); + gm_waypoint_set_selector + ->setTextSize(20.0f) + ->setOptions({tr("Waypoint set 1"), tr("Waypoint set 2"), tr("Waypoint set 3"), tr("Waypoint set 4")}) + ->setSelectionIndex(0) + ->setSize(GuiElement::GuiSizeMax, 30.0f); + + // Toggle button to show waypoints. + gm_show_waypoints_button = new GuiToggleButton(gm_player_waypoint_layout, "GM_WP_SHOW", tr("button", "Show waypoints"), + [this](bool value) + { + if (value) + main_radar->enableWaypoints(); + else + main_radar->disableWaypoints(); + } + ); + gm_show_waypoints_button + ->setTextSize(20.0f) + ->setSize(GuiElement::GuiSizeMax, 30.0f); + + (new GuiLabel(gm_player_waypoint_layout, "GM_WP_LABEL", tr("Waypoints"), 20.0f)) + ->addBackground() + ->setSize(GuiElement::GuiSizeMax, 30.0f); + chat_layer = new GuiElement(this, ""); chat_layer->setPosition(0, 0)->setSize(GuiElement::GuiSizeMax, GuiElement::GuiSizeMax); @@ -370,9 +489,18 @@ void GameMasterScreen::update(float delta) if (keys.gm_show_callsigns.getDown()) main_radar->showCallsigns(!main_radar->getCallsigns()); + // Toggle waypoint visibility. + if (keys.gm_show_waypoints.getDown()) + { + if (main_radar->getWaypoints()) + main_radar->disableWaypoints(); + else + main_radar->enableWaypoints(); + } + bool has_object = false; has_cpu_ship = false; - bool has_player_ship = false; + has_player_ship = false; // Add and remove entries from the player ship list. for (auto [entity, pc] : sp::ecs::Query()) @@ -421,6 +549,32 @@ void GameMasterScreen::update(float delta) order_layout->setVisible(has_cpu_ship); player_comms_hail->setVisible(has_player_ship); + // Manage waypoint mode state. + if (!has_player_ship && gm_delete_waypoint_mode) + { + gm_delete_waypoint_mode = false; + gm_delete_waypoint_button->setValue(false); + } + gm_player_waypoint_layout->setVisible(has_player_ship); + gm_add_waypoint_button->setVisible(main_radar->getWaypoints()); + gm_delete_waypoint_button->setVisible(main_radar->getWaypoints()); + gm_route_toggle->setVisible(gameGlobalInfo->enable_waypoint_routes && main_radar->getWaypoints()); + gm_waypoint_set_selector->setVisible(gameGlobalInfo->enable_multiple_waypoint_sets && main_radar->getWaypoints()); + + // Sync route toggle from the selected player ship. + if (has_player_ship) + { + for (auto entity : targets.getTargets()) + { + if (entity.hasComponent()) + { + if (auto wp = entity.getComponent()) + gm_route_toggle->setValue(wp->is_route[gm_waypoint_set - 1]); + break; + } + } + } + // Update mission clock info_clock->setValue(gameGlobalInfo->getMissionTime()); @@ -607,6 +761,45 @@ void GameMasterScreen::onMouseDown(sp::io::Pointer::Button button, glm::vec2 pos { if (click_and_drag_state != ClickAndDragState::None) return; + if (button == sp::io::Pointer::Button::Left) + { + // Check if the click is near any player ship waypoint. + float min_drag_distance = main_radar->getDistance() / 450.0f * 10.0f; + glm::vec2 click_screen = main_radar->worldToScreen(position); + int max_sets = (gameGlobalInfo && gameGlobalInfo->enable_multiple_waypoint_sets) ? Waypoints::MAX_SETS : 1; + for (auto [entity, waypoints] : sp::ecs::Query()) + { + for (auto& wp : waypoints.waypoints) + { + if (wp.set_id < 1 || wp.set_id > max_sets) continue; + if (gm_delete_waypoint_mode) + { + // Delete mode uses icon and label size for hitbox. + glm::vec2 wp_screen = main_radar->worldToScreen(wp.position); + glm::vec2 delta = click_screen - wp_screen; + if (delta.x >= -30.0f && delta.x <= 30.0f && delta.y >= -22.0f && delta.y <= 18.0f) + { + waypoints.remove(wp.id, wp.set_id); + return; + } + } + else if (glm::length(wp.position - position) < min_drag_distance) + { + gm_drag_waypoint_id = wp.id; + gm_drag_waypoint_set = wp.set_id; + gm_drag_waypoint_ship = entity; + drag_start_position = position; + drag_previous_position = position; + return; + } + } + } + } + + // While placing or deleting waypoints, don't enter any other drag/select states. + if (gm_add_waypoint_mode || gm_delete_waypoint_mode) + return; + if (button == sp::io::Pointer::Button::Right) { if (has_cpu_ship) click_and_drag_state = ClickAndDragState::DragViewOrOrder; @@ -636,6 +829,15 @@ void GameMasterScreen::onMouseDown(sp::io::Pointer::Button button, glm::vec2 pos void GameMasterScreen::onMouseDrag(glm::vec2 position) { + // Handle waypoint dragging. + if (gm_drag_waypoint_id >= 0) + { + if (auto wp = gm_drag_waypoint_ship.getComponent()) + wp->move(gm_drag_waypoint_id, position, gm_drag_waypoint_set); + drag_previous_position = position; + return; + } + switch(click_and_drag_state) { case ClickAndDragState::DragViewOrOrder: @@ -676,6 +878,32 @@ void GameMasterScreen::onMouseDrag(glm::vec2 position) void GameMasterScreen::onMouseUp(glm::vec2 position) { + // Finish waypoint drag. + if (gm_drag_waypoint_id >= 0) + { + if (auto wp = gm_drag_waypoint_ship.getComponent()) + wp->move(gm_drag_waypoint_id, position, gm_drag_waypoint_set); + gm_drag_waypoint_id = -1; + gm_drag_waypoint_set = -1; + gm_drag_waypoint_ship = {}; + return; + } + + // GM waypoint placement for a selected player ship. + if (gm_add_waypoint_mode && gm_waypoint_target_ship) + { + bool set_full = false; + if (auto wp = gm_waypoint_target_ship.getComponent()) + set_full = (wp->addNew(position, gm_waypoint_set) < 0); + if (set_full) + { + gm_add_waypoint_mode = false; + gm_waypoint_target_ship = {}; + gm_add_waypoint_button->setValue(false); + } + return; + } + auto mods = SDL_GetModState(); const bool shift_down = mods & KMOD_SHIFT; const bool ctrl_down = mods & KMOD_CTRL; diff --git a/src/screens/gm/gameMasterScreen.h b/src/screens/gm/gameMasterScreen.h index a62c75ba0b..3e2818b278 100644 --- a/src/screens/gm/gameMasterScreen.h +++ b/src/screens/gm/gameMasterScreen.h @@ -106,6 +106,24 @@ class GameMasterScreen : public GuiCanvas, public Updatable { return GMCursorMode(unsigned(a) & unsigned(b)); } bool has_cpu_ship = false; + bool has_player_ship = false; + + // GM waypoint placement/deletion state + bool gm_add_waypoint_mode = false; + bool gm_delete_waypoint_mode = false; + int gm_waypoint_set = 1; + sp::ecs::Entity gm_waypoint_target_ship; + int gm_drag_waypoint_id = -1; + int gm_drag_waypoint_set = -1; + sp::ecs::Entity gm_drag_waypoint_ship; + + // GM player-waypoint controls + GuiElement* gm_player_waypoint_layout; + GuiSelector* gm_waypoint_set_selector; + GuiToggleButton* gm_show_waypoints_button; + GuiToggleButton* gm_add_waypoint_button; + GuiToggleButton* gm_route_toggle; + GuiToggleButton* gm_delete_waypoint_button; GuiButton* create_button; GuiButton* cancel_action_button; From f27c25b290e954cb45f02bd05861ade44220d112 Mon Sep 17 00:00:00 2001 From: Oznogon Date: Sun, 5 Apr 2026 21:52:48 -0700 Subject: [PATCH 3/7] Move adv waypoints buttons on Ops --- src/screens/crew4/operationsScreen.cpp | 51 +++++++++++++++----------- src/screens/crew6/relayScreen.cpp | 2 +- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/screens/crew4/operationsScreen.cpp b/src/screens/crew4/operationsScreen.cpp index e390341c08..72d17a1e62 100644 --- a/src/screens/crew4/operationsScreen.cpp +++ b/src/screens/crew4/operationsScreen.cpp @@ -86,7 +86,35 @@ OperationScreen::OperationScreen(GuiContainer* owner) ); science->science_radar->setAutoRotating(PreferencesManager::get("operations_radar_lock","0")=="1"); - // Limited relay functions: comms and waypoints. + // Left column: waypoint set selector and route toggle. + auto waypoint_set_controls = new GuiElement(science->radar_view, "WAYPOINT_SET_CONTROLS"); + waypoint_set_controls + ->setPosition(-480.0f, -20.0f, sp::Alignment::BottomRight) + ->setSize(200.0f, 100.0f) + ->setAttribute("layout", "verticalbottom"); + + // Waypoint set selector, shown only when multiple sets are enabled. + waypoint_set_selector = new GuiSelector(waypoint_set_controls, "WAYPOINT_SET_SELECTOR", + [this](int index, string value) + { + active_waypoint_set = index + 1; + } + ); + waypoint_set_selector + ->setOptions({tr("Waypoint set 1"), tr("Waypoint set 2"), tr("Waypoint set 3"), tr("Waypoint set 4")}) + ->setSelectionIndex(0) + ->setSize(GuiElement::GuiSizeMax, 50.0f); + + // Route toggle, shown only when waypoint routes are enabled. + route_toggle = new GuiToggleButton(waypoint_set_controls, "WAYPOINT_ROUTE_TOGGLE", tr("Show as route"), + [this](bool value) + { + if (my_spaceship) my_player_info->commandSetWaypointRoute(value, active_waypoint_set); + } + ); + route_toggle->setSize(200.0f, 50.0f); + + // Right column: comms and waypoint placement/deletion. GuiElement* relay_functions = new GuiElement(science->radar_view, "RELAY_FUNCTIONS"); relay_functions ->setPosition(-270.0f, -20.0f, sp::Alignment::BottomRight) @@ -112,27 +140,6 @@ OperationScreen::OperationScreen(GuiContainer* owner) ); delete_waypoint_button->setSize(200.0f, 50.0f); - // Waypoint set selector, shown only when multiple sets are enabled. - waypoint_set_selector = new GuiSelector(relay_functions, "WAYPOINT_SET_SELECTOR", - [this](int index, string value) - { - active_waypoint_set = index + 1; - } - ); - waypoint_set_selector - ->setOptions({tr("Waypoint set 1"), tr("Waypoint set 2"), tr("Waypoint set 3"), tr("Waypoint set 4")}) - ->setSelectionIndex(0) - ->setSize(GuiElement::GuiSizeMax, 50.0f); - - // Route toggle. - route_toggle = new GuiToggleButton(relay_functions, "WAYPOINT_ROUTE_TOGGLE", tr("Route"), - [this](bool value) - { - if (my_spaceship) my_player_info->commandSetWaypointRoute(value, active_waypoint_set); - } - ); - route_toggle->setSize(200.0f, 50.0f); - auto stats = new GuiElement(this, "OPERATIONS_STATS"); stats->setPosition(20, 60, sp::Alignment::TopLeft)->setSize(240, 80)->setAttribute("layout", "vertical"); diff --git a/src/screens/crew6/relayScreen.cpp b/src/screens/crew6/relayScreen.cpp index dc2a73812b..77cd8068f4 100644 --- a/src/screens/crew6/relayScreen.cpp +++ b/src/screens/crew6/relayScreen.cpp @@ -217,7 +217,7 @@ RelayScreen::RelayScreen(GuiContainer* owner, bool allow_comms) ->setSize(GuiElement::GuiSizeMax, 50.0f); // Route toggle, shown only when server allows routes. - route_toggle = new GuiToggleButton(option_buttons, "WAYPOINT_ROUTE_TOGGLE", tr("Route"), + route_toggle = new GuiToggleButton(option_buttons, "WAYPOINT_ROUTE_TOGGLE", tr("Show as route"), [this](bool value) { if (my_spaceship) my_player_info->commandSetWaypointRoute(value, active_waypoint_set); From a39aa0d56955b29369886cb1ba4b57b8f68b2a3d Mon Sep 17 00:00:00 2001 From: Oznogon Date: Sun, 5 Apr 2026 22:05:40 -0700 Subject: [PATCH 4/7] Move waypoints on GM screen only if waypoints are visible --- src/screens/gm/gameMasterScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/screens/gm/gameMasterScreen.cpp b/src/screens/gm/gameMasterScreen.cpp index cfb5accd76..8236b0f452 100644 --- a/src/screens/gm/gameMasterScreen.cpp +++ b/src/screens/gm/gameMasterScreen.cpp @@ -761,7 +761,7 @@ void GameMasterScreen::onMouseDown(sp::io::Pointer::Button button, glm::vec2 pos { if (click_and_drag_state != ClickAndDragState::None) return; - if (button == sp::io::Pointer::Button::Left) + if (button == sp::io::Pointer::Button::Left && main_radar->getWaypoints()) { // Check if the click is near any player ship waypoint. float min_drag_distance = main_radar->getDistance() / 450.0f * 10.0f; From 795130724476361da8f2436a6265b4080b6cfa41 Mon Sep 17 00:00:00 2001 From: oznogon Date: Sat, 18 Apr 2026 16:38:22 -0700 Subject: [PATCH 5/7] Fix waypoint route replication --- src/multiplayer/player.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/multiplayer/player.cpp b/src/multiplayer/player.cpp index ea0bd81a98..f883399991 100644 --- a/src/multiplayer/player.cpp +++ b/src/multiplayer/player.cpp @@ -2,8 +2,24 @@ #include "multiplayer.h" namespace sp::io { - static inline DataBuffer& operator << (DataBuffer& packet, const Waypoints::Point& p) { return packet << p.id << p.position; } - static inline DataBuffer& operator >> (DataBuffer& packet, Waypoints::Point& p) { packet >> p.id >> p.position; return packet; } + static inline DataBuffer& operator << (DataBuffer& packet, const Waypoints::Point& p) { return packet << p.id << p.set_id << p.position; } + static inline DataBuffer& operator >> (DataBuffer& packet, Waypoints::Point& p) { packet >> p.id >> p.set_id >> p.position; return packet; } + + static inline DataBuffer& operator << (DataBuffer& packet, const std::array& arr) + { + uint8_t bits = 0; + for (int i = 0; i < Waypoints::MAX_SETS; i++) + if (arr[i]) bits |= (1 << i); + return packet << bits; + } + static inline DataBuffer& operator >> (DataBuffer& packet, std::array& arr) + { + uint8_t bits; + packet >> bits; + for (int i = 0; i < Waypoints::MAX_SETS; i++) + arr[i] = (bits >> i) & 1; + return packet; + } } @@ -18,4 +34,5 @@ BASIC_REPLICATION_IMPL(PlayerControlReplication, PlayerControl) BASIC_REPLICATION_IMPL(WaypointsReplication, Waypoints) REPLICATE_VECTOR_IF_DIRTY(waypoints, dirty); + BASIC_REPLICATION_FIELD(is_route); } From 186f214b3f549a14d879a2abc0c0b773e727e295 Mon Sep 17 00:00:00 2001 From: oznogon Date: Sat, 18 Apr 2026 16:47:09 -0700 Subject: [PATCH 6/7] Reset waypoint set to 1 when sets are disabled at runtime --- src/screens/crew4/operationsScreen.cpp | 6 ++++++ src/screens/crew6/relayScreen.cpp | 6 ++++++ src/screens/gm/gameMasterScreen.cpp | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/src/screens/crew4/operationsScreen.cpp b/src/screens/crew4/operationsScreen.cpp index 2a12afb2ac..ccae3246b4 100644 --- a/src/screens/crew4/operationsScreen.cpp +++ b/src/screens/crew4/operationsScreen.cpp @@ -174,6 +174,12 @@ void OperationScreen::onDraw(sp::RenderTarget& target) waypoint_set_selector->setVisible(gameGlobalInfo->enable_multiple_waypoint_sets); route_toggle->setVisible(gameGlobalInfo->enable_waypoint_routes); + if (!gameGlobalInfo->enable_multiple_waypoint_sets && active_waypoint_set != 1) + { + active_waypoint_set = 1; + waypoint_set_selector->setSelectionIndex(0); + } + // Sync route toggle if (gameGlobalInfo->enable_waypoint_routes) { diff --git a/src/screens/crew6/relayScreen.cpp b/src/screens/crew6/relayScreen.cpp index 77cd8068f4..f886582314 100644 --- a/src/screens/crew6/relayScreen.cpp +++ b/src/screens/crew6/relayScreen.cpp @@ -386,6 +386,12 @@ void RelayScreen::onDraw(sp::RenderTarget& renderer) waypoint_set_selector->setVisible(gameGlobalInfo->enable_multiple_waypoint_sets); route_toggle->setVisible(gameGlobalInfo->enable_waypoint_routes); + if (!gameGlobalInfo->enable_multiple_waypoint_sets && active_waypoint_set != 1) + { + active_waypoint_set = 1; + waypoint_set_selector->setSelectionIndex(0); + } + // Sync route toggle from current ship state if (my_spaceship && gameGlobalInfo->enable_waypoint_routes) { diff --git a/src/screens/gm/gameMasterScreen.cpp b/src/screens/gm/gameMasterScreen.cpp index 1d47619c92..c823b4c2e1 100644 --- a/src/screens/gm/gameMasterScreen.cpp +++ b/src/screens/gm/gameMasterScreen.cpp @@ -561,6 +561,12 @@ void GameMasterScreen::update(float delta) gm_route_toggle->setVisible(gameGlobalInfo->enable_waypoint_routes && main_radar->getWaypoints()); gm_waypoint_set_selector->setVisible(gameGlobalInfo->enable_multiple_waypoint_sets && main_radar->getWaypoints()); + if (!gameGlobalInfo->enable_multiple_waypoint_sets && gm_waypoint_set != 1) + { + gm_waypoint_set = 1; + gm_waypoint_set_selector->setSelectionIndex(0); + } + // Sync route toggle from the selected player ship. if (has_player_ship) { From b87ba350291100356818d07a30d7805867fea6fc Mon Sep 17 00:00:00 2001 From: Oznogon Date: Tue, 21 Apr 2026 22:03:07 -0700 Subject: [PATCH 7/7] Remove unused waypoint_set_buttons from OperationsScreen --- src/screens/crew4/operationsScreen.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/screens/crew4/operationsScreen.h b/src/screens/crew4/operationsScreen.h index 241074835e..33b0bfb88f 100644 --- a/src/screens/crew4/operationsScreen.h +++ b/src/screens/crew4/operationsScreen.h @@ -1,6 +1,5 @@ #pragma once -#include #include "gui/gui2_overlay.h" class GuiElement; @@ -34,7 +33,6 @@ class OperationScreen : public GuiOverlay GuiToggleButton* place_waypoint_button; GuiButton* delete_waypoint_button; GuiSelector* waypoint_set_selector; - std::array waypoint_set_buttons{}; GuiToggleButton* route_toggle; glm::vec2 mouse_down_position{0, 0};