# HG changeset patch # User rubidium # Date 2007-10-20 16:50:48 # Node ID ffc843cdbcf71c5d4b4323e7b832b5d6d8c58779 # Parent 878d494b5f5726aacd00d1369fdbee02b61d2de3 (svn r11313) -Codechange: prepare several pieces of code so the can handle some new slopes. Patch by frosch. diff --git a/src/bridge_map.cpp b/src/bridge_map.cpp --- a/src/bridge_map.cpp +++ b/src/bridge_map.cpp @@ -7,6 +7,7 @@ #include "bridge_map.h" #include "bridge.h" #include "variables.h" +#include "landscape.h" TileIndex GetBridgeEnd(TileIndex tile, DiagDirection dir) @@ -46,9 +47,6 @@ uint GetBridgeHeight(TileIndex t) Slope tileh = GetTileSlope(t, &h); Foundation f = GetBridgeFoundation(tileh, DiagDirToAxis(GetBridgeRampDirection(t))); - /* one height level extra if the ramp is on a flat foundation */ - return - h + TILE_HEIGHT + - (IsLeveledFoundation(f) ? TILE_HEIGHT : 0) + - (IsSteepSlope(tileh) ? TILE_HEIGHT : 0); + /* one height level extra for the ramp */ + return h + TILE_HEIGHT + ApplyFoundationToSlope(f, &tileh); } diff --git a/src/elrail.cpp b/src/elrail.cpp --- a/src/elrail.cpp +++ b/src/elrail.cpp @@ -213,6 +213,10 @@ static void DrawCatenaryRailway(const Ti /* Note that ti->tileh has already been adjusted for Foundations */ Slope tileh[TS_END] = { ti->tileh, SLOPE_FLAT }; + /* Half tile slopes coincide only with horizontal/vertical track. + * Faking a flat slope results in the correct sprites on positions. */ + if (IsHalftileSlope(tileh[TS_HOME])) tileh[TS_HOME] = SLOPE_FLAT; + TLG tlg = GetTLG(ti->tile); byte PCPstatus = 0; byte OverridePCP = 0; @@ -291,6 +295,10 @@ static void DrawCatenaryRailway(const Ti ApplyFoundationToSlope(foundation, &tileh[TS_NEIGHBOUR]); + /* Half tile slopes coincide only with horizontal/vertical track. + * Faking a flat slope results in the correct sprites on positions. */ + if (IsHalftileSlope(tileh[TS_NEIGHBOUR])) tileh[TS_NEIGHBOUR] = SLOPE_FLAT; + AdjustTileh(neighbour, &tileh[TS_NEIGHBOUR]); /* If we have a straight (and level) track, we want a pylon only every 2 tiles diff --git a/src/landscape.cpp b/src/landscape.cpp --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -104,9 +104,31 @@ uint ApplyFoundationToSlope(Foundation f uint GetPartialZ(int x, int y, Slope corners) { + if (IsHalftileSlope(corners)) { + switch (GetHalftileSlopeCorner(corners)) { + case CORNER_W: + if (x - y >= 0) return GetSlopeMaxZ(corners); + break; + + case CORNER_S: + if (x - (y ^ 0xF) >= 0) return GetSlopeMaxZ(corners); + break; + + case CORNER_E: + if (y - x >= 0) return GetSlopeMaxZ(corners); + break; + + case CORNER_N: + if ((y ^ 0xF) - x >= 0) return GetSlopeMaxZ(corners); + break; + + default: NOT_REACHED(); + } + } + int z = 0; - switch (corners) { + switch (corners & ~SLOPE_HALFTILE_MASK) { case SLOPE_W: if (x - y >= 0) z = (x - y) >> 1; @@ -209,6 +231,8 @@ uint GetSlopeZ(int x, int y) /** * Determine the Z height of the corners of a specific tile edge * + * @note If a tile has a non-continuous halftile foundation, a corner can have different heights wrt. it's edges. + * * @pre z1 and z2 must be initialized (typ. with TileZ). The corner heights just get added. * * @param tileh The slope of the tile. @@ -227,10 +251,14 @@ void GetSlopeZOnEdge(Slope tileh, DiagDi {SLOPE_W, SLOPE_N, SLOPE_STEEP_W, SLOPE_STEEP_N}, // DIAGDIR_NW, z1 = W, z2 = N }; + int halftile_test = (IsHalftileSlope(tileh) ? SlopeWithOneCornerRaised(GetHalftileSlopeCorner(tileh)) : 0); + if (halftile_test == corners[edge][0]) *z2 += TILE_HEIGHT; // The slope is non-continuous in z2. z2 is on the upper side. + if (halftile_test == corners[edge][1]) *z1 += TILE_HEIGHT; // The slope is non-continuous in z1. z1 is on the upper side. + if ((tileh & corners[edge][0]) != 0) *z1 += TILE_HEIGHT; // z1 is raised if ((tileh & corners[edge][1]) != 0) *z2 += TILE_HEIGHT; // z2 is raised - if (tileh == corners[edge][2]) *z1 += TILE_HEIGHT; // z1 is highest corner of a steep slope - if (tileh == corners[edge][3]) *z2 += TILE_HEIGHT; // z2 is highest corner of a steep slope + if ((tileh & ~SLOPE_HALFTILE_MASK) == corners[edge][2]) *z1 += TILE_HEIGHT; // z1 is highest corner of a steep slope + if ((tileh & ~SLOPE_HALFTILE_MASK) == corners[edge][3]) *z2 += TILE_HEIGHT; // z2 is highest corner of a steep slope } static Slope GetFoundationSlope(TileIndex tile, uint* z) diff --git a/src/npf.cpp b/src/npf.cpp --- a/src/npf.cpp +++ b/src/npf.cpp @@ -192,18 +192,21 @@ static inline uint NPFBridgeCost(AyStarN static uint NPFSlopeCost(AyStarNode* current) { TileIndex next = current->tile + TileOffsByDiagDir(TrackdirToExitdir((Trackdir)current->direction)); - int x,y; - int8 z1,z2; + + /* Get center of tiles */ + int x1 = TileX(current->tile) * TILE_SIZE + TILE_SIZE / 2; + int y1 = TileY(current->tile) * TILE_SIZE + TILE_SIZE / 2; + int x2 = TileX(next) * TILE_SIZE + TILE_SIZE / 2; + int y2 = TileY(next) * TILE_SIZE + TILE_SIZE / 2; - x = TileX(current->tile) * TILE_SIZE; - y = TileY(current->tile) * TILE_SIZE; - /* get the height of the center of the current tile */ - z1 = GetSlopeZ(x + TILE_SIZE / 2, y + TILE_SIZE / 2); + int dx4 = (x2 - x1) / 4; + int dy4 = (y2 - y1) / 4; - x = TileX(next) * TILE_SIZE; - y = TileY(next) * TILE_SIZE; - /* get the height of the center of the next tile */ - z2 = GetSlopeZ(x + TILE_SIZE / 2, y + TILE_SIZE / 2); + /* 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 = GetSlopeZ(x1 + dx4, y1 + dy4); + int z2 = GetSlopeZ(x2 - dx4, y2 - dy4); if (z2 - z1 > 1) { /* Slope up */ diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -1238,6 +1238,22 @@ static CommandCost ClearTile_Track(TileI #include "table/track_land.h" +/** + * Get surface height in point (x,y) + * On tiles with halftile foundations move (x,y) to a save point wrt. track + */ +static uint GetSaveSlopeZ(uint x, uint y, Track track) +{ + switch (track) { + case TRACK_UPPER: x &= ~0xF; y &= ~0xF; break; + case TRACK_LOWER: x |= 0xF; y |= 0xF; break; + case TRACK_LEFT: x |= 0xF; y &= ~0xF; break; + case TRACK_RIGHT: x &= ~0xF; y |= 0xF; break; + default: break; + } + return GetSlopeZ(x, y); +} + static void DrawSingleSignal(TileIndex tile, Track track, byte condition, uint image, uint pos) { bool side = (_opt.road_side != 0) && _patches.signal_side; @@ -1281,7 +1297,7 @@ static void DrawSingleSignal(TileIndex t sprite = _signal_base + (GetSignalType(tile, track) - 1) * 16 + GetSignalVariant(tile, track) * 64 + image + condition; } - AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, GetSlopeZ(x,y)); + AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, GetSaveSlopeZ(x, y, track)); } static uint32 _drawtile_track_palette; @@ -1338,6 +1354,7 @@ static void DrawTrackFence_NS_1(const Ti { int z = ti->z; if (ti->tileh & SLOPE_W) z += TILE_HEIGHT; + if (IsSteepSlope(ti->tileh)) z += TILE_HEIGHT; AddSortableSpriteToDraw(SPR_TRACK_FENCE_FLAT_VERT, _drawtile_track_palette, ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2, 1, 1, 4, z); } @@ -1349,6 +1366,7 @@ static void DrawTrackFence_NS_2(const Ti { int z = ti->z; if (ti->tileh & SLOPE_E) z += TILE_HEIGHT; + if (IsSteepSlope(ti->tileh)) z += TILE_HEIGHT; AddSortableSpriteToDraw(SPR_TRACK_FENCE_FLAT_VERT, _drawtile_track_palette, ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2, 1, 1, 4, z); } @@ -1360,6 +1378,7 @@ static void DrawTrackFence_WE_1(const Ti { int z = ti->z; if (ti->tileh & SLOPE_N) z += TILE_HEIGHT; + if (IsSteepSlope(ti->tileh)) z += TILE_HEIGHT; AddSortableSpriteToDraw(SPR_TRACK_FENCE_FLAT_HORZ, _drawtile_track_palette, ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2, 1, 1, 4, z); } @@ -1371,6 +1390,7 @@ static void DrawTrackFence_WE_2(const Ti { int z = ti->z; if (ti->tileh & SLOPE_S) z += TILE_HEIGHT; + if (IsSteepSlope(ti->tileh)) z += TILE_HEIGHT; AddSortableSpriteToDraw(SPR_TRACK_FENCE_FLAT_HORZ, _drawtile_track_palette, ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2, 1, 1, 4, z); } diff --git a/src/slope.h b/src/slope.h --- a/src/slope.h +++ b/src/slope.h @@ -9,12 +9,31 @@ #define SLOPE_H /** + * Enumeration of tile corners + */ +enum Corner { + CORNER_W = 0, + CORNER_S = 1, + CORNER_E = 2, + CORNER_N = 3, + CORNER_END +}; + +/** * Enumeration for the slope-type. * * This enumeration use the chars N,E,S,W corresponding the * direction north, east, south and west. The top corner of a tile * is the north-part of the tile. The whole slope is encoded with * 5 bits, 4 bits for each corner and 1 bit for a steep-flag. + * + * For halftile slopes an extra 3 bits are used to represent this + * properly; 1 bit for a halftile-flag and 2 bits to encode which + * extra side (corner) is leveled when the slope of the first 5 + * bits is applied. This means that there can only be one leveled + * slope for steep slopes, which is logical because two leveled + * slopes would mean that it is not a steep slope as halftile + * slopes only span one height level. */ enum Slope { SLOPE_FLAT = 0x00, ///< a flat tile @@ -37,18 +56,14 @@ enum Slope { SLOPE_STEEP_W = SLOPE_STEEP | SLOPE_NWS, ///< a steep slope falling to east (from west) SLOPE_STEEP_S = SLOPE_STEEP | SLOPE_WSE, ///< a steep slope falling to north (from south) SLOPE_STEEP_E = SLOPE_STEEP | SLOPE_SEN, ///< a steep slope falling to west (from east) - SLOPE_STEEP_N = SLOPE_STEEP | SLOPE_ENW ///< a steep slope falling to south (from north) -}; + SLOPE_STEEP_N = SLOPE_STEEP | SLOPE_ENW, ///< a steep slope falling to south (from north) -/** - * Enumeration of tile corners - */ -enum Corner { - CORNER_W = 0, - CORNER_S = 1, - CORNER_E = 2, - CORNER_N = 3, - CORNER_END + SLOPE_HALFTILE = 0x20, ///< one halftile is leveled (non continuous slope) + SLOPE_HALFTILE_MASK = 0xE0, ///< three bits used for halftile slopes + SLOPE_HALFTILE_W = SLOPE_HALFTILE | (CORNER_W << 6), ///< the west halftile is leveled (non continuous slope) + SLOPE_HALFTILE_S = SLOPE_HALFTILE | (CORNER_S << 6), ///< the south halftile is leveled (non continuous slope) + SLOPE_HALFTILE_E = SLOPE_HALFTILE | (CORNER_E << 6), ///< the east halftile is leveled (non continuous slope) + SLOPE_HALFTILE_N = SLOPE_HALFTILE | (CORNER_N << 6), ///< the north halftile is leveled (non continuous slope) }; /** @@ -74,43 +89,57 @@ static inline bool IsSteepSlope(Slope s) } /** + * Checks for non-continuous slope on halftile foundations. + * + * @param s The given #Slope. + * @return True if the slope is non-continuous, else false. + */ +static inline bool IsHalftileSlope(Slope s) +{ + return (s & SLOPE_HALFTILE) != 0; +} + +/** * Return the complement of a slope. * * This method returns the complement of a slope. The complement of a * slope is a slope with raised corner which aren't raised in the given * slope. * - * @pre The slope must not be steep. + * @pre The slope must neither be steep nor a halftile slope. * @param s The #Slope to get the complement. * @return a complement Slope of the given slope. */ static inline Slope ComplementSlope(Slope s) { - assert(!IsSteepSlope(s)); + assert(!IsSteepSlope(s) && !IsHalftileSlope(s)); return (Slope)(0xF ^ s); } /** * Tests if a slope has a highest corner (i.e. one corner raised or a steep slope). * + * Note: A halftile slope is ignored. + * * @param s The #Slope. * @return true iff the slope has a highest corner. */ static inline bool HasSlopeHighestCorner(Slope s) { + s = (Slope)(s & ~SLOPE_HALFTILE_MASK); return IsSteepSlope(s) || (s == SLOPE_W) || (s == SLOPE_S) || (s == SLOPE_E) || (s == SLOPE_N); } /** * Returns the highest corner of a slope (one corner raised or a steep slope). * - * @pre The slope must be a slope with one corner raised or a steep slope. + * @pre The slope must be a slope with one corner raised or a steep slope. A halftile slope is ignored. * @param s The #Slope. * @return Highest corner. */ static inline Corner GetHighestSlopeCorner(Slope s) { - switch (s) { + switch (s & ~SLOPE_HALFTILE_MASK) { case SLOPE_W: case SLOPE_STEEP_W: return CORNER_W; case SLOPE_S: @@ -124,6 +153,19 @@ static inline Corner GetHighestSlopeCorn } /** + * Returns the leveled halftile of a halftile slope. + * + * @pre The slope must be a halftile slope. + * @param s The #Slope. + * @return The corner of the leveled halftile. + */ +static inline Corner GetHalftileSlopeCorner(Slope s) +{ + assert(IsHalftileSlope(s)); + return (Corner)((s >> 6) & 3); +} + +/** * Returns the height of the highest corner of a slope relative to TileZ (= minimal height) * * @param s The #Slope. @@ -170,6 +212,19 @@ static inline Slope SlopeWithThreeCorner return ComplementSlope(SlopeWithOneCornerRaised(corner)); } +/** + * Adds a halftile slope to a slope. + * + * @param s #Slope without a halftile slope. + * @param corner The #Corner of the halftile. + * @return The #Slope s with the halftile slope added. + */ +static inline Slope HalftileSlope(Slope s, Corner corner) +{ + assert(IsValidCorner(corner)); + return (Slope)(s | SLOPE_HALFTILE | (corner << 6)); +} + /** * Enumeration for Foundations. diff --git a/src/viewport.cpp b/src/viewport.cpp --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -362,11 +362,17 @@ static Point TranslateXYToTileCoord(cons a = clamp(a, 0, (int)(MapMaxX() * TILE_SIZE) - 1); b = clamp(b, 0, (int)(MapMaxY() * TILE_SIZE) - 1); - z = GetSlopeZ(a, b ) / 2; - z = GetSlopeZ(a + z, b + z) / 2; - z = GetSlopeZ(a + z, b + z) / 2; - z = GetSlopeZ(a + z, b + z) / 2; - z = GetSlopeZ(a + z, b + z) / 2; + /* (a, b) is the X/Y-world coordinate that belongs to (x,y) if the landscape would be completely flat on height 0. + * Now find the Z-world coordinate by fix point iteration. + * This is a bit tricky because the tile height is non-continuous at foundations. + * The clicked point should be approached from the back, otherwise there are regions that are not clickable. + * (FOUNDATION_HALFTILE_LOWER on SLOPE_STEEP_S hides north halftile completely) + * So give it a z-malus of 4 in the first iterations. + */ + z = 0; + for (int i = 0; i < 5; i++) z = GetSlopeZ(a + max(z, 4u) - 4, b + max(z, 4u) - 4) / 2; + for (uint malus = 3; malus > 0; malus--) z = GetSlopeZ(a + max(z, malus) - malus, b + max(z, malus) - malus) / 2; + for (int i = 0; i < 5; i++) z = GetSlopeZ(a + z, b + z) / 2; pt.x = a + z; pt.y = b + z;