Index: src/lang/english.txt =================================================================== --- src/lang/english.txt (revision 11280) +++ src/lang/english.txt (working copy) @@ -1911,6 +1911,7 @@ STR_3805_COAST_OR_RIVERBANK :Coast or riverbank STR_3806_SHIP_DEPOT :Ship depot STR_3807_CAN_T_BUILD_ON_WATER :{WHITE}...Can't build on water +STR_3808_MUST_BE_BUILT_ON_SHALLOW_WATER :{WHITE}...must be built on shallow water STR_MUST_DEMOLISH_CANAL_FIRST :{WHITE}Must demolish canal first ##id 0x4000 Index: src/saveload.cpp =================================================================== --- src/saveload.cpp (revision 11280) +++ src/saveload.cpp (working copy) @@ -29,7 +29,7 @@ #include "strings.h" #include -extern const uint16 SAVEGAME_VERSION = 81; +extern const uint16 SAVEGAME_VERSION = 82; uint16 _sl_version; ///< the major savegame version identifier byte _sl_minor_version; ///< the minor savegame version, DO NOT USE! Index: src/station_cmd.cpp =================================================================== --- src/station_cmd.cpp (revision 11280) +++ src/station_cmd.cpp (working copy) @@ -31,6 +31,7 @@ #include "depot.h" #include "train.h" #include "roadveh.h" +#include "clear_map.h" #include "water_map.h" #include "industry_map.h" #include "newgrf_callbacks.h" @@ -43,6 +44,7 @@ #include "cargotype.h" #include "strings.h" #include "autoslope.h" +#include "water.h" DEFINE_OLD_POOL_GENERIC(Station, Station) DEFINE_OLD_POOL_GENERIC(RoadStop, RoadStop) @@ -1769,7 +1771,11 @@ { SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); + /* Check the site, must be shallow flat water */ if (!IsWaterTile(tile) || tile == 0) return_cmd_error(STR_304B_SITE_UNSUITABLE); + uint sealevelpixels = _map_sealevel * TILE_HEIGHT; + uint h; + if (GetTileSlope(tile, &h) != SLOPE_FLAT || (IsSea(tile) && h != sealevelpixels)) return_cmd_error(STR_3808_MUST_BE_BUILT_ON_SHALLOW_WATER); if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST); /* allocate and initialize new station */ @@ -2764,7 +2770,11 @@ { Station* st = GetStationByTile(tile); - MakeWater(tile); + if (TileHeight(tile) <= _map_sealevel) { + MakeWater(tile); + } else { + DoClearSquare(tile); + } st->dock_tile = 0; st->airport_tile = 0; Index: src/town_cmd.cpp =================================================================== --- src/town_cmd.cpp (revision 11280) +++ src/town_cmd.cpp (working copy) @@ -43,6 +43,7 @@ #include "misc/autoptr.hpp" #include "autoslope.h" #include "waypoint.h" +#include "water.h" /* Initialize the town-pool */ DEFINE_OLD_POOL_GENERIC(Town, Town) @@ -777,7 +778,10 @@ /* First try up, then down */ if (!TerraformTownTile(tile, ~tileh & 0xF, 1)) { - TerraformTownTile(tile, tileh & 0xF, 0); + /* Try down only when above sea level */ + if (GetMinTileHeight(tile) > _map_sealevel) { + TerraformTownTile(tile, tileh & 0xF, 0); + } } } Index: src/ship_cmd.cpp =================================================================== --- src/ship_cmd.cpp (revision 11280) +++ src/ship_cmd.cpp (working copy) @@ -34,6 +34,7 @@ #include "date.h" #include "spritecache.h" #include "misc/autoptr.hpp" +#include "water.h" static const uint16 _ship_sprites[] = {0x0E5D, 0x0E55, 0x0E65, 0x0E6D}; @@ -756,7 +757,7 @@ dir = ShipGetNewDirection(v, gp.x, gp.y); v->x_pos = gp.x; v->y_pos = gp.y; - v->z_pos = GetSlopeZ(gp.x, gp.y); + v->z_pos = GetSlopeZ_WaterSurface(TileVirtXY(gp.x, gp.y), gp.x, gp.y); getout: v->UpdateDeltaXY(dir); Index: src/misc.cpp =================================================================== --- src/misc.cpp (revision 11280) +++ src/misc.cpp (working copy) @@ -23,6 +23,7 @@ #include "date.h" #include "cargotype.h" #include "group.h" +#include "water.h" char _name_array[512][32]; @@ -103,6 +104,8 @@ { AllocateMap(size_x, size_y); + _map_sealevel = 0; + AddTypeToEngines(); // make sure all engines have a type SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, 0, WC_MAIN_WINDOW, 0); @@ -346,6 +349,7 @@ static const SaveLoadGlobVarList _map_dimensions[] = { SLEG_CONDVAR(_map_dim_x, SLE_UINT32, 6, SL_MAX_VERSION), SLEG_CONDVAR(_map_dim_y, SLE_UINT32, 6, SL_MAX_VERSION), + SLEG_CONDVAR(_map_sealevel, SLE_UINT8, 82, SL_MAX_VERSION), SLEG_END() }; Index: src/water.h =================================================================== --- src/water.h (revision 0) +++ src/water.h (revision 0) @@ -0,0 +1,20 @@ +/* $Id: water.h 0 2007-10-08 00:00:00Z boekabart $ */ + +/** @file water.h */ + +#ifndef WATER_H +#define WATER_H + +/** The global sea leavel */ +extern uint8 _map_sealevel; + +/** + * Functions to get the slope-shape and height for the surface of water. + * Since this can be different than the shape/height of the sea floor + * if the water is deep + */ + +extern uint GetSlopeZ_WaterSurface(TileIndex tile, uint x, uint y); +extern Slope GetSlopeTileh_WaterSurface(TileIndex tile, Slope tileh); + +#endif /* WATER_H */ Index: src/tile.h =================================================================== --- src/tile.h (revision 11280) +++ src/tile.h (working copy) @@ -52,6 +52,8 @@ Slope GetTileSlope(TileIndex tile, uint *h); uint GetTileZ(TileIndex tile); uint GetTileMaxZ(TileIndex tile); +uint GetMaxTileHeight(TileIndex tile); +uint GetMinTileHeight(TileIndex tile); /** * Returns the height of a tile Index: src/water_cmd.cpp =================================================================== --- src/water_cmd.cpp (revision 11280) +++ src/water_cmd.cpp (working copy) @@ -50,7 +50,22 @@ 0 }; +/* The global sea level */ +uint8 _map_sealevel; +/** + * These constants map Steep-Slope values with their deepest corner flooded + * to their equivalent with that deepest corner one level up, for choosing + * the correct shore-sprite to draw. + */ +const uint _map_steep_coast[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, +// steep: + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x04, 0x00 +}; + static Vehicle *FindFloodableVehicleOnTile(TileIndex tile); static void FloodVehicle(Vehicle *v); @@ -75,6 +90,12 @@ if (!IsWaterTile(tile) || !IsWaterTile(tile2)) return_cmd_error(STR_3801_MUST_BE_BUILT_ON_WATER); + uint sealevelpixels = _map_sealevel * TILE_HEIGHT; + uint h; + + if (GetTileSlope(tile, &h) != SLOPE_FLAT || (IsSea(tile) && h != sealevelpixels)) return_cmd_error(STR_3808_MUST_BE_BUILT_ON_SHALLOW_WATER); + if (GetTileSlope(tile2, &h) != SLOPE_FLAT || (IsSea(tile2) && h != sealevelpixels)) return_cmd_error(STR_3808_MUST_BE_BUILT_ON_SHALLOW_WATER); + if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST); ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); @@ -238,8 +259,16 @@ int sx, sy; if (p1 >= MapSize()) return CMD_ERROR; + + /* Determine the type of water to create */ + bool make_canal = !HASBIT(p2, 0); + /* As long as we don't support rivers, above sea level only canals */ + if (GetMaxTileHeight(tile) > _map_sealevel) make_canal = true; + + bool make_sea = !make_canal; + /* Outside of the editor you can only build canals, not oceans */ - if (HASBIT(p2, 0) && _game_mode != GM_EDITOR) return CMD_ERROR; + if (!make_canal && _game_mode != GM_EDITOR) return CMD_ERROR; x = TileX(tile); y = TileY(tile); @@ -259,19 +288,23 @@ BEGIN_TILE_LOOP(tile, size_x, size_y, TileXY(sx, sy)) { CommandCost ret; - if (GetTileSlope(tile, NULL) != SLOPE_FLAT) { + Slope slope = GetTileSlope(tile, NULL); + /* Canal: only flat. sea: anything (tileheight checked already) */ + if (slope != SLOPE_FLAT && make_canal) { return_cmd_error(STR_0007_FLAT_LAND_REQUIRED); } - /* can't make water of water! */ - if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || HASBIT(p2, 0))) continue; + /* Can't make same water of same water! */ + bool iscanal = IsTileType(tile, MP_WATER) && IsCanal(tile); + bool isnaturalwater = IsTileType(tile, MP_WATER) && IsSea(tile); + if ((iscanal && make_canal) || (isnaturalwater && !make_canal)) continue; ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (CmdFailed(ret)) return ret; cost.AddCost(ret); if (flags & DC_EXEC) { - if (TileHeight(tile) == 0 && HASBIT(p2, 0)) { + if (make_sea) { MakeWater(tile); } else { MakeCanal(tile, _current_player); @@ -305,7 +338,9 @@ /* Make sure no vehicle is on the tile */ if (!EnsureNoVehicle(tile)) return CMD_ERROR; - if (GetTileOwner(tile) != OWNER_WATER && GetTileOwner(tile) != OWNER_NONE && !CheckTileOwnership(tile)) return CMD_ERROR; + /* Water can flood water always */ + if (_current_player != OWNER_WATER) + if (GetTileOwner(tile) != OWNER_WATER && GetTileOwner(tile) != OWNER_NONE && !CheckTileOwnership(tile)) return CMD_ERROR; if (flags & DC_EXEC) DoClearSquare(tile); return CommandCost(_price.clear_water); @@ -453,25 +488,57 @@ } } +inline bool IsSouthernNeighbourTileBelowSea(TileIndex t, int dx, int dy) +{ + assert(dx == 0 || dx == 1); + assert(dy == 0 || dy == 1); + assert(dx + dy != 0); + TileIndex target = TILE_ADDXY(t, dx, dy); + if (IsTileType(target, MP_WATER) && IsSea(target)) return false; + if (TileHeight(target) < _map_sealevel) return true; + if (dx != dy && TileHeight(TILE_ADDXY(target, dy, dx)) < _map_sealevel) return true; + return false; +} + static void DrawTile_Water(TileInfo *ti) { switch (GetWaterTileType(ti->tile)) { - case WATER_TILE_CLEAR: - DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE); - if (IsCanal(ti->tile)) DrawCanalWater(ti->tile); - DrawBridgeMiddle(ti); - break; + case WATER_TILE_CLEAR: { + int dz = (_map_sealevel * TILE_HEIGHT) - ti->z; - case WATER_TILE_COAST: - assert(!IsSteepSlope(ti->tileh)); - if (_coast_base != 0) { - DrawGroundSprite(_coast_base + ti->tileh, PAL_NONE); + if (dz <= 0 || !IsSea(ti->tile)) { + DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE); + if (IsCanal(ti->tile)) DrawCanalWater(ti->tile); } else { - DrawGroundSprite(_water_shore_sprites[ti->tileh], PAL_NONE); + DrawGroundSpriteAt(SPR_FLAT_WATER_TILE, PAL_NONE, ti->x, ti->y, ti->z + dz); + /* At bottom edges, draw extra seafloor water tile to prevent glitches. + * Also draw them when tiles +1 to y and/or x (below on screen) are not sea (yet) + * and their tileheight in my direction is below sea */ + if (_map_sealevel > 0) { + if (((TileX(ti->tile) == MapMaxX() - 1) || (TileY(ti->tile) == MapMaxY() - 1)) || + IsSouthernNeighbourTileBelowSea(ti->tile, 1, 0) || + IsSouthernNeighbourTileBelowSea(ti->tile, 1, 1) || + IsSouthernNeighbourTileBelowSea(ti->tile, 0, 1)) { + DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE); + } + } } DrawBridgeMiddle(ti); break; + } + case WATER_TILE_COAST: { + /* Replace tiles that are partly flooded and steep by their coastal equivalent. */ + uint draw_slope = _map_steep_coast[ti->tileh]; + SpriteID coastsprite = (_coast_base != 0) ? _coast_base + draw_slope : _water_shore_sprites[draw_slope]; + /* Compensate for the change in Slope above by moving steep slopes up 1 tileheight. */ + uint coast_dz = IsSteepSlope(ti->tileh) ? TILE_HEIGHT : 0; + DrawGroundSpriteAt(coastsprite, PAL_NONE, ti->x, ti->y, ti->z + coast_dz); + + DrawBridgeMiddle(ti); + break; + } + case WATER_TILE_LOCK: { const WaterDrawTileStruct *t = _shiplift_display_seq[GetSection(ti->tile)]; DrawWaterStuff(ti, t, 0, ti->z > t[3].delta_y ? 24 : 0); @@ -496,6 +563,31 @@ } +uint GetSlopeZ_WaterSurface(TileIndex tile, uint x, uint y) +{ + if (!IsTileType(tile, MP_WATER)) { + return GetSlopeZ(x, y); + } + + uint z; + Slope tileh = GetTileSlope(tile, &z); + + if (IsSea(tile)) { + return max(_map_sealevel * (uint)TILE_HEIGHT, z); + } + + return z + GetPartialZ(x & 0xF, y & 0xF, tileh); +} + +Slope GetSlopeTileh_WaterSurface(TileIndex tile, Slope tileh) +{ + assert(IsTileType(tile, MP_WATER)); + if (IsSea(tile)) { + return SLOPE_FLAT; + } + return tileh; +} + static uint GetSlopeZ_Water(TileIndex tile, uint x, uint y) { uint z; @@ -538,22 +630,74 @@ /* not used */ } +static inline bool IsTileOnEdge(TileIndex tile) +{ + return !IS_INT_INSIDE(TileX(tile), 1, MapMaxX() - 1) || !IS_INT_INSIDE(TileY(tile), 1, MapMaxY() - 1); +} + 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; + /* Don't flood edges */ + if (IsTileOnEdge(target)) return; - if (TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[1]))) != 0 || - TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[2]))) != 0) { + /* Don't try to re-flood SEA. rest: yes (even coast, if it's below sea level!) */ + if (IsTileType(target, MP_WATER)) { + if (IsSea(target)) return; + } + + /* Don't flood uphill/sidehill */ + if (TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[1]))) > _map_sealevel || + TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[2]))) > _map_sealevel) { return; } - if (TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[3]))) != 0 || - TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[4]))) != 0) { + /* Don't flood on-water industries like oilrig if they exactly are on sealevel */ + if (IsTileType(target, MP_INDUSTRY)) { + IndustyBehaviour ind_behav = GetIndustrySpec(GetIndustryType(target))->behaviour; + if (ind_behav & INDUSTRYBEH_BUILT_ONWATER) { + if (TileHeight(target) == _map_sealevel) + return; + } + } + + /* If all points are on sea level, make sea not coast */ + uint targetmaxtileheight = GetMaxTileHeight(target); + Slope target_tile_slope = GetTileSlope(target, NULL); + + if (target_tile_slope != SLOPE_FLAT && targetmaxtileheight > _map_sealevel) { /* make coast.. */ + switch(target_tile_slope) { + /* Normal slopes always allowed */ + case SLOPE_W: + case SLOPE_S: + case SLOPE_E: + case SLOPE_N: + case SLOPE_NW: + case SLOPE_SW: + case SLOPE_SE: + case SLOPE_NE: + break; + + /* Steep slopes only if the middle hight = sea level */ + case SLOPE_STEEP_W: + case SLOPE_STEEP_S: + case SLOPE_STEEP_E: + case SLOPE_STEEP_N: + if (targetmaxtileheight != _map_sealevel + 1U) { + return; + } + break; + + /* All other slopes are impossible */ + default: + return; + } switch (GetTileType(target)) { + case MP_WATER: + return; // coast already + case MP_RAILWAY: { TrackBits tracks; Slope slope; @@ -624,7 +768,7 @@ return NULL; } - if (!IsBridgeTile(tile)) return FindVehicleOnTileZ(tile, 0); + if (!IsBridgeTile(tile)) return FindVehicleOnTileZ(tile, TilePixelHeight(tile)); TileIndex end = GetOtherBridgeEnd(tile); byte z = GetBridgeHeight(tile); Index: src/landscape.cpp =================================================================== --- src/landscape.cpp (revision 11280) +++ src/landscape.cpp (working copy) @@ -22,6 +22,7 @@ #include "water_map.h" #include "tgp.h" #include "genworld.h" +#include "water.h" extern const TileTypeProcs _tile_type_clear_procs, @@ -565,7 +566,7 @@ for (tile = 0; tile < MapSize(); ++tile) { slope = GetTileSlope(tile, &z); - if (IsTileType(tile, MP_CLEAR) && z == 0) { + if (IsTileType(tile, MP_CLEAR) && z <= _map_sealevel * (uint)TILE_HEIGHT) { /* Make both water for tiles at level 0 * and make shore, as that looks much better * during the generation. */ Index: src/tile.cpp =================================================================== --- src/tile.cpp (revision 11280) +++ src/tile.cpp (working copy) @@ -64,3 +64,22 @@ return h * TILE_HEIGHT; } + +uint GetMaxTileHeight(TileIndex tile) +{ + assert(TileX(tile) < MapMaxX()); + assert(TileY(tile) < MapMaxY()); + return max( + max(TileHeight(TILE_ADDXY(tile, 0, 0)), TileHeight(TILE_ADDXY(tile, 1, 0))), + max(TileHeight(TILE_ADDXY(tile, 0, 1)), TileHeight(TILE_ADDXY(tile, 1, 1)))); +} + +uint GetMinTileHeight(TileIndex tile) +{ + assert(TileX(tile) < MapMaxX()); + assert(TileY(tile) < MapMaxY()); + return min( + min(TileHeight(TILE_ADDXY(tile, 0, 0)), TileHeight(TILE_ADDXY(tile, 1, 0))), + min(TileHeight(TILE_ADDXY(tile, 0, 1)), TileHeight(TILE_ADDXY(tile, 1, 1)))); +} + Index: src/station_map.h =================================================================== --- src/station_map.h (revision 11280) +++ src/station_map.h (working copy) @@ -161,7 +161,8 @@ static inline bool IsSeaBuoyTile(TileIndex t) { - return IsBuoyTile(t) && IsTileOwner(t, OWNER_WATER); + extern uint8 _map_sealevel; + return IsBuoyTile(t) && IsTileOwner(t, OWNER_WATER) && TileHeight(t) <= _map_sealevel; } static inline bool IsHangarTile(TileIndex t) Index: src/industry_cmd.cpp =================================================================== --- src/industry_cmd.cpp (revision 11280) +++ src/industry_cmd.cpp (working copy) @@ -38,6 +38,7 @@ #include "newgrf_callbacks.h" #include "misc/autoptr.hpp" #include "autoslope.h" +#include "water.h" void ShowIndustryViewWindow(int industry); void BuildOilRig(TileIndex tile); @@ -1085,7 +1086,7 @@ static bool CheckNewIndustry_OilRig(TileIndex tile) { if (_game_mode == GM_EDITOR && _ignore_restrictions) return true; - if (TileHeight(tile) == 0 && + if (TileHeight(tile) == _map_sealevel && DistanceFromEdge(TILE_ADDXY(tile, 1, 1)) < _patches.oil_refinery_limit) return true; _error_message = STR_483B_CAN_ONLY_BE_POSITIONED; @@ -1231,6 +1232,10 @@ if (HASBIT(its->callback_flags, CBM_INDT_SHAPE_CHECK)) { custom_shape = true; if (!PerformIndustryTileSlopeCheck(tile, cur_tile, its, type, gfx, itspec_index)) return false; + /* Also check if the water tile is flat and shallow */ + uint h; + if (GetTileSlope(cur_tile, &h) != SLOPE_FLAT) return false; + if (TileHeight(cur_tile) != _map_sealevel) return false; } else { if (ind_behav & INDUSTRYBEH_BUILT_ONWATER) { /* As soon as the tile is not water, bail out. Index: source.list =================================================================== --- source.list (revision 11280) +++ source.list (working copy) @@ -199,6 +199,7 @@ vehicle.h vehicle_gui.h viewport.h +water.h waypoint.h music/win32_m.h sound/win32_s.h