From a242dadad24f752c8e97af8464f425a7774f0e93 Mon Sep 17 00:00:00 2001 From: frosch Date: Tue, 22 Jan 2008 17:48:08 +0000 Subject: (svn r11947) -Feature: Make use of new sprites added by Action5 type 0D. Tiles which only consist of shore do not flood anymore, instead they get removed if they are no longer connected to flooding water. --- src/genworld.cpp | 1 + src/landscape.cpp | 53 +------- src/landscape.h | 3 +- src/map_func.h | 14 +++ src/water.h | 7 +- src/water_cmd.cpp | 355 +++++++++++++++++++++++++++++++++++------------------- 6 files changed, 260 insertions(+), 173 deletions(-) (limited to 'src') 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 @@ -243,6 +243,20 @@ static inline TileIndexDiffC TileIndexDiffCByDiagDir(DiagDirection dir) return _tileoffs_by_diagdir[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. * 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; + } + } } } -- cgit v1.2.3-70-g09d2