tron@2186: /* $Id$ */ tron@2186: belugas@6889: /** @file rail_cmd.cpp */ belugas@6889: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@3189: #include "bridge_map.h" rubidium@6486: #include "bridge.h" tron@6460: #include "cmd_helper.h" hackykid@1922: #include "debug.h" rubidium@8615: #include "tile_cmd.h" tron@3101: #include "rail_map.h" tron@3144: #include "road_map.h" maedhros@6669: #include "landscape.h" tron@3319: #include "town_map.h" tron@3154: #include "tunnel_map.h" rubidium@8720: #include "viewport_func.h" rubidium@8612: #include "command_func.h" truelight@0: #include "pathfind.h" hackykid@1922: #include "engine.h" truelight@0: #include "town.h" celestar@389: #include "station.h" darkvater@405: #include "sprite.h" truelight@1313: #include "depot.h" truelight@1542: #include "waypoint.h" matthijs@1942: #include "rail.h" peter1138@3638: #include "newgrf.h" KUDr@3900: #include "yapf/yapf.h" rubidium@8035: #include "newgrf_engine.h" peter1138@3757: #include "newgrf_callbacks.h" peter1138@3757: #include "newgrf_station.h" glx@4556: #include "train.h" rubidium@7885: #include "misc/autoptr.hpp" rubidium@8707: #include "variables.h" rubidium@8078: #include "autoslope.h" belugas@8345: #include "transparency.h" rubidium@8444: #include "water.h" smatz@8579: #include "tunnelbridge_map.h" rubidium@8602: #include "window_func.h" rubidium@8640: #include "vehicle_func.h" rubidium@8653: #include "sound_func.h" smatz@8734: #include "signal_func.h" smatz@8894: #include "tunnelbridge.h" smatz@8579: rubidium@8760: #include "table/sprites.h" rubidium@8760: #include "table/strings.h" rubidium@8760: #include "table/railtypes.h" belugas@9063: #include "table/track_land.h" truelight@0: ludde@2261: const byte _track_sloped_sprites[14] = { ludde@2261: 14, 15, 22, 13, ludde@2261: 0, 21, 17, 12, ludde@2261: 23, 0, 18, 20, ludde@2261: 19, 16 ludde@2261: }; ludde@2261: truelight@0: truelight@0: /* 4 truelight@0: * --------- truelight@0: * |\ /| truelight@0: * | \ 1/ | truelight@0: * | \ / | truelight@0: * | \ / | truelight@0: * 16| \ |32 truelight@201: * | / \2 | truelight@0: * | / \ | truelight@0: * | / \ | truelight@0: * |/ \| truelight@0: * --------- truelight@0: * 8 truelight@0: */ truelight@0: truelight@0: truelight@0: darkvater@22: /* MAP2 byte: abcd???? => Signal On? Same coding as map3lo truelight@201: * MAP3LO byte: abcd???? => Signal Exists? rubidium@4434: * a and b are for diagonals, upper and left, rubidium@4434: * one for each direction. (ie a == NE->SW, b == rubidium@4434: * SW->NE, or v.v., I don't know. b and c are rubidium@4434: * similar for lower and right. truelight@0: * MAP2 byte: ????abcd => Type of ground. truelight@0: * MAP3LO byte: ????abcd => Type of rail. truelight@0: * MAP5: 00abcdef => rail truelight@0: * 01abcdef => rail w/ signals truelight@0: * 10uuuuuu => unused truelight@201: * 11uuuudd => rail depot truelight@0: */ truelight@0: maedhros@7944: smatz@8734: void *EnsureNoTrainOnTrackProc(Vehicle *v, void *data) maedhros@7944: { smatz@8577: TrackBits rail_bits = *(TrackBits *)data; smatz@8577: smatz@8577: if (v->type != VEH_TRAIN) return NULL; smatz@8577: smatz@8577: if ((v->u.rail.track != rail_bits) && !TracksOverlap(v->u.rail.track | rail_bits)) return NULL; maedhros@7944: maedhros@7944: _error_message = VehicleInTheWayErrMsg(v); maedhros@7944: return v; maedhros@7944: } maedhros@7944: maedhros@7944: /** maedhros@7944: * Tests if a vehicle interacts with the specified track. maedhros@7944: * All track bits interact except parallel TRACK_BIT_HORZ or TRACK_BIT_VERT. maedhros@7944: * maedhros@7944: * @param tile The tile. maedhros@7944: * @param track The track. maedhros@7944: */ maedhros@7944: static bool EnsureNoTrainOnTrack(TileIndex tile, Track track) maedhros@7944: { smatz@8577: TrackBits rail_bits = TrackToTrackBits(track); smatz@8577: smatz@8577: return VehicleFromPos(tile, &rail_bits, &EnsureNoTrainOnTrackProc) == NULL; maedhros@7944: } maedhros@7944: matthijs@1942: static bool CheckTrackCombination(TileIndex tile, TrackBits to_build, uint flags) truelight@0: { belugas@6916: TrackBits current; // The current track layout belugas@6916: TrackBits future; // The track layout we want to build truelight@0: _error_message = STR_1001_IMPOSSIBLE_TRACK_COMBINATION; truelight@0: tron@4182: if (!IsPlainRailTile(tile)) return false; truelight@0: matthijs@1942: /* So, we have a tile with tracks on it (and possibly signals). Let's see matthijs@1942: * what tracks first */ matthijs@1942: current = GetTrackBits(tile); tron@1946: future = current | to_build; truelight@201: matthijs@1942: /* Are we really building something new? */ matthijs@1942: if (current == future) { matthijs@1942: /* Nothing new is being built */ matthijs@1942: _error_message = STR_1007_ALREADY_BUILT; matthijs@1942: return false; matthijs@1942: } truelight@201: matthijs@1942: /* Let's see if we may build this */ tron@4182: if (flags & DC_NO_RAIL_OVERLAP || HasSignals(tile)) { matthijs@1942: /* If we are not allowed to overlap (flag is on for ai players or we have matthijs@1942: * signals on the tile), check that */ tron@3258: return future == TRACK_BIT_HORZ || future == TRACK_BIT_VERT; matthijs@1942: } else { matthijs@1942: /* Normally, we may overlap and any combination is valid */ matthijs@1942: return true; truelight@0: } truelight@0: } truelight@0: truelight@0: rubidium@8199: /** Valid TrackBits on a specific (non-steep)-slope without foundation */ rubidium@8199: static const TrackBits _valid_tracks_without_foundation[15] = { tron@3258: TRACK_BIT_ALL, matthijs@1942: TRACK_BIT_RIGHT, matthijs@1942: TRACK_BIT_UPPER, tron@3102: TRACK_BIT_X, truelight@0: matthijs@1942: TRACK_BIT_LEFT, rubidium@5838: TRACK_BIT_NONE, tron@3102: TRACK_BIT_Y, matthijs@1942: TRACK_BIT_LOWER, truelight@201: matthijs@1942: TRACK_BIT_LOWER, tron@3102: TRACK_BIT_Y, rubidium@5838: TRACK_BIT_NONE, matthijs@1942: TRACK_BIT_LEFT, truelight@201: tron@3102: TRACK_BIT_X, matthijs@1942: TRACK_BIT_UPPER, matthijs@1942: TRACK_BIT_RIGHT, rubidium@8199: }; truelight@0: rubidium@8199: /** Valid TrackBits on a specific (non-steep)-slope with leveled foundation */ rubidium@8199: static const TrackBits _valid_tracks_on_leveled_foundation[15] = { rubidium@5838: TRACK_BIT_NONE, matthijs@1942: TRACK_BIT_LEFT, matthijs@1942: TRACK_BIT_LOWER, tron@3102: TRACK_BIT_Y | TRACK_BIT_LOWER | TRACK_BIT_LEFT, truelight@0: matthijs@1942: TRACK_BIT_RIGHT, tron@3258: TRACK_BIT_ALL, tron@3102: TRACK_BIT_X | TRACK_BIT_LOWER | TRACK_BIT_RIGHT, tron@3258: TRACK_BIT_ALL, truelight@0: matthijs@1942: TRACK_BIT_UPPER, tron@3102: TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_LEFT, tron@3258: TRACK_BIT_ALL, tron@3258: TRACK_BIT_ALL, truelight@0: tron@3102: TRACK_BIT_Y | TRACK_BIT_UPPER | TRACK_BIT_RIGHT, tron@3258: TRACK_BIT_ALL, tron@3258: TRACK_BIT_ALL truelight@0: }; truelight@0: rubidium@8199: /** rubidium@8199: * Checks if a track combination is valid on a specific slope and returns the needed foundation. rubidium@8199: * rubidium@8199: * @param tileh Tile slope. rubidium@8199: * @param bits Trackbits. rubidium@8199: * @return Needed foundation or FOUNDATION_INVALID if track/slope combination is not allowed. rubidium@8199: */ rubidium@7831: Foundation GetRailFoundation(Slope tileh, TrackBits bits) truelight@0: { rubidium@8199: if (bits == TRACK_BIT_NONE) return FOUNDATION_NONE; tron@3878: rubidium@8199: if (IsSteepSlope(tileh)) { rubidium@8199: /* Test for inclined foundations */ rubidium@8199: if (bits == TRACK_BIT_X) return FOUNDATION_INCLINED_X; rubidium@8199: if (bits == TRACK_BIT_Y) return FOUNDATION_INCLINED_Y; rubidium@8199: rubidium@8199: /* Get higher track */ rubidium@8199: Corner highest_corner = GetHighestSlopeCorner(tileh); rubidium@8199: TrackBits higher_track = CornerToTrackBits(highest_corner); rubidium@8199: rubidium@8199: /* Only higher track? */ rubidium@8266: if (bits == higher_track) return HalftileFoundation(highest_corner); rubidium@8199: rubidium@8199: /* Overlap with higher track? */ rubidium@8199: if (TracksOverlap(bits | higher_track)) return FOUNDATION_INVALID; rubidium@8199: rubidium@8199: /* either lower track or both higher and lower track */ rubidium@8266: return ((bits & higher_track) != 0 ? FOUNDATION_STEEP_BOTH : FOUNDATION_STEEP_LOWER); rubidium@8199: } else { rubidium@8199: if ((~_valid_tracks_without_foundation[tileh] & bits) == 0) return FOUNDATION_NONE; rubidium@8199: rubidium@8199: bool valid_on_leveled = ((~_valid_tracks_on_leveled_foundation[tileh] & bits) == 0); rubidium@8199: rubidium@8266: Corner track_corner; rubidium@8199: switch (bits) { rubidium@8266: case TRACK_BIT_LEFT: track_corner = CORNER_W; break; rubidium@8266: case TRACK_BIT_LOWER: track_corner = CORNER_S; break; rubidium@8266: case TRACK_BIT_RIGHT: track_corner = CORNER_E; break; rubidium@8266: case TRACK_BIT_UPPER: track_corner = CORNER_N; break; rubidium@8266: rubidium@8266: case TRACK_BIT_HORZ: rubidium@8266: if (tileh == SLOPE_N) return HalftileFoundation(CORNER_N); rubidium@8266: if (tileh == SLOPE_S) return HalftileFoundation(CORNER_S); rubidium@8266: return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID); rubidium@8266: rubidium@8266: case TRACK_BIT_VERT: rubidium@8266: if (tileh == SLOPE_W) return HalftileFoundation(CORNER_W); rubidium@8266: if (tileh == SLOPE_E) return HalftileFoundation(CORNER_E); rubidium@8266: return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID); rubidium@8266: rubidium@8199: case TRACK_BIT_X: frosch@8909: if (IsSlopeWithOneCornerRaised(tileh)) return FOUNDATION_INCLINED_X; rubidium@8199: return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID); rubidium@8199: rubidium@8199: case TRACK_BIT_Y: frosch@8909: if (IsSlopeWithOneCornerRaised(tileh)) return FOUNDATION_INCLINED_Y; rubidium@8199: return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID); rubidium@8199: rubidium@8199: default: rubidium@8199: return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID); rubidium@8199: } rubidium@8266: /* Single diagonal track */ rubidium@8266: rubidium@8266: /* Track must be at least valid on leveled foundation */ rubidium@8266: if (!valid_on_leveled) return FOUNDATION_INVALID; rubidium@8266: rubidium@8266: /* If slope has three raised corners, build leveled foundation */ frosch@8909: if (IsSlopeWithThreeCornersRaised(tileh)) return FOUNDATION_LEVELED; rubidium@8266: rubidium@8266: /* If neighboured corners of track_corner are lowered, build halftile foundation */ rubidium@8266: if ((tileh & SlopeWithThreeCornersRaised(OppositeCorner(track_corner))) == SlopeWithOneCornerRaised(track_corner)) return HalftileFoundation(track_corner); rubidium@8266: rubidium@8266: /* else special anti-zig-zag foundation */ rubidium@8266: return SpecialRailFoundation(track_corner); tron@2951: } truelight@0: } truelight@0: tron@2639: rubidium@8199: /** rubidium@8199: * Tests if a track can be build on a tile. rubidium@8199: * rubidium@8199: * @param tileh Tile slope. rubidium@8199: * @param rail_bits Tracks to build. rubidium@8199: * @param existing Tracks already built. rubidium@8199: * @param tile Tile (used for water test) rubidium@8199: * @return Error message or cost for foundation building. rubidium@8199: */ rubidium@7439: static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits existing, TileIndex tile) truelight@0: { rubidium@8199: /* don't allow building on the lower side of a coast */ rubidium@8267: if (IsTileType(tile, MP_WATER) || (IsTileType(tile, MP_RAILWAY) && (GetRailGroundType(tile) == RAIL_GROUND_WATER))) { rubidium@8199: if (!IsSteepSlope(tileh) && ((~_valid_tracks_on_leveled_foundation[tileh] & (rail_bits | existing)) != 0)) return_cmd_error(STR_3807_CAN_T_BUILD_ON_WATER); rubidium@8199: } truelight@0: rubidium@8199: Foundation f_new = GetRailFoundation(tileh, rail_bits | existing); truelight@0: rubidium@8199: /* check track/slope combination */ rubidium@8199: if ((f_new == FOUNDATION_INVALID) || rubidium@8199: ((f_new != FOUNDATION_NONE) && (!_patches.build_on_slopes || _is_old_ai_player)) rubidium@8199: ) return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION); truelight@201: rubidium@8199: Foundation f_old = GetRailFoundation(tileh, existing); rubidium@8726: return CommandCost(EXPENSES_CONSTRUCTION, f_new != f_old ? _price.terraform : (Money)0); truelight@0: } truelight@0: Darkvater@1775: /* Validate functions for rail building */ matthijs@1942: static inline bool ValParamTrackOrientation(Track track) {return IsValidTrack(track);} Darkvater@1775: Darkvater@1775: /** Build a single piece of rail tron@3491: * @param tile tile to build on belugas@6979: * @param flags operation to perform Darkvater@1775: * @param p1 railtype of being built piece (normal, mono, maglev) matthijs@1942: * @param p2 rail track to build Darkvater@1775: */ rubidium@7439: CommandCost CmdBuildSingleRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { tron@3636: Slope tileh; rubidium@8732: RailType railtype = (RailType)p1; rubidium@8732: Track track = (Track)p2; matthijs@1942: TrackBits trackbit; rubidium@8726: CommandCost cost(EXPENSES_CONSTRUCTION); rubidium@7439: CommandCost ret; tron@1627: rubidium@8732: if (!ValParamRailtype(railtype) || !ValParamTrackOrientation(track)) return CMD_ERROR; tron@1627: tron@1627: tileh = GetTileSlope(tile, NULL); matthijs@1942: trackbit = TrackToTrackBits(track); truelight@0: tron@1627: switch (GetTileType(tile)) { tron@1627: case MP_RAILWAY: matthijs@1942: if (!CheckTrackCombination(tile, trackbit, flags) || maedhros@7944: !EnsureNoTrainOnTrack(tile, track)) { tron@1627: return CMD_ERROR; tron@1627: } tron@4180: if (!IsTileOwner(tile, _current_player) || peter1138@5582: !IsCompatibleRail(GetRailType(tile), railtype)) { belugas@6916: /* Get detailed error message */ tron@3491: return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); tron@1627: } tron@1627: matthijs@1942: ret = CheckRailSlope(tileh, trackbit, GetTrackBits(tile), tile); tron@1691: if (CmdFailed(ret)) return ret; rubidium@7446: cost.AddCost(ret); truelight@0: peter1138@5059: /* If the rail types don't match, try to convert only if engines of peter1138@5059: * the present rail type are powered on the new rail type. */ peter1138@5059: if (GetRailType(tile) != railtype && HasPowerOnRail(GetRailType(tile), railtype)) { tron@5058: ret = DoCommand(tile, tile, railtype, flags, CMD_CONVERT_RAIL); peter1138@5054: if (CmdFailed(ret)) return ret; rubidium@7446: cost.AddCost(ret); peter1138@5054: } peter1138@5054: tron@1719: if (flags & DC_EXEC) { celestar@3523: SetRailGroundType(tile, RAIL_GROUND_BARREN); rubidium@5662: SetTrackBits(tile, GetTrackBits(tile) | trackbit); tron@1719: } tron@1627: break; truelight@0: rubidium@7866: case MP_ROAD: tron@3142: #define M(x) (1 << (x)) tron@3142: /* Level crossings may only be built on these slopes */ skidd13@8424: if (!HasBit(M(SLOPE_SEN) | M(SLOPE_ENW) | M(SLOPE_NWS) | M(SLOPE_NS) | M(SLOPE_WSE) | M(SLOPE_EW) | M(SLOPE_FLAT), tileh)) { tron@1627: return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION); tron@3142: } tron@3142: #undef M tron@3142: belugas@6902: if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR; truelight@0: frosch@9059: if (IsNormalRoad(tile)) { tron@4140: if (HasRoadWorks(tile)) return_cmd_error(STR_ROAD_WORKS_IN_PROGRESS); tron@4140: rubidium@7157: RoadTypes roadtypes = GetRoadTypes(tile); rubidium@7157: RoadBits road = GetRoadBits(tile, ROADTYPE_ROAD); rubidium@7157: RoadBits tram = GetRoadBits(tile, ROADTYPE_TRAM); rubidium@7157: switch (roadtypes) { rubidium@7157: default: break; rubidium@7224: case ROADTYPES_TRAM: rubidium@7224: /* Tram crossings must always have road. */ rubidium@7224: SetRoadOwner(tile, ROADTYPE_ROAD, _current_player); rubidium@7224: roadtypes |= ROADTYPES_ROAD; rubidium@7224: break; rubidium@7224: rubidium@7157: case ROADTYPES_ROADTRAM: if (road == tram) break; rubidium@7157: /* FALL THROUGH */ rubidium@7157: case ROADTYPES_ROADHWAY: // Road and highway are incompatible in this case rubidium@7157: case ROADTYPES_TRAMHWAY: // Tram and highway are incompatible in this case rubidium@7157: case ROADTYPES_ALL: // Also incompatible rubidium@7157: return CMD_ERROR; rubidium@7157: } rubidium@7157: rubidium@7157: road |= tram | GetRoadBits(tile, ROADTYPE_HWAY); rubidium@7157: rubidium@7157: if ((track == TRACK_X && road == ROAD_Y) || rubidium@7157: (track == TRACK_Y && road == ROAD_X)) { tron@4140: if (flags & DC_EXEC) { rubidium@7157: MakeRoadCrossing(tile, GetRoadOwner(tile, ROADTYPE_ROAD), GetRoadOwner(tile, ROADTYPE_TRAM), GetRoadOwner(tile, ROADTYPE_HWAY), _current_player, (track == TRACK_X ? AXIS_Y : AXIS_X), railtype, roadtypes, GetTownIndex(tile)); smatz@8840: UpdateLevelCrossing(tile, false); tron@4140: } tron@4140: break; truelight@4139: } tron@1627: } tron@1627: tron@3267: if (IsLevelCrossing(tile) && GetCrossingRailBits(tile) == trackbit) { tron@1627: return_cmd_error(STR_1007_ALREADY_BUILT); tron@3267: } tron@1627: /* FALLTHROUGH */ tron@1627: tron@1627: default: frosch@8910: /* Will there be flat water on the lower halftile? */ frosch@8909: bool water_ground = IsTileType(tile, MP_WATER) && IsSlopeWithOneCornerRaised(tileh); rubidium@8267: rubidium@5838: ret = CheckRailSlope(tileh, trackbit, TRACK_BIT_NONE, tile); tron@1691: if (CmdFailed(ret)) return ret; rubidium@7446: cost.AddCost(ret); tron@1627: tron@3491: ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); tron@1691: if (CmdFailed(ret)) return ret; rubidium@7446: cost.AddCost(ret); tron@1627: rubidium@8267: if (water_ground) { rubidium@8267: cost.AddCost(-_price.clear_water); belugas@8473: cost.AddCost(_price.clear_roughland); rubidium@8267: } rubidium@8267: rubidium@8267: if (flags & DC_EXEC) { rubidium@8267: MakeRailNormal(tile, _current_player, trackbit, railtype); rubidium@8267: if (water_ground) SetRailGroundType(tile, RAIL_GROUND_WATER); rubidium@8267: } tron@1627: break; truelight@0: } truelight@0: truelight@0: if (flags & DC_EXEC) { truelight@0: MarkTileDirtyByTile(tile); smatz@8802: AddTrackToSignalBuffer(tile, track, _current_player); KUDr@3900: YapfNotifyTrackLayoutChange(tile, track); truelight@0: } truelight@0: maedhros@8226: return cost.AddCost(RailBuildCost(railtype)); truelight@0: } truelight@0: Darkvater@1775: /** Remove a single piece of track tron@3491: * @param tile tile to remove track from belugas@6979: * @param flags operation to perform Darkvater@1775: * @param p1 unused Darkvater@1775: * @param p2 rail orientation truelight@0: */ rubidium@7439: CommandCost CmdRemoveSingleRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { matthijs@1942: Track track = (Track)p2; matthijs@1942: TrackBits trackbit; rubidium@8726: CommandCost cost(EXPENSES_CONSTRUCTION, _price.remove_rail ); peter1138@3284: bool crossing = false; Darkvater@1775: rubidium@5838: if (!ValParamTrackOrientation((Track)p2)) return CMD_ERROR; matthijs@1942: trackbit = TrackToTrackBits(track); truelight@0: smatz@8814: /* Need to read tile owner now because it may change when the rail is removed smatz@8814: * Also, in case of floods, _current_player != owner smatz@8814: * There may be invalid tiletype even in exec run (when removing long track), smatz@8814: * so do not call GetTileOwner(tile) in any case here */ smatz@8814: Owner owner = INVALID_OWNER; smatz@8796: tron@3273: switch (GetTileType(tile)) { rubidium@7866: case MP_ROAD: { tron@3273: if (!IsLevelCrossing(tile) || tron@3273: GetCrossingRailBits(tile) != trackbit || tron@3273: (_current_player != OWNER_WATER && !CheckTileOwnership(tile)) || smatz@9015: (!(flags & DC_BANKRUPT) && !EnsureNoVehicleOnGround(tile))) { tron@3273: return CMD_ERROR; tron@3273: } truelight@0: tron@3273: if (flags & DC_EXEC) { smatz@8814: owner = GetTileOwner(tile); rubidium@7157: MakeRoadNormal(tile, GetCrossingRoadBits(tile), GetRoadTypes(tile), GetTownIndex(tile), GetRoadOwner(tile, ROADTYPE_ROAD), GetRoadOwner(tile, ROADTYPE_TRAM), GetRoadOwner(tile, ROADTYPE_HWAY)); tron@3273: } matthijs@1942: break; tron@3071: } tron@2548: tron@3273: case MP_RAILWAY: { tron@3273: TrackBits present; tron@3273: tron@3273: if (!IsPlainRailTile(tile) || tron@3273: (_current_player != OWNER_WATER && !CheckTileOwnership(tile)) || maedhros@7944: !EnsureNoTrainOnTrack(tile, track)) { matthijs@1942: return CMD_ERROR; tron@3273: } matthijs@1942: tron@3273: present = GetTrackBits(tile); tron@3273: if ((present & trackbit) == 0) return CMD_ERROR; peter1138@3284: if (present == (TRACK_BIT_X | TRACK_BIT_Y)) crossing = true; matthijs@1942: hackykid@1963: /* Charge extra to remove signals on the track, if they are there */ matthijs@1942: if (HasSignalOnTrack(tile, track)) rubidium@7446: cost.AddCost(DoCommand(tile, track, 0, flags, CMD_REMOVE_SIGNALS)); matthijs@1942: tron@3273: if (flags & DC_EXEC) { smatz@8814: owner = GetTileOwner(tile); tron@3273: present ^= trackbit; tron@3273: if (present == 0) { frosch@8910: Slope tileh = GetTileSlope(tile, NULL); frosch@8910: /* If there is flat water on the lower halftile, convert the tile to shore so the water remains */ frosch@8910: if (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh)) { rubidium@8267: MakeShore(tile); rubidium@8267: } else { rubidium@8267: DoClearSquare(tile); rubidium@8267: } tron@3273: } else { tron@3273: SetTrackBits(tile, present); tron@3273: } matthijs@1942: } matthijs@1942: break; tron@3273: } tron@2548: tron@3273: default: return CMD_ERROR; truelight@0: } truelight@0: tron@3273: if (flags & DC_EXEC) { smatz@8814: /* if we got that far, 'owner' variable is set correctly */ smatz@8814: assert(IsValidPlayer(owner)); smatz@8814: tron@3273: MarkTileDirtyByTile(tile); peter1138@3284: if (crossing) { peter1138@3284: /* crossing is set when only TRACK_BIT_X and TRACK_BIT_Y are set. As we peter1138@3284: * are removing one of these pieces, we'll need to update signals for peter1138@3284: * both directions explicitly, as after the track is removed it won't peter1138@3284: * 'connect' with the other piece. */ smatz@8802: AddTrackToSignalBuffer(tile, TRACK_X, owner); smatz@8802: AddTrackToSignalBuffer(tile, TRACK_Y, owner); KUDr@3900: YapfNotifyTrackLayoutChange(tile, TRACK_X); KUDr@3900: YapfNotifyTrackLayoutChange(tile, TRACK_Y); peter1138@3284: } else { smatz@8802: AddTrackToSignalBuffer(tile, track, owner); KUDr@3900: YapfNotifyTrackLayoutChange(tile, track); peter1138@3284: } tron@3273: } truelight@201: hackykid@1963: return cost; truelight@0: } truelight@0: truelight@0: rubidium@8267: /** rubidium@8267: * Called from water_cmd if a non-flat rail-tile gets flooded and should be converted to shore. rubidium@8267: * The function floods the lower halftile, if the tile has a halftile foundation. rubidium@8267: * rubidium@8267: * @param t The tile to flood. frosch@8828: * @return true if something was flooded. rubidium@8267: */ frosch@8828: bool FloodHalftile(TileIndex t) rubidium@8267: { frosch@8828: bool flooded = false; frosch@8828: if (GetRailGroundType(t) == RAIL_GROUND_WATER) return flooded; rubidium@8267: rubidium@8267: Slope tileh = GetTileSlope(t, NULL); rubidium@8267: TrackBits rail_bits = GetTrackBits(t); rubidium@8267: frosch@8909: if (IsSlopeWithOneCornerRaised(tileh)) { rubidium@8267: TrackBits lower_track = CornerToTrackBits(OppositeCorner(GetHighestSlopeCorner(tileh))); rubidium@8267: rubidium@8267: TrackBits to_remove = lower_track & rail_bits; rubidium@8267: if (to_remove != 0) { rubidium@8267: _current_player = OWNER_WATER; frosch@8828: if (CmdFailed(DoCommand(t, 0, FIND_FIRST_BIT(to_remove), DC_EXEC, CMD_REMOVE_SINGLE_RAIL))) return flooded; // not yet floodable frosch@8828: flooded = true; rubidium@8267: rail_bits = rail_bits & ~to_remove; rubidium@8267: if (rail_bits == 0) { rubidium@8267: MakeShore(t); rubidium@8267: MarkTileDirtyByTile(t); frosch@8828: return flooded; rubidium@8267: } rubidium@8267: } rubidium@8267: rubidium@8267: if (IsNonContinuousFoundation(GetRailFoundation(tileh, rail_bits))) { frosch@8828: flooded = true; rubidium@8267: SetRailGroundType(t, RAIL_GROUND_WATER); rubidium@8267: MarkTileDirtyByTile(t); rubidium@8267: } frosch@8910: } else { frosch@8910: /* Make shore on steep slopes and 'three-corners-raised'-slopes. */ frosch@8910: if (ApplyFoundationToSlope(GetRailFoundation(tileh, rail_bits), &tileh) == 0) { frosch@8910: if (IsSteepSlope(tileh) || IsSlopeWithThreeCornersRaised(tileh)) { frosch@8910: flooded = true; frosch@8910: SetRailGroundType(t, RAIL_GROUND_WATER); frosch@8910: MarkTileDirtyByTile(t); frosch@8910: } frosch@8910: } rubidium@8267: } frosch@8828: return flooded; rubidium@8267: } rubidium@8267: tron@3493: static const TileIndexDiffC _trackdelta[] = { tron@3493: { -1, 0 }, { 0, 1 }, { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, 1 }, tron@3493: { 0, 0 }, tron@3493: { 0, 0 }, tron@3493: { 1, 0 }, { 0, -1 }, { 0, -1 }, { 1, 0 }, { 0, -1 }, { -1, 0 }, tron@3493: { 0, 0 }, tron@3493: { 0, 0 } tron@3493: }; tron@3493: tron@3493: rubidium@7439: static CommandCost ValidateAutoDrag(Trackdir *trackdir, TileIndex start, TileIndex end) darkvater@1227: { tron@3493: int x = TileX(start); tron@3493: int y = TileY(start); tron@3493: int ex = TileX(end); tron@3493: int ey = TileY(end); darkvater@1227: int dx, dy, trdx, trdy; truelight@0: rubidium@5838: if (!ValParamTrackOrientation(TrackdirToTrack(*trackdir))) return CMD_ERROR; darkvater@1227: belugas@6916: /* calculate delta x,y from start to end tile */ darkvater@1227: dx = ex - x; darkvater@1227: dy = ey - y; darkvater@1227: belugas@6916: /* calculate delta x,y for the first direction */ tron@3493: trdx = _trackdelta[*trackdir].x; tron@3493: trdy = _trackdelta[*trackdir].y; darkvater@1227: matthijs@1942: if (!IsDiagonalTrackdir(*trackdir)) { tron@3493: trdx += _trackdelta[*trackdir ^ 1].x; tron@3493: trdy += _trackdelta[*trackdir ^ 1].y; darkvater@1227: } darkvater@1227: belugas@6916: /* validate the direction */ tron@2951: while ( tron@2951: (trdx <= 0 && dx > 0) || tron@2951: (trdx >= 0 && dx < 0) || tron@2951: (trdy <= 0 && dy > 0) || tron@2951: (trdy >= 0 && dy < 0) tron@2951: ) { skidd13@8424: if (!HasBit(*trackdir, 3)) { // first direction is invalid, try the other skidd13@8427: SetBit(*trackdir, 3); // reverse the direction darkvater@1227: trdx = -trdx; darkvater@1227: trdy = -trdy; tron@4077: } else { // other direction is invalid too, invalid drag darkvater@1227: return CMD_ERROR; tron@4077: } darkvater@1227: } darkvater@1227: belugas@6916: /* (for diagonal tracks, this is already made sure of by above test), but: belugas@6916: * for non-diagonal tracks, check if the start and end tile are on 1 line */ matthijs@1942: if (!IsDiagonalTrackdir(*trackdir)) { tron@3493: trdx = _trackdelta[*trackdir].x; tron@3493: trdy = _trackdelta[*trackdir].y; tron@2951: if (abs(dx) != abs(dy) && abs(dx) + abs(trdy) != abs(dy) + abs(trdx)) darkvater@1227: return CMD_ERROR; darkvater@1227: } darkvater@1227: rubidium@7446: return CommandCost(); darkvater@1227: } darkvater@1227: Darkvater@1775: /** Build a stretch of railroad tracks. tron@3491: * @param tile start tile of drag belugas@6979: * @param flags operation to perform Darkvater@1775: * @param p1 end tile of drag Darkvater@1775: * @param p2 various bitstuffed elements Darkvater@1775: * - p2 = (bit 0-3) - railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev) matthijs@1942: * - p2 = (bit 4-6) - track-orientation, valid values: 0-5 (Track enum) Darkvater@1775: * - p2 = (bit 7) - 0 = build, 1 = remove tracks truelight@0: */ rubidium@7439: static CommandCost CmdRailTrackHelper(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) darkvater@1227: { rubidium@8726: CommandCost ret, total_cost(EXPENSES_CONSTRUCTION); tron@2140: Track track = (Track)GB(p2, 4, 3); matthijs@1942: Trackdir trackdir; skidd13@8424: byte mode = HasBit(p2, 7); peter1138@2604: RailType railtype = (RailType)GB(p2, 0, 4); tron@3493: TileIndex end_tile; truelight@0: peter1138@2604: if (!ValParamRailtype(railtype) || !ValParamTrackOrientation(track)) return CMD_ERROR; tron@2934: if (p1 >= MapSize()) return CMD_ERROR; tron@3493: end_tile = p1; matthijs@1942: trackdir = TrackToTrackdir(track); Darkvater@1775: tron@3493: if (CmdFailed(ValidateAutoDrag(&trackdir, tile, end_tile))) return CMD_ERROR; truelight@201: tron@3493: if (flags & DC_EXEC) SndPlayTileFx(SND_20_SPLAT_2, tile); truelight@0: tron@2952: for (;;) { tron@3493: ret = DoCommand(tile, railtype, TrackdirToTrack(trackdir), flags, (mode == 0) ? CMD_BUILD_SINGLE_RAIL : CMD_REMOVE_SINGLE_RAIL); truelight@0: Darkvater@1775: if (CmdFailed(ret)) { Darkvater@3671: if ((_error_message != STR_1007_ALREADY_BUILT) && (mode == 0)) break; Darkvater@3671: _error_message = INVALID_STRING_ID; tron@4077: } else { rubidium@7446: total_cost.AddCost(ret); tron@4077: } truelight@0: tron@3493: if (tile == end_tile) break; truelight@0: tron@3493: tile += ToTileIndexDiff(_trackdelta[trackdir]); truelight@201: belugas@6916: /* toggle railbit for the non-diagonal tracks */ skidd13@8428: if (!IsDiagonalTrackdir(trackdir)) ToggleBit(trackdir, 0); truelight@201: } truelight@0: rubidium@7446: return (total_cost.GetCost() == 0) ? CMD_ERROR : total_cost; truelight@0: } truelight@0: Darkvater@1796: /** Build rail on a stretch of track. Darkvater@1796: * Stub for the unified rail builder/remover belugas@6979: * @param tile start tile of drag belugas@6979: * @param flags operation to perform belugas@6979: * @param p1 end tile of drag belugas@6979: * @param p2 various bitstuffed elements belugas@6979: * - p2 = (bit 0-3) - railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev) belugas@6979: * - p2 = (bit 4-6) - track-orientation, valid values: 0-5 (Track enum) belugas@6979: * - p2 = (bit 7) - 0 = build, 1 = remove tracks Darkvater@1796: * @see CmdRailTrackHelper Darkvater@1796: */ rubidium@7439: CommandCost CmdBuildRailroadTrack(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) darkvater@1227: { skidd13@8425: return CmdRailTrackHelper(tile, flags, p1, ClrBit(p2, 7)); darkvater@1227: } truelight@0: Darkvater@1796: /** Build rail on a stretch of track. Darkvater@1796: * Stub for the unified rail builder/remover belugas@6979: * @param tile start tile of drag belugas@6979: * @param flags operation to perform belugas@6979: * @param p1 end tile of drag belugas@6979: * @param p2 various bitstuffed elements belugas@6979: * - p2 = (bit 0-3) - railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev) belugas@6979: * - p2 = (bit 4-6) - track-orientation, valid values: 0-5 (Track enum) belugas@6979: * - p2 = (bit 7) - 0 = build, 1 = remove tracks Darkvater@1796: * @see CmdRailTrackHelper Darkvater@1796: */ rubidium@7439: CommandCost CmdRemoveRailroadTrack(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { skidd13@8427: return CmdRailTrackHelper(tile, flags, p1, SetBit(p2, 7)); truelight@0: } truelight@0: Darkvater@1775: /** Build a train depot tron@3491: * @param tile position of the train depot belugas@6979: * @param flags operation to perform Darkvater@1775: * @param p1 rail type tron@6460: * @param p2 bit 0..1 entrance direction (DiagDirection) celestar@2085: * celestar@2085: * @todo When checking for the tile slope, celestar@2085: * distingush between "Flat land required" and "land sloped in wrong direction" truelight@0: */ rubidium@7439: CommandCost CmdBuildTrainDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { tron@3636: Slope tileh; truelight@0: Darkvater@1775: /* check railtype and valid direction for depot (0 through 3), 4 in total */ rubidium@8732: if (!ValParamRailtype((RailType)p1)) return CMD_ERROR; truelight@0: truelight@0: tileh = GetTileSlope(tile, NULL); celestar@2085: tron@6460: DiagDirection dir = Extract(p2); tron@6460: celestar@2085: /* Prohibit construction if rubidium@4549: * The tile is non-flat AND rubidium@4549: * 1) The AI is "old-school" rubidium@4549: * 2) build-on-slopes is disabled rubidium@4549: * 3) the tile is steep i.e. spans two height levels rubidium@4549: * 4) the exit points in the wrong direction rubidium@4549: */ celestar@2085: tron@3636: if (tileh != SLOPE_FLAT && ( tron@2548: _is_old_ai_player || tron@2548: !_patches.build_on_slopes || tron@3636: IsSteepSlope(tileh) || tron@6460: !CanBuildDepotByTileh(dir, tileh) tron@2548: )) { tron@2548: return_cmd_error(STR_0007_FLAT_LAND_REQUIRED); truelight@0: } truelight@0: rubidium@8726: CommandCost cost = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); rubidium@7446: if (CmdFailed(cost)) return CMD_ERROR; truelight@0: celestar@5573: if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST); celestar@5573: rubidium@7885: Depot *d = new Depot(tile); rubidium@7885: tron@2639: if (d == NULL) return CMD_ERROR; rubidium@7885: AutoPtrT d_auto_delete = d; truelight@0: truelight@0: if (flags & DC_EXEC) { rubidium@5838: MakeRailDepot(tile, _current_player, dir, (RailType)p1); tron@3101: MarkTileDirtyByTile(tile); truelight@201: Darkvater@1775: d->town_index = ClosestTownFromTile(tile, (uint)-1)->index; truelight@0: smatz@8802: AddSideToSignalBuffer(tile, INVALID_DIAGDIR, _current_player); rubidium@5838: YapfNotifyTrackLayoutChange(tile, TrackdirToTrack(DiagdirToDiagTrackdir(dir))); rubidium@7885: d_auto_delete.Detach(); truelight@0: } truelight@0: rubidium@7446: return cost.AddCost(_price.build_train_depot); truelight@0: } truelight@0: Darkvater@1775: /** Build signals, alternate between double/single, signal/semaphore, Darkvater@5982: * pre/exit/combo-signals, and what-else not. If the rail piece does not Darkvater@5982: * have any signals, bit 4 (cycle signal-type) is ignored tron@3491: * @param tile tile where to build the signals belugas@6979: * @param flags operation to perform Darkvater@1775: * @param p1 various bitstuffed elements Darkvater@5986: * - p1 = (bit 0-2) - track-orientation, valid values: 0-5 (Track enum) rubidium@8487: * - p1 = (bit 3) - 1 = override signal/semaphore, or pre/exit/combo signal or (for bit 7) toggle variant (CTRL-toggle) Darkvater@5986: * - p1 = (bit 4) - 0 = signals, 1 = semaphores rubidium@8487: * - p1 = (bit 5-6) - type of the signal, for valid values see enum SignalType in rail_map.h rubidium@8487: * - p1 = (bit 7) - convert the present signal type and variant matthijs@1942: * @param p2 used for CmdBuildManySignals() to copy direction of first signal matthijs@1942: * TODO: p2 should be replaced by two bits for "along" and "against" the track. darkvater@58: */ rubidium@7439: CommandCost CmdBuildSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { Darkvater@5986: Track track = (Track)GB(p1, 0, 3); rubidium@8487: bool ctrl_pressed = HasBit(p1, 3); // was the CTRL button pressed rubidium@8487: SignalVariant sigvar = (ctrl_pressed ^ HasBit(p1, 4)) ? SIG_SEMAPHORE : SIG_ELECTRIC; // the signal variant of the new signal rubidium@8487: SignalType sigtype = (SignalType)GB(p1, 5, 2); // the signal type of the new signal rubidium@8487: bool convert_signal = HasBit(p1, 7); // convert button pressed rubidium@7439: CommandCost cost; truelight@201: maedhros@7944: if (!ValParamTrackOrientation(track) || !IsTileType(tile, MP_RAILWAY) || !EnsureNoTrainOnTrack(tile, track)) truelight@0: return CMD_ERROR; truelight@0: Darkvater@1775: /* Protect against invalid signal copying */ matthijs@1942: if (p2 != 0 && (p2 & SignalOnTrack(track)) == 0) return CMD_ERROR; truelight@0: matthijs@1942: /* You can only build signals on plain rail tiles, and the selected track must exist */ matthijs@1942: if (!IsPlainRailTile(tile) || !HasTrack(tile, track)) return CMD_ERROR; tron@1084: tron@1084: if (!CheckTileOwnership(tile)) return CMD_ERROR; tron@1084: truelight@0: { tron@4077: /* See if this is a valid track combination for signals, (ie, no overlap) */ tron@4077: TrackBits trackbits = GetTrackBits(tile); truelight@8329: if (KillFirstBit(trackbits) != TRACK_BIT_NONE && /* More than one track present */ tron@3258: trackbits != TRACK_BIT_HORZ && tron@4077: trackbits != TRACK_BIT_VERT) { rubidium@8487: return_cmd_error(STR_1005_NO_SUITABLE_RAILROAD_TRACK); tron@4077: } truelight@0: } truelight@0: rubidium@8487: /* you can not convert a signal if no signal is on track */ rubidium@8487: if (convert_signal && !HasSignalOnTrack(tile, track)) return CMD_ERROR; rubidium@8487: matthijs@1942: if (!HasSignalOnTrack(tile, track)) { belugas@6916: /* build new signals */ rubidium@8726: cost = CommandCost(EXPENSES_CONSTRUCTION, _price.build_signals); darkvater@1066: } else { glx@7266: if (p2 != 0 && sigvar != GetSignalVariant(tile, track)) { belugas@6916: /* convert signals <-> semaphores */ rubidium@8726: cost = CommandCost(EXPENSES_CONSTRUCTION, _price.build_signals + _price.remove_signals); rubidium@8487: rubidium@8487: } else if (convert_signal) { rubidium@8487: /* convert button pressed */ rubidium@8487: if (ctrl_pressed || GetSignalVariant(tile, track) != sigvar) { rubidium@8487: /* convert electric <-> semaphore */ rubidium@8726: cost = CommandCost(EXPENSES_CONSTRUCTION, _price.build_signals + _price.remove_signals); rubidium@8487: } else { rubidium@8487: /* it is free to change signal type: normal-pre-exit-combo */ rubidium@8487: cost = CommandCost(); rubidium@8487: } rubidium@8487: tron@1084: } else { belugas@6928: /* it is free to change orientation/pre-exit-combo signals */ rubidium@7446: cost = CommandCost(); tron@1084: } truelight@0: } truelight@0: truelight@0: if (flags & DC_EXEC) { tron@4182: if (!HasSignals(tile)) { belugas@6916: /* there are no signals at all on this tile yet */ rubidium@6498: SetHasSignals(tile, true); rubidium@7249: SetSignalStates(tile, 0xF); // all signals are on rubidium@7249: SetPresentSignals(tile, 0); // no signals built by default rubidium@8487: SetSignalType(tile, track, sigtype); glx@7266: SetSignalVariant(tile, track, sigvar); truelight@0: } truelight@0: tron@1084: if (p2 == 0) { matthijs@1942: if (!HasSignalOnTrack(tile, track)) { belugas@6916: /* build new signals */ rubidium@7249: SetPresentSignals(tile, GetPresentSignals(tile) | SignalOnTrack(track)); rubidium@8487: SetSignalType(tile, track, sigtype); glx@7266: SetSignalVariant(tile, track, sigvar); tron@1084: } else { rubidium@8487: if (convert_signal) { rubidium@8487: /* convert signal button pressed */ rubidium@8487: if (ctrl_pressed) { rubidium@8487: /* toggle the pressent signal variant: SIG_ELECTRIC <-> SIG_SEMAPHORE */ rubidium@8487: SetSignalVariant(tile, track, (GetSignalVariant(tile, track) == SIG_ELECTRIC) ? SIG_SEMAPHORE : SIG_ELECTRIC); rubidium@8487: rubidium@8487: } else { rubidium@8487: /* convert the present signal to the chosen type and variant */ rubidium@8487: SetSignalType(tile, track, sigtype); rubidium@8487: SetSignalVariant(tile, track, sigvar); rubidium@8487: } rubidium@8487: rubidium@8487: } else if (ctrl_pressed) { belugas@6916: /* cycle between normal -> pre -> exit -> combo -> ... */ rubidium@8487: sigtype = GetSignalType(tile, track); rubidium@8487: rubidium@8487: SetSignalType(tile, track, sigtype == SIGTYPE_COMBO ? SIGTYPE_NORMAL : (SignalType)(sigtype + 1)); tron@1084: } else { rubidium@8487: /* cycle the signal side: both -> left -> right -> both -> ... */ celestar@3522: CycleSignalSide(tile, track); tron@1084: } tron@1084: } darkvater@58: } else { tron@1084: /* If CmdBuildManySignals is called with copying signals, just copy the matthijs@1942: * direction of the first signal given as parameter by CmdBuildManySignals */ rubidium@7249: SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | (p2 & SignalOnTrack(track))); glx@7266: SetSignalVariant(tile, track, sigvar); truelight@0: } truelight@201: truelight@0: MarkTileDirtyByTile(tile); smatz@8802: AddTrackToSignalBuffer(tile, track, _current_player); KUDr@3900: YapfNotifyTrackLayoutChange(tile, track); truelight@0: } truelight@0: truelight@0: return cost; truelight@0: } truelight@0: peter1138@7659: static bool CheckSignalAutoFill(TileIndex &tile, Trackdir &trackdir, int &signal_ctr, bool remove) peter1138@7659: { peter1138@7659: tile = AddTileIndexDiffCWrap(tile, _trackdelta[trackdir]); peter1138@7659: if (tile == INVALID_TILE) return false; peter1138@7659: peter1138@7659: /* Check for track bits on the new tile */ peter1138@7659: uint32 ts = GetTileTrackStatus(tile, TRANSPORT_RAIL, 0); peter1138@7659: TrackdirBits trackdirbits = (TrackdirBits)(ts & TRACKDIR_BIT_MASK); peter1138@7659: peter1138@7659: if (TracksOverlap(TrackdirBitsToTrackBits(trackdirbits))) return false; peter1138@7659: trackdirbits &= TrackdirReachesTrackdirs(trackdir); peter1138@7659: peter1138@7659: /* No track bits, must stop */ peter1138@7659: if (trackdirbits == TRACKDIR_BIT_NONE) return false; peter1138@7659: peter1138@7659: /* Get the first track dir */ peter1138@7659: trackdir = RemoveFirstTrackdir(&trackdirbits); peter1138@7659: peter1138@7659: /* Any left? It's a junction so we stop */ peter1138@7659: if (trackdirbits != TRACKDIR_BIT_NONE) return false; peter1138@7659: peter1138@7659: switch (GetTileType(tile)) { peter1138@7659: case MP_RAILWAY: peter1138@7659: if (IsRailDepot(tile)) return false; peter1138@7659: if (!remove && HasSignalOnTrack(tile, TrackdirToTrack(trackdir))) return false; peter1138@7659: signal_ctr++; peter1138@7659: if (IsDiagonalTrackdir(trackdir)) { peter1138@7659: signal_ctr++; peter1138@7659: /* Ensure signal_ctr even so X and Y pieces get signals */ skidd13@8425: ClrBit(signal_ctr, 0); peter1138@7659: } peter1138@7659: return true; peter1138@7659: rubidium@7866: case MP_ROAD: peter1138@7659: if (!IsLevelCrossing(tile)) return false; peter1138@7659: signal_ctr += 2; peter1138@7659: return true; peter1138@7659: peter1138@7659: case MP_TUNNELBRIDGE: { smatz@8584: TileIndex orig_tile = tile; // backup old value smatz@8584: smatz@8584: if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return false; smatz@8584: if (GetTunnelBridgeDirection(tile) != TrackdirToExitdir(trackdir)) return false; smatz@8584: smatz@8584: /* Skip to end of tunnel or bridge smatz@8584: * note that tile is a parameter by reference, so it must be updated */ smatz@8693: tile = GetOtherTunnelBridgeEnd(tile); smatz@8584: smatz@8894: signal_ctr += (GetTunnelBridgeLength(orig_tile, tile) + 2) * 2; peter1138@7659: return true; peter1138@7659: } peter1138@7659: peter1138@7659: default: return false; peter1138@7659: } peter1138@7659: } peter1138@7659: rubidium@4434: /** Build many signals by dragging; AutoSignals tron@3491: * @param tile start tile of drag belugas@6979: * @param flags operation to perform Darkvater@1796: * @param p1 end tile of drag Darkvater@1796: * @param p2 various bitstuffed elements Darkvater@5986: * - p2 = (bit 0- 2) - track-orientation, valid values: 0-5 (Track enum) Darkvater@5986: * - p2 = (bit 3) - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle) Darkvater@5986: * - p2 = (bit 4) - 0 = signals, 1 = semaphores Darkvater@5982: * - p2 = (bit 5) - 0 = build, 1 = remove signals peter1138@7659: * - p2 = (bit 6) - 0 = selected stretch, 1 = auto fill Darkvater@1796: * - p2 = (bit 24-31) - user defined signals_density darkvater@58: */ rubidium@7439: static CommandCost CmdSignalTrackHelper(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) darkvater@58: { rubidium@8726: CommandCost ret, total_cost(EXPENSES_CONSTRUCTION); rubidium@7439: int signal_ctr; hackykid@1954: byte signals; Darkvater@1796: bool error = true; tron@3493: TileIndex end_tile; peter1138@7659: TileIndex start_tile = tile; Darkvater@1796: Darkvater@5986: Track track = (Track)GB(p2, 0, 3); skidd13@8424: bool mode = HasBit(p2, 3); skidd13@8424: bool semaphores = HasBit(p2, 4); skidd13@8424: bool remove = HasBit(p2, 5); skidd13@8424: bool autofill = HasBit(p2, 6); matthijs@1942: Trackdir trackdir = TrackToTrackdir(track); Darkvater@5982: byte signal_density = GB(p2, 24, 8); Darkvater@1796: tron@2934: if (p1 >= MapSize()) return CMD_ERROR; tron@3493: end_tile = p1; Darkvater@1796: if (signal_density == 0 || signal_density > 20) return CMD_ERROR; darkvater@58: tron@2639: if (!IsTileType(tile, MP_RAILWAY)) return CMD_ERROR; hackykid@1954: Darkvater@1796: /* for vertical/horizontal tracks, double the given signals density rubidium@4549: * since the original amount will be too dense (shorter tracks) */ peter1138@7659: signal_density *= 2; Darkvater@1796: tron@3493: if (CmdFailed(ValidateAutoDrag(&trackdir, tile, end_tile))) return CMD_ERROR; matthijs@1942: matthijs@1942: track = TrackdirToTrack(trackdir); /* trackdir might have changed, keep track in sync */ peter1138@7659: Trackdir start_trackdir = trackdir; peter1138@7659: peter1138@7659: /* Autofill must start on a valid track to be able to avoid loops */ peter1138@7659: if (autofill && !HasTrack(tile, track)) return CMD_ERROR; darkvater@58: belugas@6928: /* copy the signal-style of the first rail-piece if existing */ tron@4182: if (HasSignals(tile)) { rubidium@7249: signals = GetPresentSignals(tile) & SignalOnTrack(track); matthijs@1942: if (signals == 0) signals = SignalOnTrack(track); /* Can this actually occur? */ darkvater@58: belugas@6916: /* copy signal/semaphores style (independent of CTRL) */ glx@7266: semaphores = GetSignalVariant(tile, track) != SIG_ELECTRIC; tron@4077: } else { // no signals exist, drag a two-way signal stretch matthijs@1942: signals = SignalOnTrack(track); tron@4077: } darkvater@58: peter1138@7659: byte signal_dir = 0; skidd13@8427: if (signals & SignalAlongTrackdir(trackdir)) SetBit(signal_dir, 0); skidd13@8427: if (signals & SignalAgainstTrackdir(trackdir)) SetBit(signal_dir, 1); peter1138@7659: hackykid@1954: /* signal_ctr - amount of tiles already processed matthijs@1942: * signals_density - patch setting to put signal on every Nth tile (double space on |, -- tracks) darkvater@58: ********** hackykid@1954: * trackdir - trackdir to build with autorail hackykid@1954: * semaphores - semaphores or signals matthijs@1942: * signals - is there a signal/semaphore on the first tile, copy its style (two-way/single-way) rubidium@4549: * and convert all others to semaphore/signal Darkvater@5982: * remove - 1 remove signals, 0 build signals */ rubidium@7446: signal_ctr = 0; Darkvater@1796: for (;;) { belugas@6916: /* only build/remove signals with the specified density */ peter1138@7659: if ((remove && autofill) || signal_ctr % signal_density == 0) { Darkvater@5986: uint32 p1 = GB(TrackdirToTrack(trackdir), 0, 3); Darkvater@5986: SB(p1, 3, 1, mode); Darkvater@5986: SB(p1, 4, 1, semaphores); peter1138@7659: peter1138@7659: /* Pick the correct orientation for the track direction */ peter1138@7659: signals = 0; skidd13@8424: if (HasBit(signal_dir, 0)) signals |= SignalAlongTrackdir(trackdir); skidd13@8424: if (HasBit(signal_dir, 1)) signals |= SignalAgainstTrackdir(trackdir); peter1138@7659: Darkvater@5982: ret = DoCommand(tile, p1, signals, flags, remove ? CMD_REMOVE_SIGNALS : CMD_BUILD_SIGNALS); darkvater@58: Darkvater@5069: /* Be user-friendly and try placing signals as much as possible */ rubidium@7442: if (CmdSucceeded(ret)) { darkvater@58: error = false; rubidium@7446: total_cost.AddCost(ret); darkvater@58: } darkvater@58: } darkvater@58: peter1138@7659: if (autofill) { peter1138@7659: if (!CheckSignalAutoFill(tile, trackdir, signal_ctr, remove)) break; darkvater@58: peter1138@7659: /* Prevent possible loops */ peter1138@7659: if (tile == start_tile && trackdir == start_trackdir) break; peter1138@7659: } else { peter1138@7659: if (tile == end_tile) break; truelight@201: peter1138@7659: tile += ToTileIndexDiff(_trackdelta[trackdir]); peter1138@7659: signal_ctr++; peter1138@7659: peter1138@7659: /* toggle railbit for the non-diagonal tracks (|, -- tracks) */ peter1138@7659: if (IsDiagonalTrackdir(trackdir)) { peter1138@7659: signal_ctr++; peter1138@7659: } else { skidd13@8428: ToggleBit(trackdir, 0); peter1138@7659: } peter1138@7659: } darkvater@58: } darkvater@58: tron@2951: return error ? CMD_ERROR : total_cost; darkvater@58: } darkvater@58: Darkvater@1796: /** Build signals on a stretch of track. Darkvater@1796: * Stub for the unified signal builder/remover belugas@6979: * @param tile start tile of drag belugas@6979: * @param flags operation to perform belugas@6979: * @param p1 end tile of drag belugas@6979: * @param p2 various bitstuffed elements belugas@6979: * - p2 = (bit 0- 2) - track-orientation, valid values: 0-5 (Track enum) belugas@6979: * - p2 = (bit 3) - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle) belugas@6979: * - p2 = (bit 4) - 0 = signals, 1 = semaphores belugas@6979: * - p2 = (bit 5) - 0 = build, 1 = remove signals peter1138@7659: * - p2 = (bit 6) - 0 = selected stretch, 1 = auto fill belugas@6979: * - p2 = (bit 24-31) - user defined signals_density Darkvater@1796: * @see CmdSignalTrackHelper Darkvater@1796: */ rubidium@7439: CommandCost CmdBuildSignalTrack(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) darkvater@1227: { tron@3491: return CmdSignalTrackHelper(tile, flags, p1, p2); darkvater@1227: } darkvater@1227: Darkvater@1775: /** Remove signals tron@3491: * @param tile coordinates where signal is being deleted from belugas@6979: * @param flags operation to perform belugas@6984: * @param p1 various bitstuffed elements, only track information is used belugas@6984: * - (bit 0- 2) - track-orientation, valid values: 0-5 (Track enum) belugas@6984: * - (bit 3) - override signal/semaphore, or pre/exit/combo signal (CTRL-toggle) belugas@6984: * - (bit 4) - 0 = signals, 1 = semaphores belugas@6984: * @param p2 unused truelight@0: */ rubidium@7439: CommandCost CmdRemoveSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { Darkvater@5986: Track track = (Track)GB(p1, 0, 3); tron@1518: tron@4077: if (!ValParamTrackOrientation(track) || tron@4077: !IsTileType(tile, MP_RAILWAY) || maedhros@7944: !EnsureNoTrainOnTrack(tile, track) || tron@4077: !HasSignalOnTrack(tile, track)) { tron@1518: return CMD_ERROR; tron@4077: } tron@1518: Darkvater@1775: /* Only water can remove signals from anyone */ Darkvater@1775: if (_current_player != OWNER_WATER && !CheckTileOwnership(tile)) return CMD_ERROR; truelight@0: truelight@0: /* Do it? */ truelight@0: if (flags & DC_EXEC) { rubidium@7249: SetPresentSignals(tile, GetPresentSignals(tile) & ~SignalOnTrack(track)); truelight@201: darkvater@1066: /* removed last signal from tile? */ rubidium@7249: if (GetPresentSignals(tile) == 0) { rubidium@7249: SetSignalStates(tile, 0); rubidium@6498: SetHasSignals(tile, false); glx@7266: SetSignalVariant(tile, INVALID_TRACK, SIG_ELECTRIC); // remove any possible semaphores darkvater@1066: } tron@1109: smatz@8802: AddTrackToSignalBuffer(tile, track, GetTileOwner(tile)); KUDr@3900: YapfNotifyTrackLayoutChange(tile, track); truelight@0: truelight@0: MarkTileDirtyByTile(tile); truelight@0: } truelight@201: rubidium@8726: return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_signals); truelight@0: } truelight@0: Darkvater@1796: /** Remove signals on a stretch of track. Darkvater@1796: * Stub for the unified signal builder/remover belugas@6979: * @param tile start tile of drag belugas@6979: * @param flags operation to perform belugas@6979: * @param p1 end tile of drag belugas@6979: * @param p2 various bitstuffed elements belugas@6979: * - p2 = (bit 0- 2) - track-orientation, valid values: 0-5 (Track enum) belugas@6979: * - p2 = (bit 3) - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle) belugas@6979: * - p2 = (bit 4) - 0 = signals, 1 = semaphores belugas@6979: * - p2 = (bit 5) - 0 = build, 1 = remove signals peter1138@7659: * - p2 = (bit 6) - 0 = selected stretch, 1 = auto fill belugas@6979: * - p2 = (bit 24-31) - user defined signals_density Darkvater@1796: * @see CmdSignalTrackHelper Darkvater@1796: */ rubidium@7439: CommandCost CmdRemoveSignalTrack(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) darkvater@1227: { skidd13@8427: return CmdSignalTrackHelper(tile, flags, p1, SetBit(p2, 5)); // bit 5 is remove bit darkvater@1227: } darkvater@1227: smatz@8592: /** Update power of train under which is the railtype being converted */ rubidium@8035: void *UpdateTrainPowerProc(Vehicle *v, void *data) rubidium@8035: { rubidium@8035: /* Similiar checks as in TrainPowerChanged() */ rubidium@8035: smatz@8577: if (v->type == VEH_TRAIN && !IsArticulatedPart(v)) { rubidium@8035: const RailVehicleInfo *rvi = RailVehInfo(v->engine_type); rubidium@8035: if (GetVehicleProperty(v, 0x0B, rvi->power) != 0) TrainPowerChanged(v->First()); rubidium@8035: } rubidium@8035: rubidium@8035: return NULL; rubidium@8035: } rubidium@8035: Darkvater@1782: /** Convert one rail type to the other. You can convert normal rail to Darkvater@1782: * monorail/maglev easily or vice-versa. tron@3491: * @param tile end tile of rail conversion drag belugas@6979: * @param flags operation to perform Darkvater@1782: * @param p1 start tile of drag Darkvater@1782: * @param p2 new railtype to convert to Darkvater@1782: */ rubidium@7439: CommandCost CmdConvertRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { rubidium@8726: CommandCost cost(EXPENSES_CONSTRUCTION); rubidium@8732: RailType totype = (RailType)p2; rubidium@8732: rubidium@8732: if (!ValParamRailtype(totype)) return CMD_ERROR; tron@2934: if (p1 >= MapSize()) return CMD_ERROR; Darkvater@1782: smatz@8592: uint ex = TileX(tile); smatz@8592: uint ey = TileY(tile); smatz@8592: uint sx = TileX(p1); smatz@8592: uint sy = TileY(p1); smatz@8592: belugas@6916: /* make sure sx,sy are smaller than ex,ey */ tron@6432: if (ex < sx) Swap(ex, sx); tron@6432: if (ey < sy) Swap(ey, sy); truelight@201: smatz@8592: _error_message = STR_1005_NO_SUITABLE_RAILROAD_TRACK; // by default, there is no track to convert smatz@8592: smatz@8592: for (uint x = sx; x <= ex; ++x) { smatz@8592: for (uint y = sy; y <= ey; ++y) { tron@3493: TileIndex tile = TileXY(x, y); smatz@8592: TileType tt = GetTileType(tile); smatz@8592: smatz@8592: /* Check if there is any track on tile */ smatz@8592: switch (tt) { smatz@8592: case MP_RAILWAY: smatz@8592: break; smatz@8592: case MP_STATION: smatz@8592: if (!IsRailwayStation(tile)) continue; smatz@8592: break; smatz@8592: case MP_ROAD: smatz@8592: if (!IsLevelCrossing(tile)) continue; smatz@8592: break; smatz@8592: case MP_TUNNELBRIDGE: smatz@8592: if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue; smatz@8592: break; tron@2549: default: continue; tron@2549: } truelight@201: smatz@8592: /* Original railtype we are converting from */ rubidium@8035: RailType type = GetRailType(tile); rubidium@8035: smatz@8592: /* Converting to the same type or converting 'hidden' elrail -> rail */ smatz@8592: if (type == totype || (_patches.disable_elrails && totype == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC)) continue; smatz@8592: smatz@8592: /* Trying to convert other's rail */ smatz@8592: if (!CheckTileOwnership(tile)) continue; smatz@8592: smatz@8592: /* Vehicle on the tile when not converting Rail <-> ElRail smatz@8592: * Tunnels and bridges have special check later */ smatz@8592: if (tt != MP_TUNNELBRIDGE) { smatz@8592: if (!IsCompatibleRail(type, totype) && !EnsureNoVehicleOnGround(tile)) continue; smatz@8592: if (flags & DC_EXEC) { // we can safely convert, too smatz@8592: SetRailType(tile, totype); smatz@8592: MarkTileDirtyByTile(tile); smatz@8592: /* update power of train engines on this tile */ smatz@8592: VehicleFromPos(tile, NULL, &UpdateTrainPowerProc); smatz@8592: } rubidium@8035: } rubidium@8035: smatz@8592: switch (tt) { smatz@8592: case MP_RAILWAY: smatz@8649: switch (GetRailTileType(tile)) { smatz@8649: case RAIL_TILE_WAYPOINT: smatz@8649: if (flags & DC_EXEC) { smatz@8649: /* notify YAPF about the track layout change */ smatz@8649: YapfNotifyTrackLayoutChange(tile, AxisToTrack(GetWaypointAxis(tile))); smatz@8649: } smatz@8649: cost.AddCost(RailConvertCost(type, totype)); smatz@8649: break; smatz@8649: smatz@8649: case RAIL_TILE_DEPOT: smatz@8649: if (flags & DC_EXEC) { smatz@8649: /* notify YAPF about the track layout change */ smatz@8649: YapfNotifyTrackLayoutChange(tile, AxisToTrack(DiagDirToAxis(GetRailDepotDirection(tile)))); smatz@8649: smatz@8649: /* Update build vehicle window related to this depot */ smatz@8649: InvalidateWindowData(WC_VEHICLE_DEPOT, tile); smatz@8649: InvalidateWindowData(WC_BUILD_VEHICLE, tile); smatz@8649: } smatz@8649: cost.AddCost(RailConvertCost(type, totype)); smatz@8649: break; smatz@8649: smatz@8649: default: // RAIL_TILE_NORMAL, RAIL_TILE_SIGNALS smatz@8649: if (flags & DC_EXEC) { smatz@8649: /* notify YAPF about the track layout change */ smatz@8649: TrackBits tracks = GetTrackBits(tile); smatz@8649: while (tracks != TRACK_BIT_NONE) { smatz@8649: YapfNotifyTrackLayoutChange(tile, RemoveFirstTrack(&tracks)); smatz@8649: } smatz@8649: } smatz@8649: cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile))); smatz@8649: break; smatz@8592: } smatz@8592: break; smatz@8592: smatz@8592: case MP_TUNNELBRIDGE: { smatz@8693: TileIndex endtile = GetOtherTunnelBridgeEnd(tile); smatz@8592: smatz@8592: /* If both ends of tunnel/bridge are in the range, do not try to convert twice - smatz@8592: * it would cause assert because of different test and exec runs */ smatz@8592: if (endtile < tile && TileX(endtile) >= sx && TileX(endtile) <= ex && smatz@8592: TileY(endtile) >= sy && TileY(endtile) <= ey) continue; smatz@8592: smatz@8592: /* When not coverting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */ smatz@8592: if (!IsCompatibleRail(GetRailType(tile), totype) && smatz@8592: GetVehicleTunnelBridge(tile, endtile) != NULL) continue; smatz@8592: smatz@8592: if (flags & DC_EXEC) { smatz@8592: SetRailType(tile, totype); smatz@8592: SetRailType(endtile, totype); smatz@8592: smatz@8592: VehicleFromPos(tile, NULL, &UpdateTrainPowerProc); smatz@8592: VehicleFromPos(endtile, NULL, &UpdateTrainPowerProc); smatz@8592: smatz@8592: Track track = AxisToTrack(DiagDirToAxis(GetTunnelBridgeDirection(tile))); smatz@8592: smatz@8592: YapfNotifyTrackLayoutChange(tile, track); smatz@8592: YapfNotifyTrackLayoutChange(endtile, track); smatz@8592: smatz@8592: MarkTileDirtyByTile(tile); smatz@8592: MarkTileDirtyByTile(endtile); smatz@8592: smatz@8592: if (IsBridge(tile)) { smatz@8592: TileIndexDiff delta = TileOffsByDiagDir(GetTunnelBridgeDirection(tile)); smatz@8592: TileIndex t = tile + delta; smatz@8592: for (; t != endtile; t += delta) MarkTileDirtyByTile(t); // TODO encapsulate this into a function smatz@8592: } smatz@8592: } smatz@8592: smatz@8894: cost.AddCost((GetTunnelBridgeLength(tile, endtile) + 2) * RailConvertCost(type, totype)); smatz@8592: } break; smatz@8592: smatz@8592: default: // MP_STATION, MP_ROAD smatz@8592: if (flags & DC_EXEC) { smatz@8592: Track track = (tt == MP_STATION) ? GetRailStationTrack(tile) : AxisToTrack(OtherAxis(GetCrossingRoadAxis(tile))); smatz@8592: YapfNotifyTrackLayoutChange(tile, track); smatz@8592: } smatz@8592: smatz@8592: cost.AddCost(RailConvertCost(type, totype)); smatz@8592: break; truelight@0: } truelight@0: } truelight@0: } Darkvater@1782: smatz@8592: return (cost.GetCost() == 0) ? CMD_ERROR : cost; truelight@0: } truelight@0: rubidium@7439: static CommandCost RemoveTrainDepot(TileIndex tile, uint32 flags) truelight@0: { darkvater@149: if (!CheckTileOwnership(tile) && _current_player != OWNER_WATER) truelight@0: return CMD_ERROR; truelight@0: belugas@6902: if (!EnsureNoVehicleOnGround(tile)) truelight@0: return CMD_ERROR; truelight@0: truelight@0: if (flags & DC_EXEC) { smatz@8796: /* read variables before the depot is removed */ tron@3191: DiagDirection dir = GetRailDepotDirection(tile); smatz@8796: Owner owner = GetTileOwner(tile); truelight@0: rubidium@7885: DoClearSquare(tile); rubidium@7885: delete GetDepotByTile(tile); smatz@8802: AddSideToSignalBuffer(tile, dir, owner); KUDr@3900: YapfNotifyTrackLayoutChange(tile, TrackdirToTrack(DiagdirToDiagTrackdir(dir))); truelight@0: } truelight@0: rubidium@8726: return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_train_depot); truelight@0: } truelight@0: rubidium@7439: static CommandCost ClearTile_Track(TileIndex tile, byte flags) tron@1534: { rubidium@8726: CommandCost cost(EXPENSES_CONSTRUCTION); rubidium@7439: CommandCost ret; truelight@0: truelight@0: if (flags & DC_AUTO) { tron@1901: if (!IsTileOwner(tile, _current_player)) truelight@0: return_cmd_error(STR_1024_AREA_IS_OWNED_BY_ANOTHER); truelight@0: tron@4182: if (IsPlainRailTile(tile)) { tron@4182: return_cmd_error(STR_1008_MUST_REMOVE_RAILROAD_TRACK); tron@4182: } else { tron@4182: return_cmd_error(STR_2004_BUILDING_MUST_BE_DEMOLISHED); tron@4182: } truelight@0: } truelight@0: matthijs@1942: switch (GetRailTileType(tile)) { rubidium@3792: case RAIL_TILE_SIGNALS: rubidium@3792: case RAIL_TILE_NORMAL: { frosch@8910: Slope tileh = GetTileSlope(tile, NULL); frosch@8910: /* Is there flat water on the lower halftile, that gets cleared expensively? */ frosch@8910: bool water_ground = (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh)); rubidium@8267: rubidium@5665: TrackBits tracks = GetTrackBits(tile); rubidium@5838: while (tracks != TRACK_BIT_NONE) { KUDr@5849: Track track = RemoveFirstTrack(&tracks); rubidium@5838: ret = DoCommand(tile, 0, track, flags, CMD_REMOVE_SINGLE_RAIL); rubidium@5662: if (CmdFailed(ret)) return CMD_ERROR; rubidium@7446: cost.AddCost(ret); tron@1534: } rubidium@8267: rubidium@8267: if (water_ground) { rubidium@8267: /* The track was removed, and left a coast tile. Now also clear the water. */ rubidium@8267: if (flags & DC_EXEC) DoClearSquare(tile); rubidium@8267: cost.AddCost(_price.clear_water); rubidium@8267: } rubidium@8267: tron@1534: return cost; tron@1534: } tron@1534: rubidium@6498: case RAIL_TILE_DEPOT: rubidium@6498: return RemoveTrainDepot(tile, flags); rubidium@6498: rubidium@6498: case RAIL_TILE_WAYPOINT: rubidium@6498: return RemoveTrainWaypoint(tile, flags, false); tron@1534: tron@1534: default: tron@1534: return CMD_ERROR; tron@1534: } truelight@0: } truelight@0: rubidium@8260: /** rubidium@8260: * Get surface height in point (x,y) rubidium@8260: * On tiles with halftile foundations move (x,y) to a save point wrt. track rubidium@8260: */ rubidium@8260: static uint GetSaveSlopeZ(uint x, uint y, Track track) rubidium@8260: { rubidium@8260: switch (track) { rubidium@8260: case TRACK_UPPER: x &= ~0xF; y &= ~0xF; break; rubidium@8260: case TRACK_LOWER: x |= 0xF; y |= 0xF; break; rubidium@8260: case TRACK_LEFT: x |= 0xF; y &= ~0xF; break; rubidium@8260: case TRACK_RIGHT: x &= ~0xF; y |= 0xF; break; rubidium@8260: default: break; rubidium@8260: } rubidium@8260: return GetSlopeZ(x, y); rubidium@8260: } rubidium@8260: glx@7266: static void DrawSingleSignal(TileIndex tile, Track track, byte condition, uint image, uint pos) truelight@0: { rubidium@5838: bool side = (_opt.road_side != 0) && _patches.signal_side; celestar@3502: static const Point SignalPositions[2][12] = { celestar@3502: { /* Signals on the left side */ celestar@3502: /* LEFT LEFT RIGHT RIGHT UPPER UPPER */ celestar@3502: { 8, 5}, {14, 1}, { 1, 14}, { 9, 11}, { 1, 0}, { 3, 10}, celestar@3502: /* LOWER LOWER X X Y Y */ celestar@3502: {11, 4}, {14, 14}, {11, 3}, { 4, 13}, { 3, 4}, {11, 13} celestar@3502: }, { /* Signals on the right side */ celestar@3502: /* LEFT LEFT RIGHT RIGHT UPPER UPPER */ celestar@3502: {14, 1}, {12, 10}, { 4, 6}, { 1, 14}, {10, 4}, { 0, 1}, celestar@3502: /* LOWER LOWER X X Y Y */ celestar@3502: {14, 14}, { 5, 12}, {11, 13}, { 4, 3}, {13, 4}, { 3, 11} celestar@3502: } celestar@3502: }; truelight@0: celestar@3575: uint x = TileX(tile) * TILE_SIZE + SignalPositions[side][pos].x; celestar@3575: uint y = TileY(tile) * TILE_SIZE + SignalPositions[side][pos].y; celestar@3575: peter1138@3638: SpriteID sprite; peter1138@3638: peter1138@9043: SignalType type = GetSignalType(tile, track); peter1138@9043: SignalVariant variant = GetSignalVariant(tile, track); peter1138@9043: peter1138@9043: if (type == SIGTYPE_NORMAL && variant == SIG_ELECTRIC) { peter1138@9043: /* Normal electric signals are picked from original sprites. */ peter1138@9043: sprite = SPR_ORIGINAL_SIGNALS_BASE + image + condition; peter1138@3638: } else { peter1138@9043: /* All other signals are picked from add on sprites. */ peter1138@9043: sprite = SPR_SIGNALS_BASE + (type - 1) * 16 + variant * 64 + image + condition; peter1138@3638: } celestar@3575: rubidium@8260: AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, GetSaveSlopeZ(x, y, track)); truelight@0: } truelight@0: truelight@0: static uint32 _drawtile_track_palette; truelight@0: truelight@0: Darkvater@2436: static void DrawTrackFence_NW(const TileInfo *ti) truelight@0: { truelight@7803: SpriteID image = SPR_TRACK_FENCE_FLAT_X; truelight@7803: if (ti->tileh != SLOPE_FLAT) image = (ti->tileh & SLOPE_S) ? SPR_TRACK_FENCE_SLOPE_SW : SPR_TRACK_FENCE_SLOPE_NE; peter1138@5919: AddSortableSpriteToDraw(image, _drawtile_track_palette, tron@2951: ti->x, ti->y + 1, 16, 1, 4, ti->z); truelight@0: } truelight@0: Darkvater@2436: static void DrawTrackFence_SE(const TileInfo *ti) truelight@0: { truelight@7803: SpriteID image = SPR_TRACK_FENCE_FLAT_X; truelight@7803: if (ti->tileh != SLOPE_FLAT) image = (ti->tileh & SLOPE_S) ? SPR_TRACK_FENCE_SLOPE_SW : SPR_TRACK_FENCE_SLOPE_NE; peter1138@5919: AddSortableSpriteToDraw(image, _drawtile_track_palette, tron@3645: ti->x, ti->y + TILE_SIZE - 1, 16, 1, 4, ti->z); truelight@0: } truelight@0: Darkvater@2436: static void DrawTrackFence_NW_SE(const TileInfo *ti) truelight@0: { truelight@0: DrawTrackFence_NW(ti); truelight@0: DrawTrackFence_SE(ti); truelight@0: } truelight@0: Darkvater@2436: static void DrawTrackFence_NE(const TileInfo *ti) truelight@0: { truelight@7803: SpriteID image = SPR_TRACK_FENCE_FLAT_Y; truelight@7803: if (ti->tileh != SLOPE_FLAT) image = (ti->tileh & SLOPE_S) ? SPR_TRACK_FENCE_SLOPE_SE : SPR_TRACK_FENCE_SLOPE_NW; peter1138@5919: AddSortableSpriteToDraw(image, _drawtile_track_palette, tron@2951: ti->x + 1, ti->y, 1, 16, 4, ti->z); truelight@0: } truelight@0: Darkvater@2436: static void DrawTrackFence_SW(const TileInfo *ti) truelight@0: { truelight@7803: SpriteID image = SPR_TRACK_FENCE_FLAT_Y; truelight@7803: if (ti->tileh != SLOPE_FLAT) image = (ti->tileh & SLOPE_S) ? SPR_TRACK_FENCE_SLOPE_SE : SPR_TRACK_FENCE_SLOPE_NW; peter1138@5919: AddSortableSpriteToDraw(image, _drawtile_track_palette, tron@3645: ti->x + TILE_SIZE - 1, ti->y, 1, 16, 4, ti->z); truelight@0: } truelight@0: Darkvater@2436: static void DrawTrackFence_NE_SW(const TileInfo *ti) truelight@0: { truelight@0: DrawTrackFence_NE(ti); truelight@0: DrawTrackFence_SW(ti); truelight@0: } truelight@0: truelight@7803: /** truelight@7803: * Draw fence at eastern side of track. truelight@7803: */ Darkvater@2436: static void DrawTrackFence_NS_1(const TileInfo *ti) truelight@0: { frosch@8909: uint z = ti->z + GetSlopeZInCorner(RemoveHalftileSlope(ti->tileh), CORNER_W); truelight@7803: AddSortableSpriteToDraw(SPR_TRACK_FENCE_FLAT_VERT, _drawtile_track_palette, tron@3645: ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2, 1, 1, 4, z); truelight@0: } truelight@0: truelight@7803: /** truelight@7803: * Draw fence at western side of track. truelight@7803: */ Darkvater@2436: static void DrawTrackFence_NS_2(const TileInfo *ti) truelight@0: { frosch@8909: uint z = ti->z + GetSlopeZInCorner(RemoveHalftileSlope(ti->tileh), CORNER_E); truelight@7803: AddSortableSpriteToDraw(SPR_TRACK_FENCE_FLAT_VERT, _drawtile_track_palette, tron@3645: ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2, 1, 1, 4, z); truelight@0: } truelight@0: truelight@7803: /** truelight@7803: * Draw fence at southern side of track. truelight@7803: */ Darkvater@2436: static void DrawTrackFence_WE_1(const TileInfo *ti) truelight@0: { frosch@8909: uint z = ti->z + GetSlopeZInCorner(RemoveHalftileSlope(ti->tileh), CORNER_N); truelight@7803: AddSortableSpriteToDraw(SPR_TRACK_FENCE_FLAT_HORZ, _drawtile_track_palette, tron@3645: ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2, 1, 1, 4, z); truelight@0: } truelight@0: truelight@7803: /** truelight@7803: * Draw fence at northern side of track. truelight@7803: */ Darkvater@2436: static void DrawTrackFence_WE_2(const TileInfo *ti) truelight@0: { frosch@8909: uint z = ti->z + GetSlopeZInCorner(RemoveHalftileSlope(ti->tileh), CORNER_S); truelight@7803: AddSortableSpriteToDraw(SPR_TRACK_FENCE_FLAT_HORZ, _drawtile_track_palette, tron@3645: ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2, 1, 1, 4, z); truelight@0: } truelight@0: tron@4040: tron@4040: static void DrawTrackDetails(const TileInfo* ti) truelight@0: { tron@4040: switch (GetRailGroundType(ti->tile)) { tron@4040: case RAIL_GROUND_FENCE_NW: DrawTrackFence_NW(ti); break; tron@4040: case RAIL_GROUND_FENCE_SE: DrawTrackFence_SE(ti); break; tron@4040: case RAIL_GROUND_FENCE_SENW: DrawTrackFence_NW_SE(ti); break; tron@4040: case RAIL_GROUND_FENCE_NE: DrawTrackFence_NE(ti); break; tron@4040: case RAIL_GROUND_FENCE_SW: DrawTrackFence_SW(ti); break; tron@4040: case RAIL_GROUND_FENCE_NESW: DrawTrackFence_NE_SW(ti); break; tron@4040: case RAIL_GROUND_FENCE_VERT1: DrawTrackFence_NS_1(ti); break; tron@4040: case RAIL_GROUND_FENCE_VERT2: DrawTrackFence_NS_2(ti); break; tron@4040: case RAIL_GROUND_FENCE_HORIZ1: DrawTrackFence_WE_1(ti); break; tron@4040: case RAIL_GROUND_FENCE_HORIZ2: DrawTrackFence_WE_2(ti); break; frosch@8910: case RAIL_GROUND_WATER: { frosch@8910: Corner track_corner; frosch@8910: if (IsHalftileSlope(ti->tileh)) { frosch@8910: /* Steep slope or one-corner-raised slope with halftile foundation */ frosch@8910: track_corner = GetHalftileSlopeCorner(ti->tileh); frosch@8910: } else { frosch@8910: /* Three-corner-raised slope */ frosch@8910: track_corner = OppositeCorner(GetHighestSlopeCorner(ComplementSlope(ti->tileh))); frosch@8910: } frosch@8910: switch (track_corner) { rubidium@8267: case CORNER_W: DrawTrackFence_NS_1(ti); break; rubidium@8267: case CORNER_S: DrawTrackFence_WE_2(ti); break; rubidium@8267: case CORNER_E: DrawTrackFence_NS_2(ti); break; rubidium@8267: case CORNER_N: DrawTrackFence_WE_1(ti); break; rubidium@8267: default: NOT_REACHED(); rubidium@8267: } rubidium@8267: break; frosch@8910: } tron@4040: default: break; tron@4040: } truelight@0: } truelight@0: truelight@0: peter1138@2472: /** peter1138@2472: * Draw ground sprite and track bits peter1138@2472: * @param ti TileInfo peter1138@2472: * @param track TrackBits to draw peter1138@2472: */ tron@4081: static void DrawTrackBits(TileInfo* ti, TrackBits track) peter1138@2472: { frosch@8910: /* SubSprite for drawing the track halftile of 'three-corners-raised'-sloped rail sprites. */ frosch@8910: static const int INF = 1000; // big number compared to tilesprite size frosch@8910: static const SubSprite _halftile_sub_sprite[4] = { frosch@8910: { -INF , -INF , 32 - 33, INF }, // CORNER_W, clip 33 pixels from right frosch@8910: { -INF , 0 + 7, INF , INF }, // CORNER_S, clip 7 pixels from top frosch@8910: { -31 + 33, -INF , INF , INF }, // CORNER_E, clip 33 pixels from left frosch@8910: { -INF , -INF , INF , 30 - 23 } // CORNER_N, clip 23 pixels from bottom frosch@8910: }; frosch@8910: peter1138@2472: const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile)); rubidium@8266: RailGroundType rgt = GetRailGroundType(ti->tile); rubidium@8266: Foundation f = GetRailFoundation(ti->tileh, track); rubidium@8266: Corner halftile_corner = CORNER_INVALID; rubidium@8266: rubidium@8266: if (IsNonContinuousFoundation(f)) { rubidium@8266: /* Save halftile corner */ rubidium@8266: halftile_corner = (f == FOUNDATION_STEEP_BOTH ? GetHighestSlopeCorner(ti->tileh) : GetHalftileFoundationCorner(f)); rubidium@8266: /* Draw lower part first */ rubidium@8266: track &= ~CornerToTrackBits(halftile_corner); rubidium@8266: f = (f == FOUNDATION_STEEP_BOTH ? FOUNDATION_STEEP_LOWER : FOUNDATION_NONE); rubidium@8266: } rubidium@8266: rubidium@8266: DrawFoundation(ti, f); rubidium@8266: /* DrawFoundation modifies ti */ rubidium@8266: peter1138@5919: SpriteID image; peter1138@5919: SpriteID pal = PAL_NONE; frosch@8910: const SubSprite *sub = NULL; peter1138@2472: bool junction = false; peter1138@2472: belugas@6916: /* Select the sprite to use. */ rubidium@8266: if (track == 0) { rubidium@8266: /* Clear ground (only track on halftile foundation) */ rubidium@8267: if (rgt == RAIL_GROUND_WATER) { frosch@8910: if (IsSteepSlope(ti->tileh)) { frosch@8910: DrawShoreTile(ti->tileh); frosch@8910: image = 0; frosch@8910: } else { frosch@8910: image = SPR_FLAT_WATER_TILE; frosch@8910: } rubidium@8267: } else { rubidium@8267: switch (rgt) { rubidium@8267: case RAIL_GROUND_BARREN: image = SPR_FLAT_BARE_LAND; break; rubidium@8267: case RAIL_GROUND_ICE_DESERT: image = SPR_FLAT_SNOWY_TILE; break; rubidium@8267: default: image = SPR_FLAT_GRASS_TILE; break; rubidium@8267: } rubidium@8267: image += _tileh_to_sprite[ti->tileh]; rubidium@8266: } rubidium@8266: } else { rubidium@8266: if (ti->tileh != SLOPE_FLAT) { rubidium@8266: /* track on non-flat ground */ rubidium@8266: image = _track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.track_y; rubidium@8266: } else { rubidium@8266: /* track on flat ground */ rubidium@8266: (image = rti->base_sprites.track_y, track == TRACK_BIT_Y) || rubidium@8266: (image++, track == TRACK_BIT_X) || rubidium@8266: (image++, track == TRACK_BIT_UPPER) || rubidium@8266: (image++, track == TRACK_BIT_LOWER) || rubidium@8266: (image++, track == TRACK_BIT_RIGHT) || rubidium@8266: (image++, track == TRACK_BIT_LEFT) || rubidium@8266: (image++, track == TRACK_BIT_CROSS) || peter1138@2472: rubidium@8266: (image = rti->base_sprites.track_ns, track == TRACK_BIT_HORZ) || rubidium@8266: (image++, track == TRACK_BIT_VERT) || peter1138@2472: rubidium@8266: (junction = true, false) || rubidium@8266: (image = rti->base_sprites.ground, (track & TRACK_BIT_3WAY_NE) == 0) || rubidium@8266: (image++, (track & TRACK_BIT_3WAY_SW) == 0) || rubidium@8266: (image++, (track & TRACK_BIT_3WAY_NW) == 0) || rubidium@8266: (image++, (track & TRACK_BIT_3WAY_SE) == 0) || rubidium@8266: (image++, true); rubidium@8266: } peter1138@2472: rubidium@8266: switch (rgt) { rubidium@8266: case RAIL_GROUND_BARREN: pal = PALETTE_TO_BARE_LAND; break; rubidium@8266: case RAIL_GROUND_ICE_DESERT: image += rti->snow_offset; break; frosch@8910: case RAIL_GROUND_WATER: { frosch@8910: /* three-corner-raised slope */ frosch@8910: DrawShoreTile(ti->tileh); frosch@8910: Corner track_corner = OppositeCorner(GetHighestSlopeCorner(ComplementSlope(ti->tileh))); frosch@8910: sub = &(_halftile_sub_sprite[track_corner]); frosch@8910: break; frosch@8910: } rubidium@8266: default: break; rubidium@8266: } peter1138@2472: } peter1138@2472: frosch@8910: if (image != 0) DrawGroundSprite(image, pal, sub); peter1138@2472: belugas@6916: /* Draw track pieces individually for junction tiles */ peter1138@2472: if (junction) { peter1138@5919: if (track & TRACK_BIT_X) DrawGroundSprite(rti->base_sprites.single_y, PAL_NONE); peter1138@5919: if (track & TRACK_BIT_Y) DrawGroundSprite(rti->base_sprites.single_x, PAL_NONE); peter1138@5919: if (track & TRACK_BIT_UPPER) DrawGroundSprite(rti->base_sprites.single_n, PAL_NONE); peter1138@5919: if (track & TRACK_BIT_LOWER) DrawGroundSprite(rti->base_sprites.single_s, PAL_NONE); peter1138@5919: if (track & TRACK_BIT_LEFT) DrawGroundSprite(rti->base_sprites.single_w, PAL_NONE); peter1138@5919: if (track & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PAL_NONE); peter1138@2472: } rubidium@8266: rubidium@8266: if (IsValidCorner(halftile_corner)) { rubidium@8266: DrawFoundation(ti, HalftileFoundation(halftile_corner)); rubidium@8266: rubidium@8266: /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */ rubidium@8266: Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner)); rubidium@8266: image = _track_sloped_sprites[fake_slope - 1] + rti->base_sprites.track_y; rubidium@8266: pal = PAL_NONE; rubidium@8266: switch (rgt) { rubidium@8266: case RAIL_GROUND_BARREN: pal = PALETTE_TO_BARE_LAND; break; smatz@9019: case RAIL_GROUND_ICE_DESERT: smatz@9019: case RAIL_GROUND_HALF_SNOW: image += rti->snow_offset; break; // higher part has snow in this case too rubidium@8266: default: break; rubidium@8266: } rubidium@8266: DrawGroundSprite(image, pal, &(_halftile_sub_sprite[halftile_corner])); rubidium@8266: } peter1138@2472: } peter1138@2472: celestar@3519: static void DrawSignals(TileIndex tile, TrackBits rails) celestar@3519: { glx@7266: #define MAYBE_DRAW_SIGNAL(x,y,z,t) if (IsSignalPresent(tile, x)) DrawSingleSignal(tile, t, GetSingleSignalState(tile, x), y - 0x4FB, z) celestar@3519: celestar@3519: if (!(rails & TRACK_BIT_Y)) { celestar@3519: if (!(rails & TRACK_BIT_X)) { celestar@3519: if (rails & TRACK_BIT_LEFT) { glx@7266: MAYBE_DRAW_SIGNAL(2, 0x509, 0, TRACK_LEFT); glx@7266: MAYBE_DRAW_SIGNAL(3, 0x507, 1, TRACK_LEFT); celestar@3519: } celestar@3519: if (rails & TRACK_BIT_RIGHT) { glx@7266: MAYBE_DRAW_SIGNAL(0, 0x509, 2, TRACK_RIGHT); glx@7266: MAYBE_DRAW_SIGNAL(1, 0x507, 3, TRACK_RIGHT); celestar@3519: } celestar@3519: if (rails & TRACK_BIT_UPPER) { glx@7266: MAYBE_DRAW_SIGNAL(3, 0x505, 4, TRACK_UPPER); glx@7266: MAYBE_DRAW_SIGNAL(2, 0x503, 5, TRACK_UPPER); celestar@3519: } celestar@3519: if (rails & TRACK_BIT_LOWER) { glx@7266: MAYBE_DRAW_SIGNAL(1, 0x505, 6, TRACK_LOWER); glx@7266: MAYBE_DRAW_SIGNAL(0, 0x503, 7, TRACK_LOWER); celestar@3519: } celestar@3519: } else { glx@7266: MAYBE_DRAW_SIGNAL(3, 0x4FB, 8, TRACK_X); glx@7266: MAYBE_DRAW_SIGNAL(2, 0x4FD, 9, TRACK_X); celestar@3519: } celestar@3519: } else { glx@7266: MAYBE_DRAW_SIGNAL(3, 0x4FF, 10, TRACK_Y); glx@7266: MAYBE_DRAW_SIGNAL(2, 0x501, 11, TRACK_Y); celestar@3519: } celestar@3519: } celestar@3519: truelight@0: static void DrawTile_Track(TileInfo *ti) truelight@0: { celestar@2233: const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile)); peter1138@5919: SpriteID image; truelight@0: peter1138@5919: _drawtile_track_palette = PLAYER_SPRITE_COLOR(GetTileOwner(ti->tile)); truelight@0: tron@4182: if (IsPlainRailTile(ti->tile)) { tron@3267: TrackBits rails = GetTrackBits(ti->tile); truelight@201: tron@4081: DrawTrackBits(ti, rails); hackykid@2008: skidd13@8424: if (HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti); truelight@0: rubidium@7539: if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti); rubidium@7539: tron@4182: if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails); truelight@0: } else { belugas@6916: /* draw depot/waypoint */ tron@4225: const DrawTileSprites* dts; tron@4225: const DrawTileSeqStruct* dtss; tron@4225: uint32 relocation; truelight@0: rubidium@7831: if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED); truelight@0: rubidium@6498: if (IsRailDepot(ti->tile)) { tron@4225: dts = &_depot_gfx_table[GetRailDepotDirection(ti->tile)]; tron@4225: tron@4225: relocation = rti->total_offset; tron@4225: tron@4225: image = dts->ground_sprite; tron@4225: if (image != SPR_FLAT_GRASS_TILE) image += rti->total_offset; tron@4225: belugas@6916: /* adjust ground tile for desert belugas@6916: * don't adjust for snow, because snow in depots looks weird */ belugas@6683: if (IsSnowRailGround(ti->tile) && _opt.landscape == LT_TROPIC) { tron@4225: if (image != SPR_FLAT_GRASS_TILE) { tron@4225: image += rti->snow_offset; // tile with tracks tron@4225: } else { tron@4225: image = SPR_FLAT_SNOWY_TILE; // flat ground tron@4225: } tron@4225: } tron@4225: } else { belugas@6916: /* look for customization */ peter1138@2670: byte stat_id = GetWaypointByTile(ti->tile)->stat_id; belugas@3676: const StationSpec *statspec = GetCustomStationSpec(STAT_CLASS_WAYP, stat_id); celestar@389: belugas@3676: if (statspec != NULL) { belugas@6916: /* emulate station tile - open with building */ tron@4225: const Station* st = ComposeWaypointStation(ti->tile); tron@4225: uint gfx = 2; peter1138@3757: skidd13@8424: if (HasBit(statspec->callbackmask, CBM_STATION_SPRITE_LAYOUT)) { peter1138@3757: uint16 callback = GetStationCallback(CBID_STATION_SPRITE_LAYOUT, 0, 0, statspec, st, ti->tile); tron@4225: if (callback != CALLBACK_FAILED) gfx = callback; peter1138@3757: } peter1138@3757: peter1138@3763: if (statspec->renderdata == NULL) { rubidium@7768: dts = GetStationTileLayout(STATION_RAIL, gfx); peter1138@3763: } else { tron@4225: dts = &statspec->renderdata[(gfx < statspec->tiles ? gfx : 0) + GetWaypointAxis(ti->tile)]; peter1138@3763: } celestar@447: tron@4225: if (dts != NULL && dts->seq != NULL) { tron@4225: relocation = GetCustomStationRelocation(statspec, st, ti->tile); tron@4225: tron@4225: image = dts->ground_sprite; skidd13@8424: if (HasBit(image, SPRITE_MODIFIER_USE_OFFSET)) { peter1138@3775: image += GetCustomStationGroundRelocation(statspec, st, ti->tile); peter1138@3775: image += rti->custom_ground_offset; peter1138@3775: } else { peter1138@3775: image += rti->total_offset; peter1138@3775: } tron@4225: } else { tron@4225: goto default_waypoint; celestar@389: } tron@2549: } else { tron@4225: default_waypoint: belugas@6916: /* There is no custom layout, fall back to the default graphics */ tron@4225: dts = &_waypoint_gfx_table[GetWaypointAxis(ti->tile)]; tron@4225: relocation = 0; tron@4225: image = dts->ground_sprite + rti->total_offset; tron@4225: if (IsSnowRailGround(ti->tile)) image += rti->snow_offset; tron@2549: } truelight@0: } truelight@0: peter1138@5919: DrawGroundSprite(image, PAL_NONE); truelight@0: celestar@3355: if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti); celestar@3355: tron@4225: foreach_draw_tile_seq(dtss, dts->seq) { frosch@9066: SpriteID image = dtss->image.sprite; peter1138@5919: SpriteID pal; tron@4225: peter1138@5989: /* Unlike stations, our default waypoint has no variation for peter1138@5989: * different railtype, so don't use the railtype offset if peter1138@5989: * no relocation is set */ skidd13@8424: if (HasBit(image, SPRITE_MODIFIER_USE_OFFSET)) { peter1138@5989: image += rti->total_offset; peter1138@5989: } else { peter1138@5989: image += relocation; peter1138@5989: } peter1138@5989: peter1138@8654: if (!(!HasBit(image, SPRITE_MODIFIER_OPAQUE) && IsTransparencySet(TO_BUILDINGS)) && HasBit(image, PALETTE_MODIFIER_COLOR)) { peter1138@5919: pal = _drawtile_track_palette; peter1138@5919: } else { frosch@9066: pal = dtss->image.pal; tron@4225: } peter1138@5919: peter1138@5989: if ((byte)dtss->delta_z != 0x80) { peter1138@5989: AddSortableSpriteToDraw( peter1138@5989: image, pal, peter1138@5989: ti->x + dtss->delta_x, ti->y + dtss->delta_y, peter1138@5989: dtss->size_x, dtss->size_y, rubidium@7829: dtss->size_z, ti->z + dtss->delta_z, peter1138@8654: !HasBit(image, SPRITE_MODIFIER_OPAQUE) && IsTransparencySet(TO_BUILDINGS) peter1138@5989: ); peter1138@5989: } else { peter1138@5989: AddChildSpriteScreen(image, pal, dtss->delta_x, dtss->delta_y); peter1138@5989: } truelight@0: } truelight@0: } celestar@5573: DrawBridgeMiddle(ti); truelight@0: } truelight@0: truelight@0: peter1138@5919: static void DrawTileSequence(int x, int y, SpriteID ground, const DrawTileSeqStruct* dtss, uint32 offset) tron@4225: { peter1138@5919: SpriteID palette = PLAYER_SPRITE_COLOR(_local_player); truelight@0: peter1138@5919: DrawSprite(ground, PAL_NONE, x, y); frosch@9066: for (; dtss->image.sprite != 0; dtss++) { tron@4225: Point pt = RemapCoords(dtss->delta_x, dtss->delta_y, dtss->delta_z); frosch@9066: SpriteID image = dtss->image.sprite + offset; truelight@0: skidd13@8424: DrawSprite(image, HasBit(image, PALETTE_MODIFIER_COLOR) ? palette : PAL_NONE, x + pt.x, y + pt.y); truelight@0: } truelight@0: } truelight@0: tron@4225: void DrawTrainDepotSprite(int x, int y, int dir, RailType railtype) tron@4225: { tron@4225: const DrawTileSprites* dts = &_depot_gfx_table[dir]; peter1138@5919: SpriteID image = dts->ground_sprite; tron@4225: uint32 offset = GetRailTypeInfo(railtype)->total_offset; tron@4225: tron@4225: if (image != SPR_FLAT_GRASS_TILE) image += offset; tron@4225: DrawTileSequence(x + 33, y + 17, image, dts->seq, offset); tron@4225: } tron@4225: tron@2520: void DrawDefaultWaypointSprite(int x, int y, RailType railtype) ludde@2261: { tron@4225: uint32 offset = GetRailTypeInfo(railtype)->total_offset; tron@4225: const DrawTileSprites* dts = &_waypoint_gfx_table[AXIS_X]; ludde@2261: tron@4225: DrawTileSequence(x, y, dts->ground_sprite + offset, dts->seq, 0); ludde@2261: } ludde@2261: tron@4231: static uint GetSlopeZ_Track(TileIndex tile, uint x, uint y) truelight@0: { tron@4231: uint z; tron@4231: Slope tileh = GetTileSlope(tile, &z); tron@2951: tron@3636: if (tileh == SLOPE_FLAT) return z; tron@4231: if (IsPlainRailTile(tile)) { rubidium@7831: z += ApplyFoundationToSlope(GetRailFoundation(tileh, GetTrackBits(tile)), &tileh); tron@4231: return z + GetPartialZ(x & 0xF, y & 0xF, tileh); tron@4182: } else { tron@4182: return z + TILE_HEIGHT; truelight@0: } truelight@0: } truelight@0: rubidium@7831: static Foundation GetFoundation_Track(TileIndex tile, Slope tileh) dominik@39: { rubidium@7831: return IsPlainRailTile(tile) ? GetRailFoundation(tileh, GetTrackBits(tile)) : FlatteningFoundation(tileh); dominik@39: } dominik@39: tron@1977: static void GetAcceptedCargo_Track(TileIndex tile, AcceptedCargo ac) truelight@0: { truelight@0: /* not used */ truelight@0: } truelight@0: tron@1977: static void AnimateTile_Track(TileIndex tile) truelight@0: { truelight@0: /* not used */ truelight@0: } truelight@0: tron@1977: static void TileLoop_Track(TileIndex tile) truelight@0: { celestar@3523: RailGroundType old_ground = GetRailGroundType(tile); tron@4483: RailGroundType new_ground; truelight@0: rubidium@8267: if (old_ground == RAIL_GROUND_WATER) { rubidium@8267: TileLoop_Water(tile); rubidium@8267: return; rubidium@8267: } rubidium@8267: tron@2631: switch (_opt.landscape) { smatz@9019: case LT_ARCTIC: { smatz@9019: uint z; smatz@9019: Slope slope = GetTileSlope(tile, &z); smatz@9019: bool half = false; smatz@9019: smatz@9019: /* for non-flat track, use lower part of track smatz@9019: * in other cases, use the highest part with track */ smatz@9019: if (IsPlainRailTile(tile)) { smatz@9019: TrackBits track = GetTrackBits(tile); smatz@9019: Foundation f = GetRailFoundation(slope, track); smatz@9019: smatz@9019: switch (f) { smatz@9019: case FOUNDATION_NONE: smatz@9019: /* no foundation - is the track on the upper side of three corners raised tile? */ smatz@9019: if (IsSlopeWithThreeCornersRaised(slope)) z += TILE_HEIGHT; smatz@9019: break; smatz@9019: smatz@9019: case FOUNDATION_INCLINED_X: smatz@9019: case FOUNDATION_INCLINED_Y: smatz@9019: /* sloped track - is it on a steep slope? */ smatz@9019: if (IsSteepSlope(slope)) z += TILE_HEIGHT; smatz@9019: break; smatz@9019: smatz@9019: case FOUNDATION_STEEP_LOWER: smatz@9019: /* only lower part of steep slope */ smatz@9019: z += TILE_HEIGHT; smatz@9019: break; smatz@9019: smatz@9019: default: smatz@9019: /* if it is a steep slope, then there is a track on higher part */ smatz@9019: if (IsSteepSlope(slope)) z += TILE_HEIGHT; smatz@9019: z += TILE_HEIGHT; smatz@9019: break; smatz@9019: } smatz@9019: smatz@9019: half = IsInsideMM(f, FOUNDATION_STEEP_BOTH, FOUNDATION_HALFTILE_N + 1); smatz@9019: } else { smatz@9019: /* is the depot on a non-flat tile? */ smatz@9019: if (slope != SLOPE_FLAT) z += TILE_HEIGHT; smatz@9019: } smatz@9019: smatz@9019: /* 'z' is now the lowest part of the highest track bit - smatz@9019: * for sloped track, it is 'z' of lower part smatz@9019: * for two track bits, it is 'z' of higher track bit smatz@9019: * For non-continuous foundations (and STEEP_BOTH), 'half' is set */ smatz@9019: if (z > GetSnowLine()) { smatz@9019: if (half && z - GetSnowLine() == TILE_HEIGHT) { smatz@9019: /* track on non-continuous foundation, lower part is not under snow */ smatz@9019: new_ground = RAIL_GROUND_HALF_SNOW; smatz@9019: } else { smatz@9019: new_ground = RAIL_GROUND_ICE_DESERT; smatz@9019: } tron@4482: goto set_ground; Darkvater@3562: } tron@2631: break; smatz@9019: } truelight@0: belugas@6683: case LT_TROPIC: Darkvater@3562: if (GetTropicZone(tile) == TROPICZONE_DESERT) { Darkvater@3562: new_ground = RAIL_GROUND_ICE_DESERT; tron@4482: goto set_ground; Darkvater@3562: } celestar@3523: break; celestar@3523: } celestar@3523: tron@4182: if (!IsPlainRailTile(tile)) return; truelight@0: celestar@3523: new_ground = RAIL_GROUND_GRASS; truelight@0: belugas@6916: if (old_ground != RAIL_GROUND_BARREN) { // wait until bottom is green truelight@0: /* determine direction of fence */ tron@3267: TrackBits rail = GetTrackBits(tile); truelight@0: tron@2839: switch (rail) { tron@2839: case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break; tron@2839: case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break; tron@2839: case TRACK_BIT_LEFT: new_ground = RAIL_GROUND_FENCE_VERT1; break; tron@2839: case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2; break; truelight@0: tron@2839: default: { tron@2839: PlayerID owner = GetTileOwner(tile); truelight@0: tron@2839: if (rail == (TRACK_BIT_LOWER | TRACK_BIT_RIGHT) || ( tron@3258: (rail & TRACK_BIT_3WAY_NW) == 0 && tron@3102: (rail & TRACK_BIT_X) tron@2839: )) { tron@2839: TileIndex n = tile + TileDiffXY(0, -1); celestar@3524: TrackBits nrail = GetTrackBits(n); truelight@0: tron@2839: if (!IsTileType(n, MP_RAILWAY) || tron@2839: !IsTileOwner(n, owner) || celestar@3524: nrail == TRACK_BIT_UPPER || celestar@3524: nrail == TRACK_BIT_LEFT) { tron@2839: new_ground = RAIL_GROUND_FENCE_NW; tron@2839: } tron@2839: } tron@2839: tron@2839: if (rail == (TRACK_BIT_UPPER | TRACK_BIT_LEFT) || ( tron@3258: (rail & TRACK_BIT_3WAY_SE) == 0 && tron@3102: (rail & TRACK_BIT_X) tron@2839: )) { tron@2839: TileIndex n = tile + TileDiffXY(0, 1); celestar@3524: TrackBits nrail = GetTrackBits(n); tron@2839: tron@2839: if (!IsTileType(n, MP_RAILWAY) || tron@2839: !IsTileOwner(n, owner) || celestar@3524: nrail == TRACK_BIT_LOWER || celestar@3524: nrail == TRACK_BIT_RIGHT) { tron@2839: new_ground = (new_ground == RAIL_GROUND_FENCE_NW) ? tron@2839: RAIL_GROUND_FENCE_SENW : RAIL_GROUND_FENCE_SE; tron@2839: } tron@2839: } tron@2839: tron@2839: if (rail == (TRACK_BIT_LOWER | TRACK_BIT_LEFT) || ( tron@3258: (rail & TRACK_BIT_3WAY_NE) == 0 && tron@3102: (rail & TRACK_BIT_Y) tron@2839: )) { tron@2839: TileIndex n = tile + TileDiffXY(-1, 0); celestar@3524: TrackBits nrail = GetTrackBits(n); tron@2839: tron@2839: if (!IsTileType(n, MP_RAILWAY) || tron@2839: !IsTileOwner(n, owner) || celestar@3524: nrail == TRACK_BIT_UPPER || celestar@3524: nrail == TRACK_BIT_RIGHT) { tron@2839: new_ground = RAIL_GROUND_FENCE_NE; tron@2839: } tron@2839: } tron@2839: tron@2839: if (rail == (TRACK_BIT_UPPER | TRACK_BIT_RIGHT) || ( tron@3265: (rail & TRACK_BIT_3WAY_SW) == 0 && tron@3102: (rail & TRACK_BIT_Y) tron@2839: )) { tron@2839: TileIndex n = tile + TileDiffXY(1, 0); celestar@3524: TrackBits nrail = GetTrackBits(n); tron@2839: tron@2839: if (!IsTileType(n, MP_RAILWAY) || tron@2839: !IsTileOwner(n, owner) || celestar@3524: nrail == TRACK_BIT_LOWER || celestar@3524: nrail == TRACK_BIT_LEFT) { tron@2839: new_ground = (new_ground == RAIL_GROUND_FENCE_NE) ? tron@2839: RAIL_GROUND_FENCE_NESW : RAIL_GROUND_FENCE_SW; tron@2839: } tron@2839: } tron@2839: break; truelight@0: } truelight@0: } truelight@0: } truelight@0: tron@4482: set_ground: peter1138@2667: if (old_ground != new_ground) { celestar@3523: SetRailGroundType(tile, new_ground); truelight@0: MarkTileDirtyByTile(tile); truelight@0: } truelight@0: } truelight@0: truelight@0: rubidium@7179: static uint32 GetTileTrackStatus_Track(TileIndex tile, TransportType mode, uint sub_mode) tron@1977: { tron@2639: if (mode != TRANSPORT_RAIL) return 0; truelight@0: rubidium@6513: switch (GetRailTileType(tile)) { rubidium@6513: default: NOT_REACHED(); rubidium@6513: case RAIL_TILE_NORMAL: { rubidium@6513: TrackBits rails = GetTrackBits(tile); rubidium@6513: uint32 ret = rails * 0x101; rubidium@6513: return (rails == TRACK_BIT_CROSS) ? ret | 0x40 : ret; rubidium@6513: } tron@3267: rubidium@6513: case RAIL_TILE_SIGNALS: { rubidium@6513: uint32 ret = GetTrackBits(tile) * 0x101; rubidium@7249: byte a = GetPresentSignals(tile); rubidium@7249: uint b = GetSignalStates(tile); truelight@0: truelight@0: b &= a; truelight@0: darkvater@22: /* When signals are not present (in neither darkvater@22: * direction), we pretend them to be green. (So if darkvater@22: * signals are only one way, the other way will darkvater@22: * implicitely become `red' */ rubidium@7249: if ((a & 0xC) == 0) b |= 0xC; rubidium@7249: if ((a & 0x3) == 0) b |= 0x3; truelight@0: rubidium@7249: if ((b & 0x8) == 0) ret |= 0x10070000; rubidium@7249: if ((b & 0x4) == 0) ret |= 0x07100000; rubidium@7249: if ((b & 0x2) == 0) ret |= 0x20080000; rubidium@7249: if ((b & 0x1) == 0) ret |= 0x08200000; rubidium@6513: rubidium@6513: return ret; truelight@0: } rubidium@6513: rubidium@6513: case RAIL_TILE_DEPOT: return AxisToTrackBits(DiagDirToAxis(GetRailDepotDirection(tile))) * 0x101; rubidium@6513: case RAIL_TILE_WAYPOINT: return GetRailWaypointBits(tile) * 0x101; tron@3267: } truelight@0: } truelight@0: tron@1977: static void ClickTile_Track(TileIndex tile) truelight@0: { rubidium@6513: switch (GetRailTileType(tile)) { rubidium@6585: case RAIL_TILE_DEPOT: ShowDepotWindow(tile, VEH_TRAIN); break; rubidium@6513: case RAIL_TILE_WAYPOINT: ShowRenameWaypointWindow(GetWaypointByTile(tile)); break; rubidium@6513: default: break; tron@2548: } truelight@0: } truelight@0: tron@1590: static void GetTileDesc_Track(TileIndex tile, TileDesc *td) truelight@0: { tron@1901: td->owner = GetTileOwner(tile); matthijs@1942: switch (GetRailTileType(tile)) { rubidium@3792: case RAIL_TILE_NORMAL: truelight@0: td->str = STR_1021_RAILROAD_TRACK; tron@1590: break; tron@1590: rubidium@3792: case RAIL_TILE_SIGNALS: { glx@7266: const StringID signal_type[4][4] = { glx@7266: { glx@7266: STR_RAILROAD_TRACK_WITH_NORMAL_SIGNALS, glx@7266: STR_RAILROAD_TRACK_WITH_NORMAL_PRESIGNALS, glx@7266: STR_RAILROAD_TRACK_WITH_NORMAL_EXITSIGNALS, glx@7266: STR_RAILROAD_TRACK_WITH_NORMAL_COMBOSIGNALS glx@7266: }, glx@7266: { glx@7266: STR_RAILROAD_TRACK_WITH_NORMAL_PRESIGNALS, glx@7266: STR_RAILROAD_TRACK_WITH_PRESIGNALS, glx@7266: STR_RAILROAD_TRACK_WITH_PRE_EXITSIGNALS, glx@7266: STR_RAILROAD_TRACK_WITH_PRE_COMBOSIGNALS glx@7266: }, glx@7266: { glx@7266: STR_RAILROAD_TRACK_WITH_NORMAL_EXITSIGNALS, glx@7266: STR_RAILROAD_TRACK_WITH_PRE_EXITSIGNALS, glx@7266: STR_RAILROAD_TRACK_WITH_EXITSIGNALS, glx@7266: STR_RAILROAD_TRACK_WITH_EXIT_COMBOSIGNALS glx@7266: }, glx@7266: { glx@7266: STR_RAILROAD_TRACK_WITH_NORMAL_COMBOSIGNALS, glx@7266: STR_RAILROAD_TRACK_WITH_PRE_COMBOSIGNALS, glx@7266: STR_RAILROAD_TRACK_WITH_EXIT_COMBOSIGNALS, glx@7266: STR_RAILROAD_TRACK_WITH_COMBOSIGNALS glx@7266: } tron@1590: }; tron@1590: glx@7266: td->str = signal_type[GetSignalType(tile, TRACK_UPPER)][GetSignalType(tile, TRACK_LOWER)]; tron@1590: break; truelight@0: } tron@1590: rubidium@6498: case RAIL_TILE_DEPOT: rubidium@6498: td->str = STR_1023_RAILROAD_TRAIN_DEPOT; rubidium@6498: break; rubidium@6498: rubidium@6498: case RAIL_TILE_WAYPOINT: tron@1590: default: rubidium@6498: td->str = STR_LANDINFO_WAYPOINT; tron@1590: break; truelight@0: } truelight@0: } truelight@0: Darkvater@2436: static void ChangeTileOwner_Track(TileIndex tile, PlayerID old_player, PlayerID new_player) truelight@0: { tron@1901: if (!IsTileOwner(tile, old_player)) return; truelight@201: Darkvater@4848: if (new_player != PLAYER_SPECTATOR) { tron@1902: SetTileOwner(tile, new_player); rubidium@4434: } else { smatz@9015: DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR); truelight@0: } truelight@0: } truelight@0: truelight@0: static const byte _fractcoords_behind[4] = { 0x8F, 0x8, 0x80, 0xF8 }; truelight@0: static const byte _fractcoords_enter[4] = { 0x8A, 0x48, 0x84, 0xA8 }; rubidium@5838: static const signed char _deltacoord_leaveoffset[8] = { hackykid@1922: -1, 0, 1, 0, /* x */ hackykid@1922: 0, 1, 0, -1 /* y */ hackykid@1922: }; truelight@0: rubidium@8615: static VehicleEnterTileStatus VehicleEnter_Track(Vehicle *v, TileIndex tile, int x, int y) truelight@0: { truelight@0: byte fract_coord; hackykid@1922: byte fract_coord_leave; tron@3157: DiagDirection dir; hackykid@1922: int length; truelight@201: belugas@6916: /* this routine applies only to trains in depot tiles */ rubidium@6585: if (v->type != VEH_TRAIN || !IsTileDepotType(tile, TRANSPORT_RAIL)) return VETSB_CONTINUE; truelight@0: truelight@0: /* depot direction */ tron@3185: dir = GetRailDepotDirection(tile); truelight@201: hackykid@1922: /* calculate the point where the following wagon should be activated */ hackykid@1922: /* this depends on the length of the current vehicle */ hackykid@1922: length = v->u.rail.cached_veh_length; hackykid@1922: hackykid@1922: fract_coord_leave = rubidium@4434: ((_fractcoords_enter[dir] & 0x0F) + // x hackykid@1922: (length + 1) * _deltacoord_leaveoffset[dir]) + rubidium@4434: (((_fractcoords_enter[dir] >> 4) + // y hackykid@1922: ((length + 1) * _deltacoord_leaveoffset[dir+4])) << 4); hackykid@1922: truelight@0: fract_coord = (x & 0xF) + ((y & 0xF) << 4); hackykid@1922: truelight@0: if (_fractcoords_behind[dir] == fract_coord) { truelight@0: /* make sure a train is not entering the tile from behind */ rubidium@6317: return VETSB_CANNOT_ENTER; truelight@0: } else if (_fractcoords_enter[dir] == fract_coord) { tron@3157: if (DiagDirToDir(ReverseDiagDir(dir)) == v->direction) { truelight@0: /* enter the depot */ rubidium@6319: v->u.rail.track = TRACK_BIT_DEPOT, truelight@0: v->vehstatus |= VS_HIDDEN; /* hide it */ tron@3157: v->direction = ReverseDir(v->direction); rubidium@7988: if (v->Next() == NULL) VehicleEnterDepot(v); Darkvater@5198: v->tile = tile; Darkvater@5198: bjarni@4739: InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); rubidium@6317: return VETSB_ENTERED_WORMHOLE; truelight@0: } hackykid@1922: } else if (fract_coord_leave == fract_coord) { tron@3157: if (DiagDirToDir(dir) == v->direction) { truelight@0: /* leave the depot? */ rubidium@7988: if ((v = v->Next()) != NULL) { truelight@0: v->vehstatus &= ~VS_HIDDEN; rubidium@5838: v->u.rail.track = (DiagDirToAxis(dir) == AXIS_X ? TRACK_BIT_X : TRACK_BIT_Y); truelight@0: } truelight@0: } truelight@0: } truelight@0: rubidium@6317: return VETSB_CONTINUE; truelight@0: } truelight@0: rubidium@8078: /** rubidium@8078: * Tests if autoslope is allowed. rubidium@8078: * rubidium@8078: * @param tile The tile. rubidium@8078: * @param flags Terraform command flags. rubidium@8078: * @param z_old Old TileZ. rubidium@8078: * @param tileh_old Old TileSlope. rubidium@8078: * @param z_new New TileZ. rubidium@8078: * @param tileh_new New TileSlope. rubidium@8078: * @param rail_bits Trackbits. rubidium@8078: */ rubidium@8078: static CommandCost TestAutoslopeOnRailTile(TileIndex tile, uint flags, uint z_old, Slope tileh_old, uint z_new, Slope tileh_new, TrackBits rail_bits) rubidium@8078: { rubidium@8078: if (!_patches.build_on_slopes || !AutoslopeEnabled()) return CMD_ERROR; rubidium@8078: rubidium@8078: /* Is the slope-rail_bits combination valid in general? I.e. is it save to call GetRailFoundation() ? */ rubidium@8078: if (CmdFailed(CheckRailSlope(tileh_new, rail_bits, TRACK_BIT_NONE, tile))) return CMD_ERROR; rubidium@8078: rubidium@8078: /* Get the slopes on top of the foundations */ rubidium@8078: z_old += ApplyFoundationToSlope(GetRailFoundation(tileh_old, rail_bits), &tileh_old); rubidium@8078: z_new += ApplyFoundationToSlope(GetRailFoundation(tileh_new, rail_bits), &tileh_new); rubidium@8078: belugas@8289: Corner track_corner; rubidium@8078: switch (rail_bits) { belugas@8289: case TRACK_BIT_LEFT: track_corner = CORNER_W; break; belugas@8289: case TRACK_BIT_LOWER: track_corner = CORNER_S; break; belugas@8289: case TRACK_BIT_RIGHT: track_corner = CORNER_E; break; belugas@8289: case TRACK_BIT_UPPER: track_corner = CORNER_N; break; rubidium@8078: rubidium@8078: /* Surface slope must not be changed */ rubidium@8726: default: return (((z_old != z_new) || (tileh_old != tileh_new)) ? CMD_ERROR : CommandCost(EXPENSES_CONSTRUCTION, _price.terraform)); rubidium@8078: } rubidium@8078: rubidium@8078: /* The height of the track_corner must not be changed. The rest ensures GetRailFoundation() already. */ frosch@8909: z_old += GetSlopeZInCorner(RemoveHalftileSlope(tileh_old), track_corner); frosch@8909: z_new += GetSlopeZInCorner(RemoveHalftileSlope(tileh_new), track_corner); rubidium@8078: if (z_old != z_new) return CMD_ERROR; rubidium@8078: rubidium@8726: CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price.terraform); belugas@8289: /* Make the ground dirty, if surface slope has changed */ belugas@8289: if (tileh_old != tileh_new) { frosch@8910: /* If there is flat water on the lower halftile add the cost for clearing it */ frosch@8910: if (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh_old)) cost.AddCost(_price.clear_water); belugas@8289: if ((flags & DC_EXEC) != 0) SetRailGroundType(tile, RAIL_GROUND_BARREN); belugas@8289: } rubidium@8267: return cost; rubidium@8078: } rubidium@8078: rubidium@7990: static CommandCost TerraformTile_Track(TileIndex tile, uint32 flags, uint z_new, Slope tileh_new) rubidium@7990: { rubidium@8078: uint z_old; rubidium@8078: Slope tileh_old = GetTileSlope(tile, &z_old); rubidium@7990: if (IsPlainRailTile(tile)) { rubidium@7990: TrackBits rail_bits = GetTrackBits(tile); frosch@8910: /* Is there flat water on the lower halftile, that must be cleared expensively? */ frosch@8910: bool was_water = (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh_old)); rubidium@7990: rubidium@7990: _error_message = STR_1008_MUST_REMOVE_RAILROAD_TRACK; rubidium@7990: rubidium@8078: /* First test autoslope. However if it succeeds we still have to test the rest, because non-autoslope terraforming is cheaper. */ rubidium@8078: CommandCost autoslope_result = TestAutoslopeOnRailTile(tile, flags, z_old, tileh_old, z_new, tileh_new, rail_bits); rubidium@8078: rubidium@7990: /* When there is only a single horizontal/vertical track, one corner can be terraformed. */ rubidium@8266: Corner allowed_corner; rubidium@7990: switch (rail_bits) { rubidium@8266: case TRACK_BIT_RIGHT: allowed_corner = CORNER_W; break; rubidium@8266: case TRACK_BIT_UPPER: allowed_corner = CORNER_S; break; rubidium@8266: case TRACK_BIT_LEFT: allowed_corner = CORNER_E; break; rubidium@8266: case TRACK_BIT_LOWER: allowed_corner = CORNER_N; break; rubidium@8078: default: return autoslope_result; rubidium@7990: } rubidium@7990: rubidium@7990: Foundation f_old = GetRailFoundation(tileh_old, rail_bits); rubidium@7990: rubidium@8266: /* Do not allow terraforming if allowed_corner is part of anti-zig-zag foundations */ rubidium@8266: if (tileh_old != SLOPE_NS && tileh_old != SLOPE_EW && IsSpecialRailFoundation(f_old)) return autoslope_result; rubidium@7990: rubidium@8266: /* Everything is valid, which only changes allowed_corner */ rubidium@8266: for (Corner corner = (Corner)0; corner < CORNER_END; corner = (Corner)(corner + 1)) { rubidium@8266: if (allowed_corner == corner) continue; rubidium@8266: if (z_old + GetSlopeZInCorner(tileh_old, corner) != z_new + GetSlopeZInCorner(tileh_new, corner)) return autoslope_result; rubidium@7990: } rubidium@7990: rubidium@7990: /* Make the ground dirty */ rubidium@7990: if ((flags & DC_EXEC) != 0) SetRailGroundType(tile, RAIL_GROUND_BARREN); rubidium@7990: rubidium@8267: /* allow terraforming */ rubidium@8726: return CommandCost(EXPENSES_CONSTRUCTION, was_water ? _price.clear_water : (Money)0); rubidium@8078: } else { rubidium@8078: if (_patches.build_on_slopes && AutoslopeEnabled()) { rubidium@8078: switch (GetRailTileType(tile)) { rubidium@8078: case RAIL_TILE_WAYPOINT: { rubidium@8078: CommandCost cost = TestAutoslopeOnRailTile(tile, flags, z_old, tileh_old, z_new, tileh_new, GetRailWaypointBits(tile)); rubidium@8078: if (!CmdFailed(cost)) return cost; // allow autoslope rubidium@8078: break; rubidium@8078: } rubidium@8078: rubidium@8078: case RAIL_TILE_DEPOT: rubidium@8726: if (AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRailDepotDirection(tile))) return CommandCost(EXPENSES_CONSTRUCTION, _price.terraform); rubidium@8078: break; rubidium@8078: rubidium@8078: default: NOT_REACHED(); rubidium@8078: } rubidium@8078: } rubidium@7990: } rubidium@7990: return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); rubidium@7990: } rubidium@7990: truelight@0: rubidium@5838: extern const TileTypeProcs _tile_type_rail_procs = { rubidium@4344: DrawTile_Track, /* draw_tile_proc */ rubidium@4344: GetSlopeZ_Track, /* get_slope_z_proc */ rubidium@4344: ClearTile_Track, /* clear_tile_proc */ rubidium@4344: GetAcceptedCargo_Track, /* get_accepted_cargo_proc */ rubidium@4344: GetTileDesc_Track, /* get_tile_desc_proc */ rubidium@4344: GetTileTrackStatus_Track, /* get_tile_track_status_proc */ rubidium@4344: ClickTile_Track, /* click_tile_proc */ rubidium@4344: AnimateTile_Track, /* animate_tile_proc */ rubidium@4344: TileLoop_Track, /* tile_loop_clear */ rubidium@4344: ChangeTileOwner_Track, /* change_tile_owner_clear */ rubidium@4344: NULL, /* get_produced_cargo_proc */ rubidium@4344: VehicleEnter_Track, /* vehicle_enter_tile_proc */ rubidium@7831: GetFoundation_Track, /* get_foundation_proc */ rubidium@7990: TerraformTile_Track, /* terraform_tile_proc */ truelight@0: };