/** * Check whether the given tile is suitable for a waypoint. * @param tile the tile to check for suitability * @param axis the axis of the waypoint * @param waypoint Waypoint the waypoint to check for is already joined to. If we find another waypoint it can join to it will throw an error. */ static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *waypoint) { /* if waypoint is set, then we have special handling to allow building on top of already existing waypoints. * so waypoint points to INVALID_STATION if we can build on any waypoint. * Or it points to a waypoint if we're only allowed to build on exactly that waypoint. */ if (waypoint != NULL && IsTileType(tile, MP_STATION)) { if (!IsRailWaypoint(tile)) { return ClearTile_Station(tile, DC_AUTO); // get error message } else { StationID wp = GetStationIndex(tile); if (*waypoint == INVALID_STATION) { *waypoint = wp; } else if (*waypoint != wp) { return_cmd_error(STR_ERROR_WAYPOINT_ADJOINS_MORE_THAN_ONE_EXISTING); } } } if (GetAxisForNewWaypoint(tile) != axis) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); Owner owner = GetTileOwner(tile); CommandCost ret = CheckOwnership(owner); if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile); if (ret.Failed()) return ret; Slope tileh = GetTileSlope(tile); if (tileh != SLOPE_FLAT && (!_settings_game.construction.build_on_slopes || IsSteepSlope(tileh) || !(tileh & (0x3 << axis)) || !(tileh & ~(0x3 << axis)))) { return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); } if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); return CommandCost(); }
/** * Applies a foundation to a slope. * * @pre Foundation and slope must be valid combined. * @param f The #Foundation. * @param s The #Slope to modify. * @return Increment to the tile Z coordinate. */ uint ApplyFoundationToSlope(Foundation f, Slope *s) { if (!IsFoundation(f)) return 0; if (IsLeveledFoundation(f)) { uint dz = TILE_HEIGHT + (IsSteepSlope(*s) ? TILE_HEIGHT : 0); *s = SLOPE_FLAT; return dz; } if (f != FOUNDATION_STEEP_BOTH && IsNonContinuousFoundation(f)) { *s = HalftileSlope(*s, GetHalftileFoundationCorner(f)); return 0; } if (IsSpecialRailFoundation(f)) { *s = SlopeWithThreeCornersRaised(OppositeCorner(GetRailFoundationCorner(f))); return 0; } uint dz = IsSteepSlope(*s) ? TILE_HEIGHT : 0; Corner highest_corner = GetHighestSlopeCorner(*s); switch (f) { case FOUNDATION_INCLINED_X: *s = (((highest_corner == CORNER_W) || (highest_corner == CORNER_S)) ? SLOPE_SW : SLOPE_NE); break; case FOUNDATION_INCLINED_Y: *s = (((highest_corner == CORNER_S) || (highest_corner == CORNER_E)) ? SLOPE_SE : SLOPE_NW); break; case FOUNDATION_STEEP_LOWER: *s = SlopeWithOneCornerRaised(highest_corner); break; case FOUNDATION_STEEP_BOTH: *s = HalftileSlope(SlopeWithOneCornerRaised(highest_corner), highest_corner); break; default: NOT_REACHED(); } return dz; }
/** * Check whether the given tile is suitable for a waypoint. * @param tile the tile to check for suitability * @param axis the axis of the waypoint * @param waypoint Waypoint the waypoint to check for is already joined to. If we find another waypoint it can join to it will throw an error. * @param flags flags for the command */ static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *waypoint, DoCommandFlag flags) { /* if waypoint is set, then we have special handling to allow building on top of already existing waypoints. * so waypoint points to INVALID_STATION if we can build on any waypoint. * Or it points to a waypoint if we're only allowed to build on exactly that waypoint. */ if (waypoint != NULL && IsTileType(tile, MP_STATION)) { if (!IsRailWaypoint(tile)) { return ClearTile_Station(tile, DC_AUTO); // get error message } else { StationID wp = GetStationIndex(tile); if (*waypoint == INVALID_STATION) { *waypoint = wp; } else if (*waypoint != wp) { return_cmd_error(STR_ERROR_WAYPOINT_ADJOINS_MORE_THAN_ONE_EXISTING); } } } /* When pasting a waypoint, there may be no track yet (it will be placed when DC_EXEC'ing). * Ignore that so we can calculate the cost more precisely. */ bool ignore_lack_of_tracks = (flags & DC_PASTE) && !(flags & DC_EXEC); if (!ignore_lack_of_tracks || IsTileType(tile, MP_RAILWAY)) { if (GetAxisForNewWaypoint(tile) != axis) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); } Owner owner = GetTileOwner(tile); if (!ignore_lack_of_tracks || owner != OWNER_NONE) { CommandCost ret = CheckOwnership(owner); if (ret.Failed()) return ret; } CommandCost ret = EnsureNoVehicleOnGround(tile); if (ret.Failed()) return ret; Slope tileh = GetTileSlope(tile); if (tileh != SLOPE_FLAT && (!_settings_game.construction.build_on_slopes || IsSteepSlope(tileh) || !(tileh & (0x3 << axis)) || !(tileh & ~(0x3 << axis)))) { return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); } if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); return CommandCost(); }
/** * Lookup function for building road parts when building on slopes is enabled. * @param slope The slope of the tile to examine. * @param existing The existing neighbours. * @param start The part that should be build first. * @param end The part that will be build second. * @return 0 when the build parts do not connect, 1 when they do connect once * they are build or 2 when building the first part automatically * builds the second part. */ static int32 LookupWithBuildOnSlopes(::Slope slope, Array *existing, int32 start, int32 end) { /* Steep slopes behave the same as slopes with one corner raised. */ if (IsSteepSlope(slope)) { slope = SlopeWithOneCornerRaised(GetHighestSlopeCorner(slope)); } /* The slope is not steep. Furthermore lots of slopes are generally the * same but are only rotated. So to reduce the amount of lookup work that * needs to be done the data is made uniform. This means rotating the * existing parts and updating the slope. */ static const ::Slope base_slopes[] = { SLOPE_FLAT, SLOPE_W, SLOPE_W, SLOPE_SW, SLOPE_W, SLOPE_EW, SLOPE_SW, SLOPE_WSE, SLOPE_W, SLOPE_SW, SLOPE_EW, SLOPE_WSE, SLOPE_SW, SLOPE_WSE, SLOPE_WSE}; static const byte base_rotates[] = {0, 0, 1, 0, 2, 0, 1, 0, 3, 3, 2, 3, 2, 2, 1}; if (slope >= (::Slope)lengthof(base_slopes)) { /* This slope is an invalid slope, so ignore it. */ return -1; } byte base_rotate = base_rotates[slope]; slope = base_slopes[slope]; /* Some slopes don't need rotating, so return early when we know we do * not need to rotate. */ switch (slope) { case SLOPE_FLAT: /* Flat slopes can always be build. */ return 1; case SLOPE_EW: case SLOPE_WSE: /* A slope similar to a SLOPE_EW or SLOPE_WSE will always cause * foundations which makes them accessible from all sides. */ return 1; case SLOPE_W: case SLOPE_SW: /* A slope for which we need perform some calculations. */ break; default: /* An invalid slope. */ return -1; } /* Now perform the actual rotation. */ for (int j = 0; j < base_rotate; j++) { for (int i = 0; i < existing->size; i++) { existing->array[i] = RotateNeighbour(existing->array[i]); } start = RotateNeighbour(start); end = RotateNeighbour(end); } /* Create roadbits out of the data for easier handling. */ RoadBits start_roadbits = NeighbourToRoadBits(start); RoadBits new_roadbits = start_roadbits | NeighbourToRoadBits(end); RoadBits existing_roadbits = ROAD_NONE; for (int i = 0; i < existing->size; i++) { existing_roadbits |= NeighbourToRoadBits(existing->array[i]); } switch (slope) { case SLOPE_W: /* A slope similar to a SLOPE_W. */ switch (new_roadbits) { case ROAD_N: case ROAD_E: case ROAD_S: /* Cannot build anything with a turn from the low side. */ return 0; case ROAD_X: case ROAD_Y: /* A 'sloped' tile is going to be build. */ if ((existing_roadbits | new_roadbits) != new_roadbits) { /* There is already a foundation on the tile, or at least * another slope that is not compatible with the new one. */ return 0; } /* If the start is in the low part, it is automatically * building the second part too. */ return ((start_roadbits & ROAD_E) && !(existing_roadbits & ROAD_W)) ? 2 : 1; default: /* Roadbits causing a foundation are going to be build. * When the existing roadbits are slopes (the lower bits * are used), this cannot be done. */ if ((existing_roadbits | new_roadbits) == new_roadbits) return 1; return (existing_roadbits & ROAD_E) ? 0 : 1; } case SLOPE_SW: /* A slope similar to a SLOPE_SW. */ switch (new_roadbits) { case ROAD_N: case ROAD_E: /* Cannot build anything with a turn from the low side. */ return 0; case ROAD_X: /* A 'sloped' tile is going to be build. */ if ((existing_roadbits | new_roadbits) != new_roadbits) { /* There is already a foundation on the tile, or at least * another slope that is not compatible with the new one. */ return 0; } /* If the start is in the low part, it is automatically * building the second part too. */ return ((start_roadbits & ROAD_NE) && !(existing_roadbits & ROAD_SW)) ? 2 : 1; default: /* Roadbits causing a foundation are going to be build. * When the existing roadbits are slopes (the lower bits * are used), this cannot be done. */ return (existing_roadbits & ROAD_NE) ? 0 : 1; } default: NOT_REACHED(); } }