From 07177467b370b0b474f012bb529514f7f0b3b153 Mon Sep 17 00:00:00 2001 From: mmtunligit <156685720+mmtunligit@users.noreply.github.com> Date: Mon, 24 Nov 2025 20:56:19 +0100 Subject: [PATCH] Feature: Signs, waypoint and station names may be moved (#14744) --- src/command_type.h | 3 ++ src/lang/english.txt | 13 ++++--- src/misc_gui.cpp | 68 ++++++++++++++++++++++++++++++++--- src/signs.cpp | 6 ++-- src/signs_cmd.cpp | 32 ++++++++++++++++- src/signs_cmd.h | 2 ++ src/signs_func.h | 2 +- src/signs_gui.cpp | 53 ++++++++++++++++++++++----- src/station_cmd.cpp | 55 ++++++++++++++++++++++++++++ src/station_cmd.h | 4 +++ src/station_gui.cpp | 14 ++++++-- src/textbuf_gui.h | 1 + src/viewport.cpp | 75 +++++++++++++++++++++++++++++++++++++++ src/viewport_func.h | 2 ++ src/waypoint_cmd.cpp | 57 +++++++++++++++++++++++++++++ src/waypoint_cmd.h | 4 +++ src/waypoint_gui.cpp | 14 ++++++-- src/widgets/misc_widget.h | 2 ++ src/widgets/sign_widget.h | 1 + 19 files changed, 380 insertions(+), 28 deletions(-) diff --git a/src/command_type.h b/src/command_type.h index babf32dd54..6445913bc6 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -214,6 +214,7 @@ enum Commands : uint8_t { CMD_BUILD_RAIL_WAYPOINT, ///< build a waypoint CMD_RENAME_WAYPOINT, ///< rename a waypoint + CMD_MOVE_WAYPOINT_NAME, ///< move a waypoint name CMD_REMOVE_FROM_RAIL_WAYPOINT, ///< remove a (rectangle of) tiles from a rail waypoint CMD_BUILD_ROAD_WAYPOINT, ///< build a road waypoint @@ -275,10 +276,12 @@ enum Commands : uint8_t { CMD_RENAME_COMPANY, ///< change the company name CMD_RENAME_PRESIDENT, ///< change the president name CMD_RENAME_STATION, ///< rename a station + CMD_MOVE_STATION_NAME, ///< move a station name CMD_RENAME_DEPOT, ///< rename a depot CMD_PLACE_SIGN, ///< place a sign CMD_RENAME_SIGN, ///< rename a sign + CMD_MOVE_SIGN, ///< move a sign CMD_TURN_ROADVEH, ///< turn a road vehicle around diff --git a/src/lang/english.txt b/src/lang/english.txt index dd2f426621..04efb34743 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -312,6 +312,7 @@ STR_SHOW_HIDDEN_ENGINES_VEHICLE_AIRCRAFT_TOOLTIP :{BLACK}By enabl STR_BUTTON_DEFAULT :{BLACK}Default STR_BUTTON_CANCEL :{BLACK}Cancel STR_BUTTON_OK :{BLACK}OK +STR_BUTTON_MOVE :{BLACK}Move # On screen keyboard window STR_OSK_KEYBOARD_LAYOUT :`1234567890-= qwertyuiop[]asdfghjkl;'#\zxcvbnm,./ . @@ -3687,7 +3688,7 @@ STR_SIGN_LIST_MATCH_CASE :{BLACK}Match ca STR_SIGN_LIST_MATCH_CASE_TOOLTIP :{BLACK}Toggle matching case when comparing sign names against the filter string # Sign window -STR_EDIT_SIGN_CAPTION :{WHITE}Edit sign text +STR_EDIT_SIGN_CAPTION :{WHITE}Edit sign STR_EDIT_SIGN_LOCATION_TOOLTIP :{BLACK}Centre the main view on sign location. Ctrl+Click to open a new viewport on sign location STR_EDIT_SIGN_NEXT_SIGN_TOOLTIP :{BLACK}Go to next sign STR_EDIT_SIGN_PREVIOUS_SIGN_TOOLTIP :{BLACK}Go to previous sign @@ -3904,14 +3905,14 @@ STR_CARGO_RATING_EXCELLENT :Excellent STR_CARGO_RATING_OUTSTANDING :Outstanding STR_STATION_VIEW_CENTER_TOOLTIP :{BLACK}Centre main view on station location. Ctrl+Click to open a new viewport on station location -STR_STATION_VIEW_RENAME_TOOLTIP :{BLACK}Change name of station +STR_STATION_VIEW_EDIT_TOOLTIP :{BLACK}Rename station or move sign STR_STATION_VIEW_SCHEDULED_TRAINS_TOOLTIP :{BLACK}Show all trains which have this station on their schedule STR_STATION_VIEW_SCHEDULED_ROAD_VEHICLES_TOOLTIP :{BLACK}Show all road vehicles which have this station on their schedule STR_STATION_VIEW_SCHEDULED_AIRCRAFT_TOOLTIP :{BLACK}Show all aircraft which have this station on their schedule STR_STATION_VIEW_SCHEDULED_SHIPS_TOOLTIP :{BLACK}Show all ships which have this station on their schedule -STR_STATION_VIEW_RENAME_STATION_CAPTION :Rename station/loading area +STR_STATION_VIEW_EDIT_STATION_SIGN :{WHITE}Edit station sign STR_STATION_VIEW_CLOSE_AIRPORT :{BLACK}Close airport STR_STATION_VIEW_CLOSE_AIRPORT_TOOLTIP :{BLACK}Prevent aircraft from landing on this airport @@ -3919,11 +3920,11 @@ STR_STATION_VIEW_CLOSE_AIRPORT_TOOLTIP :{BLACK}Prevent # Waypoint/buoy view window STR_WAYPOINT_VIEW_CAPTION :{WHITE}{WAYPOINT} STR_WAYPOINT_VIEW_CENTER_TOOLTIP :{BLACK}Centre main view on waypoint location. Ctrl+Click to open a new viewport on waypoint location -STR_WAYPOINT_VIEW_CHANGE_WAYPOINT_NAME :{BLACK}Change waypoint name +STR_WAYPOINT_VIEW_EDIT_TOOLTIP :{BLACK}Rename waypoint or move sign STR_BUOY_VIEW_CENTER_TOOLTIP :{BLACK}Centre main view on buoy location. Ctrl+Click to open a new viewport on buoy location STR_BUOY_VIEW_RENAME_TOOLTIP :{BLACK}Change buoy name -STR_EDIT_WAYPOINT_NAME :{WHITE}Edit waypoint name +STR_WAYPOINT_VIEW_EDIT_WAYPOINT_SIGN :{WHITE}Edit waypoint sign # Finances window STR_FINANCES_CAPTION :{WHITE}{COMPANY} Finances {BLACK}{COMPANY_NUM} @@ -5137,6 +5138,7 @@ STR_ERROR_TOO_MANY_TRUCK_STOPS :{WHITE}Too many STR_ERROR_TOO_CLOSE_TO_ANOTHER_DOCK :{WHITE}Too close to another dock STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT :{WHITE}Too close to another airport STR_ERROR_CAN_T_RENAME_STATION :{WHITE}Can't rename station... +STR_ERROR_CAN_T_MOVE_STATION_NAME :{WHITE}Can't move station sign... STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... road owned by a town STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... road facing in the wrong direction STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... drive through stops can't have corners @@ -5168,6 +5170,7 @@ STR_ERROR_CAN_T_BUILD_RAIL_WAYPOINT :{WHITE}Can't bu STR_ERROR_CAN_T_BUILD_ROAD_WAYPOINT :{WHITE}Can't build road waypoint here... STR_ERROR_CAN_T_POSITION_BUOY_HERE :{WHITE}Can't place buoy here... STR_ERROR_CAN_T_CHANGE_WAYPOINT_NAME :{WHITE}Can't change waypoint name... +STR_ERROR_CAN_T_MOVE_WAYPOINT_NAME :{WHITE}Can't move waypoint sign... STR_ERROR_CAN_T_REMOVE_RAIL_WAYPOINT :{WHITE}Can't remove rail waypoint here... STR_ERROR_CAN_T_REMOVE_ROAD_WAYPOINT :{WHITE}Can't remove road waypoint here... diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 5b9d683a55..936480e855 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -13,11 +13,14 @@ #include "error.h" #include "gui.h" #include "gfx_layout.h" +#include "tilehighlight_func.h" #include "command_func.h" #include "company_func.h" #include "town.h" #include "string_func.h" #include "company_base.h" +#include "station_base.h" +#include "waypoint_base.h" #include "texteff.hpp" #include "strings_func.h" #include "window_func.h" @@ -27,6 +30,8 @@ #include "zoom_func.h" #include "viewport_func.h" #include "landscape_cmd.h" +#include "station_cmd.h" +#include "waypoint_cmd.h" #include "rev.h" #include "timer/timer.h" #include "timer/timer_window.h" @@ -899,6 +904,8 @@ struct QueryStringWindow : public Window QueryString editbox; ///< Editbox. QueryStringFlags flags{}; ///< Flags controlling behaviour of the window. + WidgetID last_user_action = INVALID_WIDGET; ///< Last started user action. + QueryStringWindow(std::string_view str, StringID caption, uint max_bytes, uint max_chars, WindowDesc &desc, Window *parent, CharSetFilter afilter, QueryStringFlags flags) : Window(desc), editbox(max_bytes, max_chars) { @@ -913,7 +920,9 @@ struct QueryStringWindow : public Window this->editbox.text.afilter = afilter; this->flags = flags; - this->InitNested(WN_QUERY_STRING); + this->CreateNestedTree(); + this->GetWidget(WID_QS_MOVE_SEL)->SetDisplayedPlane((this->flags.Test(QueryStringFlag::EnableMove)) ? 0 : SZSP_NONE); + this->FinishInitNested(WN_QUERY_STRING); this->parent = parent; @@ -961,16 +970,64 @@ struct QueryStringWindow : public Window case WID_QS_CANCEL: this->Close(); break; + + case WID_QS_MOVE: + this->last_user_action = widget; + + if (Station::IsExpected(Station::Get(this->parent->window_number))) { + /* this is a station */ + SetViewportStationRect(Station::Get(this->parent->window_number), !this->IsWidgetLowered(WID_QS_MOVE)); + } else { + /* this is a waypoint */ + SetViewportWaypointRect(Waypoint::Get(this->parent->window_number), !this->IsWidgetLowered(WID_QS_MOVE)); + } + + HandlePlacePushButton(this, WID_QS_MOVE, SPR_CURSOR_SIGN, HT_RECT); + break; } } + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + { + switch (this->last_user_action) { + case WID_QS_MOVE: // Move name button + if (Station::IsExpected(Station::Get(this->parent->window_number))) { + /* this is a station */ + Command::Post(STR_ERROR_CAN_T_MOVE_STATION_NAME, CcMoveStationName, this->parent->window_number, tile); + } else { + /* this is a waypoint */ + Command::Post(STR_ERROR_CAN_T_MOVE_WAYPOINT_NAME, CcMoveWaypointName, this->parent->window_number, tile); + } + break; + + default: NOT_REACHED(); + } + } + + void OnPlaceObjectAbort() override + { + if (Station::IsExpected(Station::Get(this->parent->window_number))) { + /* this is a station */ + SetViewportStationRect(Station::Get(this->parent->window_number), false); + } else { + /* this is a waypoint */ + SetViewportWaypointRect(Waypoint::Get(this->parent->window_number), false); + } + + this->RaiseButtons(); + } + void Close([[maybe_unused]] int data = 0) override { + if (this->parent->window_class == WC_STATION_VIEW) SetViewportStationRect(Station::Get(this->parent->window_number), false); + if (this->parent->window_class == WC_WAYPOINT_VIEW) SetViewportWaypointRect(Waypoint::Get(this->parent->window_number), false); + if (!this->editbox.handled && this->parent != nullptr) { Window *parent = this->parent; this->parent = nullptr; // so parent doesn't try to close us again parent->OnQueryTextFinished(std::nullopt); } + this->Window::Close(); } }; @@ -984,9 +1041,12 @@ static constexpr std::initializer_list _nested_query_string_widgets NWidget(WWT_EDITBOX, COLOUR_GREY, WID_QS_TEXT), SetMinimalSize(256, 0), SetFill(1, 0), SetPadding(2, 2, 2, 2), EndContainer(), NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_QS_DEFAULT), SetMinimalSize(87, 12), SetFill(1, 1), SetStringTip(STR_BUTTON_DEFAULT), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_QS_CANCEL), SetMinimalSize(86, 12), SetFill(1, 1), SetStringTip(STR_BUTTON_CANCEL), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_QS_OK), SetMinimalSize(87, 12), SetFill(1, 1), SetStringTip(STR_BUTTON_OK), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_QS_DEFAULT), SetMinimalSize(65, 12), SetFill(1, 1), SetStringTip(STR_BUTTON_DEFAULT), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_QS_CANCEL), SetMinimalSize(65, 12), SetFill(1, 1), SetStringTip(STR_BUTTON_CANCEL), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_QS_OK), SetMinimalSize(65, 12), SetFill(1, 1), SetStringTip(STR_BUTTON_OK), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_QS_MOVE_SEL), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_QS_MOVE), SetMinimalSize(65, 12), SetFill(1, 1), SetStringTip(STR_BUTTON_MOVE), + EndContainer(), EndContainer(), }; diff --git a/src/signs.cpp b/src/signs.cpp index a2bf7ab979..90fcf00cf2 100644 --- a/src/signs.cpp +++ b/src/signs.cpp @@ -55,11 +55,11 @@ void UpdateAllSignVirtCoords() } /** - * Check if the current company can rename a given sign. + * Check if the current company can rename or move a given sign. * @param *si The sign in question. - * @return true if the sign can be renamed, else false. + * @return true if the sign can be renamed or moved, else false. */ -bool CompanyCanRenameSign(const Sign *si) +bool CompanyCanEditSign(const Sign *si) { if (si->owner == OWNER_DEITY && _current_company != OWNER_DEITY && _game_mode != GM_EDITOR) return false; return true; diff --git a/src/signs_cmd.cpp b/src/signs_cmd.cpp index 0e02401129..9ac496ce1b 100644 --- a/src/signs_cmd.cpp +++ b/src/signs_cmd.cpp @@ -68,7 +68,7 @@ CommandCost CmdRenameSign(DoCommandFlags flags, SignID sign_id, const std::strin { Sign *si = Sign::GetIfValid(sign_id); if (si == nullptr) return CMD_ERROR; - if (!CompanyCanRenameSign(si)) return CMD_ERROR; + if (!CompanyCanEditSign(si)) return CMD_ERROR; /* Rename the signs when empty, otherwise remove it */ if (!text.empty()) { @@ -95,6 +95,36 @@ CommandCost CmdRenameSign(DoCommandFlags flags, SignID sign_id, const std::strin return CommandCost(); } +/** + * Move a sign to the given coordinates. Ownership of signs + * has no meaning/effect whatsoever except for eyecandy. + * @param flags type of operation + * @param sign_id index of the sign to be moved + * @param tile tile to place the sign at + * @return the cost of this operation or an error + */ +CommandCost CmdMoveSign(DoCommandFlags flags, SignID sign_id, TileIndex tile) +{ + Sign *si = Sign::GetIfValid(sign_id); + if (si == nullptr) return CMD_ERROR; + if (!CompanyCanEditSign(si)) return CMD_ERROR; + + /* Move the sign */ + if (flags.Test(DoCommandFlag::Execute)) { + int x = TileX(tile) * TILE_SIZE; + int y = TileY(tile) * TILE_SIZE; + + si->x = x; + si->y = y; + si->z = GetSlopePixelZ(x, y); + if (_game_mode != GM_EDITOR) si->owner = _current_company; + + si->UpdateVirtCoord(); + } + + return CommandCost(); +} + /** * Callback function that is called after a sign is placed * @param result of the operation diff --git a/src/signs_cmd.h b/src/signs_cmd.h index 3d8722b070..b51abd88d1 100644 --- a/src/signs_cmd.h +++ b/src/signs_cmd.h @@ -15,9 +15,11 @@ std::tuple CmdPlaceSign(DoCommandFlags flags, TileIndex tile, const std::string &text); CommandCost CmdRenameSign(DoCommandFlags flags, SignID sign_id, const std::string &text); +CommandCost CmdMoveSign(DoCommandFlags flags, SignID sign_id, TileIndex tile); DEF_CMD_TRAIT(CMD_PLACE_SIGN, CmdPlaceSign, CommandFlag::Deity, CommandType::OtherManagement) DEF_CMD_TRAIT(CMD_RENAME_SIGN, CmdRenameSign, CommandFlag::Deity, CommandType::OtherManagement) +DEF_CMD_TRAIT(CMD_MOVE_SIGN, CmdMoveSign, CommandFlag::Deity, CommandType::OtherManagement) void CcPlaceSign(Commands cmd, const CommandCost &result, SignID new_sign); diff --git a/src/signs_func.h b/src/signs_func.h index 18f27fc81e..21abdc98fe 100644 --- a/src/signs_func.h +++ b/src/signs_func.h @@ -17,7 +17,7 @@ struct Window; void UpdateAllSignVirtCoords(); void PlaceProc_Sign(TileIndex tile); -bool CompanyCanRenameSign(const Sign *si); +bool CompanyCanEditSign(const Sign *si); /* signs_gui.cpp */ void ShowRenameSignWindow(const Sign *si); diff --git a/src/signs_gui.cpp b/src/signs_gui.cpp index eb62f3e95d..109773b952 100644 --- a/src/signs_gui.cpp +++ b/src/signs_gui.cpp @@ -20,6 +20,7 @@ #include "viewport_func.h" #include "querystring_gui.h" #include "sortlist_type.h" +#include "tilehighlight_func.h" #include "stringfilter_type.h" #include "string_func.h" #include "core/geometry_func.hpp" @@ -387,9 +388,20 @@ static bool RenameSign(SignID index, std::string_view text) return remove; } +/** + * Actually move the sign. + * @param index the sign to move. + * @param tile on which to move the sign to. + */ +void MoveSign(SignID index, TileIndex tile) +{ + Command::Post(STR_ERROR_CAN_T_PLACE_SIGN_HERE, index, tile); +} + struct SignWindow : Window, SignList { QueryString name_editbox; SignID cur_sign{}; + WidgetID last_user_action = INVALID_WIDGET; ///< Last started user action. SignWindow(WindowDesc &desc, const Sign *si) : Window(desc), name_editbox(MAX_LENGTH_SIGN_NAME_CHARS * MAX_CHAR_LENGTH, MAX_LENGTH_SIGN_NAME_CHARS) { @@ -487,12 +499,6 @@ struct SignWindow : Window, SignList { break; } - case WID_QES_DELETE: - /* Only need to set the buffer to null, the rest is handled as the OK button */ - RenameSign(this->cur_sign, ""); - /* don't delete this, we are deleted in Sign::~Sign() -> DeleteRenameSignWindow() */ - break; - case WID_QES_OK: if (RenameSign(this->cur_sign, this->name_editbox.text.GetText())) break; [[fallthrough]]; @@ -500,8 +506,37 @@ struct SignWindow : Window, SignList { case WID_QES_CANCEL: this->Close(); break; + + case WID_QES_DELETE: + /* Only need to set the buffer to null, the rest is handled as the OK button */ + RenameSign(this->cur_sign, ""); + /* don't delete this, we are deleted in Sign::~Sign() -> DeleteRenameSignWindow() */ + break; + + case WID_QES_MOVE: + HandlePlacePushButton(this, WID_QES_MOVE, SPR_CURSOR_SIGN, HT_RECT); + this->last_user_action = widget; + break; } } + + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + { + switch (this->last_user_action) { + case WID_QES_MOVE: // Place sign button + RenameSign(this->cur_sign, this->name_editbox.text.GetText()); + MoveSign(this->cur_sign, tile); + this->Close(); + break; + + default: NOT_REACHED(); + } + } + + void OnPlaceObjectAbort() override + { + this->RaiseButtons(); + } }; static constexpr std::initializer_list _nested_query_sign_edit_widgets = { @@ -517,7 +552,7 @@ static constexpr std::initializer_list _nested_query_sign_edit_widg NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_QES_OK), SetMinimalSize(61, 12), SetStringTip(STR_BUTTON_OK), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_QES_CANCEL), SetMinimalSize(60, 12), SetStringTip(STR_BUTTON_CANCEL), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_QES_DELETE), SetMinimalSize(60, 12), SetStringTip(STR_TOWN_VIEW_DELETE_BUTTON), - NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), EndContainer(), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_QES_MOVE), SetMinimalSize(60, 12), SetStringTip(STR_BUTTON_MOVE), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_QES_PREVIOUS), SetMinimalSize(11, 12), SetArrowWidgetTypeTip(AWV_DECREASE, STR_EDIT_SIGN_PREVIOUS_SIGN_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_QES_NEXT), SetMinimalSize(11, 12), SetArrowWidgetTypeTip(AWV_INCREASE, STR_EDIT_SIGN_NEXT_SIGN_TOOLTIP), EndContainer(), @@ -536,8 +571,8 @@ static WindowDesc _query_sign_edit_desc( */ void HandleClickOnSign(const Sign *si) { - /* If we can't rename the sign, don't even open the rename GUI. */ - if (!CompanyCanRenameSign(si)) return; + /* If we can't edit the sign, don't even open the rename GUI. */ + if (!CompanyCanEditSign(si)) return; if (_ctrl_pressed && (si->owner == _local_company || (si->owner == OWNER_DEITY && _game_mode == GM_EDITOR))) { RenameSign(si->index, ""); diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index c77f4c0440..4bc5e35091 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -29,6 +29,7 @@ #include "road_internal.h" /* For drawing catenary/checking road removal */ #include "autoslope.h" #include "water.h" +#include "tilehighlight_func.h" #include "strings_func.h" #include "clear_func.h" #include "timer/timer_game_calendar.h" @@ -71,6 +72,7 @@ #include "station_layout_type.h" #include "widgets/station_widget.h" +#include "widgets/misc_widget.h" #include "table/strings.h" #include "table/station_land.h" @@ -4475,6 +4477,59 @@ CommandCost CmdRenameStation(DoCommandFlags flags, StationID station_id, const s return CommandCost(); } +/** + * Move a station name. + * @param flags type of operation + * @param station_id id of the station + * @param tile to move the station name to + * @return the cost of this operation or an error and the station ID + */ +std::tuple CmdMoveStationName(DoCommandFlags flags, StationID station_id, TileIndex tile) +{ + Station *st = Station::GetIfValid(station_id); + if (st == nullptr) return { CMD_ERROR, StationID::Invalid() }; + + if (st->owner != OWNER_NONE) { + CommandCost ret = CheckOwnership(st->owner); + if (ret.Failed()) return { ret, StationID::Invalid() }; + } + + const StationRect *r = &st->rect; + if (!r->PtInExtendedRect(TileX(tile), TileY(tile))) { + return { CommandCost(STR_ERROR_SITE_UNSUITABLE), StationID::Invalid() }; + } + + bool other_station = false; + /* Check if the tile is the base tile of another station */ + ForAllStationsRadius(tile, 0, [&](BaseStation *s) { + if (s != nullptr) { + if (s != st && s->xy == tile) other_station = true; + } + }); + if (other_station) return { CommandCost(STR_ERROR_SITE_UNSUITABLE), StationID::Invalid() }; + + if (flags.Test(DoCommandFlag::Execute)) { + st->MoveSign(tile); + + st->UpdateVirtCoord(); + } + return { CommandCost(), station_id }; +} + +/** +* Callback function that is called after a name is moved +* @param result of the operation +* @param station_id ID of the changed station +*/ +void CcMoveStationName(Commands, const CommandCost &result, StationID station_id) + { + if (result.Failed()) return; + + ResetObjectToPlace(); + Station *st = Station::Get(station_id); + SetViewportStationRect(st, false); + } + static void AddNearbyStationsByCatchment(TileIndex tile, StationList &stations, StationList &nearby) { for (Station *st : nearby) { diff --git a/src/station_cmd.h b/src/station_cmd.h index 38382cee27..5c12bf407d 100644 --- a/src/station_cmd.h +++ b/src/station_cmd.h @@ -30,6 +30,7 @@ CommandCost CmdRemoveFromRailStation(DoCommandFlags flags, TileIndex start, Tile CommandCost CmdBuildRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t length, RoadStopType stop_type, bool is_drive_through, DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent); CommandCost CmdRemoveRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width, uint8_t height, RoadStopType stop_type, bool remove_road); CommandCost CmdRenameStation(DoCommandFlags flags, StationID station_id, const std::string &text); +std::tuple CmdMoveStationName(DoCommandFlags flags, StationID station_id, TileIndex tile); CommandCost CmdOpenCloseAirport(DoCommandFlags flags, StationID station_id); DEF_CMD_TRAIT(CMD_BUILD_AIRPORT, CmdBuildAirport, CommandFlags({CommandFlag::Auto, CommandFlag::NoWater}), CommandType::LandscapeConstruction) @@ -39,6 +40,9 @@ DEF_CMD_TRAIT(CMD_REMOVE_FROM_RAIL_STATION, CmdRemoveFromRailStation, {}, DEF_CMD_TRAIT(CMD_BUILD_ROAD_STOP, CmdBuildRoadStop, CommandFlags({CommandFlag::Auto, CommandFlag::NoWater}), CommandType::LandscapeConstruction) DEF_CMD_TRAIT(CMD_REMOVE_ROAD_STOP, CmdRemoveRoadStop, {}, CommandType::LandscapeConstruction) DEF_CMD_TRAIT(CMD_RENAME_STATION, CmdRenameStation, {}, CommandType::OtherManagement) +DEF_CMD_TRAIT(CMD_MOVE_STATION_NAME, CmdMoveStationName, {}, CommandType::OtherManagement) DEF_CMD_TRAIT(CMD_OPEN_CLOSE_AIRPORT, CmdOpenCloseAirport, {}, CommandType::RouteManagement) +void CcMoveStationName(Commands cmd, const CommandCost &result, StationID station_id); + #endif /* STATION_CMD_H */ diff --git a/src/station_gui.cpp b/src/station_gui.cpp index 9cb198d434..5804a8eed2 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -35,6 +35,7 @@ #include "station_cmd.h" #include "widgets/station_widget.h" +#include "widgets/misc_widget.h" #include "table/strings.h" @@ -814,7 +815,7 @@ void ShowCompanyStations(CompanyID company) static constexpr std::initializer_list _nested_station_view_widgets = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), - NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SV_RENAME), SetAspect(WidgetDimensions::ASPECT_RENAME), SetSpriteTip(SPR_RENAME, STR_STATION_VIEW_RENAME_TOOLTIP), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SV_RENAME), SetAspect(WidgetDimensions::ASPECT_RENAME), SetSpriteTip(SPR_RENAME, STR_STATION_VIEW_EDIT_TOOLTIP), NWidget(WWT_CAPTION, COLOUR_GREY, WID_SV_CAPTION), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SV_LOCATION), SetAspect(WidgetDimensions::ASPECT_LOCATION), SetSpriteTip(SPR_GOTO_LOCATION, STR_STATION_VIEW_CENTER_TOOLTIP), NWidget(WWT_SHADEBOX, COLOUR_GREY), @@ -1957,6 +1958,8 @@ struct StationViewWindow : public Window { void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override { + Window *w = FindWindowByClass(WC_QUERY_STRING); + switch (widget) { case WID_SV_WAITING: this->HandleCargoWaitingClick(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SV_WAITING, WidgetDimensions::scaled.framerect.top) - this->vscroll->GetPosition()); @@ -1964,6 +1967,11 @@ struct StationViewWindow : public Window { case WID_SV_CATCHMENT: SetViewportCatchmentStation(Station::Get(this->window_number), !this->IsWidgetLowered(WID_SV_CATCHMENT)); + + if (w != nullptr && this->IsWidgetLowered(WID_SV_CATCHMENT)) { + if (w->parent->window_class == WC_STATION_VIEW && w->IsWidgetLowered(WID_QS_MOVE)) SetViewportStationRect(Station::Get(w->parent->window_number), true); + if (w->parent->window_class == WC_WAYPOINT_VIEW && w->IsWidgetLowered(WID_QS_MOVE)) SetViewportWaypointRect(Waypoint::Get(w->parent->window_number), true); + } break; case WID_SV_LOCATION: @@ -1990,8 +1998,8 @@ struct StationViewWindow : public Window { } case WID_SV_RENAME: - ShowQueryString(GetString(STR_STATION_NAME, this->window_number), STR_STATION_VIEW_RENAME_STATION_CAPTION, MAX_LENGTH_STATION_NAME_CHARS, - this, CS_ALPHANUMERAL, {QueryStringFlag::EnableDefault, QueryStringFlag::LengthIsInChars}); + ShowQueryString(GetString(STR_STATION_NAME, this->window_number), STR_STATION_VIEW_EDIT_STATION_SIGN, MAX_LENGTH_STATION_NAME_CHARS, + this, CS_ALPHANUMERAL, {QueryStringFlag::EnableDefault, QueryStringFlag::LengthIsInChars, QueryStringFlag::EnableMove}); break; case WID_SV_CLOSE_AIRPORT: diff --git a/src/textbuf_gui.h b/src/textbuf_gui.h index 5b758183c7..d3f4f9915d 100644 --- a/src/textbuf_gui.h +++ b/src/textbuf_gui.h @@ -19,6 +19,7 @@ enum class QueryStringFlag : uint8_t { AcceptUnchanged, ///< return success even when the text didn't change EnableDefault, ///< enable the 'Default' button ("\0" is returned) LengthIsInChars, ///< the length of the string is counted in characters + EnableMove, ///< enable the 'Move' button }; using QueryStringFlags = EnumBitSet; diff --git a/src/viewport.cpp b/src/viewport.cpp index 83d08f5c56..ea0da5a477 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -1009,7 +1009,9 @@ enum TileHighlightType : uint8_t { }; const Station *_viewport_highlight_station; ///< Currently selected station for coverage area highlight +const Station *_viewport_highlight_station_rect; ///< Currently selected station for rectangle highlight const Waypoint *_viewport_highlight_waypoint; ///< Currently selected waypoint for coverage area highlight +const Waypoint *_viewport_highlight_waypoint_rect; ///< Currently selected waypoint for rectangle highlight const Town *_viewport_highlight_town; ///< Currently selected town for coverage area highlight /** @@ -1023,10 +1025,23 @@ static TileHighlightType GetTileHighlightType(TileIndex t) if (IsTileType(t, MP_STATION) && GetStationIndex(t) == _viewport_highlight_station->index) return THT_WHITE; if (_viewport_highlight_station->TileIsInCatchment(t)) return THT_BLUE; } + + if (_viewport_highlight_station_rect != nullptr) { + if (IsTileType(t, MP_STATION) && GetStationIndex(t) == _viewport_highlight_station_rect->index) return THT_WHITE; + const StationRect *r = &_viewport_highlight_station_rect->rect; + if (r->PtInExtendedRect(TileX(t), TileY(t))) return THT_BLUE; + } + if (_viewport_highlight_waypoint != nullptr) { if (IsTileType(t, MP_STATION) && GetStationIndex(t) == _viewport_highlight_waypoint->index) return THT_BLUE; } + if (_viewport_highlight_waypoint_rect != nullptr) { + if (IsTileType(t, MP_STATION) && GetStationIndex(t) == _viewport_highlight_waypoint_rect->index) return THT_WHITE; + const StationRect *r = &_viewport_highlight_waypoint_rect->rect; + if (r->PtInExtendedRect(TileX(t), TileY(t))) return THT_BLUE; + } + if (_viewport_highlight_town != nullptr) { if (IsTileType(t, MP_HOUSE)) { if (GetTownIndex(t) == _viewport_highlight_town->index) { @@ -3635,6 +3650,7 @@ void MarkCatchmentTilesDirty() MarkWholeScreenDirty(); return; } + if (_viewport_highlight_station != nullptr) { if (_viewport_highlight_station->catchment_tiles.tile == INVALID_TILE) { MarkWholeScreenDirty(); @@ -3646,18 +3662,35 @@ void MarkCatchmentTilesDirty() } } } + + if (_viewport_highlight_station_rect != nullptr) { + if (!_viewport_highlight_station_rect->IsInUse()) { + _viewport_highlight_station_rect = nullptr; + } + MarkWholeScreenDirty(); + } + if (_viewport_highlight_waypoint != nullptr) { if (!_viewport_highlight_waypoint->IsInUse()) { _viewport_highlight_waypoint = nullptr; } MarkWholeScreenDirty(); } + + if (_viewport_highlight_waypoint_rect != nullptr) { + if (!_viewport_highlight_waypoint_rect->IsInUse()) { + _viewport_highlight_waypoint_rect = nullptr; + } + MarkWholeScreenDirty(); + } } static void SetWindowDirtyForViewportCatchment() { if (_viewport_highlight_station != nullptr) SetWindowDirty(WC_STATION_VIEW, _viewport_highlight_station->index); + if (_viewport_highlight_station_rect != nullptr) SetWindowDirty(WC_STATION_VIEW, _viewport_highlight_station_rect->index); if (_viewport_highlight_waypoint != nullptr) SetWindowDirty(WC_WAYPOINT_VIEW, _viewport_highlight_waypoint->index); + if (_viewport_highlight_waypoint_rect != nullptr) SetWindowDirty(WC_WAYPOINT_VIEW, _viewport_highlight_waypoint_rect->index); if (_viewport_highlight_town != nullptr) SetWindowDirty(WC_TOWN_VIEW, _viewport_highlight_town->index); } @@ -3665,7 +3698,9 @@ static void ClearViewportCatchment() { MarkCatchmentTilesDirty(); _viewport_highlight_station = nullptr; + _viewport_highlight_station_rect = nullptr; _viewport_highlight_waypoint = nullptr; + _viewport_highlight_waypoint_rect = nullptr; _viewport_highlight_town = nullptr; } @@ -3689,6 +3724,26 @@ void SetViewportCatchmentStation(const Station *st, bool sel) if (_viewport_highlight_station != nullptr) SetWindowDirty(WC_STATION_VIEW, _viewport_highlight_station->index); } +/** + * Select or deselect station for rectangle area highlight. + * Selecting a station will deselect a town. + * @param *st Station in question + * @param sel Select or deselect given station + */ +void SetViewportStationRect(const Station *st, bool sel) +{ + SetWindowDirtyForViewportCatchment(); + if (sel && _viewport_highlight_station_rect != st) { // mark tiles dirty for redrawing and update selected station if a different station is already highlighted + ClearViewportCatchment(); + _viewport_highlight_station_rect = st; + MarkCatchmentTilesDirty(); + } else if (!sel && _viewport_highlight_station_rect == st) { // mark tiles dirty for redrawing and clear station selection if deselecting highlight + MarkCatchmentTilesDirty(); + _viewport_highlight_station_rect = nullptr; + } + if (_viewport_highlight_station_rect != nullptr) SetWindowDirty(WC_STATION_VIEW, _viewport_highlight_station_rect->index); // redraw the currently selected station window +} + /** * Select or deselect waypoint for coverage area highlight. * Selecting a waypoint will deselect a town. @@ -3709,6 +3764,26 @@ void SetViewportCatchmentWaypoint(const Waypoint *wp, bool sel) if (_viewport_highlight_waypoint != nullptr) SetWindowDirty(WC_WAYPOINT_VIEW, _viewport_highlight_waypoint->index); } +/** + * Select or deselect waypoint for rectangle area highlight. + * Selecting a waypoint will deselect a town. + * @param *wp Waypoint in question + * @param sel Select or deselect given waypoint + */ +void SetViewportWaypointRect(const Waypoint *wp, bool sel) +{ + SetWindowDirtyForViewportCatchment(); + if (sel && _viewport_highlight_waypoint_rect != wp) { // mark tiles dirty for redrawing and update selected waypoint if a different waypoint is already highlighted + ClearViewportCatchment(); + _viewport_highlight_waypoint_rect = wp; + MarkCatchmentTilesDirty(); + } else if (!sel && _viewport_highlight_waypoint_rect == wp) { // mark tiles dirty for redrawing and clear waypoint selection if deselecting highlight + MarkCatchmentTilesDirty(); + _viewport_highlight_waypoint_rect = nullptr; + } + if (_viewport_highlight_waypoint_rect != nullptr) SetWindowDirty(WC_WAYPOINT_VIEW, _viewport_highlight_waypoint_rect->index); // redraw the currently selected waypoint window +} + /** * Select or deselect town for coverage area highlight. * Selecting a town will deselect a station. diff --git a/src/viewport_func.h b/src/viewport_func.h index e218b41fe0..f58cf5ad80 100644 --- a/src/viewport_func.h +++ b/src/viewport_func.h @@ -104,7 +104,9 @@ struct Waypoint; struct Town; void SetViewportCatchmentStation(const Station *st, bool sel); +void SetViewportStationRect(const Station *st, bool sel); void SetViewportCatchmentWaypoint(const Waypoint *wp, bool sel); +void SetViewportWaypointRect(const Waypoint *wp, bool sel); void SetViewportCatchmentTown(const Town *t, bool sel); void MarkCatchmentTilesDirty(); diff --git a/src/waypoint_cmd.cpp b/src/waypoint_cmd.cpp index f76d9bd1f7..aee8f9dbd0 100644 --- a/src/waypoint_cmd.cpp +++ b/src/waypoint_cmd.cpp @@ -16,9 +16,11 @@ #include "waypoint_base.h" #include "pathfinder/yapf/yapf_cache.h" #include "pathfinder/water_regions.h" +#include "tilehighlight_func.h" #include "strings_func.h" #include "viewport_func.h" #include "viewport_kdtree.h" +#include "station_kdtree.h" #include "window_func.h" #include "timer/timer_game_calendar.h" #include "vehicle_func.h" @@ -33,6 +35,8 @@ #include "landscape_cmd.h" #include "station_layout_type.h" +#include "widgets/misc_widget.h" + #include "table/strings.h" #include "safeguards.h" @@ -603,3 +607,56 @@ CommandCost CmdRenameWaypoint(DoCommandFlags flags, StationID waypoint_id, const } return CommandCost(); } + +/** + * Move a waypoint name. + * @param flags type of operation + * @param waypoint_id id of waypoint + * @param tile to move the waypoint name to + * @return the cost of this operation or an error and the waypoint ID + */ +std::tuple CmdMoveWaypointName(DoCommandFlags flags, StationID waypoint_id, TileIndex tile) +{ + Waypoint *wp = Waypoint::GetIfValid(waypoint_id); + if (wp == nullptr) return { CMD_ERROR, StationID::Invalid() }; + + if (wp->owner != OWNER_NONE) { + CommandCost ret = CheckOwnership(wp->owner); + if (ret.Failed()) return { ret, StationID::Invalid() }; + } + + const StationRect *r = &wp->rect; + if (!r->PtInExtendedRect(TileX(tile), TileY(tile))) { + return { CommandCost(STR_ERROR_SITE_UNSUITABLE), StationID::Invalid() }; + } + + bool other_station = false; + /* Check if the tile is the base tile of another station */ + ForAllStationsRadius(tile, 0, [&](BaseStation *st) { + if (st != nullptr) { + if (st != wp && st->xy == tile) other_station = true; + } + }); + if (other_station) return { CommandCost(STR_ERROR_SITE_UNSUITABLE), StationID::Invalid() }; + + if (flags.Test(DoCommandFlag::Execute)) { + wp->MoveSign(tile); + + wp->UpdateVirtCoord(); + } + return { CommandCost(), waypoint_id }; +} + +/** + * Callback function that is called after a name is moved + * @param result of the operation + * @param waypoint_id ID of the changed waypoint + */ +void CcMoveWaypointName(Commands, const CommandCost &result, StationID waypoint_id) +{ + if (result.Failed()) return; + + ResetObjectToPlace(); + Waypoint *wp = Waypoint::Get(waypoint_id); + SetViewportCatchmentWaypoint(wp, false); +} diff --git a/src/waypoint_cmd.h b/src/waypoint_cmd.h index 2831c5c981..61cd6f4581 100644 --- a/src/waypoint_cmd.h +++ b/src/waypoint_cmd.h @@ -22,6 +22,7 @@ CommandCost CmdBuildRoadWaypoint(DoCommandFlags flags, TileIndex start_tile, Axi CommandCost CmdRemoveFromRoadWaypoint(DoCommandFlags flags, TileIndex start, TileIndex end); CommandCost CmdBuildBuoy(DoCommandFlags flags, TileIndex tile); CommandCost CmdRenameWaypoint(DoCommandFlags flags, StationID waypoint_id, const std::string &text); +std::tuple CmdMoveWaypointName(DoCommandFlags flags, StationID waypoint_id, TileIndex tile); DEF_CMD_TRAIT(CMD_BUILD_RAIL_WAYPOINT, CmdBuildRailWaypoint, {}, CommandType::LandscapeConstruction) DEF_CMD_TRAIT(CMD_REMOVE_FROM_RAIL_WAYPOINT, CmdRemoveFromRailWaypoint, {}, CommandType::LandscapeConstruction) @@ -29,5 +30,8 @@ DEF_CMD_TRAIT(CMD_BUILD_ROAD_WAYPOINT, CmdBuildRoadWaypoint, {}, DEF_CMD_TRAIT(CMD_REMOVE_FROM_ROAD_WAYPOINT, CmdRemoveFromRoadWaypoint, {}, CommandType::LandscapeConstruction) DEF_CMD_TRAIT(CMD_BUILD_BUOY, CmdBuildBuoy, CommandFlag::Auto, CommandType::LandscapeConstruction) DEF_CMD_TRAIT(CMD_RENAME_WAYPOINT, CmdRenameWaypoint, {}, CommandType::OtherManagement) +DEF_CMD_TRAIT(CMD_MOVE_WAYPOINT_NAME, CmdMoveWaypointName, {}, CommandType::OtherManagement) + +void CcMoveWaypointName(Commands cmd, const CommandCost &result, StationID waypoint_id); #endif /* WAYPOINT_CMD_H */ diff --git a/src/waypoint_gui.cpp b/src/waypoint_gui.cpp index 6efd4cb29a..ae95632de8 100644 --- a/src/waypoint_gui.cpp +++ b/src/waypoint_gui.cpp @@ -20,10 +20,12 @@ #include "company_base.h" #include "window_func.h" #include "waypoint_base.h" +#include "station_base.h" #include "waypoint_cmd.h" #include "zoom_func.h" #include "widgets/waypoint_widget.h" +#include "widgets/misc_widget.h" #include "table/strings.h" @@ -87,7 +89,7 @@ public: } if (this->vt != VEH_SHIP) { this->GetWidget(WID_W_CENTER_VIEW)->SetToolTip(STR_WAYPOINT_VIEW_CENTER_TOOLTIP); - this->GetWidget(WID_W_RENAME)->SetToolTip(STR_WAYPOINT_VIEW_CHANGE_WAYPOINT_NAME); + this->GetWidget(WID_W_RENAME)->SetToolTip(STR_WAYPOINT_VIEW_EDIT_TOOLTIP); } this->FinishInitNested(window_number); @@ -125,6 +127,8 @@ public: void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override { + Window *w = FindWindowByClass(WC_QUERY_STRING); + switch (widget) { case WID_W_CENTER_VIEW: // scroll to location if (_ctrl_pressed) { @@ -135,7 +139,8 @@ public: break; case WID_W_RENAME: // rename - ShowQueryString(GetString(STR_WAYPOINT_NAME, this->wp->index), STR_EDIT_WAYPOINT_NAME, MAX_LENGTH_STATION_NAME_CHARS, this, CS_ALPHANUMERAL, {QueryStringFlag::EnableDefault, QueryStringFlag::LengthIsInChars}); + ShowQueryString(GetString(STR_WAYPOINT_NAME, this->wp->index), STR_WAYPOINT_VIEW_EDIT_WAYPOINT_SIGN, MAX_LENGTH_STATION_NAME_CHARS, this, CS_ALPHANUMERAL, + {QueryStringFlag::EnableDefault, QueryStringFlag::LengthIsInChars, QueryStringFlag::EnableMove}); break; case WID_W_SHOW_VEHICLES: // show list of vehicles having this waypoint in their orders @@ -144,6 +149,11 @@ public: case WID_W_CATCHMENT: SetViewportCatchmentWaypoint(Waypoint::Get(this->window_number), !this->IsWidgetLowered(WID_W_CATCHMENT)); + + if (w != nullptr && this->IsWidgetLowered(WID_W_CATCHMENT)) { + if (w->parent->window_class == WC_STATION_VIEW && w->IsWidgetLowered(WID_QS_MOVE)) SetViewportStationRect(Station::Get(w->parent->window_number), true); + if (w->parent->window_class == WC_WAYPOINT_VIEW && w->IsWidgetLowered(WID_QS_MOVE)) SetViewportWaypointRect(Waypoint::Get(w->parent->window_number), true); + } break; } } diff --git a/src/widgets/misc_widget.h b/src/widgets/misc_widget.h index 0d59ee4f8e..758d418014 100644 --- a/src/widgets/misc_widget.h +++ b/src/widgets/misc_widget.h @@ -35,6 +35,8 @@ enum QueryStringWidgets : WidgetID { WID_QS_DEFAULT, ///< Default button. WID_QS_CANCEL, ///< Cancel button. WID_QS_OK, ///< OK button. + WID_QS_MOVE, ///< Move button. + WID_QS_MOVE_SEL, ///< Container for move button, which can be hidden. }; /** Widgets of the #QueryWindow class. */ diff --git a/src/widgets/sign_widget.h b/src/widgets/sign_widget.h index e8b9a95866..d6c023465c 100644 --- a/src/widgets/sign_widget.h +++ b/src/widgets/sign_widget.h @@ -28,6 +28,7 @@ enum QueryEditSignWidgets : WidgetID { WID_QES_OK, ///< OK button. WID_QES_CANCEL, ///< Cancel button. WID_QES_DELETE, ///< Delete button. + WID_QES_MOVE, ///< Move Sign button. WID_QES_PREVIOUS, ///< Previous button. WID_QES_NEXT, ///< Next button. };