Skip to content

Commit

Permalink
Feature: Autorail tool can create rail "through" bridges tunnels and …
Browse files Browse the repository at this point in the history
…stations, and can start in depot tiles
  • Loading branch information
Kuhnovic committed Oct 17, 2021
1 parent d66dea5 commit d189fee
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 23 deletions.
77 changes: 59 additions & 18 deletions src/rail_cmd.cpp
Expand Up @@ -887,39 +887,80 @@ static CommandCost ValidateAutoDrag(Trackdir *trackdir, TileIndex start, TileInd
static CommandCost CmdRailTrackHelper(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
{
CommandCost total_cost(EXPENSES_CONSTRUCTION);
Track track = Extract<Track, 6, 3>(p2);
bool remove = HasBit(p2, 9);
bool auto_remove_signals = HasBit(p2, 11);
RailType railtype = Extract<RailType, 0, 6>(p2);
const Track track = Extract<Track, 6, 3>(p2);
const bool remove = HasBit(p2, 9);
const bool auto_remove_signals = HasBit(p2, 11);
const RailType railtype = Extract<RailType, 0, 6>(p2);

if ((!remove && !ValParamRailtype(railtype)) || !ValParamTrackOrientation(track)) return CMD_ERROR;
if (p1 >= MapSize()) return CMD_ERROR;
TileIndex end_tile = p1;
const TileIndex end_tile = p1;
Trackdir trackdir = TrackToTrackdir(track);

CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
if (ret.Failed()) return ret;

const int drag_length = DistanceManhattan(tile, end_tile) + 1;
int tiles_to_skip_remaining = 0;
bool had_success = false;
CommandCost last_error = CMD_ERROR;
for (;;) {
CommandCost ret = DoCommand(tile, remove ? 0 : railtype, TrackdirToTrack(trackdir) | (auto_remove_signals << 3), flags, remove ? CMD_REMOVE_SINGLE_RAIL : CMD_BUILD_SINGLE_RAIL);
for (int drag_step = 0; drag_step < drag_length; ++drag_step)
{
tiles_to_skip_remaining = std::max(--tiles_to_skip_remaining, 0);
const bool is_compatible_rail_or_removing = (remove || IsCompatibleRail(GetRailType(tile), railtype));

if (tiles_to_skip_remaining == 0) {
/* Allow the user to start dragging from a depot tile */
if (IsRailDepotTile(tile) && is_compatible_rail_or_removing) {
if (drag_step == 0 && TrackdirToExitdir(trackdir) == GetRailDepotDirection(tile)) {
tiles_to_skip_remaining = 1;
}
}

if (ret.Failed()) {
last_error = ret;
if (last_error.GetErrorMessage() != STR_ERROR_ALREADY_BUILT && !remove) {
if (HasBit(p2, 10)) return last_error;
break;
if ((IsBridgeTile(tile) || IsTunnelTile(tile)) && is_compatible_rail_or_removing) {
/* Allow the user to drag over a bridge or through a tunnel */
Trackdir tunnelBridgeTrackdir = DiagDirToDiagTrackdir(GetTunnelBridgeDirection(tile));
if (trackdir == tunnelBridgeTrackdir) { // We're entering the tunnel/bridge...
TileIndex otherEnd = GetOtherTunnelBridgeEnd(tile);
tiles_to_skip_remaining = GetTunnelBridgeLength(tile, otherEnd) + 2;
}
else { // We've bumped into a tunnel or bridge tile that we can't enter...
/* Allow the user to start dragging on a tunnel/bridge ramp, as long as it's an exit ramp */
if (drag_step == 0 && DiagDirToDiagTrackdir(TrackdirToExitdir(trackdir)) == ReverseTrackdir(tunnelBridgeTrackdir)) {
tiles_to_skip_remaining = 1;
}
else {
if (remove) break; // Prevents removal of tiles after the wrongly oriented tunnel/bridge ramp
}
}
}
}

/* Ownership errors are more important. */
if (last_error.GetErrorMessage() == STR_ERROR_OWNED_BY && remove) break;
} else {
had_success = true;
total_cost.AddCost(ret);
/* Allow the user to drag through / start dragging in station tiles */
bool execute_cmd_for_tile = (tiles_to_skip_remaining == 0);
if (IsRailStationTile(tile) && GetRailStationTrack(tile) == DiagDirToDiagTrack(TrackdirToExitdir(trackdir)) && is_compatible_rail_or_removing) {
execute_cmd_for_tile = false;
}

if (tile == end_tile) break;
/* Execute the command */
if (execute_cmd_for_tile)
{
CommandCost ret = DoCommand(tile, remove ? 0 : railtype, TrackdirToTrack(trackdir) | (auto_remove_signals << 3), flags, remove ? CMD_REMOVE_SINGLE_RAIL : CMD_BUILD_SINGLE_RAIL);
if (ret.Failed()) {
last_error = ret;
if (last_error.GetErrorMessage() != STR_ERROR_ALREADY_BUILT && !remove) {
if (HasBit(p2, 10)) return last_error;
break;
}

/* Ownership errors are more important. */
if (last_error.GetErrorMessage() == STR_ERROR_OWNED_BY && remove) break;
}
else {
had_success = true;
total_cost.AddCost(ret);
}
}

tile += ToTileIndexDiff(_trackdelta[trackdir]);

Expand Down
50 changes: 45 additions & 5 deletions src/viewport.cpp
Expand Up @@ -84,6 +84,9 @@
#include "town_kdtree.h"
#include "viewport_sprite_sorter.h"
#include "bridge_map.h"
#include "tunnel_map.h"
#include "tunnelbridge.h"
#include "tunnelbridge_map.h"
#include "company_base.h"
#include "command_func.h"
#include "network/network_func.h"
Expand All @@ -92,6 +95,7 @@
#include <forward_list>
#include <map>
#include <stack>
#include <unordered_set>

#include "table/strings.h"
#include "table/string_colours.h"
Expand Down Expand Up @@ -950,8 +954,37 @@ static const HighLightStyle _autorail_type[6][2] = {
* @param *ti TileInfo Tile that is being drawn
* @param autorail_type Offset into _AutorailTilehSprite[][]
*/
static void DrawAutorailSelection(const TileInfo *ti, uint autorail_type)
{
static void DrawAutorailSelection(const TileInfo *ti, uint autorail_type, bool multi_tile)
{
std::unordered_set<TileIndex> tiles_to_draw_set;
std::unordered_set<TileIndex> tiles_to_highlight_set;
const TileIndex selection_start = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
const TileIndex selection_end = TileVirtXY(_thd.selend.x, _thd.selend.y);
const DiagDirection selection_dir = DiagdirBetweenTiles(selection_start, selection_end);
const bool selection_dir_is_diagonal = (selection_dir != DiagDirection::INVALID_DIAGDIR);

/* Determine which parts of tunnels and bridges should be drawn */
if (multi_tile && selection_dir_is_diagonal) {
int steps = DistanceManhattan(selection_start, selection_end);
if (selection_dir != DiagDirection::INVALID_DIAGDIR) {
TileIndex tile = selection_start;
for (int step = 0; step <= steps; step++) {
if (IsBridgeTile(tile) || IsTunnelTile(tile)) {
if (selection_dir == GetTunnelBridgeDirection(tile)) {
const TileIndex tile_other_end = GetOtherTunnelBridgeEnd(tile);
tiles_to_highlight_set.insert(tile);
tiles_to_highlight_set.insert(tile_other_end);
step += 2 + GetTunnelBridgeLength(tile, tile_other_end);
tile = TileAddByDiagDir(tile_other_end, selection_dir);
}
}

tiles_to_draw_set.insert(tile);
tile = TileAddByDiagDir(tile, selection_dir);
}
}
}

SpriteID image;
PaletteID pal;
int offset;
Expand All @@ -977,7 +1010,14 @@ static void DrawAutorailSelection(const TileInfo *ti, uint autorail_type)
pal = PALETTE_SEL_TILE_RED;
}

DrawSelectionSprite(image, _thd.make_square_red ? PALETTE_SEL_TILE_RED : pal, ti, 7, foundation_part);
pal = _thd.make_square_red ? PALETTE_SEL_TILE_RED : pal;
if (!multi_tile || !selection_dir_is_diagonal || tiles_to_draw_set.find(ti->tile) != tiles_to_draw_set.end()) {
DrawSelectionSprite(image, pal, ti, 7, foundation_part);
}

if (tiles_to_highlight_set.find(ti->tile) != tiles_to_highlight_set.end()) {
DrawTileSelectionRect(ti, pal);
}
}

enum TileHighlightType {
Expand Down Expand Up @@ -1124,7 +1164,7 @@ static void DrawTileSelection(const TileInfo *ti)
/* autorail highlight piece under cursor */
HighLightStyle type = _thd.drawstyle & HT_DIR_MASK;
assert(type < HT_DIR_END);
DrawAutorailSelection(ti, _autorail_type[type][0]);
DrawAutorailSelection(ti, _autorail_type[type][0], false);
} else if (IsPartOfAutoLine(ti->x, ti->y)) {
/* autorail highlighting long line */
HighLightStyle dir = _thd.drawstyle & HT_DIR_MASK;
Expand All @@ -1137,7 +1177,7 @@ static void DrawTileSelection(const TileInfo *ti)
side = Delta(Delta(TileX(start), TileX(ti->tile)), Delta(TileY(start), TileY(ti->tile)));
}

DrawAutorailSelection(ti, _autorail_type[dir][side]);
DrawAutorailSelection(ti, _autorail_type[dir][side], true);
}
return;
}
Expand Down

0 comments on commit d189fee

Please sign in to comment.