From 3ef0ec81df704244e5496078774445b601c269f4 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Wed, 25 Jun 2025 09:08:03 +0100 Subject: [PATCH 1/2] Codechange: Add helper method to test if a command will succeed. --- src/airport_gui.cpp | 2 +- src/command_func.h | 11 +++++++++++ src/dock_gui.cpp | 2 +- src/rail_gui.cpp | 6 +++--- src/road_gui.cpp | 6 +++--- src/town_cmd.cpp | 4 ++-- 6 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/airport_gui.cpp b/src/airport_gui.cpp index de467e0a5c..e4d7d7fddd 100644 --- a/src/airport_gui.cpp +++ b/src/airport_gui.cpp @@ -73,7 +73,7 @@ static void PlaceAirport(TileIndex tile) auto proc = [=](bool test, StationID to_join) -> bool { if (test) { - return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), tile, airport_type, layout, StationID::Invalid(), adjacent).Succeeded(); + return Command::Query(tile, airport_type, layout, StationID::Invalid(), adjacent).Succeeded(); } else { return Command::Post(STR_ERROR_CAN_T_BUILD_AIRPORT_HERE, CcBuildAirport, tile, airport_type, layout, to_join, adjacent); } diff --git a/src/command_func.h b/src/command_func.h index cb3ba95610..8771635b53 100644 --- a/src/command_func.h +++ b/src/command_func.h @@ -164,6 +164,17 @@ public: return res; } + /** + * Shortcut to query a command with its flags to test if it will succeed. + * @param args Parameters for the command. + * @return cost of the command. + */ + static CommandCost Query(Targs... args) + { + Tret res = Do(CommandFlagsToDCFlags(GetCommandFlags()), args...); + return ExtractCommandCost(res); + } + /** * Shortcut for the long Post when not using a callback. * @param err_message Message prefix to show on error diff --git a/src/dock_gui.cpp b/src/dock_gui.cpp index 68f3959e90..d4f3555245 100644 --- a/src/dock_gui.cpp +++ b/src/dock_gui.cpp @@ -219,7 +219,7 @@ struct BuildDocksToolbarWindow : Window { bool adjacent = _ctrl_pressed; auto proc = [=](bool test, StationID to_join) -> bool { if (test) { - return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), tile, StationID::Invalid(), adjacent).Succeeded(); + return Command::Query(tile, StationID::Invalid(), adjacent).Succeeded(); } else { return Command::Post(STR_ERROR_CAN_T_BUILD_DOCK_HERE, CcBuildDocks, tile, to_join, adjacent); } diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 291039204a..f9433225be 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -219,7 +219,7 @@ static void PlaceRail_Station(TileIndex tile) auto proc = [=](bool test, StationID to_join) -> bool { if (test) { - return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), tile, rt, params.axis, numtracks, platlength, params.sel_class, params.sel_type, StationID::Invalid(), adjacent).Succeeded(); + return Command::Query(tile, rt, params.axis, numtracks, platlength, params.sel_class, params.sel_type, StationID::Invalid(), adjacent).Succeeded(); } else { return Command::Post(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION, CcStation, tile, rt, params.axis, numtracks, platlength, params.sel_class, params.sel_type, to_join, adjacent); } @@ -777,7 +777,7 @@ struct BuildRailToolbarWindow : Window { auto proc = [=](bool test, StationID to_join) -> bool { if (test) { - return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), ta.tile, axis, ta.w, ta.h, _waypoint_gui.sel_class, _waypoint_gui.sel_type, StationID::Invalid(), adjacent).Succeeded(); + return Command::Query(ta.tile, axis, ta.w, ta.h, _waypoint_gui.sel_class, _waypoint_gui.sel_type, StationID::Invalid(), adjacent).Succeeded(); } else { return Command::Post(STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT, CcPlaySound_CONSTRUCTION_RAIL, ta.tile, axis, ta.w, ta.h, _waypoint_gui.sel_class, _waypoint_gui.sel_type, to_join, adjacent); } @@ -944,7 +944,7 @@ static void HandleStationPlacement(TileIndex start, TileIndex end) auto proc = [=](bool test, StationID to_join) -> bool { if (test) { - return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), ta.tile, rt, params.axis, numtracks, platlength, params.sel_class, params.sel_type, StationID::Invalid(), adjacent).Succeeded(); + return Command::Query(ta.tile, rt, params.axis, numtracks, platlength, params.sel_class, params.sel_type, StationID::Invalid(), adjacent).Succeeded(); } else { return Command::Post(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION, CcStation, ta.tile, rt, params.axis, numtracks, platlength, params.sel_class, params.sel_type, to_join, adjacent); } diff --git a/src/road_gui.cpp b/src/road_gui.cpp index d53b21668d..e141cdd957 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -237,8 +237,8 @@ static void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, RoadStopType auto proc = [=](bool test, StationID to_join) -> bool { if (test) { - return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), ta.tile, ta.w, ta.h, stop_type, drive_through, - ddir, rt, spec_class, spec_index, StationID::Invalid(), adjacent).Succeeded(); + return Command::Query(ta.tile, ta.w, ta.h, stop_type, drive_through, + ddir, rt, spec_class, spec_index, StationID::Invalid(), adjacent); } else { return Command::Post(err_msg, CcRoadStop, ta.tile, ta.w, ta.h, stop_type, drive_through, ddir, rt, spec_class, spec_index, to_join, adjacent); @@ -765,7 +765,7 @@ struct BuildRoadToolbarWindow : Window { auto proc = [=](bool test, StationID to_join) -> bool { if (test) { - return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), ta.tile, axis, ta.w, ta.h, _waypoint_gui.sel_class, _waypoint_gui.sel_type, StationID::Invalid(), adjacent).Succeeded(); + return Command::Query(ta.tile, axis, ta.w, ta.h, _waypoint_gui.sel_class, _waypoint_gui.sel_type, StationID::Invalid(), adjacent).Succeeded(); } else { return Command::Post(STR_ERROR_CAN_T_BUILD_ROAD_WAYPOINT, CcPlaySound_CONSTRUCTION_OTHER, ta.tile, axis, ta.w, ta.h, _waypoint_gui.sel_class, _waypoint_gui.sel_type, to_join, adjacent); } diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index e9e72f1aec..3f5f18ae2a 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -1378,7 +1378,7 @@ static bool GrowTownWithBridge(const Town *t, const TileIndex tile, const DiagDi /* Can we actually build the bridge? */ RoadType rt = GetTownRoadType(); - if (Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), tile, bridge_tile, TRANSPORT_ROAD, bridge_type, rt).Succeeded()) { + if (Command::Query(tile, bridge_tile, TRANSPORT_ROAD, bridge_type, rt).Succeeded()) { Command::Do(CommandFlagsToDCFlags(GetCommandFlags()).Set(DoCommandFlag::Execute), tile, bridge_tile, TRANSPORT_ROAD, bridge_type, rt); return true; } @@ -1448,7 +1448,7 @@ static bool GrowTownWithTunnel(const Town *t, const TileIndex tile, const DiagDi /* Attempt to build the tunnel. Return false if it fails to let the town build a road instead. */ RoadType rt = GetTownRoadType(); - if (Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), tile, TRANSPORT_ROAD, rt).Succeeded()) { + if (Command::Query(tile, TRANSPORT_ROAD, rt).Succeeded()) { Command::Do(CommandFlagsToDCFlags(GetCommandFlags()).Set(DoCommandFlag::Execute), tile, TRANSPORT_ROAD, rt); return true; } From adc1cb83c8d02df83467e624d9e8c6f2c3c77bf4 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 26 Jun 2025 12:37:23 +0100 Subject: [PATCH 2/2] Change: Make tile highlight red if the place operation would fail. --- src/airport_gui.cpp | 17 +++++--- src/command_func.h | 48 ++++++++++++++++++++- src/company_gui.cpp | 4 +- src/dock_gui.cpp | 32 ++++++++------ src/industry_gui.cpp | 39 +++++++++++++++-- src/object_gui.cpp | 13 +++--- src/order_gui.cpp | 4 +- src/rail_gui.cpp | 84 ++++++++++++++++++++++--------------- src/road_gui.cpp | 90 +++++++++++++++++++++++----------------- src/signs_cmd.cpp | 3 +- src/signs_func.h | 2 +- src/story_gui.cpp | 4 +- src/terraform_gui.cpp | 58 +++++++++++++------------- src/texteff.cpp | 4 +- src/texteff.hpp | 1 + src/tilehighlight_func.h | 6 +-- src/tilehighlight_type.h | 3 ++ src/toolbar_gui.cpp | 12 +++--- src/town_gui.cpp | 9 ++-- src/tree_gui.cpp | 11 ++--- src/viewport.cpp | 74 +++++++++++++++++++++++++++++---- src/viewport_func.h | 2 +- src/viewport_type.h | 2 + src/window.cpp | 7 ++-- src/window_gui.h | 5 ++- 25 files changed, 364 insertions(+), 170 deletions(-) diff --git a/src/airport_gui.cpp b/src/airport_gui.cpp index e4d7d7fddd..1bb5677059 100644 --- a/src/airport_gui.cpp +++ b/src/airport_gui.cpp @@ -63,7 +63,7 @@ void CcBuildAirport(Commands, const CommandCost &result, TileIndex tile) * Place an airport. * @param tile Position to put the new airport. */ -static void PlaceAirport(TileIndex tile) +static void PlaceAirport(bool query, TileIndex tile) { if (_selected_airport_index == -1) return; @@ -71,6 +71,11 @@ static void PlaceAirport(TileIndex tile) uint8_t layout = _selected_airport_layout; bool adjacent = _ctrl_pressed; + if (query) { + HandleSelectionQuery(Command::Query(tile, airport_type, layout, StationID::Invalid(), adjacent)); + return; + } + auto proc = [=](bool test, StationID to_join) -> bool { if (test) { return Command::Query(tile, airport_type, layout, StationID::Invalid(), adjacent).Succeeded(); @@ -141,15 +146,15 @@ struct BuildAirToolbarWindow : Window { } - void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile, bool query) override { switch (this->last_user_action) { case WID_AT_AIRPORT: - PlaceAirport(tile); + PlaceAirport(query, tile); break; case WID_AT_DEMOLISH: - PlaceProc_DemolishArea(tile); + PlaceProc_DemolishArea(query, tile); break; default: NOT_REACHED(); @@ -161,10 +166,10 @@ struct BuildAirToolbarWindow : Window { VpSelectTilesWithMethod(pt.x, pt.y, select_method); } - void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile) override + void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile, bool query) override { if (pt.x != -1 && select_proc == DDSP_DEMOLISH_AREA) { - GUIPlaceProcDragXY(select_proc, start_tile, end_tile); + GUIPlaceProcDragXY(query, select_proc, start_tile, end_tile); } } diff --git a/src/command_func.h b/src/command_func.h index 8771635b53..7cca86d3f2 100644 --- a/src/command_func.h +++ b/src/command_func.h @@ -94,6 +94,8 @@ protected: static void LogCommandExecution(Commands cmd, StringID err_message, const CommandDataBuffer &args, bool failed); }; +void HandleSelectionQuery(CommandCost &&res); + /** * Templated wrapper that exposes the command parameter arguments * for the various Command::Do/Post calls. @@ -169,12 +171,54 @@ public: * @param args Parameters for the command. * @return cost of the command. */ - static CommandCost Query(Targs... args) + static inline CommandCost Query(Targs... args) { - Tret res = Do(CommandFlagsToDCFlags(GetCommandFlags()), args...); + Tret res = Do(CommandFlagsToDCFlags(GetCommandFlags()), std::forward(args)...); return ExtractCommandCost(res); } + /** + * Post or query a command. + * @param query Whether to query (for selection) or post the command. + * @param err_message Message prefix to show on error. + * @param callback A callback function to call after the command is finished. + * @param args Parameters for the command. + * @return \c true if the command was posted and succeeded, else \c false. + */ + template + static inline bool PostOrQuery(bool query, StringID err_message, Tcallback *callback, Targs... args) + { + if (query) { + HandleSelectionQuery(Query(std::forward(args)...)); + return false; + } else { + return Post(err_message, callback, std::forward(args)...); + } + } + + /** + * Shortcut for the long PostOrQuery when not using a callback. + * @param query Whether to query (for selection) or post the command. + * @param err_message Message prefix to show on error. + * @param args Parameters for the command. + * @return \c true if the command was posted and succeeded, else \c false. + */ + static inline bool PostOrQuery(bool query, StringID err_message, Targs... args) + { + return PostOrQuery(query, err_message, nullptr, std::forward(args)...); + } + + /** + * Shortcut for the long PostOrQuery when not using a callback or an error message. + * @param query Whether to query (for selection) or post the command. + * @param args Parameters for the command. + * @return \c true if the command was posted and succeeded, else \c false. + */ + static inline bool PostOrQuery(bool query, Targs... args) + { + return PostOrQuery(query, static_cast(0), nullptr, std::forward(args)...); + } + /** * Shortcut for the long Post when not using a callback. * @param err_message Message prefix to show on error diff --git a/src/company_gui.cpp b/src/company_gui.cpp index 9debd59c5b..cfaff43886 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -2246,9 +2246,9 @@ struct CompanyWindow : Window this->SetDirty(); }}; - void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile, bool query) override { - if (Command::Post(STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS, tile, OBJECT_HQ, 0) && !_shift_pressed) { + if (Command::PostOrQuery(query, STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS, tile, OBJECT_HQ, 0) && !_shift_pressed) { ResetObjectToPlace(); this->RaiseButtons(); } diff --git a/src/dock_gui.cpp b/src/dock_gui.cpp index d4f3555245..c03644c9df 100644 --- a/src/dock_gui.cpp +++ b/src/dock_gui.cpp @@ -192,23 +192,23 @@ struct BuildDocksToolbarWindow : Window { this->last_clicked_widget = (DockToolbarWidgets)widget; } - void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile, bool query) override { switch (this->last_clicked_widget) { case WID_DT_CANAL: // Build canal button - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CREATE_WATER); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_CREATE_WATER); break; case WID_DT_LOCK: // Build lock button - Command::Post(STR_ERROR_CAN_T_BUILD_LOCKS, CcBuildDocks, tile); + Command::PostOrQuery(query, STR_ERROR_CAN_T_BUILD_LOCKS, CcBuildDocks, tile); break; case WID_DT_DEMOLISH: // Demolish aka dynamite button - PlaceProc_DemolishArea(tile); + PlaceProc_DemolishArea(query, tile); break; case WID_DT_DEPOT: // Build depot button - Command::Post(STR_ERROR_CAN_T_BUILD_SHIP_DEPOT, CcBuildDocks, tile, _ship_depot_direction); + Command::PostOrQuery(query, STR_ERROR_CAN_T_BUILD_SHIP_DEPOT, CcBuildDocks, tile, _ship_depot_direction); break; case WID_DT_STATION: { // Build station button @@ -217,6 +217,12 @@ struct BuildDocksToolbarWindow : Window { TileIndex tile_to = (dir != INVALID_DIAGDIR ? TileAddByDiagDir(tile, ReverseDiagDir(dir)) : tile); bool adjacent = _ctrl_pressed; + + if (query) { + HandleSelectionQuery(Command::Query(tile, StationID::Invalid(), adjacent)); + break; + } + auto proc = [=](bool test, StationID to_join) -> bool { if (test) { return Command::Query(tile, StationID::Invalid(), adjacent).Succeeded(); @@ -230,15 +236,15 @@ struct BuildDocksToolbarWindow : Window { } case WID_DT_BUOY: // Build buoy button - Command::Post(STR_ERROR_CAN_T_POSITION_BUOY_HERE, CcBuildDocks, tile); + Command::PostOrQuery(query, STR_ERROR_CAN_T_POSITION_BUOY_HERE, CcBuildDocks, tile); break; case WID_DT_RIVER: // Build river button (in scenario editor) - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CREATE_RIVER); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_CREATE_RIVER); break; case WID_DT_BUILD_AQUEDUCT: // Build aqueduct button - Command::Post(STR_ERROR_CAN_T_BUILD_AQUEDUCT_HERE, CcBuildBridge, tile, GetOtherAqueductEnd(tile), TRANSPORT_WATER, 0, 0); + Command::PostOrQuery(query, STR_ERROR_CAN_T_BUILD_AQUEDUCT_HERE, CcBuildBridge, tile, GetOtherAqueductEnd(tile), TRANSPORT_WATER, 0, 0); break; default: NOT_REACHED(); @@ -250,22 +256,22 @@ struct BuildDocksToolbarWindow : Window { VpSelectTilesWithMethod(pt.x, pt.y, select_method); } - void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile) override + void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile, bool query) override { if (pt.x != -1) { switch (select_proc) { case DDSP_DEMOLISH_AREA: - GUIPlaceProcDragXY(select_proc, start_tile, end_tile); + GUIPlaceProcDragXY(query, select_proc, start_tile, end_tile); break; case DDSP_CREATE_WATER: if (_game_mode == GM_EDITOR) { - Command::Post(STR_ERROR_CAN_T_BUILD_CANALS, CcPlaySound_CONSTRUCTION_WATER, end_tile, start_tile, _ctrl_pressed ? WATER_CLASS_SEA : WATER_CLASS_CANAL, false); + Command::PostOrQuery(query, STR_ERROR_CAN_T_BUILD_CANALS, CcPlaySound_CONSTRUCTION_WATER, end_tile, start_tile, _ctrl_pressed ? WATER_CLASS_SEA : WATER_CLASS_CANAL, false); } else { - Command::Post(STR_ERROR_CAN_T_BUILD_CANALS, CcPlaySound_CONSTRUCTION_WATER, end_tile, start_tile, WATER_CLASS_CANAL, _ctrl_pressed); + Command::PostOrQuery(query, STR_ERROR_CAN_T_BUILD_CANALS, CcPlaySound_CONSTRUCTION_WATER, end_tile, start_tile, WATER_CLASS_CANAL, _ctrl_pressed); } break; case DDSP_CREATE_RIVER: - Command::Post(STR_ERROR_CAN_T_PLACE_RIVERS, CcPlaySound_CONSTRUCTION_WATER, end_tile, start_tile, WATER_CLASS_RIVER, _ctrl_pressed); + Command::PostOrQuery(query, STR_ERROR_CAN_T_PLACE_RIVERS, CcPlaySound_CONSTRUCTION_WATER, end_tile, start_tile, WATER_CLASS_RIVER, _ctrl_pressed); break; default: break; diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 6119c486dd..2e0ac2cf64 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -655,6 +655,30 @@ public: MarkWholeScreenDirty(); } + Dimension GetIndustryLayoutSize(const IndustryTileLayout &layout) const + { + int max_x = 0; + int max_y = 0; + + /* Finds dimensions of largest variant of this industry */ + for (const IndustryTileLayoutTile &it : layout) { + if (it.gfx == GFX_WATERTILE_SPECIALCHECK) continue; // watercheck tiles don't count for footprint size + if (it.ti.x > max_x) max_x = it.ti.x; + if (it.ti.y > max_y) max_y = it.ti.y; + } + + return Dimension(max_x + 1, max_y + 1); + } + + Dimension GetMaxIndustryLayoutSize() const + { + Dimension d{}; + for (const auto &layout : GetIndustrySpec(this->selected_type)->layouts) { + d = maxdim(d, GetIndustryLayoutSize(layout)); + } + return d; + } + void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override { switch (widget) { @@ -697,6 +721,11 @@ public: this->SetButtons(); if (this->enabled && click_count > 1) this->OnClick(pt, WID_DPI_FUND_WIDGET, 1); + + if (_thd.GetCallbackWnd() == this) { + auto [x, y] = this->GetMaxIndustryLayoutSize(); + SetTileSelectSize(x, y); + } } break; } @@ -712,6 +741,8 @@ public: this->HandleButtonClick(WID_DPI_FUND_WIDGET); } else { HandlePlacePushButton(this, WID_DPI_FUND_WIDGET, SPR_CURSOR_INDUSTRY, HT_RECT); + auto [x, y] = this->GetMaxIndustryLayoutSize(); + SetTileSelectSize(x, y); } } break; @@ -725,7 +756,7 @@ public: this->vscroll->SetCapacityFromWidget(this, WID_DPI_MATRIX_WIDGET); } - void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile, bool query) override { bool success = true; /* We do not need to protect ourselves against "Random Many Industries" in this mode */ @@ -735,7 +766,7 @@ public: if (_game_mode == GM_EDITOR) { /* Show error if no town exists at all */ - if (Town::GetNumItems() == 0) { + if (!query && Town::GetNumItems() == 0) { ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_BUILD_HERE, indsp->name), GetEncodedString(STR_ERROR_MUST_FOUND_TOWN_FIRST), WL_INFO, pt.x, pt.y); return; @@ -745,9 +776,9 @@ public: AutoRestoreBackup backup_generating_world(_generating_world, true); AutoRestoreBackup backup_ignore_industry_restritions(_ignore_industry_restrictions, true); - Command::Post(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, &CcBuildIndustry, tile, this->selected_type, layout_index, false, seed); + Command::PostOrQuery(query, STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, &CcBuildIndustry, tile, this->selected_type, layout_index, false, seed); } else { - success = Command::Post(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, tile, this->selected_type, layout_index, false, seed); + success = Command::PostOrQuery(query, STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, tile, this->selected_type, layout_index, false, seed); } /* If an industry has been built, just reset the cursor and the system */ diff --git a/src/object_gui.cpp b/src/object_gui.cpp index dc80fbc8c6..36c30bf721 100644 --- a/src/object_gui.cpp +++ b/src/object_gui.cpp @@ -329,14 +329,14 @@ public: } } - void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile, bool query) override { const ObjectSpec *spec = ObjectClass::Get(_object_gui.sel_class)->GetSpec(_object_gui.sel_type); if (spec->size == OBJECT_SIZE_1X1) { - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_BUILD_OBJECT); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_BUILD_OBJECT); } else { - Command::Post(STR_ERROR_CAN_T_BUILD_OBJECT, CcPlaySound_CONSTRUCTION_OTHER, tile, spec->Index(), _object_gui.sel_view); + Command::PostOrQuery(query, STR_ERROR_CAN_T_BUILD_OBJECT, CcPlaySound_CONSTRUCTION_OTHER, tile, spec->Index(), _object_gui.sel_view); } } @@ -345,7 +345,7 @@ public: VpSelectTilesWithMethod(pt.x, pt.y, select_method); } - void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, [[maybe_unused]] ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile) override + void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, [[maybe_unused]] ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile, bool query) override { if (pt.x == -1) return; @@ -358,8 +358,9 @@ public: if (TileY(end_tile) == Map::MaxY()) end_tile += TileDiffXY(0, -1); } const ObjectSpec *spec = ObjectClass::Get(_object_gui.sel_class)->GetSpec(_object_gui.sel_type); - Command::Post(STR_ERROR_CAN_T_BUILD_OBJECT, CcPlaySound_CONSTRUCTION_OTHER, - end_tile, start_tile, spec->Index(), _object_gui.sel_view, (_ctrl_pressed ? true : false)); + + Command::PostOrQuery(query, STR_ERROR_CAN_T_BUILD_OBJECT, CcPlaySound_CONSTRUCTION_OTHER, + end_tile, start_tile, spec->Index(), _object_gui.sel_view, (_ctrl_pressed ? true : false)); } void OnPlaceObjectAbort() override diff --git a/src/order_gui.cpp b/src/order_gui.cpp index b09f97588d..385d3108e7 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -1467,8 +1467,10 @@ public: return ES_HANDLED; } - void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile, bool query) override { + if (query) return; + if (this->goto_type == OPOS_GOTO) { const Order cmd = GetOrderCmdFromTile(this->vehicle, tile); if (cmd.IsType(OT_NOTHING)) return; diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index f9433225be..739f831c5a 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -75,7 +75,7 @@ struct StationPickerSelection { static StationPickerSelection _station_gui; ///< Settings of the station picker. -static void HandleStationPlacement(TileIndex start, TileIndex end); +static void HandleStationPlacement(bool query, TileIndex start, TileIndex end); static void ShowBuildTrainDepotPicker(Window *parent); static void ShowBuildWaypointPicker(Window *parent); static Window *ShowStationBuilder(Window *parent); @@ -166,18 +166,20 @@ void CcRailDepot(Commands, const CommandCost &result, TileIndex tile, RailType, * Place a rail waypoint. * @param tile Position to start dragging a waypoint. */ -static void PlaceRail_Waypoint(TileIndex tile) +static void PlaceRail_Waypoint(bool query, TileIndex tile) { if (_remove_button_clicked) { - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_STATION); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_REMOVE_STATION); return; } Axis axis = GetAxisForNewRailWaypoint(tile); if (IsValidAxis(axis)) { /* Valid tile for waypoints */ - VpStartPlaceSizing(tile, axis == AXIS_X ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_STATION); + VpStartPlaceSizing(query, tile, axis == AXIS_X ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_STATION); VpSetPlaceSizingLimit(_settings_game.station.station_spread); + } else if (query) { + SetSelectionRed(true); } else { /* Tile where we can't build rail waypoints. This is always going to fail, * but provides the user with a proper error message. */ @@ -198,13 +200,13 @@ void CcStation(Commands, const CommandCost &result, TileIndex tile) * Place a rail station. * @param tile Position to place or start dragging a station. */ -static void PlaceRail_Station(TileIndex tile) +static void PlaceRail_Station(bool query, TileIndex tile) { if (_remove_button_clicked) { - VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_REMOVE_STATION); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y_LIMITED, DDSP_REMOVE_STATION); VpSetPlaceSizingLimit(-1); } else if (_settings_client.gui.station_dragdrop) { - VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_STATION); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_STATION); VpSetPlaceSizingLimit(_settings_game.station.station_spread); } else { int w = _settings_client.gui.station_numtracks; @@ -217,6 +219,11 @@ static void PlaceRail_Station(TileIndex tile) uint8_t platlength = _settings_client.gui.station_platlength; bool adjacent = _ctrl_pressed; + if (query) { + HandleSelectionQuery(Command::Query(tile, rt, params.axis, numtracks, platlength, params.sel_class, params.sel_type, StationID::Invalid(), adjacent)); + return; + } + auto proc = [=](bool test, StationID to_join) -> bool { if (test) { return Command::Query(tile, rt, params.axis, numtracks, platlength, params.sel_class, params.sel_type, StationID::Invalid(), adjacent).Succeeded(); @@ -292,14 +299,14 @@ static void GenericPlaceSignals(TileIndex tile) * @param tile Position of the first tile of the bridge. * @param w Rail toolbar window. */ -static void PlaceRail_Bridge(TileIndex tile, Window *w) +static void PlaceRail_Bridge(bool query, TileIndex tile, Window *w) { if (IsBridgeTile(tile)) { TileIndex other_tile = GetOtherTunnelBridgeEnd(tile); Point pt = {0, 0}; - w->OnPlaceMouseUp(VPM_X_OR_Y, DDSP_BUILD_BRIDGE, pt, other_tile, tile); + if (!query) w->OnPlaceMouseUp(VPM_X_OR_Y, DDSP_BUILD_BRIDGE, pt, other_tile, tile, query); } else { - VpStartPlaceSizing(tile, VPM_X_OR_Y, DDSP_BUILD_BRIDGE); + VpStartPlaceSizing(query, tile, VPM_X_OR_Y, DDSP_BUILD_BRIDGE); } } @@ -662,59 +669,59 @@ struct BuildRailToolbarWindow : Window { return Window::OnHotkey(hotkey); } - void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile, bool query) override { switch (this->last_user_action) { case WID_RAT_BUILD_NS: - VpStartPlaceSizing(tile, VPM_FIX_VERTICAL | VPM_RAILDIRS, DDSP_PLACE_RAIL); + VpStartPlaceSizing(query, tile, VPM_FIX_VERTICAL | VPM_RAILDIRS, DDSP_PLACE_RAIL); break; case WID_RAT_BUILD_X: - VpStartPlaceSizing(tile, VPM_FIX_Y | VPM_RAILDIRS, DDSP_PLACE_RAIL); + VpStartPlaceSizing(query, tile, VPM_FIX_Y | VPM_RAILDIRS, DDSP_PLACE_RAIL); break; case WID_RAT_BUILD_EW: - VpStartPlaceSizing(tile, VPM_FIX_HORIZONTAL | VPM_RAILDIRS, DDSP_PLACE_RAIL); + VpStartPlaceSizing(query, tile, VPM_FIX_HORIZONTAL | VPM_RAILDIRS, DDSP_PLACE_RAIL); break; case WID_RAT_BUILD_Y: - VpStartPlaceSizing(tile, VPM_FIX_X | VPM_RAILDIRS, DDSP_PLACE_RAIL); + VpStartPlaceSizing(query, tile, VPM_FIX_X | VPM_RAILDIRS, DDSP_PLACE_RAIL); break; case WID_RAT_AUTORAIL: - VpStartPlaceSizing(tile, VPM_RAILDIRS, DDSP_PLACE_RAIL); + VpStartPlaceSizing(query, tile, VPM_RAILDIRS, DDSP_PLACE_RAIL); break; case WID_RAT_DEMOLISH: - PlaceProc_DemolishArea(tile); + PlaceProc_DemolishArea(query, tile); break; case WID_RAT_BUILD_DEPOT: - Command::Post(STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT, CcRailDepot, tile, _cur_railtype, _build_depot_direction); + Command::PostOrQuery(query, STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT, CcRailDepot, tile, _cur_railtype, _build_depot_direction); break; case WID_RAT_BUILD_WAYPOINT: - PlaceRail_Waypoint(tile); + PlaceRail_Waypoint(query, tile); break; case WID_RAT_BUILD_STATION: - PlaceRail_Station(tile); + PlaceRail_Station(query, tile); break; case WID_RAT_BUILD_SIGNALS: - VpStartPlaceSizing(tile, VPM_SIGNALDIRS, DDSP_BUILD_SIGNALS); + VpStartPlaceSizing(query, tile, VPM_SIGNALDIRS, DDSP_BUILD_SIGNALS); break; case WID_RAT_BUILD_BRIDGE: - PlaceRail_Bridge(tile, this); + PlaceRail_Bridge(query, tile, this); break; case WID_RAT_BUILD_TUNNEL: - Command::Post(STR_ERROR_CAN_T_BUILD_TUNNEL_HERE, CcBuildRailTunnel, tile, TRANSPORT_RAIL, _cur_railtype); + Command::PostOrQuery(query, STR_ERROR_CAN_T_BUILD_TUNNEL_HERE, CcBuildRailTunnel, tile, TRANSPORT_RAIL, _cur_railtype); break; case WID_RAT_CONVERT_RAIL: - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CONVERT_RAIL); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_CONVERT_RAIL); break; default: NOT_REACHED(); @@ -729,30 +736,31 @@ struct BuildRailToolbarWindow : Window { VpSelectTilesWithMethod(pt.x, pt.y, select_method); } - void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile) override + void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile, bool query) override { if (pt.x != -1) { switch (select_proc) { default: NOT_REACHED(); case DDSP_BUILD_BRIDGE: + if (query) return; if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); ShowBuildBridgeWindow(start_tile, end_tile, TRANSPORT_RAIL, _cur_railtype); break; case DDSP_PLACE_RAIL: - HandleAutodirPlacement(); + if (!query) HandleAutodirPlacement(); break; case DDSP_BUILD_SIGNALS: - HandleAutoSignalPlacement(); + if (!query) HandleAutoSignalPlacement(); break; case DDSP_DEMOLISH_AREA: - GUIPlaceProcDragXY(select_proc, start_tile, end_tile); + GUIPlaceProcDragXY(query, select_proc, start_tile, end_tile); break; case DDSP_CONVERT_RAIL: - Command::Post(STR_ERROR_CAN_T_CONVERT_RAIL, CcPlaySound_CONSTRUCTION_RAIL, end_tile, start_tile, _cur_railtype, _ctrl_pressed); + Command::PostOrQuery(query, STR_ERROR_CAN_T_CONVERT_RAIL, CcPlaySound_CONSTRUCTION_RAIL, end_tile, start_tile, _cur_railtype, _ctrl_pressed); break; case DDSP_REMOVE_STATION: @@ -761,20 +769,25 @@ struct BuildRailToolbarWindow : Window { /* Station */ if (_remove_button_clicked) { bool keep_rail = !_ctrl_pressed; - Command::Post(STR_ERROR_CAN_T_REMOVE_PART_OF_STATION, CcPlaySound_CONSTRUCTION_RAIL, end_tile, start_tile, keep_rail); + Command::PostOrQuery(query, STR_ERROR_CAN_T_REMOVE_PART_OF_STATION, CcPlaySound_CONSTRUCTION_RAIL, end_tile, start_tile, keep_rail); } else { - HandleStationPlacement(start_tile, end_tile); + HandleStationPlacement(query, start_tile, end_tile); } } else { /* Waypoint */ if (_remove_button_clicked) { bool keep_rail = !_ctrl_pressed; - Command::Post(STR_ERROR_CAN_T_REMOVE_TRAIN_WAYPOINT, CcPlaySound_CONSTRUCTION_RAIL, end_tile, start_tile, keep_rail); + Command::PostOrQuery(query, STR_ERROR_CAN_T_REMOVE_TRAIN_WAYPOINT, CcPlaySound_CONSTRUCTION_RAIL, end_tile, start_tile, keep_rail); } else { TileArea ta(start_tile, end_tile); Axis axis = select_method == VPM_X_LIMITED ? AXIS_X : AXIS_Y; bool adjacent = _ctrl_pressed; + if (query) { + HandleSelectionQuery(Command::Query(ta.tile, axis, ta.w, ta.h, _waypoint_gui.sel_class, _waypoint_gui.sel_type, StationID::Invalid(), adjacent)); + return; + } + auto proc = [=](bool test, StationID to_join) -> bool { if (test) { return Command::Query(ta.tile, axis, ta.w, ta.h, _waypoint_gui.sel_class, _waypoint_gui.sel_type, StationID::Invalid(), adjacent).Succeeded(); @@ -930,7 +943,7 @@ Window *ShowBuildRailToolbar(RailType railtype) /* TODO: For custom stations, respect their allowed platforms/lengths bitmasks! * --pasky */ -static void HandleStationPlacement(TileIndex start, TileIndex end) +static void HandleStationPlacement(bool query, TileIndex start, TileIndex end) { TileArea ta(start, end); uint numtracks = ta.w; @@ -942,6 +955,11 @@ static void HandleStationPlacement(TileIndex start, TileIndex end) RailType rt = _cur_railtype; bool adjacent = _ctrl_pressed; + if (query) { + HandleSelectionQuery(Command::Query(ta.tile, rt, params.axis, numtracks, platlength, params.sel_class, params.sel_type, StationID::Invalid(), adjacent)); + return; + } + auto proc = [=](bool test, StationID to_join) -> bool { if (test) { return Command::Query(ta.tile, rt, params.axis, numtracks, platlength, params.sel_class, params.sel_type, StationID::Invalid(), adjacent).Succeeded(); diff --git a/src/road_gui.cpp b/src/road_gui.cpp index e141cdd957..1ade3bf1ec 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -121,14 +121,14 @@ void CcPlaySound_CONSTRUCTION_OTHER(Commands, const CommandCost &result, TileInd * Callback to start placing a bridge. * @param tile Start tile of the bridge. */ -static void PlaceRoad_Bridge(TileIndex tile, Window *w) +static void PlaceRoad_Bridge(bool query, TileIndex tile, Window *w) { if (IsBridgeTile(tile)) { TileIndex other_tile = GetOtherTunnelBridgeEnd(tile); Point pt = {0, 0}; - w->OnPlaceMouseUp(VPM_X_OR_Y, DDSP_BUILD_BRIDGE, pt, other_tile, tile); + if (!query) w->OnPlaceMouseUp(VPM_X_OR_Y, DDSP_BUILD_BRIDGE, pt, other_tile, tile, query); } else { - VpStartPlaceSizing(tile, VPM_X_OR_Y, DDSP_BUILD_BRIDGE); + VpStartPlaceSizing(query, tile, VPM_X_OR_Y, DDSP_BUILD_BRIDGE); } } @@ -226,7 +226,7 @@ void CcRoadStop(Commands, const CommandCost &result, TileIndex tile, uint8_t wid * @param err_msg Error message to show. * @see CcRoadStop() */ -static void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, RoadStopType stop_type, bool adjacent, RoadType rt, StringID err_msg) +static void PlaceRoadStop(bool query, TileIndex start_tile, TileIndex end_tile, RoadStopType stop_type, bool adjacent, RoadType rt, StringID err_msg) { TileArea ta(start_tile, end_tile); DiagDirection ddir = _roadstop_gui.orientation; @@ -235,10 +235,16 @@ static void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, RoadStopType RoadStopClassID spec_class = _roadstop_gui.sel_class; uint16_t spec_index = _roadstop_gui.sel_type; + if (query) { + HandleSelectionQuery(Command::Query(ta.tile, ta.w, ta.h, stop_type, drive_through, + ddir, rt, spec_class, spec_index, StationID::Invalid(), adjacent)); + return; + } + auto proc = [=](bool test, StationID to_join) -> bool { if (test) { return Command::Query(ta.tile, ta.w, ta.h, stop_type, drive_through, - ddir, rt, spec_class, spec_index, StationID::Invalid(), adjacent); + ddir, rt, spec_class, spec_index, StationID::Invalid(), adjacent).Succeeded(); } else { return Command::Post(err_msg, CcRoadStop, ta.tile, ta.w, ta.h, stop_type, drive_through, ddir, rt, spec_class, spec_index, to_join, adjacent); @@ -252,18 +258,20 @@ static void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, RoadStopType * Place a road waypoint. * @param tile Position to start dragging a waypoint. */ -static void PlaceRoad_Waypoint(TileIndex tile) +static void PlaceRoad_Waypoint(bool query, TileIndex tile) { if (_remove_button_clicked) { - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_ROAD_WAYPOINT); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_REMOVE_ROAD_WAYPOINT); return; } Axis axis = GetAxisForNewRoadWaypoint(tile); if (IsValidAxis(axis)) { /* Valid tile for waypoints */ - VpStartPlaceSizing(tile, axis == AXIS_X ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_ROAD_WAYPOINT); + VpStartPlaceSizing(query, tile, axis == AXIS_X ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_ROAD_WAYPOINT); VpSetPlaceSizingLimit(_settings_game.station.station_spread); + } else if (query) { + SetSelectionRed(true); } else { /* Tile where we can't build road waypoints. This is always going to fail, * but provides the user with a proper error message. */ @@ -275,15 +283,15 @@ static void PlaceRoad_Waypoint(TileIndex tile) * Callback for placing a bus station. * @param tile Position to place the station. */ -static void PlaceRoad_BusStation(TileIndex tile) +static void PlaceRoad_BusStation(bool query, TileIndex tile) { if (_remove_button_clicked) { - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_BUSSTOP); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_REMOVE_BUSSTOP); } else { if (_roadstop_gui.orientation < DIAGDIR_END) { // Not a drive-through stop. - VpStartPlaceSizing(tile, (DiagDirToAxis(_roadstop_gui.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_BUSSTOP); + VpStartPlaceSizing(query, tile, (DiagDirToAxis(_roadstop_gui.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_BUSSTOP); } else { - VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_BUSSTOP); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_BUSSTOP); } VpSetPlaceSizingLimit(_settings_game.station.station_spread); } @@ -293,15 +301,15 @@ static void PlaceRoad_BusStation(TileIndex tile) * Callback for placing a truck station. * @param tile Position to place the station. */ -static void PlaceRoad_TruckStation(TileIndex tile) +static void PlaceRoad_TruckStation(bool query, TileIndex tile) { if (_remove_button_clicked) { - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_TRUCKSTOP); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_REMOVE_TRUCKSTOP); } else { if (_roadstop_gui.orientation < DIAGDIR_END) { // Not a drive-through stop. - VpStartPlaceSizing(tile, (DiagDirToAxis(_roadstop_gui.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_TRUCKSTOP); + VpStartPlaceSizing(query, tile, (DiagDirToAxis(_roadstop_gui.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_TRUCKSTOP); } else { - VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_TRUCKSTOP); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_TRUCKSTOP); } VpSetPlaceSizingLimit(_settings_game.station.station_spread); } @@ -602,7 +610,7 @@ struct BuildRoadToolbarWindow : Window { return Window::OnHotkey(hotkey); } - void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile, bool query) override { _remove_button_clicked = this->IsWidgetLowered(WID_ROT_REMOVE); _one_way_button_clicked = RoadTypeIsRoad(this->roadtype) ? this->IsWidgetLowered(WID_ROT_ONE_WAY) : false; @@ -610,54 +618,54 @@ struct BuildRoadToolbarWindow : Window { case WID_ROT_ROAD_X: _place_road_dir = AXIS_X; _place_road_start_half_x = _tile_fract_coords.x >= 8; - VpStartPlaceSizing(tile, VPM_FIX_Y, DDSP_PLACE_ROAD_X_DIR); + VpStartPlaceSizing(query, tile, VPM_FIX_Y, DDSP_PLACE_ROAD_X_DIR); break; case WID_ROT_ROAD_Y: _place_road_dir = AXIS_Y; _place_road_start_half_y = _tile_fract_coords.y >= 8; - VpStartPlaceSizing(tile, VPM_FIX_X, DDSP_PLACE_ROAD_Y_DIR); + VpStartPlaceSizing(query, tile, VPM_FIX_X, DDSP_PLACE_ROAD_Y_DIR); break; case WID_ROT_AUTOROAD: _place_road_dir = INVALID_AXIS; _place_road_start_half_x = _tile_fract_coords.x >= 8; _place_road_start_half_y = _tile_fract_coords.y >= 8; - VpStartPlaceSizing(tile, VPM_X_OR_Y, DDSP_PLACE_AUTOROAD); + VpStartPlaceSizing(query, tile, VPM_X_OR_Y, DDSP_PLACE_AUTOROAD); break; case WID_ROT_DEMOLISH: - PlaceProc_DemolishArea(tile); + PlaceProc_DemolishArea(query, tile); break; case WID_ROT_DEPOT: - Command::Post(GetRoadTypeInfo(this->roadtype)->strings.err_depot, CcRoadDepot, + Command::PostOrQuery(query, GetRoadTypeInfo(this->roadtype)->strings.err_depot, CcRoadDepot, tile, _cur_roadtype, _road_depot_orientation); break; case WID_ROT_BUILD_WAYPOINT: - PlaceRoad_Waypoint(tile); + PlaceRoad_Waypoint(query, tile); break; case WID_ROT_BUS_STATION: - PlaceRoad_BusStation(tile); + PlaceRoad_BusStation(query, tile); break; case WID_ROT_TRUCK_STATION: - PlaceRoad_TruckStation(tile); + PlaceRoad_TruckStation(query, tile); break; case WID_ROT_BUILD_BRIDGE: - PlaceRoad_Bridge(tile, this); + PlaceRoad_Bridge(query, tile, this); break; case WID_ROT_BUILD_TUNNEL: - Command::Post(STR_ERROR_CAN_T_BUILD_TUNNEL_HERE, CcBuildRoadTunnel, + Command::PostOrQuery(query, STR_ERROR_CAN_T_BUILD_TUNNEL_HERE, CcBuildRoadTunnel, tile, TRANSPORT_ROAD, _cur_roadtype); break; case WID_ROT_CONVERT_ROAD: - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CONVERT_ROAD); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_CONVERT_ROAD); break; default: NOT_REACHED(); @@ -724,18 +732,19 @@ struct BuildRoadToolbarWindow : Window { VpSelectTilesWithMethod(pt.x, pt.y, select_method); } - void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile) override + void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile, bool query) override { if (pt.x != -1) { switch (select_proc) { default: NOT_REACHED(); case DDSP_BUILD_BRIDGE: + if (query) break; if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); ShowBuildBridgeWindow(start_tile, end_tile, TRANSPORT_ROAD, _cur_roadtype); break; case DDSP_DEMOLISH_AREA: - GUIPlaceProcDragXY(select_proc, start_tile, end_tile); + GUIPlaceProcDragXY(query, select_proc, start_tile, end_tile); break; case DDSP_PLACE_ROAD_X_DIR: @@ -744,10 +753,10 @@ struct BuildRoadToolbarWindow : Window { bool start_half = _place_road_dir == AXIS_Y ? _place_road_start_half_y : _place_road_start_half_x; if (_remove_button_clicked) { - Command::Post(GetRoadTypeInfo(this->roadtype)->strings.err_remove_road, CcPlaySound_CONSTRUCTION_OTHER, + Command::PostOrQuery(query, GetRoadTypeInfo(this->roadtype)->strings.err_remove_road, CcPlaySound_CONSTRUCTION_OTHER, end_tile, start_tile, _cur_roadtype, _place_road_dir, start_half, _place_road_end_half); } else { - Command::Post(GetRoadTypeInfo(this->roadtype)->strings.err_build_road, CcPlaySound_CONSTRUCTION_OTHER, + Command::PostOrQuery(query, GetRoadTypeInfo(this->roadtype)->strings.err_build_road, CcPlaySound_CONSTRUCTION_OTHER, end_tile, start_tile, _cur_roadtype, _place_road_dir, _one_way_button_clicked ? DRD_NORTHBOUND : DRD_NONE, start_half, _place_road_end_half, false); } break; @@ -757,12 +766,17 @@ struct BuildRoadToolbarWindow : Window { case DDSP_REMOVE_ROAD_WAYPOINT: if (this->IsWidgetLowered(WID_ROT_BUILD_WAYPOINT)) { if (_remove_button_clicked) { - Command::Post(STR_ERROR_CAN_T_REMOVE_ROAD_WAYPOINT, CcPlaySound_CONSTRUCTION_OTHER, end_tile, start_tile); + Command::PostOrQuery(query, STR_ERROR_CAN_T_REMOVE_ROAD_WAYPOINT, CcPlaySound_CONSTRUCTION_OTHER, end_tile, start_tile); } else { TileArea ta(start_tile, end_tile); Axis axis = select_method == VPM_X_LIMITED ? AXIS_X : AXIS_Y; bool adjacent = _ctrl_pressed; + if (query) { + HandleSelectionQuery(Command::Query(ta.tile, axis, ta.w, ta.h, _waypoint_gui.sel_class, _waypoint_gui.sel_type, StationID::Invalid(), adjacent)); + return; + } + auto proc = [=](bool test, StationID to_join) -> bool { if (test) { return Command::Query(ta.tile, axis, ta.w, ta.h, _waypoint_gui.sel_class, _waypoint_gui.sel_type, StationID::Invalid(), adjacent).Succeeded(); @@ -782,10 +796,10 @@ struct BuildRoadToolbarWindow : Window { if (_remove_button_clicked) { TileArea ta(start_tile, end_tile); StringID str = GetRoadTypeInfo(this->roadtype)->strings.err_remove_station[to_underlying(RoadStopType::Bus)]; - Command::Post(str, CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w, ta.h, RoadStopType::Bus, _ctrl_pressed); + Command::PostOrQuery(query, str, CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w, ta.h, RoadStopType::Bus, _ctrl_pressed); } else { StringID str = GetRoadTypeInfo(this->roadtype)->strings.err_build_station[to_underlying(RoadStopType::Bus)]; - PlaceRoadStop(start_tile, end_tile, RoadStopType::Bus, _ctrl_pressed, _cur_roadtype, str); + PlaceRoadStop(query, start_tile, end_tile, RoadStopType::Bus, _ctrl_pressed, _cur_roadtype, str); } } break; @@ -796,16 +810,16 @@ struct BuildRoadToolbarWindow : Window { if (_remove_button_clicked) { TileArea ta(start_tile, end_tile); StringID str = GetRoadTypeInfo(this->roadtype)->strings.err_remove_station[to_underlying(RoadStopType::Truck)]; - Command::Post(str, CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w, ta.h, RoadStopType::Truck, _ctrl_pressed); + Command::PostOrQuery(query, str, CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w, ta.h, RoadStopType::Truck, _ctrl_pressed); } else { StringID str = GetRoadTypeInfo(this->roadtype)->strings.err_build_station[to_underlying(RoadStopType::Truck)]; - PlaceRoadStop(start_tile, end_tile, RoadStopType::Truck, _ctrl_pressed, _cur_roadtype, str); + PlaceRoadStop(query, start_tile, end_tile, RoadStopType::Truck, _ctrl_pressed, _cur_roadtype, str); } } break; case DDSP_CONVERT_ROAD: - Command::Post(GetRoadTypeInfo(this->roadtype)->strings.err_convert_road, CcPlaySound_CONSTRUCTION_OTHER, end_tile, start_tile, _cur_roadtype, _ctrl_pressed); + Command::PostOrQuery(query, GetRoadTypeInfo(this->roadtype)->strings.err_convert_road, CcPlaySound_CONSTRUCTION_OTHER, end_tile, start_tile, _cur_roadtype, _ctrl_pressed); break; } } diff --git a/src/signs_cmd.cpp b/src/signs_cmd.cpp index 0e02401129..6867559719 100644 --- a/src/signs_cmd.cpp +++ b/src/signs_cmd.cpp @@ -114,7 +114,8 @@ void CcPlaceSign(Commands, const CommandCost &result, SignID new_sign) * sign-tool is selected * @param tile on which to place the sign */ -void PlaceProc_Sign(TileIndex tile) +void PlaceProc_Sign(bool query, TileIndex tile) { + if (query) return; Command::Post(STR_ERROR_CAN_T_PLACE_SIGN_HERE, CcPlaceSign, tile, {}); } diff --git a/src/signs_func.h b/src/signs_func.h index 18f27fc81e..8ecca172ae 100644 --- a/src/signs_func.h +++ b/src/signs_func.h @@ -16,7 +16,7 @@ struct Window; void UpdateAllSignVirtCoords(); -void PlaceProc_Sign(TileIndex tile); +void PlaceProc_Sign(bool query, TileIndex tile); bool CompanyCanRenameSign(const Sign *si); /* signs_gui.cpp */ diff --git a/src/story_gui.cpp b/src/story_gui.cpp index f3c1d4feb0..94d911ed4d 100644 --- a/src/story_gui.cpp +++ b/src/story_gui.cpp @@ -888,8 +888,10 @@ public: this->SetWidgetDirty(WID_SB_PAGE_PANEL); } - void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile, bool query) override { + if (query) return; + const StoryPageElement *const pe = StoryPageElement::GetIfValid(this->active_button_id); if (pe == nullptr || pe->type != SPET_BUTTON_TILE) { ResetObjectToPlace(); diff --git a/src/terraform_gui.cpp b/src/terraform_gui.cpp index 346b728cc6..f20a4daef9 100644 --- a/src/terraform_gui.cpp +++ b/src/terraform_gui.cpp @@ -109,7 +109,7 @@ static void GenerateRockyArea(TileIndex end, TileIndex start) * allows for additional implements that are more local. For example X_Y drag * of convertrail which belongs in rail_gui.cpp and not terraform_gui.cpp */ -bool GUIPlaceProcDragXY(ViewportDragDropSelectionProcess proc, TileIndex start_tile, TileIndex end_tile) +bool GUIPlaceProcDragXY(bool query, ViewportDragDropSelectionProcess proc, TileIndex start_tile, TileIndex end_tile) { if (!_settings_game.construction.freeform_edges) { /* When end_tile is MP_VOID, the error tile will not be visible to the @@ -120,22 +120,22 @@ bool GUIPlaceProcDragXY(ViewportDragDropSelectionProcess proc, TileIndex start_t switch (proc) { case DDSP_DEMOLISH_AREA: - Command::Post(STR_ERROR_CAN_T_CLEAR_THIS_AREA, CcPlaySound_EXPLOSION, end_tile, start_tile, _ctrl_pressed); + Command::PostOrQuery(query, STR_ERROR_CAN_T_CLEAR_THIS_AREA, CcPlaySound_EXPLOSION, end_tile, start_tile, _ctrl_pressed); break; case DDSP_RAISE_AND_LEVEL_AREA: - Command::Post(STR_ERROR_CAN_T_RAISE_LAND_HERE, CcTerraform, end_tile, start_tile, _ctrl_pressed, LM_RAISE); + Command::PostOrQuery(query, STR_ERROR_CAN_T_RAISE_LAND_HERE, CcTerraform, end_tile, start_tile, _ctrl_pressed, LM_RAISE); break; case DDSP_LOWER_AND_LEVEL_AREA: - Command::Post(STR_ERROR_CAN_T_LOWER_LAND_HERE, CcTerraform, end_tile, start_tile, _ctrl_pressed, LM_LOWER); + Command::PostOrQuery(query, STR_ERROR_CAN_T_LOWER_LAND_HERE, CcTerraform, end_tile, start_tile, _ctrl_pressed, LM_LOWER); break; case DDSP_LEVEL_AREA: - Command::Post(STR_ERROR_CAN_T_LEVEL_LAND_HERE, CcTerraform, end_tile, start_tile, _ctrl_pressed, LM_LEVEL); + Command::PostOrQuery(query, STR_ERROR_CAN_T_LEVEL_LAND_HERE, CcTerraform, end_tile, start_tile, _ctrl_pressed, LM_LEVEL); break; case DDSP_CREATE_ROCKS: - GenerateRockyArea(end_tile, start_tile); + if (!query) GenerateRockyArea(end_tile, start_tile); break; case DDSP_CREATE_DESERT: - GenerateDesertArea(end_tile, start_tile); + if (!query) GenerateDesertArea(end_tile, start_tile); break; default: return false; @@ -148,9 +148,9 @@ bool GUIPlaceProcDragXY(ViewportDragDropSelectionProcess proc, TileIndex start_t * Start a drag for demolishing an area. * @param tile Position of one corner. */ -void PlaceProc_DemolishArea(TileIndex tile) +void PlaceProc_DemolishArea(bool query, TileIndex tile) { - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_DEMOLISH_AREA); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_DEMOLISH_AREA); } /** Terra form toolbar managing class. */ @@ -218,31 +218,31 @@ struct TerraformToolbarWindow : Window { } } - void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile, bool query) override { switch (this->last_user_action) { case WID_TT_LOWER_LAND: // Lower land button - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_LOWER_AND_LEVEL_AREA); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_LOWER_AND_LEVEL_AREA); break; case WID_TT_RAISE_LAND: // Raise land button - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_RAISE_AND_LEVEL_AREA); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_RAISE_AND_LEVEL_AREA); break; case WID_TT_LEVEL_LAND: // Level land button - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_LEVEL_AREA); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_LEVEL_AREA); break; case WID_TT_DEMOLISH: // Demolish aka dynamite button - PlaceProc_DemolishArea(tile); + PlaceProc_DemolishArea(query, tile); break; case WID_TT_BUY_LAND: // Buy land button - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_BUILD_OBJECT); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_BUILD_OBJECT); break; case WID_TT_PLACE_SIGN: // Place sign button - PlaceProc_Sign(tile); + PlaceProc_Sign(query, tile); break; default: NOT_REACHED(); @@ -261,7 +261,7 @@ struct TerraformToolbarWindow : Window { return pt; } - void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile) override + void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile, bool query) override { if (pt.x != -1) { switch (select_proc) { @@ -270,7 +270,7 @@ struct TerraformToolbarWindow : Window { case DDSP_RAISE_AND_LEVEL_AREA: case DDSP_LOWER_AND_LEVEL_AREA: case DDSP_LEVEL_AREA: - GUIPlaceProcDragXY(select_proc, start_tile, end_tile); + GUIPlaceProcDragXY(query, select_proc, start_tile, end_tile); break; case DDSP_BUILD_OBJECT: if (!_settings_game.construction.freeform_edges) { @@ -279,8 +279,8 @@ struct TerraformToolbarWindow : Window { if (TileX(end_tile) == Map::MaxX()) end_tile += TileDiffXY(-1, 0); if (TileY(end_tile) == Map::MaxY()) end_tile += TileDiffXY(0, -1); } - Command::Post(STR_ERROR_CAN_T_PURCHASE_THIS_LAND, CcPlaySound_CONSTRUCTION_RAIL, - end_tile, start_tile, OBJECT_OWNED_LAND, 0, (_ctrl_pressed ? true : false)); + Command::PostOrQuery(query, STR_ERROR_CAN_T_PURCHASE_THIS_LAND, CcPlaySound_CONSTRUCTION_RAIL, + end_tile, start_tile, OBJECT_OWNED_LAND, 0, (_ctrl_pressed ? true : false)); break; } } @@ -650,31 +650,31 @@ struct ScenarioEditorLandscapeGenerationWindow : Window { } } - void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile, bool query) override { switch (this->last_user_action) { case WID_ETT_DEMOLISH: // Demolish aka dynamite button - PlaceProc_DemolishArea(tile); + PlaceProc_DemolishArea(query, tile); break; case WID_ETT_LOWER_LAND: // Lower land button - CommonRaiseLowerBigLand(tile, false); + if (!query) CommonRaiseLowerBigLand(tile, false); break; case WID_ETT_RAISE_LAND: // Raise land button - CommonRaiseLowerBigLand(tile, true); + if (!query) CommonRaiseLowerBigLand(tile, true); break; case WID_ETT_LEVEL_LAND: // Level land button - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_LEVEL_AREA); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_LEVEL_AREA); break; case WID_ETT_PLACE_ROCKS: // Place rocks button - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CREATE_ROCKS); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_CREATE_ROCKS); break; case WID_ETT_PLACE_DESERT: // Place desert button (in tropical climate) - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CREATE_DESERT); + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_CREATE_DESERT); break; default: NOT_REACHED(); @@ -686,7 +686,7 @@ struct ScenarioEditorLandscapeGenerationWindow : Window { VpSelectTilesWithMethod(pt.x, pt.y, select_method); } - void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile) override + void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile, bool query) override { if (pt.x != -1) { switch (select_proc) { @@ -697,7 +697,7 @@ struct ScenarioEditorLandscapeGenerationWindow : Window { case DDSP_LOWER_AND_LEVEL_AREA: case DDSP_LEVEL_AREA: case DDSP_DEMOLISH_AREA: - GUIPlaceProcDragXY(select_proc, start_tile, end_tile); + GUIPlaceProcDragXY(query, select_proc, start_tile, end_tile); break; } } diff --git a/src/texteff.cpp b/src/texteff.cpp index 82f832e2de..fff62e4cd2 100644 --- a/src/texteff.cpp +++ b/src/texteff.cpp @@ -126,8 +126,8 @@ void DrawTextEffects(DrawPixelInfo *dpi) for (const TextEffect &te : _text_effects) { if (!te.IsValid()) continue; - if (te.mode == TE_RISING || _settings_client.gui.loading_indicators) { - std::string *str = ViewportAddString(dpi, &te, flags, INVALID_COLOUR); + if (te.mode == TE_RISING || te.mode == TE_ERROR || _settings_client.gui.loading_indicators) { + std::string *str = ViewportAddString(dpi, &te, te.mode == TE_ERROR ? flags | ViewportStringFlag::TextColour | ViewportStringFlag::TransparentRect : flags, te.mode == TE_ERROR ? COLOUR_RED : INVALID_COLOUR); if (str == nullptr) continue; *str = te.msg.GetDecodedString(); diff --git a/src/texteff.hpp b/src/texteff.hpp index 6dc656117c..c6716841c6 100644 --- a/src/texteff.hpp +++ b/src/texteff.hpp @@ -21,6 +21,7 @@ enum TextEffectMode : uint8_t { TE_INVALID, ///< Text effect is invalid. TE_RISING, ///< Make the text effect slowly go upwards TE_STATIC, ///< Keep the text effect static + TE_ERROR, ///< Error text effect. }; using TextEffectID = uint16_t; diff --git a/src/tilehighlight_func.h b/src/tilehighlight_func.h index 572c5bd43e..fbe3f5862d 100644 --- a/src/tilehighlight_func.h +++ b/src/tilehighlight_func.h @@ -13,8 +13,8 @@ #include "gfx_type.h" #include "tilehighlight_type.h" -void PlaceProc_DemolishArea(TileIndex tile); -bool GUIPlaceProcDragXY(ViewportDragDropSelectionProcess proc, TileIndex start_tile, TileIndex end_tile); +void PlaceProc_DemolishArea(bool query, TileIndex tile); +bool GUIPlaceProcDragXY(bool query, ViewportDragDropSelectionProcess proc, TileIndex start_tile, TileIndex end_tile); bool HandlePlacePushButton(Window *w, WidgetID widget, CursorID cursor, HighLightStyle mode); void SetObjectToPlaceWnd(CursorID icon, PaletteID pal, HighLightStyle mode, Window *w); @@ -23,7 +23,7 @@ void ResetObjectToPlace(); void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method); void VpStartDragging(ViewportDragDropSelectionProcess process); -void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process); +void VpStartPlaceSizing(bool query, TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process); void VpSetPresizeRange(TileIndex from, TileIndex to); void VpSetPlaceSizingLimit(int limit); diff --git a/src/tilehighlight_type.h b/src/tilehighlight_type.h index 10cc3eeaf2..f0662e3c07 100644 --- a/src/tilehighlight_type.h +++ b/src/tilehighlight_type.h @@ -11,6 +11,7 @@ #define TILEHIGHLIGHT_TYPE_H #include "core/geometry_type.hpp" +#include "texteff.hpp" #include "window_type.h" #include "tile_type.h" #include "viewport_type.h" @@ -74,6 +75,8 @@ struct TileHighlightData { ViewportPlaceMethod select_method; ///< The method which governs how tiles are selected. ViewportDragDropSelectionProcess select_proc; ///< The procedure that has to be called when the selection is done. + TextEffectID error = INVALID_TE_ID; + void Reset(); bool IsDraggingDiagonal(); diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index a82d581990..0f27d785e6 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -2083,15 +2083,15 @@ struct MainToolbarWindow : Window { return ES_HANDLED; } - void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile, bool query) override { switch (_last_started_action) { case CBF_PLACE_SIGN: - PlaceProc_Sign(tile); + PlaceProc_Sign(query, tile); break; case CBF_PLACE_LANDINFO: - ShowLandInfo(tile); + if (!query) ShowLandInfo(tile); break; default: NOT_REACHED(); @@ -2443,15 +2443,15 @@ struct ScenarioEditorToolbarWindow : Window { return ES_HANDLED; } - void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile, bool query) override { switch (_last_started_action) { case CBF_PLACE_SIGN: - PlaceProc_Sign(tile); + PlaceProc_Sign(query, tile); break; case CBF_PLACE_LANDINFO: - ShowLandInfo(tile); + if (!query) ShowLandInfo(tile); break; default: NOT_REACHED(); diff --git a/src/town_gui.cpp b/src/town_gui.cpp index 89f4bf655e..31b0b2454f 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -1306,8 +1306,10 @@ public: old_generating_world.Restore(); } - void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile, bool query) override { + if (query) return; + this->ExecuteFoundTownCommand(tile, false, STR_ERROR_CAN_T_FOUND_TOWN_HERE, CcFoundTown); } @@ -1773,10 +1775,11 @@ struct BuildHouseWindow : public PickerWindow { this->SetWidgetDisabledState(WID_BH_PROTECT_ON, hasflag); } - void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile, bool query) override { const HouseSpec *spec = HouseSpec::Get(HousePickerCallbacks::sel_type); - Command::Post(STR_ERROR_CAN_T_BUILD_HOUSE, CcPlaySound_CONSTRUCTION_OTHER, tile, spec->Index(), this->house_protected); + + Command::PostOrQuery(query, STR_ERROR_CAN_T_BUILD_HOUSE, CcPlaySound_CONSTRUCTION_OTHER, tile, spec->Index(), this->house_protected); } const IntervalTimer view_refresh_interval = {std::chrono::milliseconds(2500), [this](auto) { diff --git a/src/tree_gui.cpp b/src/tree_gui.cpp index 79cf837db3..07a56b1753 100644 --- a/src/tree_gui.cpp +++ b/src/tree_gui.cpp @@ -20,6 +20,7 @@ #include "zoom_func.h" #include "tree_map.h" #include "tree_cmd.h" +#include "viewport_func.h" #include "widgets/tree_widget.h" @@ -212,11 +213,11 @@ public: } } - void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile, bool query) override { if (_game_mode != GM_EDITOR && this->mode == PM_NORMAL) { - VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_PLANT_TREES); - } else { + VpStartPlaceSizing(query, tile, VPM_X_AND_Y, DDSP_PLANT_TREES); + } else if (!query) { VpStartDragging(DDSP_PLANT_TREES); } } @@ -236,10 +237,10 @@ public: } } - void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile) override + void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile, bool query) override { if (_game_mode != GM_EDITOR && this->mode == PM_NORMAL && pt.x != -1 && select_proc == DDSP_PLANT_TREES) { - Command::Post(STR_ERROR_CAN_T_PLANT_TREE_HERE, end_tile, start_tile, this->tree_to_plant, _ctrl_pressed); + Command::PostOrQuery(query, STR_ERROR_CAN_T_PLANT_TREE_HERE, end_tile, start_tile, this->tree_to_plant, _ctrl_pressed); } } diff --git a/src/viewport.cpp b/src/viewport.cpp index 4699990cce..a03b52017c 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -1761,7 +1761,7 @@ static void ViewportDrawStrings(ZoomLevel zoom, const StringSpriteToDrawVector * } if (ss.flags.Test(ViewportStringFlag::TextColour)) { - if (ss.colour != INVALID_COLOUR) colour = static_cast(GetColourGradient(ss.colour, SHADE_LIGHTER) | TC_IS_PALETTE_COLOUR); + if (ss.colour != INVALID_COLOUR) colour = static_cast(GetColourGradient(ss.colour, SHADE_LIGHTER) | TC_IS_PALETTE_COLOUR | TC_FORCED); } int left = x + WidgetDimensions::scaled.fullbevel.left; @@ -2226,10 +2226,32 @@ static void SetSelectionTilesDirty() void SetSelectionRed(bool b) { + if (_thd.make_square_red == b) return; _thd.make_square_red = b; SetSelectionTilesDirty(); } +/** + * Update tile highlight selection to reflect the command cost. + * @param cost CommandCost. + */ +void HandleSelectionQuery(CommandCost &&cost) +{ + SetSelectionRed(cost.Failed()); + if (cost.Failed()) { + if (_thd.error != INVALID_TE_ID) RemoveTextEffect(_thd.error); + Point pt = RemapCoords(_thd.new_pos.x, _thd.new_pos.y, GetTilePixelZ(TileXY(_thd.new_pos.x / TILE_SIZE, _thd.new_pos.y / TILE_SIZE))); + + EncodedString error = std::move(cost.GetEncodedMessage()); + if (error.empty()) error = GetEncodedStringIfValid(cost.GetErrorMessage()); + + _thd.error = AddTextEffect(std::move(error), pt.x, pt.y, 0, TextEffectMode::TE_ERROR); + } else { + if (_thd.error != INVALID_TE_ID) RemoveTextEffect(_thd.error); + _thd.error = INVALID_TE_ID; + } +} + /** * Test whether a sign is below the mouse * @param vp the clicked viewport @@ -2442,7 +2464,7 @@ static bool CheckClickOnLandscape(const Viewport &vp, int x, int y) return true; } -static void PlaceObject() +static void PlaceObject(bool query) { Point pt; Window *w; @@ -2459,24 +2481,35 @@ static void PlaceObject() _tile_fract_coords.y = pt.y & TILE_UNIT_MASK; w = _thd.GetCallbackWnd(); - if (w != nullptr) w->OnPlaceObject(pt, TileVirtXY(pt.x, pt.y)); + if (w != nullptr) { + TileIndex tile = TileVirtXY(pt.x, pt.y); + if (query) { + /* Query only if moved to a new tile. */ + static TileIndex last_tile = INVALID_TILE; + if (tile == last_tile) return; + last_tile = tile; + } + w->OnPlaceObject(pt, tile, query); + } } -bool HandleViewportClicked(const Viewport &vp, int x, int y) +bool HandleViewportClicked(const Viewport &vp, int x, int y, bool query) { const Vehicle *v = CheckClickOnVehicle(vp, x, y); if (_thd.place_mode & HT_VEHICLE) { - if (v != nullptr && VehicleClicked(v)) return true; + if (!query && v != nullptr && VehicleClicked(v)) return true; } /* Vehicle placement mode already handled above. */ if ((_thd.place_mode & HT_DRAG_MASK) != HT_NONE) { - PlaceObject(); + PlaceObject(query); return true; } + if (query) return false; + if (CheckClickOnViewportSign(vp, x, y)) return true; bool result = CheckClickOnLandscape(vp, x, y); @@ -2618,6 +2651,9 @@ void TileHighlightData::Reset() this->pos.y = 0; this->new_pos.x = 0; this->new_pos.y = 0; + + if (this->error != INVALID_TE_ID) RemoveTextEffect(this->error); + this->error = INVALID_TE_ID; } /** @@ -2766,8 +2802,10 @@ static void HideMeasurementTooltips() } /** highlighting tiles while only going over them with the mouse */ -void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process) +void VpStartPlaceSizing(bool query, TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process) { + if (_thd.select_method != method || _thd.select_proc != process) SetSelectionRed(false); + _thd.select_method = method; _thd.select_proc = process; _thd.selend.x = TileX(tile) * TILE_SIZE; @@ -2785,6 +2823,8 @@ void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDrag _thd.selstart.y += TILE_SIZE / 2; } + if (query) return; + HighLightStyle others = _thd.place_mode & ~(HT_DRAG_MASK | HT_DIR_MASK); if ((_thd.place_mode & HT_DRAG_MASK) == HT_RECT) { _thd.place_mode = HT_SPECIAL | others; @@ -3426,6 +3466,20 @@ calc_heightdiff_single_direction:; */ EventState VpHandlePlaceSizingDrag() { + if (_thd.window_class != WC_INVALID && _thd.select_proc != DDSP_NONE) { + static TileIndex last_start, last_end; + Point selend = _special_mouse_mode == WSM_NONE ? _thd.new_pos : _thd.selend; + TileIndex start = TileVirtXY(_thd.new_pos.x, _thd.new_pos.y); + TileIndex end = TileVirtXY(selend.x, selend.y); + if (start != last_start || end != last_end) { + last_start = start; + last_end = end; + if (Window *w = _thd.GetCallbackWnd(); w != nullptr) { + w->OnPlaceMouseUp(_thd.select_method, _thd.select_proc, selend, start, end, true); + } + } + } + if (_special_mouse_mode != WSM_SIZING && _special_mouse_mode != WSM_DRAGGING) return ES_NOT_HANDLED; /* stop drag mode if the window has been closed */ @@ -3466,7 +3520,7 @@ EventState VpHandlePlaceSizingDrag() SetTileSelectSize(1, 1); HideMeasurementTooltips(); - w->OnPlaceMouseUp(_thd.select_method, _thd.select_proc, _thd.selend, TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y)); + w->OnPlaceMouseUp(_thd.select_method, _thd.select_proc, _thd.selend, TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y), false); return ES_HANDLED; } @@ -3524,10 +3578,14 @@ void SetObjectToPlace(CursorID icon, PaletteID pal, HighLightStyle mode, WindowC _special_mouse_mode = WSM_NONE; } + _thd.select_proc = DDSP_NONE; _thd.place_mode = mode; _thd.window_class = window_class; _thd.window_number = window_num; + if (_thd.error != INVALID_TE_ID) RemoveTextEffect(_thd.error); + _thd.error = INVALID_TE_ID; + if ((mode & HT_DRAG_MASK) == HT_SPECIAL) { // special tools, like tunnels or docks start with presizing mode VpStartPreSizing(); } diff --git a/src/viewport_func.h b/src/viewport_func.h index 88e00edd73..3af078192f 100644 --- a/src/viewport_func.h +++ b/src/viewport_func.h @@ -59,7 +59,7 @@ std::string *ViewportAddString(const DrawPixelInfo *dpi, const ViewportSign *sig void StartSpriteCombine(); void EndSpriteCombine(); -bool HandleViewportClicked(const Viewport &vp, int x, int y); +bool HandleViewportClicked(const Viewport &vp, int x, int y, bool query); void SetRedErrorSquare(TileIndex tile); void SetTileSelectSize(int w, int h); void SetTileSelectBigSize(int ox, int oy, int sx, int sy); diff --git a/src/viewport_type.h b/src/viewport_type.h index 6e7f8b2dc7..88a476d0d2 100644 --- a/src/viewport_type.h +++ b/src/viewport_type.h @@ -113,6 +113,8 @@ DECLARE_ENUM_AS_BIT_SET(ViewportPlaceMethod) * you've selected it. */ enum ViewportDragDropSelectionProcess : uint8_t { + DDSP_NONE, + DDSP_DEMOLISH_AREA, ///< Clear area DDSP_RAISE_AND_LEVEL_AREA, ///< Raise / level area DDSP_LOWER_AND_LEVEL_AREA, ///< Lower / level area diff --git a/src/window.cpp b/src/window.cpp index 6ae18eda28..1f7f4ff51f 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2841,14 +2841,14 @@ static void MouseLoop(MouseClick click, int mousewheel) HandleMouseOver(); bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == SWS_SCROLL_MAP && _cursor.wheel_moved; - if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return; + if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling && (_thd.drawstyle & HT_DRAG_MASK) == HT_NONE) return; int x = _cursor.pos.x; int y = _cursor.pos.y; Window *w = FindWindowFromPt(x, y); if (w == nullptr) return; - if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return; + if (click != MC_NONE && click != MC_HOVER && !MaybeBringWindowToFront(w)) return; Viewport *vp = IsPtInWindowViewport(w, x, y); /* Don't allow any action in a viewport if either in menu or when having a modal progress window */ @@ -2876,7 +2876,7 @@ static void MouseLoop(MouseClick click, int mousewheel) switch (click) { case MC_DOUBLE_LEFT: case MC_LEFT: - if (HandleViewportClicked(*vp, x, y)) return; + if (HandleViewportClicked(*vp, x, y, false)) return; if (!w->flags.Test(WindowFlag::DisableVpScroll) && _settings_client.gui.scroll_mode == VSM_MAP_LMB) { _scrolling_viewport = true; @@ -2897,6 +2897,7 @@ static void MouseLoop(MouseClick click, int mousewheel) break; default: + HandleViewportClicked(*vp, x, y, true); break; } } diff --git a/src/window_gui.h b/src/window_gui.h index edfbf94da8..bc73e62b95 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -796,8 +796,9 @@ public: * has been set. * @param pt the exact point on the map that has been clicked. * @param tile the tile on the map that has been clicked. + * @param query set if the tile was not actually clicked. */ - virtual void OnPlaceObject([[maybe_unused]] Point pt, [[maybe_unused]] TileIndex tile) {} + virtual void OnPlaceObject([[maybe_unused]] Point pt, [[maybe_unused]] TileIndex tile, [[maybe_unused]] bool query) {} /** * The user clicked on a vehicle while HT_VEHICLE has been set. @@ -839,7 +840,7 @@ public: * @param start_tile the begin tile of the drag. * @param end_tile the end tile of the drag. */ - virtual void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, [[maybe_unused]] ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, [[maybe_unused]] TileIndex start_tile, [[maybe_unused]] TileIndex end_tile) {} + virtual void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, [[maybe_unused]] ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, [[maybe_unused]] TileIndex start_tile, [[maybe_unused]] TileIndex end_tile, [[maybe_unused]] bool query) {} /** * The user moves over the map when a tile highlight mode has been set