diff options
-rw-r--r-- | src/genworld.cpp | 1 | ||||
-rw-r--r-- | src/landscape.cpp | 53 | ||||
-rw-r--r-- | src/landscape.h | 3 | ||||
-rw-r--r-- | src/map_func.h | 14 | ||||
-rw-r--r-- | src/water.h | 7 | ||||
-rw-r--r-- | src/water_cmd.cpp | 355 |
6 files changed, 260 insertions, 173 deletions
diff --git a/src/genworld.cpp b/src/genworld.cpp index c6c15cc39..f33df1a25 100644 --- a/src/genworld.cpp +++ b/src/genworld.cpp @@ -24,6 +24,7 @@ #include "engine.h" #include "settings_type.h" #include "newgrf_storage.h" +#include "water.h" #include "table/sprites.h" diff --git a/src/landscape.cpp b/src/landscape.cpp index adeaf0046..279e785c3 100644 --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -23,6 +23,7 @@ #include "date_func.h" #include "vehicle_func.h" #include "settings_type.h" +#include "water.h" #include "table/sprites.h" @@ -292,11 +293,12 @@ void GetSlopeZOnEdge(Slope tileh, DiagDirection edge, int *z1, int *z2) 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) +Slope GetFoundationSlope(TileIndex tile, uint* z) { Slope tileh = GetTileSlope(tile, z); Foundation f = _tile_type_procs[GetTileType(tile)]->get_foundation_proc(tile, tileh); - *z += ApplyFoundationToSlope(f, &tileh); + uint z_inc = ApplyFoundationToSlope(f, &tileh); + if (z != NULL) *z += z_inc; return tileh; } @@ -637,53 +639,6 @@ void InitializeLandscape() for (x = 0; x < sizex; x++) MakeVoid(sizex * y + x); } -void ConvertGroundTilesIntoWaterTiles() -{ - TileIndex tile; - uint z; - Slope slope; - - for (tile = 0; tile < MapSize(); ++tile) { - slope = GetTileSlope(tile, &z); - if (IsTileType(tile, MP_CLEAR) && z == 0) { - /* Make both water for tiles at level 0 - * and make shore, as that looks much better - * during the generation. */ - switch (slope) { - case SLOPE_FLAT: - MakeWater(tile); - break; - - case SLOPE_N: - case SLOPE_E: - case SLOPE_S: - case SLOPE_W: - MakeShore(tile); - break; - - case SLOPE_NW: - if (GetTileSlope(TileAddByDiagDir(tile, DIAGDIR_SE), NULL) != SLOPE_SE) MakeShore(tile); - break; - - case SLOPE_SW: - if (GetTileSlope(TileAddByDiagDir(tile, DIAGDIR_NE), NULL) != SLOPE_NE) MakeShore(tile); - break; - - case SLOPE_SE: - if (GetTileSlope(TileAddByDiagDir(tile, DIAGDIR_NW), NULL) != SLOPE_NW) MakeShore(tile); - break; - - case SLOPE_NE: - if (GetTileSlope(TileAddByDiagDir(tile, DIAGDIR_SW), NULL) != SLOPE_SW) MakeShore(tile); - break; - - default: - break; - } - } - } -} - static const byte _genterrain_tbl_1[5] = { 10, 22, 33, 37, 4 }; static const byte _genterrain_tbl_2[5] = { 0, 0, 0, 0, 33 }; diff --git a/src/landscape.h b/src/landscape.h index 130deb4b6..178579efb 100644 --- a/src/landscape.h +++ b/src/landscape.h @@ -32,6 +32,7 @@ uint GetPartialZ(int x, int y, Slope corners); uint GetSlopeZ(int x, int y); void GetSlopeZOnEdge(Slope tileh, DiagDirection edge, int *z1, int *z2); int GetSlopeZInCorner(Slope tileh, Corner corner); +Slope GetFoundationSlope(TileIndex tile, uint* z); static inline Point RemapCoords(int x, int y, int z) { @@ -55,8 +56,6 @@ void RunTileLoop(); void InitializeLandscape(); void GenerateLandscape(byte mode); -void ConvertGroundTilesIntoWaterTiles(); - TileIndex AdjustTileCoordRandomly(TileIndex a, byte rng); #endif /* LANDSCAPE_H */ diff --git a/src/map_func.h b/src/map_func.h index 4687668a2..dc40b1011 100644 --- a/src/map_func.h +++ b/src/map_func.h @@ -244,6 +244,20 @@ static inline TileIndexDiffC TileIndexDiffCByDiagDir(DiagDirection dir) } /** + * Returns the TileIndexDiffC offset from a Direction. + * + * @param dir The given direction + * @return The offset as TileIndexDiffC value + */ +static inline TileIndexDiffC TileIndexDiffCByDir(Direction dir) +{ + extern const TileIndexDiffC _tileoffs_by_dir[DIR_END]; + + assert(IsValidDirection(dir)); + return _tileoffs_by_dir[dir]; +} + +/** * Add a TileIndexDiffC to a TileIndex and returns the new one. * * Returns tile + the diff given in diff. If the result tile would end up diff --git a/src/water.h b/src/water.h index edd270006..94570612c 100644 --- a/src/water.h +++ b/src/water.h @@ -6,10 +6,15 @@ #define WATER_H void TileLoop_Water(TileIndex tile); +bool FloodHalftile(TileIndex t); + +void ConvertGroundTilesIntoWaterTiles(); + void DrawShipDepotSprite(int x, int y, int image); void DrawCanalWater(TileIndex tile); +void DrawShoreTile(Slope tileh); + void MakeWaterOrCanalDependingOnOwner(TileIndex tile, Owner o); void MakeWaterOrCanalDependingOnSurroundings(TileIndex t, Owner o); -bool FloodHalftile(TileIndex t); #endif /* WATER_H */ diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index eeed4a696..681ccb331 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -33,12 +33,46 @@ #include "variables.h" #include "player_func.h" #include "settings_type.h" +#include "clear_map.h" #include "table/sprites.h" #include "table/strings.h" -static Vehicle *FindFloodableVehicleOnTile(TileIndex tile); -static void FloodVehicle(Vehicle *v); +/** + * Describes the behaviour of a tile during flooding. + */ +enum FloodingBehaviour { + FLOOD_NONE, ///< The tile does not flood neighboured tiles. + FLOOD_ACTIVE, ///< The tile floods neighboured tiles. + FLOOD_PASSIVE, ///< The tile does not actively flood neighboured tiles, but it prevents them from drying up. + FLOOD_DRYUP, ///< The tile drys up if it is not constantly flooded from neighboured tiles. +}; + +/** + * Describes from which directions a specific slope can be flooded (if the tile is floodable at all). + */ +static const uint8 _flood_from_dirs[] = { + (1 << DIR_NW) | (1 << DIR_SW) | (1 << DIR_SE) | (1 << DIR_NE), // SLOPE_FLAT + (1 << DIR_NE) | (1 << DIR_SE), // SLOPE_W + (1 << DIR_NW) | (1 << DIR_NE), // SLOPE_S + (1 << DIR_NE), // SLOPE_SW + (1 << DIR_NW) | (1 << DIR_SW), // SLOPE_E + 0, // SLOPE_EW + (1 << DIR_NW), // SLOPE_SE + (1 << DIR_N ) | (1 << DIR_NW) | (1 << DIR_NE), // SLOPE_WSE, SLOPE_STEEP_S + (1 << DIR_SW) | (1 << DIR_SE), // SLOPE_N + (1 << DIR_SE), // SLOPE_NW + 0, // SLOPE_NS + (1 << DIR_E ) | (1 << DIR_NE) | (1 << DIR_SE), // SLOPE_NWS, SLOPE_STEEP_W + (1 << DIR_SW), // SLOPE_NE + (1 << DIR_S ) | (1 << DIR_SW) | (1 << DIR_SE), // SLOPE_ENW, SLOPE_STEEP_N + (1 << DIR_W ) | (1 << DIR_SW) | (1 << DIR_NW), // SLOPE_SEN, SLOPE_STEEP_E +}; + +/** + * Slopes that contain flat water and not only shore. + */ +static const uint32 _active_water_slopes = (1 << SLOPE_FLAT) | (1 << SLOPE_W) | (1 << SLOPE_S) | (1 << SLOPE_E) | (1 << SLOPE_N); /** * Makes a tile canal or water depending on the surroundings. @@ -520,6 +554,23 @@ static void DrawRiverWater(const TileInfo *ti) DrawWaterEdges(edges_base, ti->tile); } +void DrawShoreTile(Slope tileh) +{ + /* Converts the enum Slope into an offset based on SPR_SHORE_BASE. + * This allows to calculate the proper sprite to display for this Slope */ + static const byte tileh_to_shoresprite[32] = { + 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0, + }; + + assert(!IsHalftileSlope(tileh)); // Halftile slopes need to get handled earlier. + assert(tileh != SLOPE_FLAT); // Shore is never flat + + assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS)); // No suitable sprites for current flooding behaviour + + DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE); +} + static void DrawTile_Water(TileInfo *ti) { switch (GetWaterTileType(ti->tile)) { @@ -530,15 +581,7 @@ static void DrawTile_Water(TileInfo *ti) break; case WATER_TILE_COAST: { - /* Converts the enum Slope into an offset based on SPR_SHORE_BASE. - * This allows to calculate the proper sprite to display for this Slope */ - static const byte tileh_to_shoresprite[32] = { - 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0, - }; - - assert(!IsSteepSlope(ti->tileh)); - DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[ti->tileh], PAL_NONE); + DrawShoreTile(ti->tileh); DrawBridgeMiddle(ti); } break; @@ -625,84 +668,6 @@ static inline void MarkTileDirtyIfCanal(TileIndex tile) if (IsTileType(tile, MP_WATER) && IsCanal(tile)) MarkTileDirtyByTile(tile); } -/** - * Floods neighboured floodable tiles - * - * @param tile The water source tile that causes the flooding. - * @param offs[0] Destination tile to flood. - * @param offs[1] First corner of edge between source and dest tile. - * @param offs[2] Second corder of edge between source and dest tile. - * @param offs[3] Third corner of dest tile. - * @param offs[4] Fourth corner of dest tile. - */ -static void TileLoopWaterHelper(TileIndex tile, const TileIndexDiffC *offs) -{ - TileIndex target = TILE_ADD(tile, ToTileIndexDiff(offs[0])); - - /* type of this tile mustn't be water already. */ - if (IsTileType(target, MP_WATER)) return; - - /* Are both corners of the edge between source and dest on height 0 ? */ - if (TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[1]))) != 0 || - TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[2]))) != 0) { - return; - } - - bool flooded = false; // Will be set to true, when something is flooded - - /* Is any corner of the dest tile raised? (First two corners already checked above. */ - if (TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[3]))) != 0 || - TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[4]))) != 0) { - /* make coast.. */ - switch (GetTileType(target)) { - case MP_RAILWAY: { - if (!IsPlainRailTile(target)) break; - - flooded = FloodHalftile(target); - - Vehicle *v = FindFloodableVehicleOnTile(target); - if (v != NULL) FloodVehicle(v); - - break; - } - - case MP_CLEAR: - case MP_TREES: - _current_player = OWNER_WATER; - if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) { - flooded = true; - MakeShore(target); - MarkTileDirtyByTile(target); - } - break; - - default: - break; - } - } else { - /* Flood vehicles */ - _current_player = OWNER_WATER; - - Vehicle *v = FindFloodableVehicleOnTile(target); - if (v != NULL) FloodVehicle(v); - - /* flood flat tile */ - if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) { - flooded = true; - MakeWater(target); - MarkTileDirtyByTile(target); - } - } - - if (flooded) { - /* Mark surrounding canal tiles dirty too to avoid glitches */ - for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) { - MarkTileDirtyIfCanal(target + TileOffsByDir(dir)); - } - /* update signals if needed */ - UpdateSignalsInBuffer(); - } -} /** * Finds a vehicle to flood. @@ -827,54 +792,202 @@ static void FloodVehicle(Vehicle *v) } /** - * Let a water tile floods its diagonal adjoining tiles - * called from tunnelbridge_cmd, and by TileLoop_Industry() and TileLoop_Track() + * Returns the behaviour of a tile during flooding. * - * @param tile the water/shore tile that floods + * @return Behaviour of the tile */ -void TileLoop_Water(TileIndex tile) +static FloodingBehaviour GetFloodingBehaviour(TileIndex tile) { - static const TileIndexDiffC _tile_loop_offs_array[][5] = { - // tile to mod shore? shore? - {{-1, 0}, {0, 0}, {0, 1}, {-1, 0}, {-1, 1}}, - {{ 0, 1}, {0, 1}, {1, 1}, { 0, 2}, { 1, 2}}, - {{ 1, 0}, {1, 0}, {1, 1}, { 2, 0}, { 2, 1}}, - {{ 0, -1}, {0, 0}, {1, 0}, { 0, -1}, { 1, -1}} - }; + /* FLOOD_ACTIVE: 'single-corner-raised'-coast, sea, sea-shipdepots, sea-buoys, rail with flooded halftile + * FLOOD_DRYUP: coast with more than one corner raised + * FLOOD_PASSIVE: oilrig, dock, water-industries + * FLOOD_NONE: canals, rivers, everything else + */ + switch (GetTileType(tile)) { + case MP_WATER: + if (IsCoast(tile)) { + Slope tileh = GetTileSlope(tile, NULL); + return (HasBit(_active_water_slopes, tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP); + } else { + return ((IsSea(tile) || (IsShipDepot(tile) && (GetShipDepotWaterOwner(tile) == OWNER_WATER))) ? FLOOD_ACTIVE : FLOOD_NONE); + } + + case MP_RAILWAY: + return ((GetRailGroundType(tile) == RAIL_GROUND_WATER) ? FLOOD_ACTIVE : FLOOD_NONE); - /* Ensure buoys on canal borders do not flood */ - if (IsCanalBuoyTile(tile)) return; - /* Ensure only sea and coast floods, not canals or rivers */ - if (IsTileType(tile, MP_WATER) && !(IsSea(tile) || IsCoast(tile))) return; + case MP_STATION: + if (IsSeaBuoyTile(tile)) return FLOOD_ACTIVE; + if (IsOilRig(tile) || IsDock(tile)) return FLOOD_PASSIVE; + return FLOOD_NONE; - /* floods in all four diagonal directions with the exception of the edges */ - if (IsInsideMM(TileX(tile), 1, MapSizeX() - 3 + 1) && - IsInsideMM(TileY(tile), 1, MapSizeY() - 3 + 1)) { - uint i; + case MP_INDUSTRY: + return ((GetIndustrySpec(GetIndustryType(tile))->behaviour & INDUSTRYBEH_BUILT_ONWATER) != 0 ? FLOOD_PASSIVE : FLOOD_NONE); - for (i = 0; i != lengthof(_tile_loop_offs_array); i++) { - TileLoopWaterHelper(tile, _tile_loop_offs_array[i]); + default: + return FLOOD_NONE; + } +} + +/** + * Floods a tile. + */ +static void DoFloodTile(TileIndex target) +{ + if (IsTileType(target, MP_WATER)) return; + + bool flooded = false; // Will be set to true if something is changed. + + _current_player = OWNER_WATER; + + if (GetTileSlope(target, NULL) != SLOPE_FLAT) { + /* make coast.. */ + switch (GetTileType(target)) { + case MP_RAILWAY: { + if (!IsPlainRailTile(target)) break; + + flooded = FloodHalftile(target); + + Vehicle *v = FindFloodableVehicleOnTile(target); + if (v != NULL) FloodVehicle(v); + + break; + } + + case MP_CLEAR: + case MP_TREES: + if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) { + MakeShore(target); + MarkTileDirtyByTile(target); + flooded = true; + } + break; + + default: + break; + } + } else { + /* Flood vehicles */ + Vehicle *v = FindFloodableVehicleOnTile(target); + if (v != NULL) FloodVehicle(v); + + /* flood flat tile */ + if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) { + MakeWater(target); + MarkTileDirtyByTile(target); + flooded = true; } } - /* _current_player can be changed by TileLoopWaterHelper.. reset it back here */ - _current_player = OWNER_NONE; + if (flooded) { + /* Mark surrounding canal tiles dirty too to avoid glitches */ + for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) { + MarkTileDirtyIfCanal(target + TileOffsByDir(dir)); + } - /* edges */ - if (TileX(tile) == 0 && IsInsideMM(TileY(tile), 1, MapSizeY() - 3 + 1)) { //NE - TileLoopWaterHelper(tile, _tile_loop_offs_array[2]); + /* update signals if needed */ + UpdateSignalsInBuffer(); } - if (TileX(tile) == MapSizeX() - 2 && IsInsideMM(TileY(tile), 1, MapSizeY() - 3 + 1)) { //SW - TileLoopWaterHelper(tile, _tile_loop_offs_array[0]); + _current_player = OWNER_NONE; +} + +/** + * Drys a tile up. + */ +static void DoDryUp(TileIndex tile) +{ + assert(IsTileType(tile, MP_WATER) && IsCoast(tile)); + _current_player = OWNER_WATER; + + if (CmdSucceeded(DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) { + MakeClear(tile, CLEAR_GRASS, 3); + MarkTileDirtyByTile(tile); } - if (TileY(tile) == 0 && IsInsideMM(TileX(tile), 1, MapSizeX() - 3 + 1)) { //NW - TileLoopWaterHelper(tile, _tile_loop_offs_array[1]); + _current_player = OWNER_NONE; +} + +/** + * Let a water tile floods its diagonal adjoining tiles + * called from tunnelbridge_cmd, and by TileLoop_Industry() and TileLoop_Track() + * + * @param tile the water/shore tile that floods + */ +void TileLoop_Water(TileIndex tile) +{ + switch (GetFloodingBehaviour(tile)) { + case FLOOD_ACTIVE: + for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) { + TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir)); + if (dest == INVALID_TILE) continue; + + uint z_dest; + Slope slope_dest = (Slope)(GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP); + if (z_dest > 0) continue; + + if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue; + + DoFloodTile(dest); + } + break; + + case FLOOD_DRYUP: { + Slope slope_here = (Slope)(GetFoundationSlope(tile, NULL) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP); + uint check_dirs = _flood_from_dirs[slope_here]; + uint dir; + FOR_EACH_SET_BIT(dir, check_dirs) { + TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir((Direction)dir)); + if (dest == INVALID_TILE) continue; + + FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest); + if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return; + } + DoDryUp(tile); + break; + } + + default: return; } +} - if (TileY(tile) == MapSizeY() - 2 && IsInsideMM(TileX(tile), 1, MapSizeX() - 3 + 1)) { //SE - TileLoopWaterHelper(tile, _tile_loop_offs_array[3]); +void ConvertGroundTilesIntoWaterTiles() +{ + TileIndex tile; + uint z; + Slope slope; + + for (tile = 0; tile < MapSize(); ++tile) { + slope = GetTileSlope(tile, &z); + if (IsTileType(tile, MP_CLEAR) && z == 0) { + /* Make both water for tiles at level 0 + * and make shore, as that looks much better + * during the generation. */ + switch (slope) { + case SLOPE_FLAT: + MakeWater(tile); + break; + + case SLOPE_N: + case SLOPE_E: + case SLOPE_S: + case SLOPE_W: + MakeShore(tile); + break; + + default: + uint check_dirs = _flood_from_dirs[slope & ~SLOPE_STEEP]; + uint dir; + FOR_EACH_SET_BIT(dir, check_dirs) { + TileIndex dest = TILE_ADD(tile, TileOffsByDir((Direction)dir)); + Slope slope_dest = (Slope)(GetTileSlope(dest, NULL) & ~SLOPE_STEEP); + if (HasBit(_active_water_slopes, slope_dest)) { + MakeShore(tile); + break; + } + } + break; + } + } } } |