From 18649c800b1b5867835a6085d93841ae560d423b Mon Sep 17 00:00:00 2001 From: Richard Wheeler <2762690+zephyris@users.noreply.github.com> Date: Mon, 24 Nov 2025 19:43:12 +0000 Subject: [PATCH] Fix: Bridge deck sprites were not toyland aware (#14821) --- media/baseset/openttd.grf | Bin 641653 -> 650822 bytes media/baseset/openttd.grf.hash | 2 +- media/baseset/openttd/CMakeLists.txt | 1 + media/baseset/openttd/bridge_decks.nfo | 34 ++++++++ .../baseset/openttd/bridge_decks_toyland.png | Bin 0 -> 21885 bytes 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 | 15 +++- src/textbuf_gui.h | 1 + src/viewport.cpp | 75 ++++++++++++++++++ src/viewport_func.h | 2 + src/viewport_type.h | 1 + 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 + 25 files changed, 417 insertions(+), 30 deletions(-) create mode 100644 media/baseset/openttd/bridge_decks_toyland.png diff --git a/media/baseset/openttd.grf b/media/baseset/openttd.grf index 4464c415f3a7cfa8e124513b97ec6b079eda21fa..ed897ecd4ee4e9ae67723c6efd8ef914eb7e7909 100644 GIT binary patch delta 9250 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 delta 311 zcmX?hNB!#*bx{Te_aL_>UM?vv+e}6V1{MYehW|c|qFY57gE9n}K|EGz36SVtGj0Zk z=~Wqw=i6f$SlVr~Sley0*xGHg*xPNhIJVnnaY|V;nr)9*%E`$% zJ$xx=^z=Q;IM*{;Y|mNFxrLF@a=PM5&a;eG)8DV;JjrOiedj99+l;I>+zbp?rtdkx z8NB_?8qO9*M%(R$>o~77GTLqT+`#F~#Av_0cN6ClMn;G2Mq4NADjFHiEd;KBKGmMO0+kK93Dl##8Z_hghGSFxHrQ@7gjEuh9ZBKG) JF@ZvX0RU-pV=Djv diff --git a/media/baseset/openttd.grf.hash b/media/baseset/openttd.grf.hash index f940ca5d62..9c734c87a4 100644 --- a/media/baseset/openttd.grf.hash +++ b/media/baseset/openttd.grf.hash @@ -1 +1 @@ -b779126d7cd1567eb09a0a7871f70a71 +c56aad31380c077d619e124bd87c8270 diff --git a/media/baseset/openttd/CMakeLists.txt b/media/baseset/openttd/CMakeLists.txt index a7d768326e..5a37b35148 100644 --- a/media/baseset/openttd/CMakeLists.txt +++ b/media/baseset/openttd/CMakeLists.txt @@ -33,6 +33,7 @@ if(GRFCODEC_FOUND) ${CMAKE_CURRENT_SOURCE_DIR}/aqueduct.png ${CMAKE_CURRENT_SOURCE_DIR}/autorail.png ${CMAKE_CURRENT_SOURCE_DIR}/bridge_decks.png + ${CMAKE_CURRENT_SOURCE_DIR}/bridge_decks_toyland.png ${CMAKE_CURRENT_SOURCE_DIR}/canals.png ${CMAKE_CURRENT_SOURCE_DIR}/canal_locks.png ${CMAKE_CURRENT_SOURCE_DIR}/chars.png diff --git a/media/baseset/openttd/bridge_decks.nfo b/media/baseset/openttd/bridge_decks.nfo index beced3f59f..b7edf0f143 100644 --- a/media/baseset/openttd/bridge_decks.nfo +++ b/media/baseset/openttd/bridge_decks.nfo @@ -4,6 +4,7 @@ // See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . // -1 * 0 0C "Bridge decks" + -1 * 0 07 83 01 \7= 03 19 -1 * 3 05 1B 18 -1 sprites/bridge_decks.png 8bpp 96 16 64 31 -31 0 normal @@ -34,3 +35,36 @@ -1 sprites/bridge_decks.png 8bpp 256 181 64 23 -31 0 normal -1 sprites/bridge_decks.png 8bpp 336 181 64 23 -31 0 normal -1 sprites/bridge_decks.png 8bpp 416 181 64 39 -31 -8 normal + +// Toyland bridge decks, skip if not toyland. + -1 * 0 07 83 01 \7! 03 19 + -1 * 3 05 1B 18 + + -1 sprites/bridge_decks_toyland.png 8bpp 96 16 64 31 -31 0 normal + -1 sprites/bridge_decks_toyland.png 8bpp 16 16 64 31 -31 0 normal + -1 sprites/bridge_decks_toyland.png 8bpp 176 16 64 39 -31 -8 normal + -1 sprites/bridge_decks_toyland.png 8bpp 256 16 64 23 -31 0 normal + -1 sprites/bridge_decks_toyland.png 8bpp 336 16 64 23 -31 0 normal + -1 sprites/bridge_decks_toyland.png 8bpp 416 16 64 39 -31 -8 normal + + -1 sprites/bridge_decks_toyland.png 8bpp 96 71 64 31 -31 0 normal + -1 sprites/bridge_decks_toyland.png 8bpp 16 71 64 31 -31 0 normal + -1 sprites/bridge_decks_toyland.png 8bpp 176 71 64 39 -31 -8 normal + -1 sprites/bridge_decks_toyland.png 8bpp 256 71 64 23 -31 0 normal + -1 sprites/bridge_decks_toyland.png 8bpp 336 71 64 23 -31 0 normal + -1 sprites/bridge_decks_toyland.png 8bpp 416 71 64 39 -31 -8 normal + + -1 sprites/bridge_decks_toyland.png 8bpp 96 126 64 31 -31 0 normal + -1 sprites/bridge_decks_toyland.png 8bpp 16 126 64 31 -31 0 normal + -1 sprites/bridge_decks_toyland.png 8bpp 176 126 64 39 -31 -8 normal + -1 sprites/bridge_decks_toyland.png 8bpp 256 126 64 23 -31 0 normal + -1 sprites/bridge_decks_toyland.png 8bpp 336 126 64 23 -31 0 normal + -1 sprites/bridge_decks_toyland.png 8bpp 416 126 64 39 -31 -8 normal + +// X and Y axis are swapped for road surface. + -1 sprites/bridge_decks_toyland.png 8bpp 16 181 64 31 -31 0 normal + -1 sprites/bridge_decks_toyland.png 8bpp 96 181 64 31 -31 0 normal + -1 sprites/bridge_decks_toyland.png 8bpp 176 181 64 39 -31 -8 normal + -1 sprites/bridge_decks_toyland.png 8bpp 256 181 64 23 -31 0 normal + -1 sprites/bridge_decks_toyland.png 8bpp 336 181 64 23 -31 0 normal + -1 sprites/bridge_decks_toyland.png 8bpp 416 181 64 39 -31 -8 normal diff --git a/media/baseset/openttd/bridge_decks_toyland.png b/media/baseset/openttd/bridge_decks_toyland.png new file mode 100644 index 0000000000000000000000000000000000000000..0c1495d766c539b307674b84d3ed10a5ea2bb060 GIT binary patch literal 21885 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 literal 0 HcmV?d00001 diff --git a/src/command_type.h b/src/command_type.h index 0f13917d0a..d62ce06f5c 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -218,6 +218,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 @@ -279,10 +280,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 2b72e86f95..73353498f5 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -313,6 +313,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,./ . @@ -3690,7 +3691,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 @@ -3907,14 +3908,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 @@ -3922,11 +3923,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} @@ -5140,6 +5141,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 @@ -5171,6 +5173,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 8734d1fe08..04b24a87c6 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" @@ -938,6 +943,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) { @@ -952,7 +959,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; @@ -1000,16 +1009,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, CM_DDSP_MOVE_SIGN); + 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(); } }; @@ -1023,9 +1080,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 be1072e9c8..ce1c3f7440 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" @@ -389,9 +390,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) { @@ -489,12 +501,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]]; @@ -502,8 +508,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, CM_DDSP_MOVE_SIGN); + 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 = { @@ -519,7 +554,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(), @@ -538,8 +573,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 (citymania::_fn_mod && (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 0a9718d1ee..6f1a63ea36 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" @@ -4482,6 +4484,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 a73380730e..a56666bd3e 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 f6e6be8608..161c0b5640 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -36,6 +36,7 @@ #include "station_cmd.h" #include "widgets/station_widget.h" +#include "widgets/misc_widget.h" #include "table/strings.h" @@ -842,7 +843,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), @@ -1987,6 +1988,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()); @@ -1994,7 +1997,11 @@ struct StationViewWindow : public Window { case WID_SV_CATCHMENT: citymania::SetHighlightCoverageStation(Station::Get(this->window_number), !this->IsWidgetLowered(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: @@ -2021,8 +2028,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 10b8ac73d4..0f4d81df4f 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -1076,7 +1076,9 @@ static void DrawAutorailSelection(const TileInfo *ti, HighLightStyle autorail_ty // }; 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 /** @@ -1093,10 +1095,23 @@ static TileHighlightType GetTileHighlightType(TileIndex t) } #endif // TODO CM should handle this too + + 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) { @@ -4213,6 +4228,7 @@ void MarkCatchmentTilesDirty() MarkWholeScreenDirty(); return; } + if (_viewport_highlight_station != nullptr) { if (_viewport_highlight_station->catchment_tiles.tile == INVALID_TILE) { MarkWholeScreenDirty(); @@ -4224,18 +4240,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); } @@ -4243,7 +4276,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; } @@ -4267,6 +4302,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. @@ -4287,6 +4342,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 f21edd24fe..5e4a5ea72d 100644 --- a/src/viewport_func.h +++ b/src/viewport_func.h @@ -105,7 +105,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/viewport_type.h b/src/viewport_type.h index 031d7d92fc..20214c63ec 100644 --- a/src/viewport_type.h +++ b/src/viewport_type.h @@ -219,6 +219,7 @@ enum ViewportDragDropSelectionProcess : uint8_t { CM_DDSP_RAIL_BLUEPRINT, CM_DDSP_BLUEPRINT_AREA, ///< Blueprint area to copy selection CM_DDSP_BUILD_HOUSE, + CM_DDSP_MOVE_SIGN, }; 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 a27d77a319..698009adec 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" @@ -89,7 +91,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); @@ -127,6 +129,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 (citymania::_fn_mod) { @@ -137,7 +141,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 @@ -146,6 +151,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. };