diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e57a9b412a..db6d9db160 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,6 +23,7 @@ add_subdirectory(saveload) add_subdirectory(sound) add_subdirectory(spriteloader) add_subdirectory(table) +add_subdirectory(tests) add_subdirectory(video) add_subdirectory(widgets) diff --git a/src/clear_cmd.cpp b/src/clear_cmd.cpp index 1032fe09f6..2035b9d5d5 100644 --- a/src/clear_cmd.cpp +++ b/src/clear_cmd.cpp @@ -197,7 +197,7 @@ static void DrawTile_Clear(TileInfo *ti, DrawTileProcParams params) DrawBridgeMiddle(ti); } -static int GetSlopePixelZ_Clear(TileIndex tile, uint x, uint y) +static int GetSlopePixelZ_Clear(TileIndex tile, uint x, uint y, bool ground_vehicle) { int z; Slope tileh = GetTilePixelSlope(tile, &z); diff --git a/src/core/bitmath_func.hpp b/src/core/bitmath_func.hpp index b931205ccd..e082a89bab 100644 --- a/src/core/bitmath_func.hpp +++ b/src/core/bitmath_func.hpp @@ -31,7 +31,7 @@ * @return The selected bits, aligned to a LSB. */ template -debug_inline static uint GB(const T x, const uint8 s, const uint8 n) +debug_inline constexpr static uint GB(const T x, const uint8 s, const uint8 n) { return (x >> s) & (((T)1U << n) - 1); } diff --git a/src/core/math_func.hpp b/src/core/math_func.hpp index 7d5bfa008b..ea0345ce5c 100644 --- a/src/core/math_func.hpp +++ b/src/core/math_func.hpp @@ -224,7 +224,7 @@ static inline bool IsInsideBS(const T x, const size_t base, const size_t size) * @see IsInsideBS() */ template -static inline bool IsInsideMM(const T x, const size_t min, const size_t max) +static inline constexpr bool IsInsideMM(const T x, const size_t min, const size_t max) { return (size_t)(x - min) < (max - min); } diff --git a/src/elrail.cpp b/src/elrail.cpp index e079fb30b4..de661fdb4f 100644 --- a/src/elrail.cpp +++ b/src/elrail.cpp @@ -266,7 +266,7 @@ static int GetPCPElevation(TileIndex tile, DiagDirection PCPpos) */ int z = GetSlopePixelZ(TileX(tile) * TILE_SIZE + std::min(x_pcp_offsets[PCPpos], TILE_SIZE - 1), - TileY(tile) * TILE_SIZE + std::min(y_pcp_offsets[PCPpos], TILE_SIZE - 1)); + TileY(tile) * TILE_SIZE + std::min(y_pcp_offsets[PCPpos], TILE_SIZE - 1), true); return (z + 2) & ~3; // this means z = (z + TILE_HEIGHT / 4) / (TILE_HEIGHT / 2) * (TILE_HEIGHT / 2); } @@ -591,7 +591,7 @@ static void DrawRailCatenaryRailway(const TileInfo *ti) * down to the nearest full height change. */ AddSortableSpriteToDraw(wire_base + sss->image_offset, PAL_NONE, ti->x + sss->x_offset, ti->y + sss->y_offset, - sss->x_size, sss->y_size, sss->z_size, (GetSlopePixelZ(ti->x + sss->x_offset, ti->y + sss->y_offset) + 4) / 8 * 8 + sss->z_offset, + sss->x_size, sss->y_size, sss->z_size, (GetSlopePixelZ(ti->x + sss->x_offset, ti->y + sss->y_offset, true) + 4) / 8 * 8 + sss->z_offset, IsTransparencySet(TO_CATENARY)); } } diff --git a/src/ground_vehicle.hpp b/src/ground_vehicle.hpp index 48b86b3625..87e51e25b5 100644 --- a/src/ground_vehicle.hpp +++ b/src/ground_vehicle.hpp @@ -151,7 +151,7 @@ struct GroundVehicle : public SpecializedVehicle { */ inline void UpdateZPositionAndInclination() { - this->z_pos = GetSlopePixelZ(this->x_pos, this->y_pos); + this->z_pos = GetSlopePixelZ(this->x_pos, this->y_pos, true); ClrBit(this->gv_flags, GVF_GOINGUP_BIT); ClrBit(this->gv_flags, GVF_GOINGDOWN_BIT); @@ -160,7 +160,7 @@ struct GroundVehicle : public SpecializedVehicle { * direction it is sloped, we get the 'z' at the center of * the tile (middle_z) and the edge of the tile (old_z), * which we then can compare. */ - int middle_z = GetSlopePixelZ((this->x_pos & ~TILE_UNIT_MASK) | (TILE_SIZE / 2), (this->y_pos & ~TILE_UNIT_MASK) | (TILE_SIZE / 2)); + int middle_z = GetSlopePixelZ((this->x_pos & ~TILE_UNIT_MASK) | (TILE_SIZE / 2), (this->y_pos & ~TILE_UNIT_MASK) | (TILE_SIZE / 2), true); if (middle_z != this->z_pos) { SetBit(this->gv_flags, (middle_z > this->z_pos) ? GVF_GOINGUP_BIT : GVF_GOINGDOWN_BIT); @@ -183,25 +183,25 @@ struct GroundVehicle : public SpecializedVehicle { if (HasBit(this->gv_flags, GVF_GOINGUP_BIT)) { switch (this->direction) { case DIR_NE: - this->z_pos += (this->x_pos & 1); break; - case DIR_SW: this->z_pos += (this->x_pos & 1) ^ 1; break; + case DIR_SW: + this->z_pos += (this->x_pos & 1); break; case DIR_NW: - this->z_pos += (this->y_pos & 1); break; - case DIR_SE: this->z_pos += (this->y_pos & 1) ^ 1; break; + case DIR_SE: + this->z_pos += (this->y_pos & 1); break; default: break; } } else if (HasBit(this->gv_flags, GVF_GOINGDOWN_BIT)) { switch (this->direction) { case DIR_NE: - this->z_pos -= (this->x_pos & 1); break; - case DIR_SW: this->z_pos -= (this->x_pos & 1) ^ 1; break; + case DIR_SW: + this->z_pos -= (this->x_pos & 1); break; case DIR_NW: - this->z_pos -= (this->y_pos & 1); break; - case DIR_SE: this->z_pos -= (this->y_pos & 1) ^ 1; break; + case DIR_SE: + this->z_pos -= (this->y_pos & 1); break; default: break; } } @@ -218,7 +218,7 @@ struct GroundVehicle : public SpecializedVehicle { if (HasBit(this->gv_flags, GVF_GOINGUP_BIT) || HasBit(this->gv_flags, GVF_GOINGDOWN_BIT)) { if (T::From(this)->HasToUseGetSlopePixelZ()) { /* In some cases, we have to use GetSlopePixelZ() */ - this->z_pos = GetSlopePixelZ(this->x_pos, this->y_pos); + this->z_pos = GetSlopePixelZ(this->x_pos, this->y_pos, true); return; } /* DirToDiagDir() is a simple right shift */ @@ -230,8 +230,7 @@ struct GroundVehicle : public SpecializedVehicle { int8 d = DiagDirToAxis(dir) == AXIS_X ? x_pos : y_pos; /* We need only the least significant bit */ d &= 1; - /* Conditional "^ 1". Optimised to "(dir - 1) <= 1". */ - d ^= (int8)(dir == DIAGDIR_SW || dir == DIAGDIR_SE); + d ^= (int8)(dir == DIAGDIR_NW || dir == DIAGDIR_NE); /* Subtraction instead of addition because we are testing for GVF_GOINGUP_BIT. * GVF_GOINGUP_BIT is used because it's bit 0, so simple AND can be used, * without any shift */ @@ -239,7 +238,7 @@ struct GroundVehicle : public SpecializedVehicle { } #ifdef _DEBUG - assert(this->z_pos == GetSlopePixelZ(this->x_pos, this->y_pos)); + assert(this->z_pos == GetSlopePixelZ(this->x_pos, this->y_pos, true)); #endif if (HasBit(this->gv_flags, GVF_CHUNNEL_BIT) && !IsTunnelTile(this->tile)) { diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index 94ea2bf9b3..0adf0801b4 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -387,7 +387,7 @@ static void DrawTile_Industry(TileInfo *ti, DrawTileProcParams params) } } -static int GetSlopePixelZ_Industry(TileIndex tile, uint x, uint y) +static int GetSlopePixelZ_Industry(TileIndex tile, uint x, uint y, bool ground_vehicle) { return GetTileMaxPixelZ(tile); } diff --git a/src/landscape.cpp b/src/landscape.cpp index 5381006de2..56ec9b25e7 100644 --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -226,144 +226,112 @@ uint ApplyFoundationToSlope(Foundation f, Slope *s) /** - * Determines height at given coordinate of a slope - * @param x x coordinate - * @param y y coordinate + * Determines height at given coordinate of a slope. + * + * At the northern corner (0, 0) the result is always a multiple of TILE_HEIGHT. + * When the height is a fractional Z, then the height is rounded down. For example, + * when at the height is 0 at x = 0 and the height is 8 at x = 16 (actually x = 0 + * of the next tile), then height is 0 at x = 1, 1 at x = 2, and 7 at x = 15. + * @param x x coordinate (value from 0 to 15) + * @param y y coordinate (value from 0 to 15) * @param corners slope to examine * @return height of given point of given slope */ -uint GetPartialPixelZ(int x, int y, Slope corners) +static constexpr uint InternalGetPartialPixelZ(int x, int y, Slope corners) { if (IsHalftileSlope(corners)) { + /* A foundation is placed on half the tile at a specific corner. This means that, + * depending on the corner, that one half of the tile is at the maximum height. */ switch (GetHalftileSlopeCorner(corners)) { case CORNER_W: - if (x - y >= 0) return GetSlopeMaxPixelZ(corners); + if (x > y) return GetSlopeMaxPixelZ(corners); break; case CORNER_S: - if (x - (y ^ 0xF) >= 0) return GetSlopeMaxPixelZ(corners); + if (x + y >= (int)TILE_SIZE) return GetSlopeMaxPixelZ(corners); break; case CORNER_E: - if (y - x >= 0) return GetSlopeMaxPixelZ(corners); + if (x <= y) return GetSlopeMaxPixelZ(corners); break; case CORNER_N: - if ((y ^ 0xF) - x >= 0) return GetSlopeMaxPixelZ(corners); + if (x + y < (int)TILE_SIZE) return GetSlopeMaxPixelZ(corners); break; default: NOT_REACHED(); } } - int z = 0; - switch (RemoveHalftileSlope(corners)) { - case SLOPE_W: - if (x - y >= 0) { - z = (x - y) >> 1; - } - break; + case SLOPE_FLAT: return 0; - case SLOPE_S: - y ^= 0xF; - if ((x - y) >= 0) { - z = (x - y) >> 1; - } - break; + /* One corner is up.*/ + case SLOPE_N: return x + y <= (int)TILE_SIZE ? (TILE_SIZE - x - y) >> 1 : 0; + case SLOPE_E: return y >= x ? (1 + y - x) >> 1 : 0; + case SLOPE_S: return x + y >= (int)TILE_SIZE ? (1 + x + y - TILE_SIZE) >> 1 : 0; + case SLOPE_W: return x >= y ? (x - y) >> 1 : 0; - case SLOPE_SW: - z = (x >> 1) + 1; - break; + /* Two corners next to eachother are up. */ + case SLOPE_NE: return (TILE_SIZE - x) >> 1; + case SLOPE_SE: return (y + 1) >> 1; + case SLOPE_SW: return (x + 1) >> 1; + case SLOPE_NW: return (TILE_SIZE - y) >> 1; - case SLOPE_E: - if (y - x >= 0) { - z = (y - x) >> 1; - } - break; + /* Three corners are up on the same level. */ + case SLOPE_ENW: return x + y >= (int)TILE_SIZE ? TILE_HEIGHT - ((1 + x + y - TILE_SIZE) >> 1) : TILE_HEIGHT; + case SLOPE_SEN: return y < x ? TILE_HEIGHT - ((x - y) >> 1) : TILE_HEIGHT; + case SLOPE_WSE: return x + y <= (int)TILE_SIZE ? TILE_HEIGHT - ((TILE_SIZE - x - y) >> 1) : TILE_HEIGHT; + case SLOPE_NWS: return x < y ? TILE_HEIGHT - ((1 + y - x) >> 1) : TILE_HEIGHT; - case SLOPE_EW: - case SLOPE_NS: - case SLOPE_ELEVATED: - z = 4; - break; + /* Two corners at opposite sides are up. */ + case SLOPE_NS: return x + y < (int)TILE_SIZE ? (TILE_SIZE - x - y) >> 1 : (1 + x + y - TILE_SIZE) >> 1; + case SLOPE_EW: return x >= y ? (x - y) >> 1 : (1 + y - x) >> 1; - case SLOPE_SE: - z = (y >> 1) + 1; - break; + /* Very special cases. */ + case SLOPE_ELEVATED: return TILE_HEIGHT; - case SLOPE_WSE: - z = 8; - y ^= 0xF; - if (x - y < 0) { - z += (x - y) >> 1; - } - break; + /* Steep slopes. The top is at 2 * TILE_HEIGHT. */ + case SLOPE_STEEP_N: return (TILE_SIZE - x + TILE_SIZE - y) >> 1; + case SLOPE_STEEP_E: return (TILE_SIZE + 1 + y - x) >> 1; + case SLOPE_STEEP_S: return (1 + x + y) >> 1; + case SLOPE_STEEP_W: return (TILE_SIZE + x - y) >> 1; - case SLOPE_N: - y ^= 0xF; - if (y - x >= 0) { - z = (y - x) >> 1; - } - break; - - case SLOPE_NW: - z = (y ^ 0xF) >> 1; - break; - - case SLOPE_NWS: - z = 8; - if (x - y < 0) { - z += (x - y) >> 1; - } - break; - - case SLOPE_NE: - z = (x ^ 0xF) >> 1; - break; - - case SLOPE_ENW: - z = 8; - y ^= 0xF; - if (y - x < 0) { - z += (y - x) >> 1; - } - break; - - case SLOPE_SEN: - z = 8; - if (y - x < 0) { - z += (y - x) >> 1; - } - break; - - case SLOPE_STEEP_S: - z = 1 + ((x + y) >> 1); - break; - - case SLOPE_STEEP_W: - z = 1 + ((x + (y ^ 0xF)) >> 1); - break; - - case SLOPE_STEEP_N: - z = 1 + (((x ^ 0xF) + (y ^ 0xF)) >> 1); - break; - - case SLOPE_STEEP_E: - z = 1 + (((x ^ 0xF) + y) >> 1); - break; - - default: break; + default: NOT_REACHED(); } - - return z; } -int GetSlopePixelZ(int x, int y) +#include "tests/landscape_partial_pixel_z.h" + +/** + * Determines height at given coordinate of a slope. + * See #InternalGetPartialPixelZ. + * @param x x coordinate (value from 0 to 15) + * @param y y coordinate (value from 0 to 15) + * @param corners slope to examine + * @return height of given point of given slope + */ +uint GetPartialPixelZ(int x, int y, Slope corners) +{ + return InternalGetPartialPixelZ(x, y, corners); +} + +/** + * Return world \c Z coordinate of a given point of a tile. Normally this is the + * Z of the ground/foundation at the given location, but in some cases the + * ground/foundation can differ from the Z coordinate that the (ground) vehicle + * passing over it would take. For example when entering a tunnel or bridge. + * + * @param x World X coordinate in tile "units". + * @param y World Y coordinate in tile "units". + * @param ground_vehicle Whether to get the Z coordinate of the ground vehicle, or the ground. + * @return World Z coordinate at tile ground (vehicle) level, including slopes and foundations. + */ +int GetSlopePixelZ(int x, int y, bool ground_vehicle) { TileIndex tile = TileVirtXY(x, y); - return _tile_type_procs[GetTileType(tile)]->get_slope_z_proc(tile, x, y); + return _tile_type_procs[GetTileType(tile)]->get_slope_z_proc(tile, x, y, ground_vehicle); } /** @@ -377,9 +345,9 @@ int GetSlopePixelZ(int x, int y) int GetSlopePixelZOutsideMap(int x, int y) { if (IsInsideBS(x, 0, MapSizeX() * TILE_SIZE) && IsInsideBS(y, 0, MapSizeY() * TILE_SIZE)) { - return GetSlopePixelZ(x, y); + return GetSlopePixelZ(x, y, false); } else { - return _tile_type_procs[MP_VOID]->get_slope_z_proc(INVALID_TILE, x, y); + return _tile_type_procs[MP_VOID]->get_slope_z_proc(INVALID_TILE, x, y, false); } } diff --git a/src/landscape.h b/src/landscape.h index d0f15586a2..a6f9e14e7f 100644 --- a/src/landscape.h +++ b/src/landscape.h @@ -67,7 +67,7 @@ Slope GetFoundationSlopeFromTileSlope(TileIndex tile, Slope tileh, int *z = null Slope GetFoundationSlope(TileIndex tile, int *z = nullptr); uint GetPartialPixelZ(int x, int y, Slope corners); -int GetSlopePixelZ(int x, int y); +int GetSlopePixelZ(int x, int y, bool ground_vehicle = false); int GetSlopePixelZOutsideMap(int x, int y); void GetSlopePixelZOnEdge(Slope tileh, DiagDirection edge, int *z1, int *z2); @@ -127,7 +127,7 @@ static inline Point RemapCoords(int x, int y, int z) */ static inline Point RemapCoords2(int x, int y) { - return RemapCoords(x, y, GetSlopePixelZ(x, y)); + return RemapCoords(x, y, GetSlopePixelZ(x, y, false)); } /** diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp index f0b0eee69d..2ba8a90c15 100644 --- a/src/object_cmd.cpp +++ b/src/object_cmd.cpp @@ -659,7 +659,7 @@ static void DrawTile_Object(TileInfo *ti, DrawTileProcParams params) DrawBridgeMiddle(ti); } -static int GetSlopePixelZ_Object(TileIndex tile, uint x, uint y) +static int GetSlopePixelZ_Object(TileIndex tile, uint x, uint y, bool ground_vehicle) { if (IsObjectType(tile, OBJECT_OWNED_LAND)) { int z; diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp index d4b82b1f09..c261af2d6e 100644 --- a/src/pathfinder/npf/npf.cpp +++ b/src/pathfinder/npf/npf.cpp @@ -226,8 +226,8 @@ static uint NPFSlopeCost(AyStarNode *current) /* Get the height on both sides of the tile edge. * Avoid testing the height on the tile-center. This will fail for halftile-foundations. */ - int z1 = GetSlopePixelZ(x1 + dx4, y1 + dy4); - int z2 = GetSlopePixelZ(x2 - dx4, y2 - dy4); + int z1 = GetSlopePixelZ(x1 + dx4, y1 + dy4, true); + int z2 = GetSlopePixelZ(x2 - dx4, y2 - dy4, true); if (z2 - z1 > 1) { /* Slope up */ diff --git a/src/pathfinder/yapf/yapf_road.cpp b/src/pathfinder/yapf/yapf_road.cpp index df33fcbedb..3bc96521e5 100644 --- a/src/pathfinder/yapf/yapf_road.cpp +++ b/src/pathfinder/yapf/yapf_road.cpp @@ -53,12 +53,12 @@ protected: /* height of the center of the current tile */ int x1 = TileX(tile) * TILE_SIZE; int y1 = TileY(tile) * TILE_SIZE; - int z1 = GetSlopePixelZ(x1 + TILE_SIZE / 2, y1 + TILE_SIZE / 2); + int z1 = GetSlopePixelZ(x1 + TILE_SIZE / 2, y1 + TILE_SIZE / 2, true); /* height of the center of the next tile */ int x2 = TileX(next_tile) * TILE_SIZE; int y2 = TileY(next_tile) * TILE_SIZE; - int z2 = GetSlopePixelZ(x2 + TILE_SIZE / 2, y2 + TILE_SIZE / 2); + int z2 = GetSlopePixelZ(x2 + TILE_SIZE / 2, y2 + TILE_SIZE / 2, true); if (z2 - z1 > 1) { /* Slope up */ diff --git a/src/pbs.cpp b/src/pbs.cpp index 3c0a034d49..dea262268e 100644 --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -439,7 +439,7 @@ static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Tra default: NOT_REACHED(); } } - z = GetSlopePixelZ(x, y); + z = GetSlopePixelZ(x, y, true); } } }; diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 9d68908f78..32b732e7ee 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -3752,7 +3752,7 @@ void DrawTrainDepotSprite(int x, int y, int dir, RailType railtype) DrawRailTileSeqInGUI(x, y, dts, offset, 0, palette); } -static int GetSlopePixelZ_Track(TileIndex tile, uint x, uint y) +static int GetSlopePixelZ_Track(TileIndex tile, uint x, uint y, bool ground_vehicle) { if (IsPlainRail(tile)) { int z; diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 8a7e45acf0..95022fbee7 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -2494,7 +2494,7 @@ void UpdateNearestTownForRoadTiles(bool invalidate) } } -static int GetSlopePixelZ_Road(TileIndex tile, uint x, uint y) +static int GetSlopePixelZ_Road(TileIndex tile, uint x, uint y, bool ground_vehicle) { if (IsNormalRoad(tile)) { diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 1cc528b6bc..bf46c8929d 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -288,7 +288,7 @@ CommandCost CmdBuildRoadVehicle(TileIndex tile, DoCommandFlag flags, const Engin int y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2; v->x_pos = x; v->y_pos = y; - v->z_pos = GetSlopePixelZ(x, y); + v->z_pos = GetSlopePixelZ(x, y, true); v->state = RVSB_IN_DEPOT; v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL; @@ -2013,7 +2013,7 @@ again: * A vehicle has to spend at least 9 frames on a tile, so the following articulated part can follow. * (The following part may only be one tile behind, and the front part is moved before the following ones.) * The short (inner) curve has 8 frames, this elongates it to 10. */ - v->UpdateInclination(false, true); + v->UpdateViewport(true, true); return true; } diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 964106b5ec..cf750cd6ab 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -569,12 +569,12 @@ static uint FixVehicleInclination(Vehicle *v, Direction dir) case INVALID_DIR: break; default: NOT_REACHED(); } - byte entry_z = GetSlopePixelZ(entry_x, entry_y); + byte entry_z = GetSlopePixelZ(entry_x, entry_y, true); /* Compute middle of the tile. */ int middle_x = (v->x_pos & ~TILE_UNIT_MASK) + TILE_SIZE / 2; int middle_y = (v->y_pos & ~TILE_UNIT_MASK) + TILE_SIZE / 2; - byte middle_z = GetSlopePixelZ(middle_x, middle_y); + byte middle_z = GetSlopePixelZ(middle_x, middle_y, true); /* middle_z == entry_z, no height change. */ if (middle_z == entry_z) return 0; @@ -586,6 +586,25 @@ static uint FixVehicleInclination(Vehicle *v, Direction dir) return 1U << GVF_GOINGUP_BIT; } +/** + * Check whether the ground vehicles are at the correct Z-coordinate. When they + * are not, this will cause all kinds of problems later on as the vehicle might + * not get onto bridges and so on. + */ +static void CheckGroundVehiclesAtCorrectZ() +{ + for (Vehicle *v : Vehicle::Iterate()) { + if (v->IsGroundVehicle()) { + /* + * Either the vehicle is not actually on the given tile, i.e. it is + * in the wormhole of a bridge or a tunnel, or the Z-coordinate must + * be the same as when it would be recalculated right now. + */ + assert(v->tile != TileVirtXY(v->x_pos, v->y_pos) || v->z_pos == GetSlopePixelZ(v->x_pos, v->y_pos, true)); + } + } +} + /** * Checks for the possibility that a bridge may be on this tile * These are in fact all the tile types on which a bridge can be found @@ -1440,7 +1459,7 @@ bool AfterLoadGame() case DIAGDIR_SW: if ((v->x_pos & 0xF) != TILE_SIZE - 1) continue; break; case DIAGDIR_NW: if ((v->y_pos & 0xF) != 0) continue; break; } - } else if (v->z_pos > GetSlopePixelZ(v->x_pos, v->y_pos)) { + } else if (v->z_pos > GetTileMaxPixelZ(TileVirtXY(v->x_pos, v->y_pos))) { v->tile = GetNorthernBridgeEnd(v->tile); v->UpdatePosition(); } else { @@ -2895,7 +2914,7 @@ bool AfterLoadGame() if (!IsTunnelTile(vtile)) continue; /* Are we actually in this tunnel? Or maybe a lower tunnel? */ - if (GetSlopePixelZ(v->x_pos, v->y_pos) != v->z_pos) continue; + if (GetSlopePixelZ(v->x_pos, v->y_pos, true) != v->z_pos) continue; /* What way are we going? */ const DiagDirection dir = GetTunnelBridgeDirection(vtile); @@ -2974,6 +2993,23 @@ bool AfterLoadGame() } } + if (IsSavegameVersionBefore(SLV_CONSISTENT_PARTIAL_Z) && SlXvIsFeatureMissing(XSLFI_CONSISTENT_PARTIAL_Z)) { + /* + * The logic of GetPartialPixelZ has been changed, so the resulting Zs on + * the map are consistent. This requires that the Z position of some + * vehicles is updated to reflect this new situation. + * + * This needs to be before SLV_158, because that performs asserts using + * GetSlopePixelZ which internally uses GetPartialPixelZ. + */ + for (Vehicle *v : Vehicle::Iterate()) { + if (v->IsGroundVehicle() && TileVirtXY(v->x_pos, v->y_pos) == v->tile) { + /* Vehicle is on the ground, and not in a wormhole. */ + v->z_pos = GetSlopePixelZ(v->x_pos, v->y_pos, true); + } + } + } + if (IsSavegameVersionBefore(SLV_158)) { for (Vehicle *v : Vehicle::Iterate()) { switch (v->type) { @@ -3040,7 +3076,7 @@ bool AfterLoadGame() /* In old versions, z_pos was 1 unit lower on bridge heads. * However, this invalid state could be converted to new savegames * by loading and saving the game in a new version. */ - v->z_pos = GetSlopePixelZ(v->x_pos, v->y_pos); + v->z_pos = GetSlopePixelZ(v->x_pos, v->y_pos, true); DiagDirection dir = GetTunnelBridgeDirection(v->tile); if (v->type == VEH_TRAIN && !(v->vehstatus & VS_CRASHED) && v->direction != DiagDirToDir(dir)) { @@ -3054,7 +3090,7 @@ bool AfterLoadGame() /* If the vehicle is really above v->tile (not in a wormhole), * it should have set v->z_pos correctly. */ - assert(v->tile != TileVirtXY(v->x_pos, v->y_pos) || v->z_pos == GetSlopePixelZ(v->x_pos, v->y_pos)); + assert(v->tile != TileVirtXY(v->x_pos, v->y_pos) || v->z_pos == GetSlopePixelZ(v->x_pos, v->y_pos, true)); } /* Fill Vehicle::cur_real_order_index */ @@ -4209,6 +4245,14 @@ bool AfterLoadGame() AfterLoad_LinkGraphPauseControl(); } + if (SlXvIsFeatureMissing(XSLFI_CONSISTENT_PARTIAL_Z)) { + CheckGroundVehiclesAtCorrectZ(); + } else { +#ifdef _DEBUG + CheckGroundVehiclesAtCorrectZ(); +#endif + } + _game_load_cur_date_ymd = _cur_date_ymd; _game_load_date_fract = _date_fract; _game_load_tick_skip_counter = _tick_skip_counter; diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 2918763a38..242be770f8 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -194,6 +194,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_LAST_LOADING_TICK, XSCF_NULL, 2, 2, "last_loading_tick", nullptr, nullptr, nullptr }, { XSLFI_SCRIPT_LEAGUE_TABLES, XSCF_NULL, 1, 1, "script_league_tables", nullptr, nullptr, "LEAE,LEAT" }, { XSLFI_VELOCITY_NAUTICAL, XSCF_IGNORABLE_ALL, 1, 1, "velocity_nautical", nullptr, nullptr, nullptr }, + { XSLFI_CONSISTENT_PARTIAL_Z, XSCF_NULL, 1, 1, "consistent_partial_z", nullptr, nullptr, nullptr }, { XSLFI_NULL, XSCF_NULL, 0, 0, nullptr, nullptr, nullptr, nullptr },// This is the end marker }; diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h index bf64ae591f..d3d9811cdd 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -146,6 +146,7 @@ enum SlXvFeatureIndex { XSLFI_LAST_LOADING_TICK, ///< See: SLV_LAST_LOADING_TICK XSLFI_SCRIPT_LEAGUE_TABLES, ///< See: Scriptable league tables (PR #10001) XSLFI_VELOCITY_NAUTICAL, ///< See: SLV_VELOCITY_NAUTICAL (PR #10594) + XSLFI_CONSISTENT_PARTIAL_Z, ///< See: SLV_CONSISTENT_PARTIAL_Z (PR #10570) XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit XSLFI_HEIGHT_8_BIT, ///< Map tile height is 8 bit instead of 4 bit, but savegame version may be before this became true in trunk diff --git a/src/saveload/saveload_common.h b/src/saveload/saveload_common.h index 10679d1d56..21619dd254 100644 --- a/src/saveload/saveload_common.h +++ b/src/saveload/saveload_common.h @@ -357,6 +357,7 @@ enum SaveLoadVersion : uint16 { SLV_LINKGRAPH_EDGES, ///< 304 PR#10314 Explicitly store link graph edges destination. SLV_VELOCITY_NAUTICAL, ///< 305 PR#10594 Separation of land and nautical velocity (knots!) + SLV_CONSISTENT_PARTIAL_Z, ///< 306 PR#10570 Conversion from an inconsistent partial Z calculation for slopes, to one that is (more) consistent. SL_MAX_VERSION, ///< Highest possible saveload version diff --git a/src/slope_func.h b/src/slope_func.h index e37d9b4c41..5cf125f8c4 100644 --- a/src/slope_func.h +++ b/src/slope_func.h @@ -21,7 +21,7 @@ * @param corner A #Corner. * @return true iff corner is in a valid range. */ -static inline bool IsValidCorner(Corner corner) +static constexpr inline bool IsValidCorner(Corner corner) { return IsInsideMM(corner, 0, CORNER_END); } @@ -33,7 +33,7 @@ static inline bool IsValidCorner(Corner corner) * @param s The given #Slope. * @return True if the slope is steep, else false. */ -static inline bool IsSteepSlope(Slope s) +static constexpr inline bool IsSteepSlope(Slope s) { return (s & SLOPE_STEEP) != 0; } @@ -44,7 +44,7 @@ static inline bool IsSteepSlope(Slope s) * @param s The given #Slope. * @return True if the slope is non-continuous, else false. */ -static inline bool IsHalftileSlope(Slope s) +static constexpr inline bool IsHalftileSlope(Slope s) { return (s & SLOPE_HALFTILE) != 0; } @@ -57,7 +57,7 @@ static inline bool IsHalftileSlope(Slope s) * @param s A #Slope. * @return The slope s without its halftile slope. */ -static inline Slope RemoveHalftileSlope(Slope s) +static constexpr inline Slope RemoveHalftileSlope(Slope s) { return s & ~SLOPE_HALFTILE_MASK; } @@ -145,7 +145,7 @@ static inline Corner GetHighestSlopeCorner(Slope s) * @param s The #Slope. * @return The corner of the leveled halftile. */ -static inline Corner GetHalftileSlopeCorner(Slope s) +static constexpr inline Corner GetHalftileSlopeCorner(Slope s) { dbg_assert(IsHalftileSlope(s)); return (Corner)((s >> 6) & 3); @@ -157,7 +157,7 @@ static inline Corner GetHalftileSlopeCorner(Slope s) * @param s The #Slope. * @return Relative height of highest corner. */ -static inline int GetSlopeMaxZ(Slope s) +static constexpr inline int GetSlopeMaxZ(Slope s) { if (s == SLOPE_FLAT) return 0; if (IsSteepSlope(s)) return 2; @@ -170,7 +170,7 @@ static inline int GetSlopeMaxZ(Slope s) * @param s The #Slope. * @return Relative height of highest corner. */ -static inline int GetSlopeMaxPixelZ(Slope s) +static constexpr inline int GetSlopeMaxPixelZ(Slope s) { return GetSlopeMaxZ(s) * TILE_HEIGHT; } @@ -271,7 +271,7 @@ static inline Slope InclinedSlope(DiagDirection dir) * @param corner The #Corner of the halftile. * @return The #Slope s with the halftile slope added. */ -static inline Slope HalftileSlope(Slope s, Corner corner) +static constexpr inline Slope HalftileSlope(Slope s, Corner corner) { dbg_assert(IsValidCorner(corner)); return (Slope)(s | SLOPE_HALFTILE | (corner << 6)); diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index be59517812..5e9a730625 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -3668,7 +3668,7 @@ void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, Ro DrawRailTileSeqInGUI(x, y, t, (st == STATION_WAYPOINT || st == STATION_ROADWAYPOINT) ? 0 : total_offset, 0, pal); } -static int GetSlopePixelZ_Station(TileIndex tile, uint x, uint y) +static int GetSlopePixelZ_Station(TileIndex tile, uint x, uint y, bool ground_vehicle) { return GetTileMaxPixelZ(tile); } diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt new file mode 100644 index 0000000000..d028125ae9 --- /dev/null +++ b/src/tests/CMakeLists.txt @@ -0,0 +1,3 @@ +add_files( + landscape_partial_pixel_z.h +) diff --git a/src/tests/landscape_partial_pixel_z.h b/src/tests/landscape_partial_pixel_z.h new file mode 100644 index 0000000000..a825709392 --- /dev/null +++ b/src/tests/landscape_partial_pixel_z.h @@ -0,0 +1,552 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file landscape_partial_pixel_z.h Tests for consistency/validity of the results of GetPartialPixelZ. */ + +/** + * Check whether the addition of two slope's GetPartialPixelZ values results in + * the GetPartialPixelZ values of the expected slope. + * This iterates over all sub-pixel locations within a single tile. + * @param slope_expected The slope that is expected. + * @param slope_a The first slope of the addition. + * @param slope_b The second slope of the addition. + * @return True iff at all GetPartialPixelZ results are the same for each sub-tile position. + */ +constexpr bool CheckPartialPixelZSlopeAddition(Slope slope_expected, Slope slope_a, Slope slope_b) +{ + for (uint x = 0; x < TILE_SIZE; x++) { + for (uint y = 0; y < TILE_SIZE; y++) { + int z_a = InternalGetPartialPixelZ(x, y, slope_a); + int z_b = InternalGetPartialPixelZ(x, y, slope_b); + int z_result = InternalGetPartialPixelZ(x, y, slope_expected); + if (z_result != z_a + z_b) return false; + } + } + return true; +} + +/* A one corner slope, plus the opposite three corner slope results in a flat but elevated slope. */ +static_assert(CheckPartialPixelZSlopeAddition(SLOPE_ELEVATED, SLOPE_N, SLOPE_WSE)); +static_assert(CheckPartialPixelZSlopeAddition(SLOPE_ELEVATED, SLOPE_E, SLOPE_NWS)); +static_assert(CheckPartialPixelZSlopeAddition(SLOPE_ELEVATED, SLOPE_S, SLOPE_ENW)); +static_assert(CheckPartialPixelZSlopeAddition(SLOPE_ELEVATED, SLOPE_W, SLOPE_SEN)); + +/* Diagonal slopes with their opposite slope result in a flat but elevated slope. */ +static_assert(CheckPartialPixelZSlopeAddition(SLOPE_ELEVATED, SLOPE_NW, SLOPE_SE)); +static_assert(CheckPartialPixelZSlopeAddition(SLOPE_ELEVATED, SLOPE_SW, SLOPE_NE)); + +/* Half tile slopes with their opposite half tile slope result in a flat but elevated slope. */ +static_assert(CheckPartialPixelZSlopeAddition(SLOPE_ELEVATED, HalftileSlope(SLOPE_N, CORNER_N), HalftileSlope(SLOPE_S, CORNER_S))); +static_assert(CheckPartialPixelZSlopeAddition(SLOPE_ELEVATED, HalftileSlope(SLOPE_E, CORNER_E), HalftileSlope(SLOPE_W, CORNER_W))); + +/* Two opposite one corner slopes result in the two corner slope with opposite corners. */ +static_assert(CheckPartialPixelZSlopeAddition(SLOPE_NS, SLOPE_N, SLOPE_S)); +static_assert(CheckPartialPixelZSlopeAddition(SLOPE_EW, SLOPE_E, SLOPE_W)); + +/* A steep slope is a one corner slope on top of a three corner slope. */ +static_assert(CheckPartialPixelZSlopeAddition(SLOPE_STEEP_N, SLOPE_N, SLOPE_ENW)); +static_assert(CheckPartialPixelZSlopeAddition(SLOPE_STEEP_E, SLOPE_E, SLOPE_SEN)); +static_assert(CheckPartialPixelZSlopeAddition(SLOPE_STEEP_S, SLOPE_S, SLOPE_WSE)); +static_assert(CheckPartialPixelZSlopeAddition(SLOPE_STEEP_W, SLOPE_W, SLOPE_NWS)); + +/* A half tile steep slope is a one corner half tile on top of a three corner slope. */ +static_assert(CheckPartialPixelZSlopeAddition(HalftileSlope(SLOPE_STEEP_N, CORNER_N), HalftileSlope(SLOPE_N, CORNER_N), SLOPE_ENW)); +static_assert(CheckPartialPixelZSlopeAddition(HalftileSlope(SLOPE_STEEP_E, CORNER_E), HalftileSlope(SLOPE_E, CORNER_E), SLOPE_SEN)); +static_assert(CheckPartialPixelZSlopeAddition(HalftileSlope(SLOPE_STEEP_S, CORNER_S), HalftileSlope(SLOPE_S, CORNER_S), SLOPE_WSE)); +static_assert(CheckPartialPixelZSlopeAddition(HalftileSlope(SLOPE_STEEP_W, CORNER_W), HalftileSlope(SLOPE_W, CORNER_W), SLOPE_NWS)); + + +/** + * Check whether the partial pixel Z values are the expected values. The arrays + * are as if the map is rotated 45 degrees counterclockwise. + * @param slope The slope that is to be checked. + * @param expected The expect partial pixels Z values. + * @return True iff at all GetPartialPixelZ results are the same as the expected Z-coordinates. + */ +constexpr bool CheckPartialPixelZ(Slope slope, std::array expected) +{ + for (uint i = 0; i < expected.size(); i++) { + int actual = InternalGetPartialPixelZ(GB(i, 4, 4), GB(i, 0, 4), slope); + if (actual != expected[i]) return false; + } + return true; +} + +/* One corner up slopes. */ +static_assert(CheckPartialPixelZ(SLOPE_N, { + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, + 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, + 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, + 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, + 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, + 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})); + +static_assert(CheckPartialPixelZ(SLOPE_E, { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, + 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})); + +static_assert(CheckPartialPixelZ(SLOPE_S, { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, + 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, + 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7})); + +static_assert(CheckPartialPixelZ(SLOPE_W, { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, + 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, + 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, + 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, + 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0})); + +/* Two corner up, diagonal slopes. */ +static_assert(CheckPartialPixelZ(SLOPE_NE, { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})); + +static_assert(CheckPartialPixelZ(SLOPE_SE, { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8})); + +static_assert(CheckPartialPixelZ(SLOPE_SW, { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8})); + +static_assert(CheckPartialPixelZ(SLOPE_NW, { + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0})); + +/* Two opposite corner up slopes. */ +static_assert(CheckPartialPixelZ(SLOPE_NS, { + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, + 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 1, + 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 1, 1, + 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 1, 1, 2, + 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 1, 1, 2, 2, + 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, + 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, + 4, 3, 3, 2, 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, 4, + 3, 3, 2, 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, + 3, 2, 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, + 2, 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, + 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, + 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7})); + +static_assert(CheckPartialPixelZ(SLOPE_EW, { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, + 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, + 2, 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, + 3, 2, 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, + 3, 3, 2, 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, + 4, 3, 3, 2, 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, 4, + 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, + 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, + 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 1, 1, 2, 2, + 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 1, 1, 2, + 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 1, 1, + 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 1, + 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0})); + +/* Three corner up slopes. */ +static_assert(CheckPartialPixelZ(SLOPE_ENW, { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, 5, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, 5, 5, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, 5, 5, 4, + 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, + 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, + 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, + 8, 8, 8, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, + 8, 8, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, + 8, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, + 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1})); + +static_assert(CheckPartialPixelZ(SLOPE_SEN, { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, + 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, + 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, + 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, + 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, + 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8})); + +static_assert(CheckPartialPixelZ(SLOPE_WSE, { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, + 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, + 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, + 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, + 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8})); + +static_assert(CheckPartialPixelZ(SLOPE_NWS, { + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, + 8, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, + 8, 8, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, + 8, 8, 8, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, + 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, + 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, + 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, 5, 5, 4, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, 5, 5, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, 5, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8})); + +/* Normal half tile slopes. */ +static_assert(CheckPartialPixelZ(HalftileSlope(SLOPE_N, CORNER_N), { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})); + +static_assert(CheckPartialPixelZ(HalftileSlope(SLOPE_E, CORNER_E), { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8})); + +static_assert(CheckPartialPixelZ(HalftileSlope(SLOPE_S, CORNER_S), { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8})); + +static_assert(CheckPartialPixelZ(HalftileSlope(SLOPE_W, CORNER_W), { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0})); + +/* Steep slopes. */ +static_assert(CheckPartialPixelZ(SLOPE_STEEP_N, { + 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, + 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, + 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, + 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, + 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, + 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, + 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, + 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, + 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, + 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, + 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, + 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, + 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, + 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, + 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, + 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1})); + +static_assert(CheckPartialPixelZ(SLOPE_STEEP_E, { + 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, + 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, + 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, + 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, + 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, + 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, + 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, + 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, + 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, + 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, + 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, + 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, + 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8})); + +static_assert(CheckPartialPixelZ(SLOPE_STEEP_S, { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, + 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, + 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, + 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, + 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, + 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, + 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, + 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, + 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, + 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, + 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15})); + +static_assert(CheckPartialPixelZ(SLOPE_STEEP_W, { + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, + 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, + 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, + 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, + 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, + 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, + 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, + 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, + 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, + 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, + 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, + 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, + 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, + 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, + 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8})); + +/* Half tile on top of steep slopes. */ +static_assert(CheckPartialPixelZ(HalftileSlope(SLOPE_STEEP_N, CORNER_N), { + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 7, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 7, 7, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 7, 7, 6, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 7, 7, 6, 6, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 7, 7, 6, 6, 5, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 7, 7, 6, 6, 5, 5, + 16, 16, 16, 16, 16, 16, 16, 16, 8, 7, 7, 6, 6, 5, 5, 4, + 16, 16, 16, 16, 16, 16, 16, 8, 7, 7, 6, 6, 5, 5, 4, 4, + 16, 16, 16, 16, 16, 16, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, + 16, 16, 16, 16, 16, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, + 16, 16, 16, 16, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, + 16, 16, 16, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, + 16, 16, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, + 16, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1})); + +static_assert(CheckPartialPixelZ(HalftileSlope(SLOPE_STEEP_E, CORNER_E), { + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 7, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 7, 7, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 6, 7, 7, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 6, 6, 7, 7, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 5, 6, 6, 7, 7, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 5, 5, 6, 6, 7, 7, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 4, 5, 5, 6, 6, 7, 7, 8, 16, 16, 16, 16, 16, 16, 16, 16, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 16, 16, 16, 16, 16, 16, 16, + 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 16, 16, 16, 16, 16, 16, + 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 16, 16, 16, 16, 16, + 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 16, 16, 16, 16, + 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 16, 16, 16, + 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 16, 16, + 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 16})); + +static_assert(CheckPartialPixelZ(HalftileSlope(SLOPE_STEEP_S, CORNER_S), { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 16, + 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 16, 16, + 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 16, 16, 16, + 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 16, 16, 16, 16, + 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 16, 16, 16, 16, 16, + 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 16, 16, 16, 16, 16, 16, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 16, 16, 16, 16, 16, 16, 16, + 4, 5, 5, 6, 6, 7, 7, 8, 16, 16, 16, 16, 16, 16, 16, 16, + 5, 5, 6, 6, 7, 7, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 5, 6, 6, 7, 7, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 6, 6, 7, 7, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 6, 7, 7, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 7, 7, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 7, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16})); + +static_assert(CheckPartialPixelZ(HalftileSlope(SLOPE_STEEP_W, CORNER_W), { + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 16, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, + 16, 16, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, + 16, 16, 16, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, + 16, 16, 16, 16, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, + 16, 16, 16, 16, 16, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, + 16, 16, 16, 16, 16, 16, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, + 16, 16, 16, 16, 16, 16, 16, 8, 7, 7, 6, 6, 5, 5, 4, 4, + 16, 16, 16, 16, 16, 16, 16, 16, 8, 7, 7, 6, 6, 5, 5, 4, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 7, 7, 6, 6, 5, 5, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 7, 7, 6, 6, 5, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 7, 7, 6, 6, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 7, 7, 6, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 7, 7, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 7, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8})); diff --git a/src/tile_cmd.h b/src/tile_cmd.h index f764f726de..7f0e07942a 100644 --- a/src/tile_cmd.h +++ b/src/tile_cmd.h @@ -81,7 +81,20 @@ struct DrawTileProcParams { * @param ti Information about the tile to draw */ typedef void DrawTileProc(TileInfo *ti, DrawTileProcParams params); -typedef int GetSlopeZProc(TileIndex tile, uint x, uint y); + +/** + * Tile callback function signature for obtaining the world \c Z coordinate of a given + * point of a tile. + * + * @param tile The queries tile for the Z coordinate. + * @param x World X coordinate in tile "units". + * @param y World Y coordinate in tile "units". + * @param ground_vehicle Whether to get the Z coordinate of the ground vehicle, or the ground. + * @return World Z coordinate at tile ground (vehicle) level, including slopes and foundations. + * @see GetSlopePixelZ + */ +typedef int GetSlopeZProc(TileIndex tile, uint x, uint y, bool ground_vehicle); + typedef CommandCost ClearTileProc(TileIndex tile, DoCommandFlag flags); /** diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 8486101f6c..5bc6ee0ae5 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -413,7 +413,7 @@ void DrawHouseImage(HouseID house_id, int left, int top, int right, int bottom) } } -static int GetSlopePixelZ_Town(TileIndex tile, uint x, uint y) +static int GetSlopePixelZ_Town(TileIndex tile, uint x, uint y, bool ground_vehicle) { return GetTileMaxPixelZ(tile); } diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 17408516ef..4bf31387d3 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1462,7 +1462,7 @@ static CommandCost CmdBuildRailWagon(TileIndex tile, DoCommandFlag flags, const v->x_pos = x; v->y_pos = y; - v->z_pos = GetSlopePixelZ(x, y); + v->z_pos = GetSlopePixelZ(x, y, true); v->owner = _current_company; v->track = TRACK_BIT_DEPOT; v->vehstatus = VS_HIDDEN | VS_DEFPAL; @@ -1602,7 +1602,7 @@ CommandCost CmdBuildRailVehicle(TileIndex tile, DoCommandFlag flags, const Engin v->owner = _current_company; v->x_pos = x; v->y_pos = y; - v->z_pos = GetSlopePixelZ(x, y); + v->z_pos = GetSlopePixelZ(x, y, true); v->track = TRACK_BIT_DEPOT; SetBit(v->flags, VRF_CONSIST_SPEED_REDUCTION); v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL; @@ -5778,7 +5778,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) if (v->IsFrontEngine()) { /* Check if track in front is free and see if we can leave wormhole. */ - int z = GetSlopePixelZ(gp.x, gp.y) - v->z_pos; + int z = GetSlopePixelZ(gp.x, gp.y, true) - v->z_pos; if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && !(abs(z) > 2)) { if (CheckTrainStayInWormHole(v, gp.new_tile)) { v->cur_speed = 0; diff --git a/src/tree_cmd.cpp b/src/tree_cmd.cpp index 80c842258f..4434eb6313 100644 --- a/src/tree_cmd.cpp +++ b/src/tree_cmd.cpp @@ -788,7 +788,7 @@ static void DrawTile_Trees(TileInfo *ti, DrawTileProcParams params) } -static int GetSlopePixelZ_Trees(TileIndex tile, uint x, uint y) +static int GetSlopePixelZ_Trees(TileIndex tile, uint x, uint y, bool ground_vehicle) { int z; Slope tileh = GetTilePixelSlope(tile, &z); diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index e76155b0d9..2f4dd7529b 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -2563,7 +2563,7 @@ void DrawBridgeMiddle(const TileInfo *ti) } -static int GetSlopePixelZ_TunnelBridge(TileIndex tile, uint x, uint y) +static int GetSlopePixelZ_TunnelBridge(TileIndex tile, uint x, uint y, bool ground_vehicle) { int z; Slope tileh = GetTilePixelSlope(tile, &z); @@ -2572,34 +2572,27 @@ static int GetSlopePixelZ_TunnelBridge(TileIndex tile, uint x, uint y) y &= 0xF; if (IsTunnel(tile)) { - uint pos = (DiagDirToAxis(GetTunnelBridgeDirection(tile)) == AXIS_X ? y : x); - /* In the tunnel entrance? */ - if (5 <= pos && pos <= 10) return z; + if (ground_vehicle) return z; } else { // IsBridge(tile) if (IsCustomBridgeHeadTile(tile)) { return z + TILE_HEIGHT + (IsSteepSlope(tileh) ? TILE_HEIGHT : 0); } DiagDirection dir = GetTunnelBridgeDirection(tile); - uint pos = (DiagDirToAxis(dir) == AXIS_X ? y : x); - z += ApplyPixelFoundationToSlope(GetBridgeFoundation(tileh, DiagDirToAxis(dir)), &tileh); /* On the bridge ramp? */ - if (5 <= pos && pos <= 10) { - int delta; - + if (ground_vehicle) { if (tileh != SLOPE_FLAT) return z + TILE_HEIGHT; switch (dir) { default: NOT_REACHED(); - case DIAGDIR_NE: delta = (TILE_SIZE - 1 - x) / 2; break; - case DIAGDIR_SE: delta = y / 2; break; - case DIAGDIR_SW: delta = x / 2; break; - case DIAGDIR_NW: delta = (TILE_SIZE - 1 - y) / 2; break; + case DIAGDIR_NE: tileh = SLOPE_NE; break; + case DIAGDIR_SE: tileh = SLOPE_SE; break; + case DIAGDIR_SW: tileh = SLOPE_SW; break; + case DIAGDIR_NW: tileh = SLOPE_NW; break; } - return z + 1 + delta; } } @@ -3063,6 +3056,29 @@ static void ChangeTileOwner_TunnelBridge(TileIndex tile, Owner old_owner, Owner } } +/** + * Helper to prepare the ground vehicle when entering a bridge. This get called + * when entering the bridge, at the last frame of travel on the bridge head. + * Our calling function gets called before UpdateInclination/UpdateZPosition, + * which normally controls the Z-coordinate. However, in the wormhole of the + * bridge the vehicle is in a strange state so UpdateInclination does not get + * called for the wormhole of the bridge and as such the going up/down bits + * would remain set. As such, this function clears those. In doing so, the call + * to UpdateInclination will not update the Z-coordinate, so that has to be + * done here as well. + * @param gv The ground vehicle entering the bridge. + */ +template +static void PrepareToEnterBridge(T *gv) +{ + if (HasBit(gv->gv_flags, GVF_GOINGUP_BIT)) { + gv->z_pos++; + ClrBit(gv->gv_flags, GVF_GOINGUP_BIT); + } else { + ClrBit(gv->gv_flags, GVF_GOINGDOWN_BIT); + } +} + /** * Frame when the 'enter tunnel' sound should be played. This is the second * frame on a tile, so the sound is played shortly after entering the tunnel @@ -3098,7 +3114,7 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti y += offset.y; } - int z = GetSlopePixelZ(x, y) - v->z_pos; + int z = GetSlopePixelZ(x, y, true) - v->z_pos; if (abs(z) > 2) return VETSB_CANNOT_ENTER; @@ -3188,8 +3204,7 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti Train *t = Train::From(v); t->track = TRACK_BIT_WORMHOLE; SetBit(t->First()->flags, VRF_CONSIST_SPEED_REDUCTION); - ClrBit(t->gv_flags, GVF_GOINGUP_BIT); - ClrBit(t->gv_flags, GVF_GOINGDOWN_BIT); + PrepareToEnterBridge(t); break; } @@ -3205,9 +3220,7 @@ static VehicleEnterTileStatus VehicleEnter_TunnelBridge(Vehicle *v, TileIndex ti } rv->InvalidateImageCache(); rv->state = RVSB_WORMHOLE; - /* There are no slopes inside bridges / tunnels. */ - ClrBit(rv->gv_flags, GVF_GOINGUP_BIT); - ClrBit(rv->gv_flags, GVF_GOINGDOWN_BIT); + PrepareToEnterBridge(rv); break; } diff --git a/src/void_cmd.cpp b/src/void_cmd.cpp index fc3895c422..d492eefe8b 100644 --- a/src/void_cmd.cpp +++ b/src/void_cmd.cpp @@ -25,7 +25,7 @@ static void DrawTile_Void(TileInfo *ti, DrawTileProcParams params) } -static int GetSlopePixelZ_Void(TileIndex tile, uint x, uint y) +static int GetSlopePixelZ_Void(TileIndex tile, uint x, uint y, bool ground_vehicle) { /* This function may be called on tiles outside the map, don't assume * that 'tile' is a valid tile index. See GetSlopePixelZOutsideMap. */ diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index 5a67678ffa..c515acb82f 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -997,7 +997,7 @@ void DrawShipDepotSprite(int x, int y, Axis axis, DepotPart part) } -static int GetSlopePixelZ_Water(TileIndex tile, uint x, uint y) +static int GetSlopePixelZ_Water(TileIndex tile, uint x, uint y, bool ground_vehicle) { int z; Slope tileh = GetTilePixelSlope(tile, &z);