diff --git a/src/newgrf_callbacks.h b/src/newgrf_callbacks.h index 6af6ee8844..93d42ef0e5 100644 --- a/src/newgrf_callbacks.h +++ b/src/newgrf_callbacks.h @@ -211,7 +211,7 @@ enum CallbackID { CBID_HOUSE_WATCHED_CARGO_ACCEPTED = 0x148, // 15 bit callback, not implemented /** Callback done for each tile of a station to check the slope. */ - CBID_STATION_LAND_SLOPE_CHECK = 0x149, // 15 bit callback, not implemented + CBID_STATION_LAND_SLOPE_CHECK = 0x149, // 15 bit callback /** Called to determine the colour of an industry. */ CBID_INDUSTRY_DECIDE_COLOUR = 0x14A, // 4 bit callback diff --git a/src/newgrf_commons.cpp b/src/newgrf_commons.cpp index 8b01c12687..83d1d4f447 100644 --- a/src/newgrf_commons.cpp +++ b/src/newgrf_commons.cpp @@ -413,9 +413,10 @@ uint32 GetTerrainType(TileIndex tile, TileContext context) * @param parameter The NewGRF "encoded" offset. * @param tile The tile to base the offset from. * @param signed_offsets Whether the offsets are to be interpreted as signed or not. + * @param axis Axis of a railways station. * @return The tile at the offset. */ -TileIndex GetNearbyTile(byte parameter, TileIndex tile, bool signed_offsets) +TileIndex GetNearbyTile(byte parameter, TileIndex tile, bool signed_offsets, Axis axis) { int8 x = GB(parameter, 0, 4); int8 y = GB(parameter, 4, 4); @@ -424,7 +425,8 @@ TileIndex GetNearbyTile(byte parameter, TileIndex tile, bool signed_offsets) if (signed_offsets && y >= 8) y -= 16; /* Swap width and height depending on axis for railway stations */ - if (HasStationTileRail(tile) && GetRailStationAxis(tile) == AXIS_Y) Swap(x, y); + if (axis == INVALID_AXIS && HasStationTileRail(tile)) axis = GetRailStationAxis(tile); + if (axis == AXIS_Y) Swap(x, y); /* Make sure we never roam outside of the map, better wrap in that case */ return TILE_MASK(tile + TileDiffXY(x, y)); diff --git a/src/newgrf_commons.h b/src/newgrf_commons.h index f1977b252f..77d62479ec 100644 --- a/src/newgrf_commons.h +++ b/src/newgrf_commons.h @@ -20,6 +20,7 @@ #include "core/alloc_type.hpp" #include "core/smallvec_type.hpp" #include "command_type.h" +#include "direction_type.h" /** Context for tile accesses */ enum TileContext { @@ -266,7 +267,7 @@ extern AirportTileOverrideManager _airporttile_mngr; extern ObjectOverrideManager _object_mngr; uint32 GetTerrainType(TileIndex tile, TileContext context = TCX_NORMAL); -TileIndex GetNearbyTile(byte parameter, TileIndex tile, bool signed_offsets = true); +TileIndex GetNearbyTile(byte parameter, TileIndex tile, bool signed_offsets = true, Axis axis = INVALID_AXIS); uint32 GetNearbyTileInformation(TileIndex tile); CommandCost GetErrorMessageFromLocationCallbackResult(uint16 cb_res, uint32 grfid, StringID default_error); diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index d18ad643fa..757e92e932 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -334,6 +334,7 @@ struct ResolverObject { struct BaseStation *st; const struct StationSpec *statspec; CargoID cargo_type; + Axis axis; ///< Station axis, used only for the slope check callback. } station; struct { TileIndex tile; diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index e3178feaa8..f5236159d0 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -279,7 +279,7 @@ static uint32 StationGetVariable(const ResolverObject *object, byte variable, by } if (st == NULL) { - /* Station does not exist, so we're in a purchase list */ + /* Station does not exist, so we're in a purchase list or the land slope check callback. */ switch (variable) { case 0x40: case 0x41: @@ -289,6 +289,17 @@ static uint32 StationGetVariable(const ResolverObject *object, byte variable, by case 0x42: return 0; // Rail type (XXX Get current type from GUI?) case 0x43: return _current_company; // Station owner case 0x44: return 2; // PBS status + case 0x67: // Land info of nearby tile + if (object->u.station.axis != INVALID_AXIS && tile != INVALID_TILE) { + if (parameter != 0) tile = GetNearbyTile(parameter, tile, true, object->u.station.axis); // only perform if it is required + + Slope tileh = GetTileSlope(tile, NULL); + bool swap = (object->u.station.axis == AXIS_Y && HasBit(tileh, CORNER_W) != HasBit(tileh, CORNER_E)); + + return GetNearbyTileInformation(tile) ^ (swap ? SLOPE_EW : 0); + } + break; + case 0xFA: return Clamp(_date - DAYS_TILL_ORIGINAL_BASE_YEAR, 0, 65535); // Build date, clamped to a 16 bit value } @@ -544,6 +555,7 @@ static void NewStationResolver(ResolverObject *res, const StationSpec *statspec, res->u.station.st = st; res->u.station.statspec = statspec; res->u.station.tile = tile; + res->u.station.axis = INVALID_AXIS; res->callback = CBID_NO_CALLBACK; res->callback_param1 = 0; @@ -653,6 +665,39 @@ uint16 GetStationCallback(CallbackID callback, uint32 param1, uint32 param2, con return group->GetCallbackResult(); } +/** + * Check the slope of a tile of a new station. + * @param north_tile Norther tile of the station rect. + * @param cur_tile Tile to check. + * @param statspec Station spec. + * @param axis Axis of the new station. + * @param plat_len Platform length. + * @param numtracks Number of platforms. + * @return Succeeded or failed command. + */ +CommandCost PerformStationTileSlopeCheck(TileIndex north_tile, TileIndex cur_tile, const StationSpec *statspec, Axis axis, byte plat_len, byte numtracks) +{ + TileIndexDiff diff = cur_tile - north_tile; + Slope slope = GetTileSlope(cur_tile, NULL); + + ResolverObject object; + NewStationResolver(&object, statspec, NULL, cur_tile); + + object.callback = CBID_STATION_LAND_SLOPE_CHECK; + object.callback_param1 = slope << 4 | slope ^ (axis == AXIS_Y && HasBit(slope, CORNER_W) != HasBit(slope, CORNER_E) ? SLOPE_EW : 0); + object.callback_param2 = numtracks << 24 | plat_len << 16 | (axis == AXIS_Y ? TileX(diff) << 8 | TileY(diff) : TileY(diff) << 8 | TileX(diff)); + object.u.station.axis = axis; + + const SpriteGroup *group = ResolveStation(&object); + uint16 cb_res = group != NULL ? group->GetCallbackResult() : CALLBACK_FAILED; + + /* Failed callback means success. */ + if (cb_res == CALLBACK_FAILED) return CommandCost(); + + /* The meaning of bit 10 is inverted in the result of this callback. */ + return GetErrorMessageFromLocationCallbackResult(ToggleBit(cb_res, 10), statspec->grf_prop.grffile->grfid, STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); +} + /** * Allocate a StationSpec to a Station. This is called once per build operation. diff --git a/src/newgrf_station.h b/src/newgrf_station.h index 82656ec048..7c9f80694f 100644 --- a/src/newgrf_station.h +++ b/src/newgrf_station.h @@ -114,6 +114,7 @@ uint32 GetPlatformInfo(Axis axis, byte tile, int platforms, int length, int x, i SpriteID GetCustomStationRelocation(const StationSpec *statspec, BaseStation *st, TileIndex tile, uint32 var10 = 0); SpriteID GetCustomStationFoundationRelocation(const StationSpec *statspec, BaseStation *st, TileIndex tile, uint layout, uint edge_info); uint16 GetStationCallback(CallbackID callback, uint32 param1, uint32 param2, const StationSpec *statspec, BaseStation *st, TileIndex tile); +CommandCost PerformStationTileSlopeCheck(TileIndex north_tile, TileIndex cur_tile, const StationSpec *statspec, Axis axis, byte plat_len, byte numtracks); /* Allocate a StationSpec to a Station. This is called once per build operation. */ int AllocateSpecToStation(const StationSpec *statspec, BaseStation *st, bool exec); diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 54f619fd82..3495f111d3 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -749,22 +749,36 @@ CommandCost CheckFlatLand(TileArea tile_area, DoCommandFlag flags) * Checks if a rail station can be built at the given area. * @param tile_area Area to check. * @param flags Operation to perform. - * @param invalid_dirs Prohibited directions (set of #DiagDirection). + * @param axis Rail station axis. * @param station StationID to be queried and returned if available. * @param rt The rail type to check for (overbuilding rail stations over rail). * @param affected_vehicles List of trains with PBS reservations on the tiles + * @param spec_class Station class. + * @param spec_index Index into the station class. + * @param plat_len Platform length. + * @param numtracks Number of platforms. * @return The cost in case of success, or an error code if it failed. */ -static CommandCost CheckFlatLandRailStation(TileArea tile_area, DoCommandFlag flags, uint invalid_dirs, StationID *station, RailType rt, SmallVector &affected_vehicles) +static CommandCost CheckFlatLandRailStation(TileArea tile_area, DoCommandFlag flags, Axis axis, StationID *station, RailType rt, SmallVector &affected_vehicles, StationClassID spec_class, byte spec_index, byte plat_len, byte numtracks) { CommandCost cost(EXPENSES_CONSTRUCTION); int allowed_z = -1; + uint invalid_dirs = 5 << axis; + + const StationSpec *statspec = StationClass::Get(spec_class, spec_index); + bool slope_cb = statspec != NULL && HasBit(statspec->callback_mask, CBM_STATION_SLOPE_CHECK); TILE_AREA_LOOP(tile_cur, tile_area) { CommandCost ret = CheckBuildableTile(tile_cur, invalid_dirs, allowed_z); if (ret.Failed()) return ret; cost.AddCost(ret); + if (slope_cb) { + /* Do slope check if requested. */ + ret = PerformStationTileSlopeCheck(tile_area.tile, tile_cur, statspec, axis, plat_len, numtracks); + if (ret.Failed()) return ret; + } + /* if station is set, then we have special handling to allow building on top of already existing stations. * so station points to INVALID_STATION if we can build on any station. * Or it points to a station if we're only allowed to build on exactly that station. */ @@ -1140,7 +1154,7 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 StationID est = INVALID_STATION; SmallVector affected_vehicles; /* Clear the land below the station. */ - CommandCost cost = CheckFlatLandRailStation(TileArea(tile_org, w_org, h_org), flags, 5 << axis, &est, rt, affected_vehicles); + CommandCost cost = CheckFlatLandRailStation(TileArea(tile_org, w_org, h_org), flags, axis, &est, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks); if (cost.Failed()) return cost; /* Add construction expenses. */ cost.AddCost((numtracks * _price[PR_BUILD_STATION_RAIL] + _price[PR_BUILD_STATION_RAIL_LENGTH]) * plat_len);