#include "../stdafx.h" #include "cm_rail_gui.hpp" #include "cm_commands.hpp" #include "cm_hotkeys.hpp" #include "../rail_cmd.h" #include "../tilehighlight_func.h" #include "../tile_map.h" #include "../safeguards.h" extern TileIndex _cm_rail_track_endtile; // rail_cmd.cpp namespace citymania { static sp GenericPlaceRail(TileIndex tile, RailType railtype, Track track, bool remove_mode) { if (remove_mode) { auto res = std::make_shared(tile, track); res->with_error(STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK); return res; } else { auto res = std::make_shared(tile, railtype, track, _settings_client.gui.auto_remove_signals); res->with_error(STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK); return res; } } static sp DoRailroadTrack(TileIndex start_tile, TileIndex end_tile, RailType railtype, Track track, bool remove_mode) { if (remove_mode) { auto res = std::make_shared(end_tile, start_tile, track); res->with_error(STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK); return res; } else { auto res = std::make_shared(end_tile, start_tile, railtype, track, _settings_client.gui.auto_remove_signals, false); res->with_error(STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK); return res; } } static bool DoAutodirTerraform(bool diagonal, TileIndex start_tile, TileIndex /* end_tile */, Track track, sp rail_cmd, TileIndex s1, TileIndex e1, TileIndex s2, TileIndex e2, bool /* remove_mode */) { auto rail_callback = [rail_cmd, start_tile, track, estimate=citymania::_estimate_mod](bool res) -> bool{ if (rail_cmd->call({DoCommandFlag::Auto, DoCommandFlag::NoWater}).GetErrorMessage() != STR_ERROR_ALREADY_BUILT || _cm_rail_track_endtile == INVALID_TILE) { if (!rail_cmd->post()) return false; } if (!estimate && _cm_rail_track_endtile != INVALID_TILE) StoreRailPlacementEndpoints(start_tile, _cm_rail_track_endtile, track, true); return res; }; auto h1 = TileHeight(s1); auto h2 = TileHeight(s2); auto cmd1 = cmd::LevelLand(e1, s1, diagonal, h1 < h2 ? LM_RAISE : LM_LEVEL); auto cmd2 = cmd::LevelLand(e2, s2, diagonal, h2 < h1 ? LM_RAISE : LM_LEVEL); auto c1_fail = cmd1.call({DoCommandFlag::Auto, DoCommandFlag::NoWater}).Failed(); auto c2_fail = cmd2.call({DoCommandFlag::Auto, DoCommandFlag::NoWater}).Failed(); if (c1_fail && c2_fail) return rail_callback(true); if (c2_fail) return cmd1.with_callback(rail_callback).post(); if (!c1_fail) cmd1.post(); return cmd2.with_callback(rail_callback).post(); } static bool HandleAutodirTerraform(TileIndex start_tile, TileIndex end_tile, Track track, sp rail_cmd, bool remove_mode) { bool eq = (TileX(end_tile) - TileY(end_tile) == TileX(start_tile) - TileY(start_tile)); bool ez = (TileX(end_tile) + TileY(end_tile) == TileX(start_tile) + TileY(start_tile)); // StoreRailPlacementEndpoints(start_tile, end_tile, track, true); switch (_thd.cm_poly_dir) { case TRACKDIR_X_NE: return DoAutodirTerraform(false, start_tile, end_tile, track, std::move(rail_cmd), TileAddXY(start_tile, 1, 0), end_tile, TileAddXY(start_tile, 1, 1), TileAddXY(end_tile, 0, 1), remove_mode); break; case TRACKDIR_X_SW: return DoAutodirTerraform(false, start_tile, end_tile, track, std::move(rail_cmd), start_tile, TileAddXY(end_tile, 1, 0), TileAddXY(start_tile, 0, 1), TileAddXY(end_tile, 1, 1), remove_mode); break; case TRACKDIR_Y_SE: return DoAutodirTerraform(false, start_tile, end_tile, track, std::move(rail_cmd), start_tile, TileAddXY(end_tile, 0, 1), TileAddXY(start_tile, 1, 0), TileAddXY(end_tile, 1, 1), remove_mode); break; case TRACKDIR_Y_NW: return DoAutodirTerraform(false, start_tile, end_tile, track, std::move(rail_cmd), TileAddXY(start_tile, 0, 1), end_tile, TileAddXY(start_tile, 1, 1), TileAddXY(end_tile, 1, 0), remove_mode); break; case TRACKDIR_LEFT_N: { return DoAutodirTerraform(true, start_tile, end_tile, track, std::move(rail_cmd), TileAddXY(start_tile, 1, 0), TileAddXY(end_tile, eq, 0), TileAddXY(start_tile, 1, 1), TileAddXY(end_tile, 0, !eq), remove_mode); break; } case TRACKDIR_RIGHT_N: { return DoAutodirTerraform(true, start_tile, end_tile, track, std::move(rail_cmd), TileAddXY(start_tile, 0, 1), TileAddXY(end_tile, 0, eq), TileAddXY(start_tile, 1, 1), TileAddXY(end_tile, !eq, 0), remove_mode); break; } case TRACKDIR_LEFT_S: { return DoAutodirTerraform(true, start_tile, end_tile, track, std::move(rail_cmd), TileAddXY(start_tile, 1, 0), TileAddXY(end_tile, 1, !eq), start_tile, TileAddXY(end_tile, eq, 1), remove_mode); break; } case TRACKDIR_RIGHT_S: { return DoAutodirTerraform(true, start_tile, end_tile, track, std::move(rail_cmd), TileAddXY(start_tile, 0, 1), TileAddXY(end_tile, !eq, 1), start_tile, TileAddXY(end_tile, 1, eq), remove_mode); break; } case TRACKDIR_UPPER_E: { return DoAutodirTerraform(true, start_tile, end_tile, track, std::move(rail_cmd), start_tile, TileAddXY(end_tile, 0, !ez), TileAddXY(start_tile, 1, 0), TileAddXY(end_tile, !ez, 1), remove_mode); break; } case TRACKDIR_LOWER_E: { return DoAutodirTerraform(true, start_tile, end_tile, track, std::move(rail_cmd), TileAddXY(start_tile, 1, 1), TileAddXY(end_tile, ez, 1), TileAddXY(start_tile, 1, 0), TileAddXY(end_tile, 0, ez), remove_mode); break; } case TRACKDIR_UPPER_W: { return DoAutodirTerraform(true, start_tile, end_tile, track, std::move(rail_cmd), start_tile, TileAddXY(end_tile, !ez, 0), TileAddXY(start_tile, 0, 1), TileAddXY(end_tile, 1, !ez), remove_mode); break; } case TRACKDIR_LOWER_W: { return DoAutodirTerraform(true, start_tile, end_tile, track, std::move(rail_cmd), TileAddXY(start_tile, 1, 1), TileAddXY(end_tile, 1, ez), TileAddXY(start_tile, 0, 1), TileAddXY(end_tile, ez, 0), remove_mode); break; } default: break; } return true; } void HandleAutodirPlacement(RailType railtype, bool remove_mode) { Track trackstat = static_cast( _thd.drawstyle & HT_DIR_MASK); // 0..5 TileIndex start_tile = TileVirtXY(_thd.selstart.x, _thd.selstart.y); TileIndex end_tile = TileVirtXY(_thd.selend.x, _thd.selend.y); auto cmd = (_thd.drawstyle & HT_RAIL) ? GenericPlaceRail(end_tile, railtype, trackstat, remove_mode) : DoRailroadTrack(start_tile, end_tile, railtype, trackstat, remove_mode); /* When overbuilding existing tracks in polyline mode we want to move the * snap point over the last overbuilt track piece. In such case we don't * wan't to show any errors to the user. Don't execute the command right * away, first check if overbuilding. */ if (citymania::_estimate_mod || !(_thd.place_mode & HT_POLY) || remove_mode) { if (!cmd->post(CcPlaySound_CONSTRUCTION_RAIL)) return; } else if (_thd.cm_poly_terra) { Track trackstat = static_cast( _thd.drawstyle & HT_DIR_MASK); // 0..5 citymania::HandleAutodirTerraform(start_tile, end_tile, trackstat, std::move(cmd), remove_mode); return; } else if (cmd->call({DoCommandFlag::Auto, DoCommandFlag::NoWater}).GetErrorMessage() != STR_ERROR_ALREADY_BUILT || _cm_rail_track_endtile == INVALID_TILE) { if (!cmd->post(CcPlaySound_CONSTRUCTION_RAIL)) return; } /* Save new snap points for the polyline tool, no matter if the command * succeeded, the snapping will be extended over overbuilt track pieces. */ if (!citymania::_estimate_mod && _cm_rail_track_endtile != INVALID_TILE) { StoreRailPlacementEndpoints(start_tile, _cm_rail_track_endtile, trackstat, true); } } } // namaespace citymania