tron@2186: /* $Id$ */ tron@2186: rubidium@9111: /** @file rail_cmd.cpp Handling of rail tiles. */ belugas@6393: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@3189: #include "bridge_map.h" rubidium@6160: #include "bridge.h" tron@6134: #include "cmd_helper.h" hackykid@1922: #include "debug.h" rubidium@8119: #include "tile_cmd.h" tron@3101: #include "rail_map.h" tron@3144: #include "road_map.h" maedhros@6343: #include "landscape.h" tron@3319: #include "town_map.h" tron@3154: #include "tunnel_map.h" rubidium@8224: #include "viewport_func.h" rubidium@8116: #include "command_func.h" truelight@0: #include "pathfind.h" rubidium@8786: #include "engine_func.h" peter1138@9070: #include "engine_base.h" truelight@0: #include "town.h" darkvater@405: #include "sprite.h" rubidium@8962: #include "depot_base.h" rubidium@8962: #include "depot_func.h" truelight@1542: #include "waypoint.h" matthijs@1942: #include "rail.h" peter1138@3638: #include "newgrf.h" KUDr@3900: #include "yapf/yapf.h" rubidium@7539: #include "newgrf_engine.h" peter1138@3757: #include "newgrf_callbacks.h" peter1138@3757: #include "newgrf_station.h" frosch@10355: #include "newgrf_commons.h" glx@4556: #include "train.h" rubidium@8211: #include "variables.h" rubidium@7582: #include "autoslope.h" belugas@7849: #include "transparency.h" rubidium@7948: #include "water.h" smatz@8083: #include "tunnelbridge_map.h" rubidium@8106: #include "window_func.h" rubidium@8144: #include "vehicle_func.h" rubidium@8157: #include "sound_func.h" smatz@8238: #include "signal_func.h" smatz@8398: #include "tunnelbridge.h" rubidium@8962: #include "station_map.h" rubidium@8962: #include "water_map.h" rubidium@9006: #include "functions.h" smatz@9154: #include "elrail_func.h" rubidium@9038: #include "oldpool_func.h" rubidium@9815: #include "pbs.h" rubidium@9815: #include "core/smallvec_type.hpp" smatz@8083: rubidium@8264: #include "table/sprites.h" rubidium@8264: #include "table/strings.h" rubidium@8264: #include "table/railtypes.h" belugas@8567: #include "table/track_land.h" truelight@0: peter1138@10001: RailtypeInfo _railtypes[RAILTYPE_END]; peter1138@10001: peter1138@10001: assert_compile(sizeof(_original_railtypes) <= sizeof(_railtypes)); peter1138@10001: peter1138@10001: /** peter1138@10001: * Initialize rail type information. peter1138@10001: */ peter1138@10001: void ResetRailTypes() peter1138@10001: { peter1138@10001: memset(_railtypes, 0, sizeof(_railtypes)); peter1138@10001: memcpy(_railtypes, _original_railtypes, sizeof(_original_railtypes)); peter1138@10001: } peter1138@10001: 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@7448: rubidium@9775: Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data) maedhros@7448: { smatz@8081: TrackBits rail_bits = *(TrackBits *)data; smatz@8081: smatz@8081: if (v->type != VEH_TRAIN) return NULL; smatz@8081: smatz@8081: if ((v->u.rail.track != rail_bits) && !TracksOverlap(v->u.rail.track | rail_bits)) return NULL; maedhros@7448: maedhros@7448: _error_message = VehicleInTheWayErrMsg(v); maedhros@7448: return v; maedhros@7448: } maedhros@7448: maedhros@7448: /** maedhros@7448: * Tests if a vehicle interacts with the specified track. maedhros@7448: * All track bits interact except parallel TRACK_BIT_HORZ or TRACK_BIT_VERT. maedhros@7448: * maedhros@7448: * @param tile The tile. maedhros@7448: * @param track The track. maedhros@7448: */ maedhros@7448: static bool EnsureNoTrainOnTrack(TileIndex tile, Track track) maedhros@7448: { smatz@8081: TrackBits rail_bits = TrackToTrackBits(track); smatz@8081: rubidium@10083: return !HasVehicleOnPos(tile, &rail_bits, &EnsureNoTrainOnTrackProc); maedhros@7448: } maedhros@7448: matthijs@1942: static bool CheckTrackCombination(TileIndex tile, TrackBits to_build, uint flags) truelight@0: { belugas@6420: TrackBits current; // The current track layout belugas@6420: 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)) { rubidium@10207: /* If we are not allowed to overlap (flag is on for ai companies 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@7703: /** Valid TrackBits on a specific (non-steep)-slope without foundation */ rubidium@7703: 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@5587: 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@5587: 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@7703: }; truelight@0: rubidium@7703: /** Valid TrackBits on a specific (non-steep)-slope with leveled foundation */ rubidium@7703: static const TrackBits _valid_tracks_on_leveled_foundation[15] = { rubidium@5587: 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@7703: /** rubidium@7703: * Checks if a track combination is valid on a specific slope and returns the needed foundation. rubidium@7703: * rubidium@7703: * @param tileh Tile slope. rubidium@7703: * @param bits Trackbits. rubidium@7703: * @return Needed foundation or FOUNDATION_INVALID if track/slope combination is not allowed. rubidium@7703: */ rubidium@7335: Foundation GetRailFoundation(Slope tileh, TrackBits bits) truelight@0: { rubidium@7703: if (bits == TRACK_BIT_NONE) return FOUNDATION_NONE; tron@3878: rubidium@7703: if (IsSteepSlope(tileh)) { rubidium@7703: /* Test for inclined foundations */ rubidium@7703: if (bits == TRACK_BIT_X) return FOUNDATION_INCLINED_X; rubidium@7703: if (bits == TRACK_BIT_Y) return FOUNDATION_INCLINED_Y; rubidium@7703: rubidium@7703: /* Get higher track */ rubidium@7703: Corner highest_corner = GetHighestSlopeCorner(tileh); rubidium@7703: TrackBits higher_track = CornerToTrackBits(highest_corner); rubidium@7703: rubidium@7703: /* Only higher track? */ rubidium@7770: if (bits == higher_track) return HalftileFoundation(highest_corner); rubidium@7703: rubidium@7703: /* Overlap with higher track? */ rubidium@7703: if (TracksOverlap(bits | higher_track)) return FOUNDATION_INVALID; rubidium@7703: rubidium@7703: /* either lower track or both higher and lower track */ rubidium@7770: return ((bits & higher_track) != 0 ? FOUNDATION_STEEP_BOTH : FOUNDATION_STEEP_LOWER); rubidium@7703: } else { rubidium@7703: if ((~_valid_tracks_without_foundation[tileh] & bits) == 0) return FOUNDATION_NONE; rubidium@7703: rubidium@7703: bool valid_on_leveled = ((~_valid_tracks_on_leveled_foundation[tileh] & bits) == 0); rubidium@7703: rubidium@7770: Corner track_corner; rubidium@7703: switch (bits) { rubidium@7770: case TRACK_BIT_LEFT: track_corner = CORNER_W; break; rubidium@7770: case TRACK_BIT_LOWER: track_corner = CORNER_S; break; rubidium@7770: case TRACK_BIT_RIGHT: track_corner = CORNER_E; break; rubidium@7770: case TRACK_BIT_UPPER: track_corner = CORNER_N; break; rubidium@7770: rubidium@7770: case TRACK_BIT_HORZ: rubidium@7770: if (tileh == SLOPE_N) return HalftileFoundation(CORNER_N); rubidium@7770: if (tileh == SLOPE_S) return HalftileFoundation(CORNER_S); rubidium@7770: return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID); rubidium@7770: rubidium@7770: case TRACK_BIT_VERT: rubidium@7770: if (tileh == SLOPE_W) return HalftileFoundation(CORNER_W); rubidium@7770: if (tileh == SLOPE_E) return HalftileFoundation(CORNER_E); rubidium@7770: return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID); rubidium@7770: rubidium@7703: case TRACK_BIT_X: frosch@8413: if (IsSlopeWithOneCornerRaised(tileh)) return FOUNDATION_INCLINED_X; rubidium@7703: return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID); rubidium@7703: rubidium@7703: case TRACK_BIT_Y: frosch@8413: if (IsSlopeWithOneCornerRaised(tileh)) return FOUNDATION_INCLINED_Y; rubidium@7703: return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID); rubidium@7703: rubidium@7703: default: rubidium@7703: return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID); rubidium@7703: } rubidium@7770: /* Single diagonal track */ rubidium@7770: rubidium@7770: /* Track must be at least valid on leveled foundation */ rubidium@7770: if (!valid_on_leveled) return FOUNDATION_INVALID; rubidium@7770: rubidium@7770: /* If slope has three raised corners, build leveled foundation */ frosch@8413: if (IsSlopeWithThreeCornersRaised(tileh)) return FOUNDATION_LEVELED; rubidium@7770: rubidium@7770: /* If neighboured corners of track_corner are lowered, build halftile foundation */ rubidium@7770: if ((tileh & SlopeWithThreeCornersRaised(OppositeCorner(track_corner))) == SlopeWithOneCornerRaised(track_corner)) return HalftileFoundation(track_corner); rubidium@7770: rubidium@7770: /* else special anti-zig-zag foundation */ rubidium@7770: return SpecialRailFoundation(track_corner); tron@2951: } truelight@0: } truelight@0: tron@2639: rubidium@7703: /** rubidium@7703: * Tests if a track can be build on a tile. rubidium@7703: * rubidium@7703: * @param tileh Tile slope. rubidium@7703: * @param rail_bits Tracks to build. rubidium@7703: * @param existing Tracks already built. rubidium@7703: * @param tile Tile (used for water test) rubidium@7703: * @return Error message or cost for foundation building. rubidium@7703: */ rubidium@6943: static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits existing, TileIndex tile) truelight@0: { rubidium@7703: /* don't allow building on the lower side of a coast */ rubidium@7771: if (IsTileType(tile, MP_WATER) || (IsTileType(tile, MP_RAILWAY) && (GetRailGroundType(tile) == RAIL_GROUND_WATER))) { rubidium@7703: if (!IsSteepSlope(tileh) && ((~_valid_tracks_on_leveled_foundation[tileh] & (rail_bits | existing)) != 0)) return_cmd_error(STR_3807_CAN_T_BUILD_ON_WATER); rubidium@7703: } truelight@0: rubidium@7703: Foundation f_new = GetRailFoundation(tileh, rail_bits | existing); truelight@0: rubidium@7703: /* check track/slope combination */ rubidium@7703: if ((f_new == FOUNDATION_INVALID) || rubidium@10207: ((f_new != FOUNDATION_NONE) && (!_settings_game.construction.build_on_slopes || _is_old_ai_company))) { smatz@9860: return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION); smatz@9860: } truelight@201: rubidium@7703: Foundation f_old = GetRailFoundation(tileh, existing); rubidium@8230: 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@6483: * @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@6943: CommandCost CmdBuildSingleRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { tron@3636: Slope tileh; rubidium@8236: RailType railtype = (RailType)p1; rubidium@8236: Track track = (Track)p2; matthijs@1942: TrackBits trackbit; rubidium@8230: CommandCost cost(EXPENSES_CONSTRUCTION); rubidium@6943: CommandCost ret; tron@1627: rubidium@8236: 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@7448: !EnsureNoTrainOnTrack(tile, track)) { tron@1627: return CMD_ERROR; tron@1627: } rubidium@10207: if (!IsTileOwner(tile, _current_company) || peter1138@5394: !IsCompatibleRail(GetRailType(tile), railtype)) { belugas@6420: /* 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@6950: cost.AddCost(ret); truelight@0: peter1138@5059: /* If the rail types don't match, try to convert only if engines of peter1138@10006: * the new rail type are not powered on the present rail type and engines of peter1138@5059: * the present rail type are powered on the new rail type. */ peter1138@10006: if (GetRailType(tile) != railtype && !HasPowerOnRail(railtype, GetRailType(tile))) { peter1138@10006: if (HasPowerOnRail(GetRailType(tile), railtype)) { peter1138@10006: ret = DoCommand(tile, tile, railtype, flags, CMD_CONVERT_RAIL); peter1138@10006: if (CmdFailed(ret)) return ret; peter1138@10006: cost.AddCost(ret); peter1138@10006: } else { peter1138@10006: return CMD_ERROR; peter1138@10006: } peter1138@5054: } peter1138@5054: tron@1719: if (flags & DC_EXEC) { celestar@3523: SetRailGroundType(tile, RAIL_GROUND_BARREN); rubidium@5411: SetTrackBits(tile, GetTrackBits(tile) | trackbit); tron@1719: } tron@1627: break; truelight@0: rubidium@7370: case MP_ROAD: tron@3142: #define M(x) (1 << (x)) tron@3142: /* Level crossings may only be built on these slopes */ skidd13@7928: 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@6406: if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR; truelight@0: frosch@8563: if (IsNormalRoad(tile)) { tron@4140: if (HasRoadWorks(tile)) return_cmd_error(STR_ROAD_WORKS_IN_PROGRESS); tron@4140: peter1138@8664: if (GetDisallowedRoadDirections(tile) != DRD_NONE) return_cmd_error(STR_ERR_CROSSING_ON_ONEWAY_ROAD); peter1138@8664: rubidium@6661: RoadTypes roadtypes = GetRoadTypes(tile); rubidium@6661: RoadBits road = GetRoadBits(tile, ROADTYPE_ROAD); rubidium@6661: RoadBits tram = GetRoadBits(tile, ROADTYPE_TRAM); rubidium@6661: switch (roadtypes) { rubidium@6661: default: break; rubidium@6728: case ROADTYPES_TRAM: rubidium@6728: /* Tram crossings must always have road. */ rubidium@10207: if (flags & DC_EXEC) SetRoadOwner(tile, ROADTYPE_ROAD, _current_company); rubidium@6728: roadtypes |= ROADTYPES_ROAD; rubidium@6728: break; rubidium@6728: rubidium@6661: case ROADTYPES_ROADTRAM: if (road == tram) break; rubidium@6661: /* FALL THROUGH */ rubidium@6661: case ROADTYPES_ROADHWAY: // Road and highway are incompatible in this case rubidium@6661: case ROADTYPES_TRAMHWAY: // Tram and highway are incompatible in this case rubidium@6661: case ROADTYPES_ALL: // Also incompatible rubidium@6661: return CMD_ERROR; rubidium@6661: } rubidium@6661: rubidium@6661: road |= tram | GetRoadBits(tile, ROADTYPE_HWAY); rubidium@6661: rubidium@6661: if ((track == TRACK_X && road == ROAD_Y) || rubidium@6661: (track == TRACK_Y && road == ROAD_X)) { tron@4140: if (flags & DC_EXEC) { rubidium@10207: MakeRoadCrossing(tile, GetRoadOwner(tile, ROADTYPE_ROAD), GetRoadOwner(tile, ROADTYPE_TRAM), GetRoadOwner(tile, ROADTYPE_HWAY), _current_company, (track == TRACK_X ? AXIS_Y : AXIS_X), railtype, roadtypes, GetTownIndex(tile)); smatz@8344: 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@8414: /* Will there be flat water on the lower halftile? */ frosch@8413: bool water_ground = IsTileType(tile, MP_WATER) && IsSlopeWithOneCornerRaised(tileh); rubidium@7771: rubidium@5587: ret = CheckRailSlope(tileh, trackbit, TRACK_BIT_NONE, tile); tron@1691: if (CmdFailed(ret)) return ret; rubidium@6950: cost.AddCost(ret); tron@1627: tron@3491: ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); tron@1691: if (CmdFailed(ret)) return ret; rubidium@6950: cost.AddCost(ret); tron@1627: rubidium@7771: if (water_ground) { rubidium@7771: cost.AddCost(-_price.clear_water); belugas@7977: cost.AddCost(_price.clear_roughland); rubidium@7771: } rubidium@7771: rubidium@7771: if (flags & DC_EXEC) { rubidium@10207: MakeRailNormal(tile, _current_company, trackbit, railtype); rubidium@7771: if (water_ground) SetRailGroundType(tile, RAIL_GROUND_WATER); rubidium@7771: } tron@1627: break; truelight@0: } truelight@0: truelight@0: if (flags & DC_EXEC) { truelight@0: MarkTileDirtyByTile(tile); rubidium@10207: AddTrackToSignalBuffer(tile, track, _current_company); KUDr@3900: YapfNotifyTrackLayoutChange(tile, track); truelight@0: } truelight@0: maedhros@7730: 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@6483: * @param flags operation to perform Darkvater@1775: * @param p1 unused Darkvater@1775: * @param p2 rail orientation truelight@0: */ rubidium@6943: CommandCost CmdRemoveSingleRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { matthijs@1942: Track track = (Track)p2; matthijs@1942: TrackBits trackbit; rubidium@8230: CommandCost cost(EXPENSES_CONSTRUCTION, _price.remove_rail ); peter1138@3284: bool crossing = false; Darkvater@1775: rubidium@5587: if (!ValParamTrackOrientation((Track)p2)) return CMD_ERROR; matthijs@1942: trackbit = TrackToTrackBits(track); truelight@0: smatz@8318: /* Need to read tile owner now because it may change when the rail is removed rubidium@10207: * Also, in case of floods, _current_company != owner smatz@8318: * There may be invalid tiletype even in exec run (when removing long track), smatz@8318: * so do not call GetTileOwner(tile) in any case here */ smatz@8318: Owner owner = INVALID_OWNER; smatz@8300: rubidium@9815: Vehicle *v = NULL; rubidium@9815: tron@3273: switch (GetTileType(tile)) { rubidium@7370: case MP_ROAD: { tron@3273: if (!IsLevelCrossing(tile) || tron@3273: GetCrossingRailBits(tile) != trackbit || rubidium@10207: (_current_company != OWNER_WATER && !CheckTileOwnership(tile)) || smatz@8519: (!(flags & DC_BANKRUPT) && !EnsureNoVehicleOnGround(tile))) { tron@3273: return CMD_ERROR; tron@3273: } truelight@0: tron@3273: if (flags & DC_EXEC) { rubidium@9815: if (HasReservedTracks(tile, trackbit)) { rubidium@9815: v = GetTrainForReservation(tile, track); rubidium@9815: if (v != NULL) FreeTrainTrackReservation(v); rubidium@9815: } smatz@8318: owner = GetTileOwner(tile); rubidium@6661: 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) || rubidium@10207: (_current_company != OWNER_WATER && !CheckTileOwnership(tile)) || maedhros@7448: !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@6950: cost.AddCost(DoCommand(tile, track, 0, flags, CMD_REMOVE_SIGNALS)); matthijs@1942: tron@3273: if (flags & DC_EXEC) { rubidium@9815: if (HasReservedTracks(tile, trackbit)) { rubidium@9815: v = GetTrainForReservation(tile, track); rubidium@9815: if (v != NULL) FreeTrainTrackReservation(v); rubidium@9815: } smatz@8318: owner = GetTileOwner(tile); tron@3273: present ^= trackbit; tron@3273: if (present == 0) { frosch@8414: Slope tileh = GetTileSlope(tile, NULL); frosch@8414: /* If there is flat water on the lower halftile, convert the tile to shore so the water remains */ frosch@8414: if (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh)) { rubidium@7771: MakeShore(tile); rubidium@7771: } else { rubidium@7771: DoClearSquare(tile); rubidium@7771: } tron@3273: } else { tron@3273: SetTrackBits(tile, present); rubidium@9788: SetTrackReservation(tile, GetTrackReservation(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@8318: /* if we got that far, 'owner' variable is set correctly */ rubidium@10207: assert(IsValidCompanyID(owner)); smatz@8318: 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@8306: AddTrackToSignalBuffer(tile, TRACK_X, owner); smatz@8306: AddTrackToSignalBuffer(tile, TRACK_Y, owner); KUDr@3900: YapfNotifyTrackLayoutChange(tile, TRACK_X); KUDr@3900: YapfNotifyTrackLayoutChange(tile, TRACK_Y); peter1138@3284: } else { smatz@8306: AddTrackToSignalBuffer(tile, track, owner); KUDr@3900: YapfNotifyTrackLayoutChange(tile, track); peter1138@3284: } rubidium@9815: rubidium@9815: if (v != NULL) TryPathReserve(v, true); tron@3273: } truelight@201: hackykid@1963: return cost; truelight@0: } truelight@0: truelight@0: rubidium@7771: /** rubidium@7771: * Called from water_cmd if a non-flat rail-tile gets flooded and should be converted to shore. rubidium@7771: * The function floods the lower halftile, if the tile has a halftile foundation. rubidium@7771: * rubidium@7771: * @param t The tile to flood. frosch@8332: * @return true if something was flooded. rubidium@7771: */ frosch@8332: bool FloodHalftile(TileIndex t) rubidium@7771: { frosch@8332: bool flooded = false; frosch@8332: if (GetRailGroundType(t) == RAIL_GROUND_WATER) return flooded; rubidium@7771: rubidium@7771: Slope tileh = GetTileSlope(t, NULL); rubidium@7771: TrackBits rail_bits = GetTrackBits(t); rubidium@7771: frosch@8413: if (IsSlopeWithOneCornerRaised(tileh)) { rubidium@7771: TrackBits lower_track = CornerToTrackBits(OppositeCorner(GetHighestSlopeCorner(tileh))); rubidium@7771: rubidium@7771: TrackBits to_remove = lower_track & rail_bits; rubidium@7771: if (to_remove != 0) { rubidium@10207: _current_company = OWNER_WATER; frosch@8332: if (CmdFailed(DoCommand(t, 0, FIND_FIRST_BIT(to_remove), DC_EXEC, CMD_REMOVE_SINGLE_RAIL))) return flooded; // not yet floodable frosch@8332: flooded = true; rubidium@7771: rail_bits = rail_bits & ~to_remove; rubidium@7771: if (rail_bits == 0) { rubidium@7771: MakeShore(t); rubidium@7771: MarkTileDirtyByTile(t); frosch@8332: return flooded; rubidium@7771: } rubidium@7771: } rubidium@7771: rubidium@7771: if (IsNonContinuousFoundation(GetRailFoundation(tileh, rail_bits))) { frosch@8332: flooded = true; rubidium@7771: SetRailGroundType(t, RAIL_GROUND_WATER); rubidium@7771: MarkTileDirtyByTile(t); rubidium@7771: } frosch@8414: } else { frosch@8414: /* Make shore on steep slopes and 'three-corners-raised'-slopes. */ frosch@8414: if (ApplyFoundationToSlope(GetRailFoundation(tileh, rail_bits), &tileh) == 0) { frosch@8414: if (IsSteepSlope(tileh) || IsSlopeWithThreeCornersRaised(tileh)) { frosch@8414: flooded = true; frosch@8414: SetRailGroundType(t, RAIL_GROUND_WATER); frosch@8414: MarkTileDirtyByTile(t); frosch@8414: } frosch@8414: } rubidium@7771: } frosch@8332: return flooded; rubidium@7771: } rubidium@7771: 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@6943: 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@5587: if (!ValParamTrackOrientation(TrackdirToTrack(*trackdir))) return CMD_ERROR; darkvater@1227: belugas@6420: /* calculate delta x,y from start to end tile */ darkvater@1227: dx = ex - x; darkvater@1227: dy = ey - y; darkvater@1227: belugas@6420: /* 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@6420: /* 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@7928: if (!HasBit(*trackdir, 3)) { // first direction is invalid, try the other skidd13@7931: 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@6420: /* (for diagonal tracks, this is already made sure of by above test), but: belugas@6420: * 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@6950: return CommandCost(); darkvater@1227: } darkvater@1227: Darkvater@1775: /** Build a stretch of railroad tracks. tron@3491: * @param tile start tile of drag belugas@6483: * @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@6943: static CommandCost CmdRailTrackHelper(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) darkvater@1227: { rubidium@8230: CommandCost ret, total_cost(EXPENSES_CONSTRUCTION); tron@2140: Track track = (Track)GB(p2, 4, 3); matthijs@1942: Trackdir trackdir; skidd13@7928: 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@6950: 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@6420: /* toggle railbit for the non-diagonal tracks */ skidd13@7932: if (!IsDiagonalTrackdir(trackdir)) ToggleBit(trackdir, 0); truelight@201: } truelight@0: rubidium@10330: return (total_cost.GetCost() == 0) ? CommandCost(STR_1007_ALREADY_BUILT) : 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@6483: * @param tile start tile of drag belugas@6483: * @param flags operation to perform belugas@6483: * @param p1 end tile of drag belugas@6483: * @param p2 various bitstuffed elements belugas@6483: * - p2 = (bit 0-3) - railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev) belugas@6483: * - p2 = (bit 4-6) - track-orientation, valid values: 0-5 (Track enum) belugas@6483: * - p2 = (bit 7) - 0 = build, 1 = remove tracks Darkvater@1796: * @see CmdRailTrackHelper Darkvater@1796: */ rubidium@6943: CommandCost CmdBuildRailroadTrack(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) darkvater@1227: { skidd13@7929: 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@6483: * @param tile start tile of drag belugas@6483: * @param flags operation to perform belugas@6483: * @param p1 end tile of drag belugas@6483: * @param p2 various bitstuffed elements belugas@6483: * - p2 = (bit 0-3) - railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev) belugas@6483: * - p2 = (bit 4-6) - track-orientation, valid values: 0-5 (Track enum) belugas@6483: * - p2 = (bit 7) - 0 = build, 1 = remove tracks Darkvater@1796: * @see CmdRailTrackHelper Darkvater@1796: */ rubidium@6943: CommandCost CmdRemoveRailroadTrack(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { skidd13@7931: 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@6483: * @param flags operation to perform Darkvater@1775: * @param p1 rail type tron@6134: * @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@6943: 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@8236: if (!ValParamRailtype((RailType)p1)) return CMD_ERROR; truelight@0: truelight@0: tileh = GetTileSlope(tile, NULL); celestar@2085: tron@6134: DiagDirection dir = Extract(p2); tron@6134: 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 && ( rubidium@10207: _is_old_ai_company || rubidium@9413: !_settings_game.construction.build_on_slopes || tron@3636: IsSteepSlope(tileh) || tron@6134: !CanBuildDepotByTileh(dir, tileh) tron@2548: )) { tron@2548: return_cmd_error(STR_0007_FLAT_LAND_REQUIRED); truelight@0: } truelight@0: rubidium@8230: CommandCost cost = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); rubidium@6950: if (CmdFailed(cost)) return CMD_ERROR; truelight@0: celestar@5385: if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST); celestar@5385: rubidium@9036: if (!Depot::CanAllocateItem()) return CMD_ERROR; truelight@0: truelight@0: if (flags & DC_EXEC) { rubidium@9036: Depot *d = new Depot(tile); rubidium@10207: MakeRailDepot(tile, _current_company, dir, (RailType)p1); tron@3101: MarkTileDirtyByTile(tile); truelight@201: rubidium@10236: d->town_index = ClosestTownFromTile(tile, UINT_MAX)->index; truelight@0: rubidium@10207: AddSideToSignalBuffer(tile, INVALID_DIAGDIR, _current_company); smatz@9224: YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir)); truelight@0: } truelight@0: rubidium@6950: return cost.AddCost(_price.build_train_depot); truelight@0: } truelight@0: Darkvater@1775: /** Build signals, alternate between double/single, signal/semaphore, Darkvater@5731: * pre/exit/combo-signals, and what-else not. If the rail piece does not Darkvater@5731: * have any signals, bit 4 (cycle signal-type) is ignored tron@3491: * @param tile tile where to build the signals belugas@6483: * @param flags operation to perform Darkvater@1775: * @param p1 various bitstuffed elements Darkvater@5735: * - p1 = (bit 0-2) - track-orientation, valid values: 0-5 (Track enum) rubidium@7991: * - p1 = (bit 3) - 1 = override signal/semaphore, or pre/exit/combo signal or (for bit 7) toggle variant (CTRL-toggle) Darkvater@5735: * - p1 = (bit 4) - 0 = signals, 1 = semaphores rubidium@9793: * - p1 = (bit 5-7) - type of the signal, for valid values see enum SignalType in rail_map.h rubidium@9793: * - p1 = (bit 8) - convert the present signal type and variant rubidium@9793: * - p1 = (bit 9-11)- start cycle from this signal type rubidium@9793: * - p1 = (bit 12-14)-wrap around after this signal type glx@10172: * - p1 = (bit 15-16)-cycle the signal direction this many times 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@6943: CommandCost CmdBuildSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { Darkvater@5735: Track track = (Track)GB(p1, 0, 3); rubidium@7991: bool ctrl_pressed = HasBit(p1, 3); // was the CTRL button pressed rubidium@7991: SignalVariant sigvar = (ctrl_pressed ^ HasBit(p1, 4)) ? SIG_SEMAPHORE : SIG_ELECTRIC; // the signal variant of the new signal rubidium@9793: SignalType sigtype = (SignalType)GB(p1, 5, 3); // the signal type of the new signal rubidium@9793: bool convert_signal = HasBit(p1, 8); // convert button pressed rubidium@9793: SignalType cycle_start = (SignalType)GB(p1, 9, 3); rubidium@9793: SignalType cycle_stop = (SignalType)GB(p1, 12, 3); rubidium@6943: CommandCost cost; glx@10172: uint num_dir_cycle = GB(p1, 15, 2); truelight@201: rubidium@9793: if (sigtype > SIGTYPE_LAST) return CMD_ERROR; rubidium@9793: maedhros@7448: 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@7833: if (KillFirstBit(trackbits) != TRACK_BIT_NONE && /* More than one track present */ tron@3258: trackbits != TRACK_BIT_HORZ && tron@4077: trackbits != TRACK_BIT_VERT) { rubidium@7991: return_cmd_error(STR_1005_NO_SUITABLE_RAILROAD_TRACK); tron@4077: } truelight@0: } truelight@0: rubidium@7991: /* you can not convert a signal if no signal is on track */ rubidium@7991: if (convert_signal && !HasSignalOnTrack(tile, track)) return CMD_ERROR; rubidium@7991: matthijs@1942: if (!HasSignalOnTrack(tile, track)) { belugas@6420: /* build new signals */ rubidium@8230: cost = CommandCost(EXPENSES_CONSTRUCTION, _price.build_signals); darkvater@1066: } else { glx@6770: if (p2 != 0 && sigvar != GetSignalVariant(tile, track)) { belugas@6420: /* convert signals <-> semaphores */ rubidium@8230: cost = CommandCost(EXPENSES_CONSTRUCTION, _price.build_signals + _price.remove_signals); rubidium@7991: rubidium@7991: } else if (convert_signal) { rubidium@7991: /* convert button pressed */ rubidium@7991: if (ctrl_pressed || GetSignalVariant(tile, track) != sigvar) { rubidium@7991: /* convert electric <-> semaphore */ rubidium@8230: cost = CommandCost(EXPENSES_CONSTRUCTION, _price.build_signals + _price.remove_signals); rubidium@7991: } else { rubidium@7991: /* it is free to change signal type: normal-pre-exit-combo */ rubidium@7991: cost = CommandCost(); rubidium@7991: } rubidium@7991: tron@1084: } else { belugas@6432: /* it is free to change orientation/pre-exit-combo signals */ rubidium@6950: cost = CommandCost(); tron@1084: } truelight@0: } truelight@0: truelight@0: if (flags & DC_EXEC) { smatz@9830: Vehicle *v = NULL; smatz@9830: /* The new/changed signal could block our path. As this can lead to smatz@9830: * stale reservations, we clear the path reservation here and try smatz@9830: * to redo it later on. */ smatz@9830: if (HasReservedTracks(tile, TrackToTrackBits(track))) { smatz@9830: v = GetTrainForReservation(tile, track); smatz@9830: if (v != NULL) FreeTrainTrackReservation(v); smatz@9830: } smatz@9830: tron@4182: if (!HasSignals(tile)) { belugas@6420: /* there are no signals at all on this tile yet */ rubidium@6172: SetHasSignals(tile, true); rubidium@6753: SetSignalStates(tile, 0xF); // all signals are on rubidium@6753: SetPresentSignals(tile, 0); // no signals built by default rubidium@7991: SetSignalType(tile, track, sigtype); glx@6770: SetSignalVariant(tile, track, sigvar); truelight@0: } truelight@0: tron@1084: if (p2 == 0) { matthijs@1942: if (!HasSignalOnTrack(tile, track)) { belugas@6420: /* build new signals */ rubidium@9793: SetPresentSignals(tile, GetPresentSignals(tile) | (IsPbsSignal(sigtype) ? KillFirstBit(SignalOnTrack(track)) : SignalOnTrack(track))); rubidium@7991: SetSignalType(tile, track, sigtype); glx@6770: SetSignalVariant(tile, track, sigvar); glx@10172: while (num_dir_cycle-- > 0) CycleSignalSide(tile, track); tron@1084: } else { rubidium@7991: if (convert_signal) { rubidium@7991: /* convert signal button pressed */ rubidium@7991: if (ctrl_pressed) { rubidium@7991: /* toggle the pressent signal variant: SIG_ELECTRIC <-> SIG_SEMAPHORE */ rubidium@7991: SetSignalVariant(tile, track, (GetSignalVariant(tile, track) == SIG_ELECTRIC) ? SIG_SEMAPHORE : SIG_ELECTRIC); frosch@9972: /* Query current signal type so the check for PBS signals below works. */ frosch@9972: sigtype = GetSignalType(tile, track); rubidium@7991: } else { rubidium@7991: /* convert the present signal to the chosen type and variant */ rubidium@7991: SetSignalType(tile, track, sigtype); rubidium@7991: SetSignalVariant(tile, track, sigvar); rubidium@9795: if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) { rubidium@9795: SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | KillFirstBit(SignalOnTrack(track))); rubidium@9795: } rubidium@7991: } rubidium@7991: rubidium@7991: } else if (ctrl_pressed) { rubidium@9793: /* cycle between cycle_start and cycle_end */ rubidium@9793: sigtype = (SignalType)(GetSignalType(tile, track) + 1); rubidium@9793: rubidium@9793: if (sigtype < cycle_start || sigtype > cycle_stop) sigtype = cycle_start; rubidium@9793: rubidium@9793: SetSignalType(tile, track, sigtype); rubidium@9795: if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) { rubidium@9795: SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | KillFirstBit(SignalOnTrack(track))); rubidium@9795: } tron@1084: } else { rubidium@7991: /* cycle the signal side: both -> left -> right -> both -> ... */ celestar@3522: CycleSignalSide(tile, track); frosch@9972: /* Query current signal type so the check for PBS signals below works. */ frosch@9972: sigtype = GetSignalType(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@6753: SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | (p2 & SignalOnTrack(track))); glx@6770: SetSignalVariant(tile, track, sigvar); rubidium@9794: SetSignalType(tile, track, sigtype); truelight@0: } truelight@201: rubidium@9793: if (IsPbsSignal(sigtype)) { frosch@9972: /* PBS signals should show red unless they are on a reservation. */ rubidium@9793: uint mask = GetPresentSignals(tile) & SignalOnTrack(track); rubidium@10236: SetSignalStates(tile, (GetSignalStates(tile) & ~mask) | ((HasBit(GetTrackReservation(tile), track) ? UINT_MAX : 0) & mask)); rubidium@9793: } truelight@0: MarkTileDirtyByTile(tile); rubidium@10207: AddTrackToSignalBuffer(tile, track, _current_company); KUDr@3900: YapfNotifyTrackLayoutChange(tile, track); smatz@9830: if (v != NULL) TryPathReserve(v, true); truelight@0: } truelight@0: truelight@0: return cost; truelight@0: } truelight@0: peter1138@7163: static bool CheckSignalAutoFill(TileIndex &tile, Trackdir &trackdir, int &signal_ctr, bool remove) peter1138@7163: { peter1138@7163: tile = AddTileIndexDiffCWrap(tile, _trackdelta[trackdir]); peter1138@7163: if (tile == INVALID_TILE) return false; peter1138@7163: peter1138@7163: /* Check for track bits on the new tile */ frosch@8616: TrackdirBits trackdirbits = TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)); peter1138@7163: peter1138@7163: if (TracksOverlap(TrackdirBitsToTrackBits(trackdirbits))) return false; peter1138@7163: trackdirbits &= TrackdirReachesTrackdirs(trackdir); peter1138@7163: peter1138@7163: /* No track bits, must stop */ peter1138@7163: if (trackdirbits == TRACKDIR_BIT_NONE) return false; peter1138@7163: peter1138@7163: /* Get the first track dir */ peter1138@7163: trackdir = RemoveFirstTrackdir(&trackdirbits); peter1138@7163: peter1138@7163: /* Any left? It's a junction so we stop */ peter1138@7163: if (trackdirbits != TRACKDIR_BIT_NONE) return false; peter1138@7163: peter1138@7163: switch (GetTileType(tile)) { peter1138@7163: case MP_RAILWAY: peter1138@7163: if (IsRailDepot(tile)) return false; peter1138@7163: if (!remove && HasSignalOnTrack(tile, TrackdirToTrack(trackdir))) return false; peter1138@7163: signal_ctr++; peter1138@7163: if (IsDiagonalTrackdir(trackdir)) { peter1138@7163: signal_ctr++; peter1138@7163: /* Ensure signal_ctr even so X and Y pieces get signals */ skidd13@7929: ClrBit(signal_ctr, 0); peter1138@7163: } peter1138@7163: return true; peter1138@7163: rubidium@7370: case MP_ROAD: peter1138@7163: if (!IsLevelCrossing(tile)) return false; peter1138@7163: signal_ctr += 2; peter1138@7163: return true; peter1138@7163: peter1138@7163: case MP_TUNNELBRIDGE: { smatz@8088: TileIndex orig_tile = tile; // backup old value smatz@8088: smatz@8088: if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return false; smatz@8088: if (GetTunnelBridgeDirection(tile) != TrackdirToExitdir(trackdir)) return false; smatz@8088: smatz@8088: /* Skip to end of tunnel or bridge smatz@8088: * note that tile is a parameter by reference, so it must be updated */ smatz@8197: tile = GetOtherTunnelBridgeEnd(tile); smatz@8088: smatz@8398: signal_ctr += (GetTunnelBridgeLength(orig_tile, tile) + 2) * 2; peter1138@7163: return true; peter1138@7163: } peter1138@7163: peter1138@7163: default: return false; peter1138@7163: } peter1138@7163: } peter1138@7163: rubidium@4434: /** Build many signals by dragging; AutoSignals tron@3491: * @param tile start tile of drag belugas@6483: * @param flags operation to perform Darkvater@1796: * @param p1 end tile of drag Darkvater@1796: * @param p2 various bitstuffed elements Darkvater@5735: * - p2 = (bit 0- 2) - track-orientation, valid values: 0-5 (Track enum) Darkvater@5735: * - p2 = (bit 3) - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle) Darkvater@5735: * - p2 = (bit 4) - 0 = signals, 1 = semaphores Darkvater@5731: * - p2 = (bit 5) - 0 = build, 1 = remove signals peter1138@7163: * - p2 = (bit 6) - 0 = selected stretch, 1 = auto fill rubidium@9794: * - p2 = (bit 7- 9) - default signal type Darkvater@1796: * - p2 = (bit 24-31) - user defined signals_density darkvater@58: */ rubidium@6943: static CommandCost CmdSignalTrackHelper(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) darkvater@58: { rubidium@8230: CommandCost ret, total_cost(EXPENSES_CONSTRUCTION); rubidium@6943: int signal_ctr; hackykid@1954: byte signals; Darkvater@1796: bool error = true; tron@3493: TileIndex end_tile; peter1138@7163: TileIndex start_tile = tile; Darkvater@1796: Darkvater@5735: Track track = (Track)GB(p2, 0, 3); skidd13@7928: bool mode = HasBit(p2, 3); skidd13@7928: bool semaphores = HasBit(p2, 4); skidd13@7928: bool remove = HasBit(p2, 5); skidd13@7928: bool autofill = HasBit(p2, 6); matthijs@1942: Trackdir trackdir = TrackToTrackdir(track); Darkvater@5731: 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@7163: 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@7163: Trackdir start_trackdir = trackdir; peter1138@7163: rubidium@9867: /* Must start on a valid track to be able to avoid loops */ rubidium@9867: if (!HasTrack(tile, track)) return CMD_ERROR; darkvater@58: rubidium@9794: SignalType sigtype = (SignalType)GB(p2, 7, 3); rubidium@9794: if (sigtype > SIGTYPE_LAST) return CMD_ERROR; rubidium@9794: belugas@6432: /* copy the signal-style of the first rail-piece if existing */ tron@4182: if (HasSignals(tile)) { rubidium@6753: signals = GetPresentSignals(tile) & SignalOnTrack(track); matthijs@1942: if (signals == 0) signals = SignalOnTrack(track); /* Can this actually occur? */ darkvater@58: belugas@6420: /* copy signal/semaphores style (independent of CTRL) */ glx@6770: semaphores = GetSignalVariant(tile, track) != SIG_ELECTRIC; rubidium@9794: rubidium@9794: sigtype = GetSignalType(tile, track); peter1138@9866: /* Don't but copy pre-signal type */ peter1138@9866: if (sigtype < SIGTYPE_PBS) sigtype = SIGTYPE_NORMAL; tron@4077: } else { // no signals exist, drag a two-way signal stretch rubidium@9794: signals = IsPbsSignal(sigtype) ? SignalAlongTrackdir(trackdir) : SignalOnTrack(track); tron@4077: } darkvater@58: peter1138@7163: byte signal_dir = 0; skidd13@7931: if (signals & SignalAlongTrackdir(trackdir)) SetBit(signal_dir, 0); skidd13@7931: if (signals & SignalAgainstTrackdir(trackdir)) SetBit(signal_dir, 1); peter1138@7163: 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@5731: * remove - 1 remove signals, 0 build signals */ rubidium@6950: signal_ctr = 0; Darkvater@1796: for (;;) { belugas@6420: /* only build/remove signals with the specified density */ peter1138@7163: if ((remove && autofill) || signal_ctr % signal_density == 0) { Darkvater@5735: uint32 p1 = GB(TrackdirToTrack(trackdir), 0, 3); Darkvater@5735: SB(p1, 3, 1, mode); Darkvater@5735: SB(p1, 4, 1, semaphores); rubidium@9794: SB(p1, 5, 3, sigtype); peter1138@7163: peter1138@7163: /* Pick the correct orientation for the track direction */ peter1138@7163: signals = 0; skidd13@7928: if (HasBit(signal_dir, 0)) signals |= SignalAlongTrackdir(trackdir); skidd13@7928: if (HasBit(signal_dir, 1)) signals |= SignalAgainstTrackdir(trackdir); peter1138@7163: Darkvater@5731: 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@6946: if (CmdSucceeded(ret)) { darkvater@58: error = false; rubidium@6950: total_cost.AddCost(ret); darkvater@58: } darkvater@58: } darkvater@58: peter1138@7163: if (autofill) { peter1138@7163: if (!CheckSignalAutoFill(tile, trackdir, signal_ctr, remove)) break; darkvater@58: peter1138@7163: /* Prevent possible loops */ peter1138@7163: if (tile == start_tile && trackdir == start_trackdir) break; peter1138@7163: } else { peter1138@7163: if (tile == end_tile) break; truelight@201: peter1138@7163: tile += ToTileIndexDiff(_trackdelta[trackdir]); peter1138@7163: signal_ctr++; peter1138@7163: peter1138@7163: /* toggle railbit for the non-diagonal tracks (|, -- tracks) */ peter1138@7163: if (IsDiagonalTrackdir(trackdir)) { peter1138@7163: signal_ctr++; peter1138@7163: } else { skidd13@7932: ToggleBit(trackdir, 0); peter1138@7163: } peter1138@7163: } 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@6483: * @param tile start tile of drag belugas@6483: * @param flags operation to perform belugas@6483: * @param p1 end tile of drag belugas@6483: * @param p2 various bitstuffed elements belugas@6483: * - p2 = (bit 0- 2) - track-orientation, valid values: 0-5 (Track enum) belugas@6483: * - p2 = (bit 3) - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle) belugas@6483: * - p2 = (bit 4) - 0 = signals, 1 = semaphores belugas@6483: * - p2 = (bit 5) - 0 = build, 1 = remove signals peter1138@7163: * - p2 = (bit 6) - 0 = selected stretch, 1 = auto fill rubidium@9794: * - p2 = (bit 7- 9) - default signal type belugas@6483: * - p2 = (bit 24-31) - user defined signals_density Darkvater@1796: * @see CmdSignalTrackHelper Darkvater@1796: */ rubidium@6943: 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@6483: * @param flags operation to perform belugas@6488: * @param p1 various bitstuffed elements, only track information is used belugas@6488: * - (bit 0- 2) - track-orientation, valid values: 0-5 (Track enum) belugas@6488: * - (bit 3) - override signal/semaphore, or pre/exit/combo signal (CTRL-toggle) belugas@6488: * - (bit 4) - 0 = signals, 1 = semaphores belugas@6488: * @param p2 unused truelight@0: */ rubidium@6943: CommandCost CmdRemoveSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { Darkvater@5735: Track track = (Track)GB(p1, 0, 3); tron@1518: tron@4077: if (!ValParamTrackOrientation(track) || tron@4077: !IsTileType(tile, MP_RAILWAY) || smatz@9587: !HasTrack(tile, track) || maedhros@7448: !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 */ rubidium@10207: if (_current_company != OWNER_WATER && !CheckTileOwnership(tile)) return CMD_ERROR; truelight@0: truelight@0: /* Do it? */ truelight@0: if (flags & DC_EXEC) { smatz@9830: Vehicle *v = NULL; smatz@9830: if (HasReservedTracks(tile, TrackToTrackBits(track))) { smatz@9830: v = GetTrainForReservation(tile, track); smatz@9830: } rubidium@6753: SetPresentSignals(tile, GetPresentSignals(tile) & ~SignalOnTrack(track)); truelight@201: darkvater@1066: /* removed last signal from tile? */ rubidium@6753: if (GetPresentSignals(tile) == 0) { rubidium@6753: SetSignalStates(tile, 0); rubidium@6172: SetHasSignals(tile, false); glx@6770: SetSignalVariant(tile, INVALID_TRACK, SIG_ELECTRIC); // remove any possible semaphores darkvater@1066: } tron@1109: smatz@8306: AddTrackToSignalBuffer(tile, track, GetTileOwner(tile)); KUDr@3900: YapfNotifyTrackLayoutChange(tile, track); smatz@9830: if (v != NULL) TryPathReserve(v, false); truelight@0: truelight@0: MarkTileDirtyByTile(tile); truelight@0: } truelight@201: rubidium@8230: 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@6483: * @param tile start tile of drag belugas@6483: * @param flags operation to perform belugas@6483: * @param p1 end tile of drag belugas@6483: * @param p2 various bitstuffed elements belugas@6483: * - p2 = (bit 0- 2) - track-orientation, valid values: 0-5 (Track enum) belugas@6483: * - p2 = (bit 3) - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle) belugas@6483: * - p2 = (bit 4) - 0 = signals, 1 = semaphores belugas@6483: * - p2 = (bit 5) - 0 = build, 1 = remove signals peter1138@7163: * - p2 = (bit 6) - 0 = selected stretch, 1 = auto fill rubidium@9794: * - p2 = (bit 7- 9) - default signal type belugas@6483: * - p2 = (bit 24-31) - user defined signals_density Darkvater@1796: * @see CmdSignalTrackHelper Darkvater@1796: */ rubidium@6943: CommandCost CmdRemoveSignalTrack(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) darkvater@1227: { skidd13@7931: return CmdSignalTrackHelper(tile, flags, p1, SetBit(p2, 5)); // bit 5 is remove bit darkvater@1227: } darkvater@1227: smatz@8096: /** Update power of train under which is the railtype being converted */ rubidium@9775: Vehicle *UpdateTrainPowerProc(Vehicle *v, void *data) rubidium@7539: { rubidium@7539: /* Similiar checks as in TrainPowerChanged() */ rubidium@7539: smatz@8081: if (v->type == VEH_TRAIN && !IsArticulatedPart(v)) { rubidium@7539: const RailVehicleInfo *rvi = RailVehInfo(v->engine_type); rubidium@7539: if (GetVehicleProperty(v, 0x0B, rvi->power) != 0) TrainPowerChanged(v->First()); rubidium@7539: } rubidium@7539: rubidium@7539: return NULL; rubidium@7539: } rubidium@7539: 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@6483: * @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@6943: CommandCost CmdConvertRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { rubidium@8230: CommandCost cost(EXPENSES_CONSTRUCTION); rubidium@8236: RailType totype = (RailType)p2; rubidium@8236: rubidium@8236: if (!ValParamRailtype(totype)) return CMD_ERROR; tron@2934: if (p1 >= MapSize()) return CMD_ERROR; Darkvater@1782: smatz@8096: uint ex = TileX(tile); smatz@8096: uint ey = TileY(tile); smatz@8096: uint sx = TileX(p1); smatz@8096: uint sy = TileY(p1); smatz@8096: belugas@6420: /* make sure sx,sy are smaller than ex,ey */ tron@6106: if (ex < sx) Swap(ex, sx); tron@6106: if (ey < sy) Swap(ey, sy); truelight@201: smatz@8096: _error_message = STR_1005_NO_SUITABLE_RAILROAD_TRACK; // by default, there is no track to convert smatz@8096: smatz@8096: for (uint x = sx; x <= ex; ++x) { smatz@8096: for (uint y = sy; y <= ey; ++y) { tron@3493: TileIndex tile = TileXY(x, y); smatz@8096: TileType tt = GetTileType(tile); smatz@8096: smatz@8096: /* Check if there is any track on tile */ smatz@8096: switch (tt) { smatz@8096: case MP_RAILWAY: smatz@8096: break; smatz@8096: case MP_STATION: smatz@8096: if (!IsRailwayStation(tile)) continue; smatz@8096: break; smatz@8096: case MP_ROAD: smatz@8096: if (!IsLevelCrossing(tile)) continue; smatz@8096: break; smatz@8096: case MP_TUNNELBRIDGE: smatz@8096: if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue; smatz@8096: break; tron@2549: default: continue; tron@2549: } truelight@201: smatz@8096: /* Original railtype we are converting from */ rubidium@7539: RailType type = GetRailType(tile); rubidium@7539: smatz@8096: /* Converting to the same type or converting 'hidden' elrail -> rail */ rubidium@9413: if (type == totype || (_settings_game.vehicle.disable_elrails && totype == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC)) continue; smatz@8096: smatz@8096: /* Trying to convert other's rail */ smatz@8096: if (!CheckTileOwnership(tile)) continue; smatz@8096: rubidium@9815: SmallVector vehicles_affected; rubidium@9815: smatz@8096: /* Vehicle on the tile when not converting Rail <-> ElRail smatz@8096: * Tunnels and bridges have special check later */ smatz@8096: if (tt != MP_TUNNELBRIDGE) { smatz@8096: if (!IsCompatibleRail(type, totype) && !EnsureNoVehicleOnGround(tile)) continue; smatz@8096: if (flags & DC_EXEC) { // we can safely convert, too rubidium@9815: TrackBits reserved = GetReservedTrackbits(tile); rubidium@9815: Track track; rubidium@9815: while ((track = RemoveFirstTrack(&reserved)) != INVALID_TRACK) { rubidium@9815: Vehicle *v = GetTrainForReservation(tile, track); rubidium@9815: if (v != NULL && !HasPowerOnRail(v->u.rail.railtype, totype)) { rubidium@9815: /* No power on new rail type, reroute. */ rubidium@9815: FreeTrainTrackReservation(v); rubidium@9815: *vehicles_affected.Append() = v; rubidium@9815: } rubidium@9815: } rubidium@9815: smatz@8096: SetRailType(tile, totype); smatz@8096: MarkTileDirtyByTile(tile); smatz@8096: /* update power of train engines on this tile */ rubidium@10083: FindVehicleOnPos(tile, NULL, &UpdateTrainPowerProc); smatz@8096: } rubidium@7539: } rubidium@7539: smatz@8096: switch (tt) { smatz@8096: case MP_RAILWAY: smatz@8153: switch (GetRailTileType(tile)) { smatz@8153: case RAIL_TILE_WAYPOINT: smatz@8153: if (flags & DC_EXEC) { smatz@8153: /* notify YAPF about the track layout change */ smatz@9224: YapfNotifyTrackLayoutChange(tile, GetRailWaypointTrack(tile)); smatz@8153: } smatz@8153: cost.AddCost(RailConvertCost(type, totype)); smatz@8153: break; smatz@8153: smatz@8153: case RAIL_TILE_DEPOT: smatz@8153: if (flags & DC_EXEC) { smatz@8153: /* notify YAPF about the track layout change */ smatz@9224: YapfNotifyTrackLayoutChange(tile, GetRailDepotTrack(tile)); smatz@8153: smatz@8153: /* Update build vehicle window related to this depot */ smatz@8153: InvalidateWindowData(WC_VEHICLE_DEPOT, tile); smatz@8153: InvalidateWindowData(WC_BUILD_VEHICLE, tile); smatz@8153: } smatz@8153: cost.AddCost(RailConvertCost(type, totype)); smatz@8153: break; smatz@8153: smatz@8153: default: // RAIL_TILE_NORMAL, RAIL_TILE_SIGNALS smatz@8153: if (flags & DC_EXEC) { smatz@8153: /* notify YAPF about the track layout change */ smatz@8153: TrackBits tracks = GetTrackBits(tile); smatz@8153: while (tracks != TRACK_BIT_NONE) { smatz@8153: YapfNotifyTrackLayoutChange(tile, RemoveFirstTrack(&tracks)); smatz@8153: } smatz@8153: } smatz@8153: cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile))); smatz@8153: break; smatz@8096: } smatz@8096: break; smatz@8096: smatz@8096: case MP_TUNNELBRIDGE: { smatz@8197: TileIndex endtile = GetOtherTunnelBridgeEnd(tile); smatz@8096: smatz@8096: /* If both ends of tunnel/bridge are in the range, do not try to convert twice - smatz@8096: * it would cause assert because of different test and exec runs */ smatz@8096: if (endtile < tile && TileX(endtile) >= sx && TileX(endtile) <= ex && smatz@8096: TileY(endtile) >= sy && TileY(endtile) <= ey) continue; smatz@8096: smatz@8096: /* When not coverting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */ smatz@8096: if (!IsCompatibleRail(GetRailType(tile), totype) && smatz@10165: HasVehicleOnTunnelBridge(tile, endtile)) continue; smatz@8096: smatz@8096: if (flags & DC_EXEC) { rubidium@9815: Track track = DiagDirToDiagTrack(GetTunnelBridgeDirection(tile)); rubidium@9815: if (GetTunnelBridgeReservation(tile)) { rubidium@9815: Vehicle *v = GetTrainForReservation(tile, track); michi_cc@10313: if (v != NULL && !HasPowerOnRail(v->u.rail.railtype, totype)) { michi_cc@10313: /* No power on new rail type, reroute. */ rubidium@9815: FreeTrainTrackReservation(v); rubidium@9815: *vehicles_affected.Append() = v; rubidium@9815: } rubidium@9815: } smatz@8096: SetRailType(tile, totype); smatz@8096: SetRailType(endtile, totype); smatz@8096: rubidium@10083: FindVehicleOnPos(tile, NULL, &UpdateTrainPowerProc); rubidium@10083: FindVehicleOnPos(endtile, NULL, &UpdateTrainPowerProc); smatz@8096: smatz@8096: YapfNotifyTrackLayoutChange(tile, track); smatz@8096: YapfNotifyTrackLayoutChange(endtile, track); smatz@8096: smatz@8096: MarkTileDirtyByTile(tile); smatz@8096: MarkTileDirtyByTile(endtile); smatz@8096: smatz@8096: if (IsBridge(tile)) { smatz@8096: TileIndexDiff delta = TileOffsByDiagDir(GetTunnelBridgeDirection(tile)); smatz@8096: TileIndex t = tile + delta; smatz@8096: for (; t != endtile; t += delta) MarkTileDirtyByTile(t); // TODO encapsulate this into a function smatz@8096: } smatz@8096: } smatz@8096: smatz@8398: cost.AddCost((GetTunnelBridgeLength(tile, endtile) + 2) * RailConvertCost(type, totype)); smatz@8096: } break; smatz@8096: smatz@8096: default: // MP_STATION, MP_ROAD smatz@8096: if (flags & DC_EXEC) { smatz@8598: Track track = ((tt == MP_STATION) ? GetRailStationTrack(tile) : GetCrossingRailTrack(tile)); smatz@8096: YapfNotifyTrackLayoutChange(tile, track); smatz@8096: } smatz@8096: smatz@8096: cost.AddCost(RailConvertCost(type, totype)); smatz@8096: break; truelight@0: } rubidium@9815: rubidium@9815: for (uint i = 0; i < vehicles_affected.Length(); ++i) { rubidium@9815: TryPathReserve(vehicles_affected[i], true); rubidium@9815: } truelight@0: } truelight@0: } Darkvater@1782: smatz@8096: return (cost.GetCost() == 0) ? CMD_ERROR : cost; truelight@0: } truelight@0: rubidium@6943: static CommandCost RemoveTrainDepot(TileIndex tile, uint32 flags) truelight@0: { rubidium@10207: if (!CheckTileOwnership(tile) && _current_company != OWNER_WATER) truelight@0: return CMD_ERROR; truelight@0: belugas@6406: if (!EnsureNoVehicleOnGround(tile)) truelight@0: return CMD_ERROR; truelight@0: truelight@0: if (flags & DC_EXEC) { smatz@8300: /* read variables before the depot is removed */ tron@3191: DiagDirection dir = GetRailDepotDirection(tile); smatz@8300: Owner owner = GetTileOwner(tile); rubidium@9815: Vehicle *v = NULL; rubidium@9815: rubidium@9815: if (GetDepotWaypointReservation(tile)) { rubidium@9815: v = GetTrainForReservation(tile, DiagDirToDiagTrack(dir)); rubidium@9815: if (v != NULL) FreeTrainTrackReservation(v); rubidium@9815: } truelight@0: rubidium@7389: DoClearSquare(tile); rubidium@7389: delete GetDepotByTile(tile); smatz@8306: AddSideToSignalBuffer(tile, dir, owner); smatz@9224: YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir)); rubidium@9815: if (v != NULL) TryPathReserve(v, true); truelight@0: } truelight@0: rubidium@8230: return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_train_depot); truelight@0: } truelight@0: rubidium@6943: static CommandCost ClearTile_Track(TileIndex tile, byte flags) tron@1534: { rubidium@8230: CommandCost cost(EXPENSES_CONSTRUCTION); rubidium@6943: CommandCost ret; truelight@0: truelight@0: if (flags & DC_AUTO) { rubidium@10207: if (!IsTileOwner(tile, _current_company)) 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@8414: Slope tileh = GetTileSlope(tile, NULL); frosch@8414: /* Is there flat water on the lower halftile, that gets cleared expensively? */ frosch@8414: bool water_ground = (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh)); rubidium@7771: rubidium@5414: TrackBits tracks = GetTrackBits(tile); rubidium@5587: while (tracks != TRACK_BIT_NONE) { KUDr@5598: Track track = RemoveFirstTrack(&tracks); rubidium@5587: ret = DoCommand(tile, 0, track, flags, CMD_REMOVE_SINGLE_RAIL); rubidium@5411: if (CmdFailed(ret)) return CMD_ERROR; rubidium@6950: cost.AddCost(ret); tron@1534: } rubidium@7771: smatz@9504: /* when bankrupting, don't make water dirty, there could be a ship on lower halftile */ smatz@9504: if (water_ground && !(flags & DC_BANKRUPT)) { rubidium@9503: if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR; rubidium@9503: rubidium@7771: /* The track was removed, and left a coast tile. Now also clear the water. */ rubidium@7771: if (flags & DC_EXEC) DoClearSquare(tile); rubidium@7771: cost.AddCost(_price.clear_water); rubidium@7771: } rubidium@7771: tron@1534: return cost; tron@1534: } tron@1534: rubidium@6172: case RAIL_TILE_DEPOT: rubidium@6172: return RemoveTrainDepot(tile, flags); rubidium@6172: rubidium@6172: case RAIL_TILE_WAYPOINT: rubidium@6172: return RemoveTrainWaypoint(tile, flags, false); tron@1534: tron@1534: default: tron@1534: return CMD_ERROR; tron@1534: } truelight@0: } truelight@0: rubidium@7764: /** rubidium@7764: * Get surface height in point (x,y) smatz@10017: * On tiles with halftile foundations move (x,y) to a safe point wrt. track rubidium@7764: */ rubidium@7764: static uint GetSaveSlopeZ(uint x, uint y, Track track) rubidium@7764: { rubidium@7764: switch (track) { rubidium@7764: case TRACK_UPPER: x &= ~0xF; y &= ~0xF; break; rubidium@7764: case TRACK_LOWER: x |= 0xF; y |= 0xF; break; rubidium@7764: case TRACK_LEFT: x |= 0xF; y &= ~0xF; break; rubidium@7764: case TRACK_RIGHT: x &= ~0xF; y |= 0xF; break; rubidium@7764: default: break; rubidium@7764: } rubidium@7764: return GetSlopeZ(x, y); rubidium@7764: } rubidium@7764: glx@6770: static void DrawSingleSignal(TileIndex tile, Track track, byte condition, uint image, uint pos) truelight@0: { rubidium@9413: bool side = (_settings_game.vehicle.road_side != 0) && _settings_game.construction.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@8547: SignalType type = GetSignalType(tile, track); peter1138@8547: SignalVariant variant = GetSignalVariant(tile, track); peter1138@8547: peter1138@8547: if (type == SIGTYPE_NORMAL && variant == SIG_ELECTRIC) { peter1138@8547: /* Normal electric signals are picked from original sprites. */ peter1138@8547: sprite = SPR_ORIGINAL_SIGNALS_BASE + image + condition; peter1138@3638: } else { peter1138@8547: /* All other signals are picked from add on sprites. */ rubidium@9790: sprite = SPR_SIGNALS_BASE + (type - 1) * 16 + variant * 64 + image + condition + (type > SIGTYPE_LAST_NOPBS ? 64 : 0); peter1138@3638: } celestar@3575: rubidium@7764: 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@7307: SpriteID image = SPR_TRACK_FENCE_FLAT_X; truelight@7307: if (ti->tileh != SLOPE_FLAT) image = (ti->tileh & SLOPE_S) ? SPR_TRACK_FENCE_SLOPE_SW : SPR_TRACK_FENCE_SLOPE_NE; peter1138@5668: 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@7307: SpriteID image = SPR_TRACK_FENCE_FLAT_X; truelight@7307: if (ti->tileh != SLOPE_FLAT) image = (ti->tileh & SLOPE_S) ? SPR_TRACK_FENCE_SLOPE_SW : SPR_TRACK_FENCE_SLOPE_NE; peter1138@5668: 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@7307: SpriteID image = SPR_TRACK_FENCE_FLAT_Y; truelight@7307: if (ti->tileh != SLOPE_FLAT) image = (ti->tileh & SLOPE_S) ? SPR_TRACK_FENCE_SLOPE_SE : SPR_TRACK_FENCE_SLOPE_NW; peter1138@5668: 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@7307: SpriteID image = SPR_TRACK_FENCE_FLAT_Y; truelight@7307: if (ti->tileh != SLOPE_FLAT) image = (ti->tileh & SLOPE_S) ? SPR_TRACK_FENCE_SLOPE_SE : SPR_TRACK_FENCE_SLOPE_NW; peter1138@5668: 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@7307: /** truelight@7307: * Draw fence at eastern side of track. truelight@7307: */ Darkvater@2436: static void DrawTrackFence_NS_1(const TileInfo *ti) truelight@0: { frosch@8413: uint z = ti->z + GetSlopeZInCorner(RemoveHalftileSlope(ti->tileh), CORNER_W); truelight@7307: 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@7307: /** truelight@7307: * Draw fence at western side of track. truelight@7307: */ Darkvater@2436: static void DrawTrackFence_NS_2(const TileInfo *ti) truelight@0: { frosch@8413: uint z = ti->z + GetSlopeZInCorner(RemoveHalftileSlope(ti->tileh), CORNER_E); truelight@7307: 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@7307: /** truelight@7307: * Draw fence at southern side of track. truelight@7307: */ Darkvater@2436: static void DrawTrackFence_WE_1(const TileInfo *ti) truelight@0: { frosch@8413: uint z = ti->z + GetSlopeZInCorner(RemoveHalftileSlope(ti->tileh), CORNER_N); truelight@7307: 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@7307: /** truelight@7307: * Draw fence at northern side of track. truelight@7307: */ Darkvater@2436: static void DrawTrackFence_WE_2(const TileInfo *ti) truelight@0: { frosch@8413: uint z = ti->z + GetSlopeZInCorner(RemoveHalftileSlope(ti->tileh), CORNER_S); truelight@7307: 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@8414: case RAIL_GROUND_WATER: { frosch@8414: Corner track_corner; frosch@8414: if (IsHalftileSlope(ti->tileh)) { frosch@8414: /* Steep slope or one-corner-raised slope with halftile foundation */ frosch@8414: track_corner = GetHalftileSlopeCorner(ti->tileh); frosch@8414: } else { frosch@8414: /* Three-corner-raised slope */ frosch@8414: track_corner = OppositeCorner(GetHighestSlopeCorner(ComplementSlope(ti->tileh))); frosch@8414: } frosch@8414: switch (track_corner) { rubidium@7771: case CORNER_W: DrawTrackFence_NS_1(ti); break; rubidium@7771: case CORNER_S: DrawTrackFence_WE_2(ti); break; rubidium@7771: case CORNER_E: DrawTrackFence_NS_2(ti); break; rubidium@7771: case CORNER_N: DrawTrackFence_WE_1(ti); break; rubidium@7771: default: NOT_REACHED(); rubidium@7771: } rubidium@7771: break; frosch@8414: } 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@8414: /* SubSprite for drawing the track halftile of 'three-corners-raised'-sloped rail sprites. */ frosch@8414: static const int INF = 1000; // big number compared to tilesprite size frosch@8414: static const SubSprite _halftile_sub_sprite[4] = { frosch@8414: { -INF , -INF , 32 - 33, INF }, // CORNER_W, clip 33 pixels from right frosch@8414: { -INF , 0 + 7, INF , INF }, // CORNER_S, clip 7 pixels from top frosch@8414: { -31 + 33, -INF , INF , INF }, // CORNER_E, clip 33 pixels from left frosch@8414: { -INF , -INF , INF , 30 - 23 } // CORNER_N, clip 23 pixels from bottom frosch@8414: }; frosch@8414: peter1138@2472: const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile)); rubidium@7770: RailGroundType rgt = GetRailGroundType(ti->tile); rubidium@7770: Foundation f = GetRailFoundation(ti->tileh, track); rubidium@7770: Corner halftile_corner = CORNER_INVALID; rubidium@7770: rubidium@7770: if (IsNonContinuousFoundation(f)) { rubidium@7770: /* Save halftile corner */ rubidium@7770: halftile_corner = (f == FOUNDATION_STEEP_BOTH ? GetHighestSlopeCorner(ti->tileh) : GetHalftileFoundationCorner(f)); rubidium@7770: /* Draw lower part first */ rubidium@7770: track &= ~CornerToTrackBits(halftile_corner); rubidium@7770: f = (f == FOUNDATION_STEEP_BOTH ? FOUNDATION_STEEP_LOWER : FOUNDATION_NONE); rubidium@7770: } rubidium@7770: rubidium@7770: DrawFoundation(ti, f); rubidium@7770: /* DrawFoundation modifies ti */ rubidium@7770: peter1138@5668: SpriteID image; peter1138@5668: SpriteID pal = PAL_NONE; frosch@8414: const SubSprite *sub = NULL; peter1138@2472: bool junction = false; peter1138@2472: belugas@6420: /* Select the sprite to use. */ rubidium@7770: if (track == 0) { rubidium@7770: /* Clear ground (only track on halftile foundation) */ rubidium@7771: if (rgt == RAIL_GROUND_WATER) { frosch@8414: if (IsSteepSlope(ti->tileh)) { frosch@8414: DrawShoreTile(ti->tileh); frosch@8414: image = 0; frosch@8414: } else { frosch@8414: image = SPR_FLAT_WATER_TILE; frosch@8414: } rubidium@7771: } else { rubidium@7771: switch (rgt) { rubidium@7771: case RAIL_GROUND_BARREN: image = SPR_FLAT_BARE_LAND; break; rubidium@7771: case RAIL_GROUND_ICE_DESERT: image = SPR_FLAT_SNOWY_TILE; break; rubidium@7771: default: image = SPR_FLAT_GRASS_TILE; break; rubidium@7771: } rubidium@7771: image += _tileh_to_sprite[ti->tileh]; rubidium@7770: } rubidium@7770: } else { rubidium@7770: if (ti->tileh != SLOPE_FLAT) { rubidium@7770: /* track on non-flat ground */ rubidium@7770: image = _track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.track_y; rubidium@7770: } else { rubidium@7770: /* track on flat ground */ rubidium@7770: (image = rti->base_sprites.track_y, track == TRACK_BIT_Y) || rubidium@7770: (image++, track == TRACK_BIT_X) || rubidium@7770: (image++, track == TRACK_BIT_UPPER) || rubidium@7770: (image++, track == TRACK_BIT_LOWER) || rubidium@7770: (image++, track == TRACK_BIT_RIGHT) || rubidium@7770: (image++, track == TRACK_BIT_LEFT) || rubidium@7770: (image++, track == TRACK_BIT_CROSS) || peter1138@2472: rubidium@7770: (image = rti->base_sprites.track_ns, track == TRACK_BIT_HORZ) || rubidium@7770: (image++, track == TRACK_BIT_VERT) || peter1138@2472: rubidium@7770: (junction = true, false) || rubidium@7770: (image = rti->base_sprites.ground, (track & TRACK_BIT_3WAY_NE) == 0) || rubidium@7770: (image++, (track & TRACK_BIT_3WAY_SW) == 0) || rubidium@7770: (image++, (track & TRACK_BIT_3WAY_NW) == 0) || rubidium@7770: (image++, (track & TRACK_BIT_3WAY_SE) == 0) || rubidium@7770: (image++, true); rubidium@7770: } peter1138@2472: rubidium@7770: switch (rgt) { rubidium@7770: case RAIL_GROUND_BARREN: pal = PALETTE_TO_BARE_LAND; break; rubidium@7770: case RAIL_GROUND_ICE_DESERT: image += rti->snow_offset; break; frosch@8414: case RAIL_GROUND_WATER: { frosch@8414: /* three-corner-raised slope */ frosch@8414: DrawShoreTile(ti->tileh); frosch@8414: Corner track_corner = OppositeCorner(GetHighestSlopeCorner(ComplementSlope(ti->tileh))); frosch@8414: sub = &(_halftile_sub_sprite[track_corner]); frosch@8414: break; frosch@8414: } rubidium@7770: default: break; rubidium@7770: } peter1138@2472: } peter1138@2472: frosch@8414: if (image != 0) DrawGroundSprite(image, pal, sub); peter1138@2472: belugas@6420: /* Draw track pieces individually for junction tiles */ peter1138@2472: if (junction) { peter1138@5668: if (track & TRACK_BIT_X) DrawGroundSprite(rti->base_sprites.single_y, PAL_NONE); peter1138@5668: if (track & TRACK_BIT_Y) DrawGroundSprite(rti->base_sprites.single_x, PAL_NONE); peter1138@5668: if (track & TRACK_BIT_UPPER) DrawGroundSprite(rti->base_sprites.single_n, PAL_NONE); peter1138@5668: if (track & TRACK_BIT_LOWER) DrawGroundSprite(rti->base_sprites.single_s, PAL_NONE); peter1138@5668: if (track & TRACK_BIT_LEFT) DrawGroundSprite(rti->base_sprites.single_w, PAL_NONE); peter1138@5668: if (track & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PAL_NONE); peter1138@2472: } rubidium@7770: rubidium@9785: /* PBS debugging, draw reserved tracks darker */ smatz@10109: if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation) { rubidium@9785: TrackBits pbs = GetTrackReservation(ti->tile); rubidium@9785: if (pbs & TRACK_BIT_X) { rubidium@9785: if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) { rubidium@9785: DrawGroundSprite(rti->base_sprites.single_y, PALETTE_CRASH); rubidium@9785: } else { rubidium@9785: DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH); rubidium@9785: } rubidium@9785: } rubidium@9785: if (pbs & TRACK_BIT_Y) { rubidium@9785: if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) { rubidium@9785: DrawGroundSprite(rti->base_sprites.single_x, PALETTE_CRASH); rubidium@9785: } else { rubidium@9785: DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH); rubidium@9785: } rubidium@9785: } rubidium@9785: if (pbs & TRACK_BIT_UPPER) AddSortableSpriteToDraw(rti->base_sprites.single_n, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + (ti->tileh & SLOPE_N ? 8 : 0)); rubidium@9785: if (pbs & TRACK_BIT_LOWER) AddSortableSpriteToDraw(rti->base_sprites.single_s, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + (ti->tileh & SLOPE_S ? 8 : 0)); rubidium@9785: if (pbs & TRACK_BIT_LEFT) AddSortableSpriteToDraw(rti->base_sprites.single_w, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + (ti->tileh & SLOPE_W ? 8 : 0)); rubidium@9785: if (pbs & TRACK_BIT_RIGHT) AddSortableSpriteToDraw(rti->base_sprites.single_e, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + (ti->tileh & SLOPE_E ? 8 : 0)); rubidium@9785: } rubidium@9785: rubidium@7770: if (IsValidCorner(halftile_corner)) { rubidium@7770: DrawFoundation(ti, HalftileFoundation(halftile_corner)); rubidium@7770: rubidium@7770: /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */ rubidium@7770: Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner)); rubidium@7770: image = _track_sloped_sprites[fake_slope - 1] + rti->base_sprites.track_y; rubidium@7770: pal = PAL_NONE; rubidium@7770: switch (rgt) { rubidium@7770: case RAIL_GROUND_BARREN: pal = PALETTE_TO_BARE_LAND; break; smatz@8523: case RAIL_GROUND_ICE_DESERT: smatz@8523: case RAIL_GROUND_HALF_SNOW: image += rti->snow_offset; break; // higher part has snow in this case too rubidium@7770: default: break; rubidium@7770: } rubidium@7770: DrawGroundSprite(image, pal, &(_halftile_sub_sprite[halftile_corner])); rubidium@9785: smatz@10109: if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && IsSteepSlope(ti->tileh) && HasReservedTracks(ti->tile, CornerToTrackBits(halftile_corner))) { rubidium@9785: static const byte _corner_to_track_sprite[] = {3, 1, 2, 0}; rubidium@9785: AddSortableSpriteToDraw(_corner_to_track_sprite[halftile_corner] + rti->base_sprites.single_n, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + 16); rubidium@9785: } rubidium@7770: } peter1138@2472: } peter1138@2472: belugas@8674: /** Enums holding the offsets from base signal sprite, belugas@8674: * according to the side it is representing. belugas@8674: * The addtion of 2 per enum is necessary in order to "jump" over the belugas@8674: * green state sprite, all signal sprites being in pair, belugas@8674: * starting with the off-red state */ belugas@8674: enum { belugas@8676: SIGNAL_TO_SOUTHWEST = 0, belugas@8676: SIGNAL_TO_NORTHEAST = 2, belugas@8676: SIGNAL_TO_SOUTHEAST = 4, belugas@8676: SIGNAL_TO_NORTHWEST = 6, belugas@8676: SIGNAL_TO_EAST = 8, belugas@8676: SIGNAL_TO_WEST = 10, belugas@8676: SIGNAL_TO_SOUTH = 12, belugas@8676: SIGNAL_TO_NORTH = 14, belugas@8674: }; belugas@8674: celestar@3519: static void DrawSignals(TileIndex tile, TrackBits rails) celestar@3519: { belugas@8674: #define MAYBE_DRAW_SIGNAL(x,y,z,t) if (IsSignalPresent(tile, x)) DrawSingleSignal(tile, t, GetSingleSignalState(tile, x), y, z) celestar@3519: celestar@3519: if (!(rails & TRACK_BIT_Y)) { celestar@3519: if (!(rails & TRACK_BIT_X)) { celestar@3519: if (rails & TRACK_BIT_LEFT) { belugas@8676: MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTH, 0, TRACK_LEFT); belugas@8676: MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTH, 1, TRACK_LEFT); celestar@3519: } celestar@3519: if (rails & TRACK_BIT_RIGHT) { belugas@8676: MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_NORTH, 2, TRACK_RIGHT); belugas@8676: MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_SOUTH, 3, TRACK_RIGHT); celestar@3519: } celestar@3519: if (rails & TRACK_BIT_UPPER) { belugas@8676: MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_WEST, 4, TRACK_UPPER); belugas@8676: MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_EAST, 5, TRACK_UPPER); celestar@3519: } celestar@3519: if (rails & TRACK_BIT_LOWER) { belugas@8676: MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_WEST, 6, TRACK_LOWER); belugas@8676: MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_EAST, 7, TRACK_LOWER); celestar@3519: } celestar@3519: } else { belugas@8676: MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHWEST, 8, TRACK_X); belugas@8676: MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHEAST, 9, TRACK_X); celestar@3519: } celestar@3519: } else { belugas@8676: MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHEAST, 10, TRACK_Y); belugas@8676: MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHWEST, 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@5668: SpriteID image; truelight@0: rubidium@10207: _drawtile_track_palette = COMPANY_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@7928: if (HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti); truelight@0: smatz@9154: if (HasCatenaryDrawn(GetRailType(ti->tile))) DrawCatenary(ti); rubidium@7043: tron@4182: if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails); truelight@0: } else { belugas@6420: /* draw depot/waypoint */ tron@4225: const DrawTileSprites* dts; tron@4225: const DrawTileSeqStruct* dtss; tron@4225: uint32 relocation; frosch@10355: SpriteID pal = PAL_NONE; truelight@0: rubidium@7335: if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED); truelight@0: rubidium@6172: if (IsRailDepot(ti->tile)) { smatz@8806: if (IsInvisibilitySet(TO_BUILDINGS)) { smatz@8806: /* Draw rail instead of depot */ smatz@8806: dts = &_depot_invisible_gfx_table[GetRailDepotDirection(ti->tile)]; smatz@8806: } else { smatz@8806: dts = &_depot_gfx_table[GetRailDepotDirection(ti->tile)]; smatz@8806: } tron@4225: tron@4225: relocation = rti->total_offset; tron@4225: frosch@8571: image = dts->ground.sprite; tron@4225: if (image != SPR_FLAT_GRASS_TILE) image += rti->total_offset; tron@4225: belugas@6420: /* adjust ground tile for desert belugas@6420: * don't adjust for snow, because snow in depots looks weird */ rubidium@9413: if (IsSnowRailGround(ti->tile) && _settings_game.game_creation.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@6420: /* 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@6420: /* emulate station tile - open with building */ tron@4225: const Station* st = ComposeWaypointStation(ti->tile); tron@4225: uint gfx = 2; peter1138@3757: skidd13@7928: 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@7272: 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: frosch@8571: image = dts->ground.sprite; skidd13@7928: 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: } frosch@10355: frosch@10355: pal = dts->ground.pal; tron@4225: } else { tron@4225: goto default_waypoint; celestar@389: } tron@2549: } else { tron@4225: default_waypoint: belugas@6420: /* There is no custom layout, fall back to the default graphics */ tron@4225: dts = &_waypoint_gfx_table[GetWaypointAxis(ti->tile)]; tron@4225: relocation = 0; frosch@8571: image = dts->ground.sprite + rti->total_offset; tron@4225: if (IsSnowRailGround(ti->tile)) image += rti->snow_offset; tron@2549: } truelight@0: } truelight@0: frosch@10355: DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, _drawtile_track_palette)); truelight@0: rubidium@9785: /* PBS debugging, draw reserved tracks darker */ smatz@10159: if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && GetDepotWaypointReservation(ti->tile) && smatz@10159: (!IsRailDepot(ti->tile) || GetRailDepotDirection(ti->tile) == DIAGDIR_SW || GetRailDepotDirection(ti->tile) == DIAGDIR_SE)) { rubidium@9785: DrawGroundSprite(GetWaypointAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_y : rti->base_sprites.single_x, PALETTE_CRASH); rubidium@9785: } rubidium@9785: smatz@9154: if (HasCatenaryDrawn(GetRailType(ti->tile))) DrawCatenary(ti); celestar@3355: tron@4225: foreach_draw_tile_seq(dtss, dts->seq) { frosch@8570: SpriteID image = dtss->image.sprite; frosch@10355: SpriteID pal = dtss->image.pal; tron@4225: smatz@9549: /* Stop drawing sprite sequence once we meet a sprite that doesn't have to be opaque */ smatz@9549: if (IsInvisibilitySet(TO_BUILDINGS) && !HasBit(image, SPRITE_MODIFIER_OPAQUE)) return; smatz@9549: peter1138@5738: /* Unlike stations, our default waypoint has no variation for peter1138@5738: * different railtype, so don't use the railtype offset if peter1138@5738: * no relocation is set */ skidd13@7928: if (HasBit(image, SPRITE_MODIFIER_USE_OFFSET)) { peter1138@5738: image += rti->total_offset; peter1138@5738: } else { peter1138@5738: image += relocation; peter1138@5738: } peter1138@5738: frosch@10355: pal = SpriteLayoutPaletteTransform(image, pal, _drawtile_track_palette); peter1138@5668: peter1138@5738: if ((byte)dtss->delta_z != 0x80) { peter1138@5738: AddSortableSpriteToDraw( peter1138@5738: image, pal, peter1138@5738: ti->x + dtss->delta_x, ti->y + dtss->delta_y, peter1138@5738: dtss->size_x, dtss->size_y, rubidium@7333: dtss->size_z, ti->z + dtss->delta_z, peter1138@8158: !HasBit(image, SPRITE_MODIFIER_OPAQUE) && IsTransparencySet(TO_BUILDINGS) peter1138@5738: ); peter1138@5738: } else { frosch@10257: /* For stations and original spritelayouts delta_x and delta_y are signed */ frosch@10257: AddChildSpriteScreen(image, pal, dtss->delta_x, dtss->delta_y, !HasBit(image, SPRITE_MODIFIER_OPAQUE) && IsTransparencySet(TO_BUILDINGS)); peter1138@5738: } truelight@0: } truelight@0: } celestar@5385: DrawBridgeMiddle(ti); truelight@0: } truelight@0: truelight@0: peter1138@5668: static void DrawTileSequence(int x, int y, SpriteID ground, const DrawTileSeqStruct* dtss, uint32 offset) tron@4225: { rubidium@10207: SpriteID palette = COMPANY_SPRITE_COLOR(_local_company); truelight@0: peter1138@5668: DrawSprite(ground, PAL_NONE, x, y); frosch@8570: for (; dtss->image.sprite != 0; dtss++) { tron@4225: Point pt = RemapCoords(dtss->delta_x, dtss->delta_y, dtss->delta_z); frosch@8570: SpriteID image = dtss->image.sprite + offset; truelight@0: skidd13@7928: 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]; frosch@8571: 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: frosch@8571: 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@7335: 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@7335: static Foundation GetFoundation_Track(TileIndex tile, Slope tileh) dominik@39: { rubidium@7335: 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@7771: if (old_ground == RAIL_GROUND_WATER) { rubidium@7771: TileLoop_Water(tile); rubidium@7771: return; rubidium@7771: } rubidium@7771: rubidium@9413: switch (_settings_game.game_creation.landscape) { smatz@8523: case LT_ARCTIC: { smatz@8523: uint z; smatz@8523: Slope slope = GetTileSlope(tile, &z); smatz@8523: bool half = false; smatz@8523: smatz@8523: /* for non-flat track, use lower part of track smatz@8523: * in other cases, use the highest part with track */ smatz@8523: if (IsPlainRailTile(tile)) { smatz@8523: TrackBits track = GetTrackBits(tile); smatz@8523: Foundation f = GetRailFoundation(slope, track); smatz@8523: smatz@8523: switch (f) { smatz@8523: case FOUNDATION_NONE: smatz@8523: /* no foundation - is the track on the upper side of three corners raised tile? */ smatz@8523: if (IsSlopeWithThreeCornersRaised(slope)) z += TILE_HEIGHT; smatz@8523: break; smatz@8523: smatz@8523: case FOUNDATION_INCLINED_X: smatz@8523: case FOUNDATION_INCLINED_Y: smatz@8523: /* sloped track - is it on a steep slope? */ smatz@8523: if (IsSteepSlope(slope)) z += TILE_HEIGHT; smatz@8523: break; smatz@8523: smatz@8523: case FOUNDATION_STEEP_LOWER: smatz@8523: /* only lower part of steep slope */ smatz@8523: z += TILE_HEIGHT; smatz@8523: break; smatz@8523: smatz@8523: default: smatz@8523: /* if it is a steep slope, then there is a track on higher part */ smatz@8523: if (IsSteepSlope(slope)) z += TILE_HEIGHT; smatz@8523: z += TILE_HEIGHT; smatz@8523: break; smatz@8523: } smatz@8523: smatz@8523: half = IsInsideMM(f, FOUNDATION_STEEP_BOTH, FOUNDATION_HALFTILE_N + 1); smatz@8523: } else { smatz@8523: /* is the depot on a non-flat tile? */ smatz@8523: if (slope != SLOPE_FLAT) z += TILE_HEIGHT; smatz@8523: } smatz@8523: smatz@8523: /* 'z' is now the lowest part of the highest track bit - smatz@8523: * for sloped track, it is 'z' of lower part smatz@8523: * for two track bits, it is 'z' of higher track bit smatz@8523: * For non-continuous foundations (and STEEP_BOTH), 'half' is set */ smatz@8523: if (z > GetSnowLine()) { smatz@8523: if (half && z - GetSnowLine() == TILE_HEIGHT) { smatz@8523: /* track on non-continuous foundation, lower part is not under snow */ smatz@8523: new_ground = RAIL_GROUND_HALF_SNOW; smatz@8523: } else { smatz@8523: new_ground = RAIL_GROUND_ICE_DESERT; smatz@8523: } tron@4482: goto set_ground; Darkvater@3562: } tron@2631: break; smatz@8523: } truelight@0: belugas@6357: 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@6420: 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: { rubidium@10207: Owner 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: frosch@8616: static TrackStatus GetTileTrackStatus_Track(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side) tron@1977: { rubidium@9503: /* Case of half tile slope with water. */ rubidium@9503: if (mode == TRANSPORT_WATER && IsPlainRailTile(tile) && GetRailGroundType(tile) == RAIL_GROUND_WATER) { rubidium@9503: TrackBits tb = GetTrackBits(tile); rubidium@9503: switch (tb) { rubidium@9503: default: NOT_REACHED(); rubidium@9503: case TRACK_BIT_UPPER: tb = TRACK_BIT_LOWER; break; rubidium@9503: case TRACK_BIT_LOWER: tb = TRACK_BIT_UPPER; break; rubidium@9503: case TRACK_BIT_LEFT: tb = TRACK_BIT_RIGHT; break; rubidium@9503: case TRACK_BIT_RIGHT: tb = TRACK_BIT_LEFT; break; rubidium@9503: } rubidium@9503: return CombineTrackStatus(TrackBitsToTrackdirBits(tb), TRACKDIR_BIT_NONE); rubidium@9503: } rubidium@9503: tron@2639: if (mode != TRANSPORT_RAIL) return 0; truelight@0: frosch@8616: TrackBits trackbits = TRACK_BIT_NONE; frosch@8616: TrackdirBits red_signals = TRACKDIR_BIT_NONE; frosch@8616: rubidium@6187: switch (GetRailTileType(tile)) { rubidium@6187: default: NOT_REACHED(); smatz@8596: case RAIL_TILE_NORMAL: frosch@8616: trackbits = GetTrackBits(tile); frosch@8616: break; tron@3267: rubidium@6187: case RAIL_TILE_SIGNALS: { frosch@8616: trackbits = GetTrackBits(tile); rubidium@6753: byte a = GetPresentSignals(tile); rubidium@6753: uint b = GetSignalStates(tile); truelight@0: truelight@0: b &= a; truelight@0: rubidium@9791: /* When signals are not present (in neither direction), rubidium@9791: * we pretend them to be green. Otherwise, it depends on rubidium@9791: * the signal type. For signals that are only active from rubidium@9791: * one side, we set the missing signals explicitely to rubidium@9791: * `green'. Otherwise, they implicitely become `red'. */ rubidium@9791: if (!IsOnewaySignal(tile, TRACK_UPPER) || (a & SignalOnTrack(TRACK_UPPER)) == 0) b |= ~a & SignalOnTrack(TRACK_UPPER); rubidium@9791: if (!IsOnewaySignal(tile, TRACK_LOWER) || (a & SignalOnTrack(TRACK_LOWER)) == 0) b |= ~a & SignalOnTrack(TRACK_LOWER); truelight@0: frosch@8616: if ((b & 0x8) == 0) red_signals |= (TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_X_NE | TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_UPPER_E); frosch@8616: if ((b & 0x4) == 0) red_signals |= (TRACKDIR_BIT_LEFT_S | TRACKDIR_BIT_X_SW | TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_UPPER_W); frosch@8616: if ((b & 0x2) == 0) red_signals |= (TRACKDIR_BIT_RIGHT_N | TRACKDIR_BIT_LOWER_E); frosch@8616: if ((b & 0x1) == 0) red_signals |= (TRACKDIR_BIT_RIGHT_S | TRACKDIR_BIT_LOWER_W); rubidium@6187: frosch@8616: break; truelight@0: } rubidium@6187: smatz@8596: case RAIL_TILE_DEPOT: { smatz@8596: DiagDirection dir = GetRailDepotDirection(tile); smatz@8596: frosch@8616: if (side != INVALID_DIAGDIR && side != dir) break; smatz@8596: smatz@9224: trackbits = DiagDirToDiagTrackBits(dir); frosch@8616: break; smatz@8596: } smatz@8596: smatz@8596: case RAIL_TILE_WAYPOINT: frosch@8616: trackbits = GetRailWaypointBits(tile); frosch@8616: break; tron@3267: } frosch@8616: frosch@8616: return CombineTrackStatus(TrackBitsToTrackdirBits(trackbits), red_signals); truelight@0: } truelight@0: tron@1977: static void ClickTile_Track(TileIndex tile) truelight@0: { rubidium@6187: switch (GetRailTileType(tile)) { belugas@9949: case RAIL_TILE_DEPOT: ShowDepotWindow(tile, VEH_TRAIN); break; belugas@9949: case RAIL_TILE_WAYPOINT: ShowWaypointWindow(GetWaypointByTile(tile)); break; rubidium@6187: default: break; tron@2548: } truelight@0: } truelight@0: tron@1590: static void GetTileDesc_Track(TileIndex tile, TileDesc *td) truelight@0: { frosch@9322: td->owner[0] = 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: { rubidium@9790: const StringID signal_type[6][6] = { glx@6770: { glx@6770: STR_RAILROAD_TRACK_WITH_NORMAL_SIGNALS, glx@6770: STR_RAILROAD_TRACK_WITH_NORMAL_PRESIGNALS, glx@6770: STR_RAILROAD_TRACK_WITH_NORMAL_EXITSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_NORMAL_COMBOSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_NORMAL_PBSSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_NORMAL_NOENTRYSIGNALS glx@6770: }, glx@6770: { glx@6770: STR_RAILROAD_TRACK_WITH_NORMAL_PRESIGNALS, glx@6770: STR_RAILROAD_TRACK_WITH_PRESIGNALS, glx@6770: STR_RAILROAD_TRACK_WITH_PRE_EXITSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_PRE_COMBOSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_PRE_PBSSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_PRE_NOENTRYSIGNALS glx@6770: }, glx@6770: { glx@6770: STR_RAILROAD_TRACK_WITH_NORMAL_EXITSIGNALS, glx@6770: STR_RAILROAD_TRACK_WITH_PRE_EXITSIGNALS, glx@6770: STR_RAILROAD_TRACK_WITH_EXITSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_EXIT_COMBOSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_EXIT_PBSSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_EXIT_NOENTRYSIGNALS glx@6770: }, glx@6770: { glx@6770: STR_RAILROAD_TRACK_WITH_NORMAL_COMBOSIGNALS, glx@6770: STR_RAILROAD_TRACK_WITH_PRE_COMBOSIGNALS, glx@6770: STR_RAILROAD_TRACK_WITH_EXIT_COMBOSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_COMBOSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_COMBO_PBSSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_COMBO_NOENTRYSIGNALS rubidium@9790: }, rubidium@9790: { rubidium@9790: STR_RAILROAD_TRACK_WITH_NORMAL_PBSSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_PRE_PBSSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_EXIT_PBSSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_COMBO_PBSSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_PBSSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_PBS_NOENTRYSIGNALS rubidium@9790: }, rubidium@9790: { rubidium@9790: STR_RAILROAD_TRACK_WITH_NORMAL_NOENTRYSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_PRE_NOENTRYSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_EXIT_NOENTRYSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_COMBO_NOENTRYSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_PBS_NOENTRYSIGNALS, rubidium@9790: STR_RAILROAD_TRACK_WITH_NOENTRYSIGNALS glx@6770: } tron@1590: }; tron@1590: rubidium@10213: SignalType primary_signal; rubidium@10213: SignalType secondary_signal; rubidium@10212: if (HasSignalOnTrack(tile, TRACK_UPPER)) { rubidium@10212: primary_signal = GetSignalType(tile, TRACK_UPPER); rubidium@10212: secondary_signal = HasSignalOnTrack(tile, TRACK_LOWER) ? GetSignalType(tile, TRACK_LOWER) : primary_signal; rubidium@10212: } else { rubidium@10212: secondary_signal = primary_signal = GetSignalType(tile, TRACK_LOWER); rubidium@10212: } rubidium@10212: rubidium@10212: td->str = signal_type[secondary_signal][primary_signal]; tron@1590: break; truelight@0: } tron@1590: rubidium@6172: case RAIL_TILE_DEPOT: rubidium@6172: td->str = STR_1023_RAILROAD_TRAIN_DEPOT; rubidium@6172: break; rubidium@6172: rubidium@6172: case RAIL_TILE_WAYPOINT: tron@1590: default: rubidium@6172: td->str = STR_LANDINFO_WAYPOINT; tron@1590: break; truelight@0: } truelight@0: } truelight@0: rubidium@10207: static void ChangeTileOwner_Track(TileIndex tile, Owner old_owner, Owner new_owner) truelight@0: { rubidium@10207: if (!IsTileOwner(tile, old_owner)) return; rubidium@10207: rubidium@10207: if (new_owner != INVALID_OWNER) { rubidium@10207: SetTileOwner(tile, new_owner); rubidium@4434: } else { smatz@8519: 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@5587: 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: smatz@8712: smatz@8712: /** Compute number of ticks when next wagon will leave a depot. smatz@8712: * Negative means next wagon should have left depot n ticks before. smatz@8712: * @param v vehicle outside (leaving) the depot smatz@8712: * @return number of ticks when the next wagon will leave smatz@8712: */ smatz@8712: int TicksToLeaveDepot(const Vehicle *v) smatz@8712: { smatz@8712: DiagDirection dir = GetRailDepotDirection(v->tile); smatz@8712: int length = v->u.rail.cached_veh_length; smatz@8712: smatz@8712: switch (dir) { smatz@8712: case DIAGDIR_NE: return ((int)(v->x_pos & 0x0F) - ((_fractcoords_enter[dir] & 0x0F) - (length + 1))); smatz@8712: case DIAGDIR_SE: return -((int)(v->y_pos & 0x0F) - ((_fractcoords_enter[dir] >> 4) + (length + 1))); smatz@8712: case DIAGDIR_SW: return -((int)(v->x_pos & 0x0F) - ((_fractcoords_enter[dir] & 0x0F) + (length + 1))); smatz@8712: default: smatz@8712: case DIAGDIR_NW: return ((int)(v->y_pos & 0x0F) - ((_fractcoords_enter[dir] >> 4) - (length + 1))); smatz@8712: } smatz@8712: smatz@8712: return 0; // make compilers happy smatz@8712: } smatz@8712: rubidium@10260: /** Tile callback routine when vehicle enters tile rubidium@10260: * @see vehicle_enter_tile_proc */ rubidium@8119: 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@6420: /* this routine applies only to trains in depot tiles */ smatz@8961: if (v->type != VEH_TRAIN || !IsRailDepotTile(tile)) 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@5991: 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@5993: v->u.rail.track = TRACK_BIT_DEPOT, truelight@0: v->vehstatus |= VS_HIDDEN; /* hide it */ tron@3157: v->direction = ReverseDir(v->direction); rubidium@7492: if (v->Next() == NULL) VehicleEnterDepot(v); Darkvater@5198: v->tile = tile; Darkvater@5198: bjarni@4739: InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); rubidium@5991: 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@7492: if ((v = v->Next()) != NULL) { truelight@0: v->vehstatus &= ~VS_HIDDEN; rubidium@5587: v->u.rail.track = (DiagDirToAxis(dir) == AXIS_X ? TRACK_BIT_X : TRACK_BIT_Y); truelight@0: } truelight@0: } truelight@0: } truelight@0: rubidium@5991: return VETSB_CONTINUE; truelight@0: } truelight@0: rubidium@7582: /** rubidium@7582: * Tests if autoslope is allowed. rubidium@7582: * rubidium@7582: * @param tile The tile. rubidium@7582: * @param flags Terraform command flags. rubidium@7582: * @param z_old Old TileZ. rubidium@7582: * @param tileh_old Old TileSlope. rubidium@7582: * @param z_new New TileZ. rubidium@7582: * @param tileh_new New TileSlope. rubidium@7582: * @param rail_bits Trackbits. rubidium@7582: */ rubidium@7582: static CommandCost TestAutoslopeOnRailTile(TileIndex tile, uint flags, uint z_old, Slope tileh_old, uint z_new, Slope tileh_new, TrackBits rail_bits) rubidium@7582: { rubidium@9413: if (!_settings_game.construction.build_on_slopes || !AutoslopeEnabled()) return CMD_ERROR; rubidium@7582: smatz@10017: /* Is the slope-rail_bits combination valid in general? I.e. is it safe to call GetRailFoundation() ? */ rubidium@7582: if (CmdFailed(CheckRailSlope(tileh_new, rail_bits, TRACK_BIT_NONE, tile))) return CMD_ERROR; rubidium@7582: rubidium@7582: /* Get the slopes on top of the foundations */ rubidium@7582: z_old += ApplyFoundationToSlope(GetRailFoundation(tileh_old, rail_bits), &tileh_old); rubidium@7582: z_new += ApplyFoundationToSlope(GetRailFoundation(tileh_new, rail_bits), &tileh_new); rubidium@7582: belugas@7793: Corner track_corner; rubidium@7582: switch (rail_bits) { belugas@7793: case TRACK_BIT_LEFT: track_corner = CORNER_W; break; belugas@7793: case TRACK_BIT_LOWER: track_corner = CORNER_S; break; belugas@7793: case TRACK_BIT_RIGHT: track_corner = CORNER_E; break; belugas@7793: case TRACK_BIT_UPPER: track_corner = CORNER_N; break; rubidium@7582: rubidium@7582: /* Surface slope must not be changed */ rubidium@8230: default: return (((z_old != z_new) || (tileh_old != tileh_new)) ? CMD_ERROR : CommandCost(EXPENSES_CONSTRUCTION, _price.terraform)); rubidium@7582: } rubidium@7582: rubidium@7582: /* The height of the track_corner must not be changed. The rest ensures GetRailFoundation() already. */ frosch@8413: z_old += GetSlopeZInCorner(RemoveHalftileSlope(tileh_old), track_corner); frosch@8413: z_new += GetSlopeZInCorner(RemoveHalftileSlope(tileh_new), track_corner); rubidium@7582: if (z_old != z_new) return CMD_ERROR; rubidium@7582: rubidium@8230: CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price.terraform); belugas@7793: /* Make the ground dirty, if surface slope has changed */ belugas@7793: if (tileh_old != tileh_new) { frosch@8414: /* If there is flat water on the lower halftile add the cost for clearing it */ frosch@8414: if (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh_old)) cost.AddCost(_price.clear_water); belugas@7793: if ((flags & DC_EXEC) != 0) SetRailGroundType(tile, RAIL_GROUND_BARREN); belugas@7793: } rubidium@7771: return cost; rubidium@7582: } rubidium@7582: rubidium@7494: static CommandCost TerraformTile_Track(TileIndex tile, uint32 flags, uint z_new, Slope tileh_new) rubidium@7494: { rubidium@7582: uint z_old; rubidium@7582: Slope tileh_old = GetTileSlope(tile, &z_old); rubidium@7494: if (IsPlainRailTile(tile)) { rubidium@7494: TrackBits rail_bits = GetTrackBits(tile); frosch@8414: /* Is there flat water on the lower halftile, that must be cleared expensively? */ frosch@8414: bool was_water = (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh_old)); rubidium@7494: rubidium@7494: _error_message = STR_1008_MUST_REMOVE_RAILROAD_TRACK; rubidium@7494: rubidium@7582: /* First test autoslope. However if it succeeds we still have to test the rest, because non-autoslope terraforming is cheaper. */ rubidium@7582: CommandCost autoslope_result = TestAutoslopeOnRailTile(tile, flags, z_old, tileh_old, z_new, tileh_new, rail_bits); rubidium@7582: rubidium@7494: /* When there is only a single horizontal/vertical track, one corner can be terraformed. */ rubidium@7770: Corner allowed_corner; rubidium@7494: switch (rail_bits) { rubidium@7770: case TRACK_BIT_RIGHT: allowed_corner = CORNER_W; break; rubidium@7770: case TRACK_BIT_UPPER: allowed_corner = CORNER_S; break; rubidium@7770: case TRACK_BIT_LEFT: allowed_corner = CORNER_E; break; rubidium@7770: case TRACK_BIT_LOWER: allowed_corner = CORNER_N; break; rubidium@7582: default: return autoslope_result; rubidium@7494: } rubidium@7494: rubidium@7494: Foundation f_old = GetRailFoundation(tileh_old, rail_bits); rubidium@7494: rubidium@7770: /* Do not allow terraforming if allowed_corner is part of anti-zig-zag foundations */ rubidium@7770: if (tileh_old != SLOPE_NS && tileh_old != SLOPE_EW && IsSpecialRailFoundation(f_old)) return autoslope_result; rubidium@7494: rubidium@7770: /* Everything is valid, which only changes allowed_corner */ rubidium@7770: for (Corner corner = (Corner)0; corner < CORNER_END; corner = (Corner)(corner + 1)) { rubidium@7770: if (allowed_corner == corner) continue; rubidium@7770: if (z_old + GetSlopeZInCorner(tileh_old, corner) != z_new + GetSlopeZInCorner(tileh_new, corner)) return autoslope_result; rubidium@7494: } rubidium@7494: rubidium@7494: /* Make the ground dirty */ rubidium@7494: if ((flags & DC_EXEC) != 0) SetRailGroundType(tile, RAIL_GROUND_BARREN); rubidium@7494: rubidium@7771: /* allow terraforming */ rubidium@8230: return CommandCost(EXPENSES_CONSTRUCTION, was_water ? _price.clear_water : (Money)0); rubidium@7582: } else { rubidium@9413: if (_settings_game.construction.build_on_slopes && AutoslopeEnabled()) { rubidium@7582: switch (GetRailTileType(tile)) { rubidium@7582: case RAIL_TILE_WAYPOINT: { rubidium@7582: CommandCost cost = TestAutoslopeOnRailTile(tile, flags, z_old, tileh_old, z_new, tileh_new, GetRailWaypointBits(tile)); rubidium@7582: if (!CmdFailed(cost)) return cost; // allow autoslope rubidium@7582: break; rubidium@7582: } rubidium@7582: rubidium@7582: case RAIL_TILE_DEPOT: rubidium@8230: if (AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRailDepotDirection(tile))) return CommandCost(EXPENSES_CONSTRUCTION, _price.terraform); rubidium@7582: break; rubidium@7582: rubidium@7582: default: NOT_REACHED(); rubidium@7582: } rubidium@7582: } rubidium@7494: } rubidium@7494: return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); rubidium@7494: } rubidium@7494: truelight@0: rubidium@5587: 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@7335: GetFoundation_Track, /* get_foundation_proc */ rubidium@7494: TerraformTile_Track, /* terraform_tile_proc */ truelight@0: };