diff --git a/docs/landscape.html b/docs/landscape.html index 78318f217a..57b755d027 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -1855,6 +1855,7 @@
  • m7: animation counter
  • m4 bits 7..5: update counter (for objects using land ground sprites), incremented on every periodic processing.
    For snow and desert, these bits are not used, tile is updated on every periodic processing.
  • +
  • m4 bits 4: tile has no effective foundation.
  • m4 bits 3..2: ground type (for objects using land ground sprites): @@ -1866,6 +1867,11 @@ + + + + +
    1  snow or desert
    2  shore/coast
  • m4 bits 1..0: density (for objects using land ground sprites): diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html index f3c4ffea26..305899fad9 100644 --- a/docs/landscape_grid.html +++ b/docs/landscape_grid.html @@ -324,7 +324,7 @@ the array so you can quickly see what is used and what is not. OXX XXXXX XXXX XXXX XXXX XXXX XXXX XXXX - PPPO PP PP + PPP P PP PP XXXX XXXX OOOO OOOO XXXX XXXX diff --git a/src/newgrf_extension.cpp b/src/newgrf_extension.cpp index 7833f25e3a..e95df4862c 100644 --- a/src/newgrf_extension.cpp +++ b/src/newgrf_extension.cpp @@ -43,6 +43,7 @@ extern const GRFFeatureInfo _grf_feature_list[] = { GRFFeatureInfo("action0_signals_extra_aspects", 1), GRFFeatureInfo("action3_signals_custom_signal_sprites", 1), GRFFeatureInfo("action0_object_use_land_ground", 1), + GRFFeatureInfo("action0_object_edge_foundation_mode", 1), GRFFeatureInfo(), }; diff --git a/src/newgrf_object.cpp b/src/newgrf_object.cpp index c4101c483f..90b6998bf1 100644 --- a/src/newgrf_object.cpp +++ b/src/newgrf_object.cpp @@ -427,7 +427,7 @@ static void DrawTileLayout(TileInfo *ti, const TileLayoutSpriteGroup *group, con PaletteID pal = dts->ground.pal; if (spec->ctrl_flags & OBJECT_CTRL_FLAG_USE_LAND_GROUND) { - if (IsTileOnWater(ti->tile)) { + if (IsTileOnWater(ti->tile) && GetObjectGroundType(ti->tile) != OBJECT_GROUND_SHORE) { DrawWaterClassGround(ti); } else { switch (GetObjectGroundType(ti->tile)) { @@ -439,6 +439,10 @@ static void DrawTileLayout(TileInfo *ti, const TileLayoutSpriteGroup *group, con DrawGroundSprite(GetSpriteIDForSnowDesert(ti->tileh, GetObjectGroundDensity(ti->tile)), PAL_NONE); break; + case OBJECT_GROUND_SHORE: + DrawShoreTile(ti->tileh); + break; + default: /* This should never be reached, just draw a black sprite to make the problem clear without being unnecessarily punitive */ DrawGroundSprite(SPR_FLAT_BARE_LAND + SlopeToSpriteOffset(ti->tileh), PALETTE_ALL_BLACK); diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp index 2d341d6e66..599c6d2ae3 100644 --- a/src/object_cmd.cpp +++ b/src/object_cmd.cpp @@ -71,6 +71,40 @@ void InitializeObjects() Object::ResetTypeCounts(); } +/** + * Set the object has no effective foundation flag for this tile. + * Set tileh to SLOPE_ELEVATED if not known, it will be redetermined if required. + */ +void SetShouldObjectHaveNoFoundation(TileIndex tile, Slope tileh, ObjectType type, const ObjectSpec *spec) +{ + if (type == OBJECT_OWNED_LAND) { + SetObjectHasNoEffectiveFoundation(tile, true); + return; + } + + if (((spec->flags & OBJECT_FLAG_HAS_NO_FOUNDATION) == 0) && (spec->ctrl_flags & OBJECT_CTRL_FLAG_EDGE_FOUNDATION)) { + if (tileh == SLOPE_ELEVATED) tileh = GetTileSlope(tile); + + if (IsSteepSlope(tileh)) { + SetObjectHasNoEffectiveFoundation(tile, false); + return; + } + + if (tileh == SLOPE_FLAT) { + SetObjectHasNoEffectiveFoundation(tile, true); + return; + } + + uint8 flags = spec->edge_foundation[Object::GetByTile(tile)->view]; + DiagDirection edge = (DiagDirection)GB(flags, 0, 2); + Slope incline = InclinedSlope(edge); + + SetObjectHasNoEffectiveFoundation(tile, !(IsOddParity(incline & tileh) || (flags & OBJECT_EF_FLAG_FOUNDATION_LOWER && !(tileh & incline)))); + } else { + SetObjectHasNoEffectiveFoundation(tile, false); + } +} + /** * Actually build the object. * @param type The type of object to build. @@ -129,6 +163,7 @@ void BuildObject(ObjectType type, TileIndex tile, CompanyID owner, Town *town, u if ((spec->ctrl_flags & OBJECT_CTRL_FLAG_USE_LAND_GROUND) && wc == WATER_CLASS_INVALID) { SetObjectGroundTypeDensity(t, OBJECT_GROUND_GRASS, 0); } + SetShouldObjectHaveNoFoundation(t, SLOPE_ELEVATED, type, spec); MarkTileDirtyByTile(t, VMDF_NOT_MAP_MODE); } @@ -605,7 +640,7 @@ static int GetSlopePixelZ_Object(TileIndex tile, uint x, uint y) static Foundation GetFoundation_Object(TileIndex tile, Slope tileh) { - return IsObjectType(tile, OBJECT_OWNED_LAND) ? FOUNDATION_NONE : FlatteningFoundation(tileh); + return GetObjectHasNoEffectiveFoundation(tile) ? FOUNDATION_NONE : FlatteningFoundation(tileh); } /** @@ -854,9 +889,13 @@ static void TileLoop_Object(TileIndex tile) if (IsTileOnWater(tile)) { TileLoop_Water(tile); } else if (spec->ctrl_flags & OBJECT_CTRL_FLAG_USE_LAND_GROUND) { - switch (_settings_game.game_creation.landscape) { - case LT_TROPIC: TileLoopObjectGroundDesert(tile); break; - case LT_ARCTIC: TileLoopObjectGroundAlps(tile); break; + if (GetObjectGroundType(tile) == OBJECT_GROUND_SHORE) { + TileLoop_Water(tile); + } else { + switch (_settings_game.game_creation.landscape) { + case LT_TROPIC: TileLoopObjectGroundDesert(tile); break; + case LT_ARCTIC: TileLoopObjectGroundAlps(tile); break; + } } if (GetObjectGroundType(tile) == OBJECT_GROUND_GRASS && GetObjectGroundDensity(tile) != 3) { @@ -1105,6 +1144,13 @@ static CommandCost TerraformTile_Object(TileIndex tile, DoCommandFlag flags, int CommandCost ret = CheckTileOwnership(tile); if (ret.Succeeded()) return CommandCost(); } else if (AutoslopeEnabled() && type != OBJECT_TRANSMITTER && type != OBJECT_LIGHTHOUSE) { + const ObjectSpec *spec = ObjectSpec::Get(type); + + if (flags & DC_EXEC) { + SetShouldObjectHaveNoFoundation(tile, tileh_new, type, spec); + if (GetObjectGroundType(tile) == OBJECT_GROUND_SHORE) SetObjectGroundTypeDensity(tile, OBJECT_GROUND_GRASS, 0); + } + /* Behaviour: * - Both new and old slope must not be steep. * - TileMaxZ must not be changed. @@ -1113,7 +1159,7 @@ static CommandCost TerraformTile_Object(TileIndex tile, DoCommandFlag flags, int */ int z_old; Slope tileh_old = GetTileSlope(tile, &z_old); - const ObjectSpec *spec = ObjectSpec::Get(type); + /* Object height must not be changed. Slopes must not be steep. */ if (!IsSteepSlope(tileh_old) && !IsSteepSlope(tileh_new) && (GetObjectEffectiveZ(tile, spec, z_old, tileh_old) == GetObjectEffectiveZ(tile, spec, z_new, tileh_new))) { diff --git a/src/object_map.h b/src/object_map.h index 5c6880229d..571030223d 100644 --- a/src/object_map.h +++ b/src/object_map.h @@ -16,6 +16,7 @@ enum ObjectGround { OBJECT_GROUND_GRASS = 0, ///< Grass or bare OBJECT_GROUND_SNOW_DESERT = 1, ///< Snow or desert + OBJECT_GROUND_SHORE = 2, ///< Shore }; ObjectType GetObjectType(TileIndex t); @@ -155,6 +156,18 @@ static inline void SetObjectGroundTypeDensity(TileIndex t, ObjectGround type, ui _m[t].m4 = 0 << 5 | type << 2 | density; } +static inline bool GetObjectHasNoEffectiveFoundation(TileIndex t) +{ + assert_tile(IsTileType(t, MP_OBJECT), t); + return GB(_m[t].m4, 4, 1); +} + +static inline void SetObjectHasNoEffectiveFoundation(TileIndex t, bool no_foundation) +{ + assert_tile(IsTileType(t, MP_OBJECT), t); + SB(_m[t].m4, 4, 1, no_foundation ? 1 : 0); +} + /** * Make an Object tile. * @param t The tile to make and object tile. diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index 826359790f..eb7029811a 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -861,8 +861,12 @@ class NIHObject : public NIHelper { uint class_id = ObjectClass::Get(spec->cls_id)->global_id; seprintf(buffer, lastof(buffer), " index: %u, type ID: %u, class ID: %c%c%c%c", id, GetObjectType(index), class_id >> 24, class_id >> 16, class_id >> 8, class_id); output.print(buffer); - seprintf(buffer, lastof(buffer), " view: %u, colour: %u", obj->view, obj->colour); + seprintf(buffer, lastof(buffer), " view: %u, colour: %u, effective foundation: %u", obj->view, obj->colour, !GetObjectHasNoEffectiveFoundation(index)); output.print(buffer); + if (spec->ctrl_flags & OBJECT_CTRL_FLAG_USE_LAND_GROUND) { + seprintf(buffer, lastof(buffer), " ground type: %u, density: %u, counter: %u, water class: %u", GetObjectGroundType(index), GetObjectGroundDensity(index), GetObjectGroundCounter(index), GetWaterClass(index)); + output.print(buffer); + } seprintf(buffer, lastof(buffer), " animation: frames: %u, status: %u, speed: %u, triggers: 0x%X", spec->animation.frames, spec->animation.status, spec->animation.speed, spec->animation.triggers); output.print(buffer); seprintf(buffer, lastof(buffer), " size: %ux%u, height: %u, views: %u", GB(spec->size, 4, 4), GB(spec->size, 0, 4), spec->height, spec->views); diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index f4bff6b62f..0137f782ac 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -38,6 +38,9 @@ #include "company_gui.h" #include "newgrf_generic.h" #include "industry.h" +#include "object_base.h" +#include "object_map.h" +#include "newgrf_object.h" #include "table/strings.h" @@ -1121,7 +1124,6 @@ FloodingBehaviour GetFloodingBehaviour(TileIndex tile) FALLTHROUGH; case MP_STATION: case MP_INDUSTRY: - case MP_OBJECT: return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE; case MP_RAILWAY: @@ -1133,6 +1135,9 @@ FloodingBehaviour GetFloodingBehaviour(TileIndex tile) case MP_TREES: return (GetTreeGround(tile) == TREE_GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE); + case MP_OBJECT: + return (GetObjectGroundType(tile) == OBJECT_GROUND_SHORE ? FLOOD_DRYUP : ((GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE)); + default: return FLOOD_NONE; } @@ -1177,6 +1182,30 @@ void DoFloodTile(TileIndex target) } break; + case MP_OBJECT: { + const ObjectSpec *spec = ObjectSpec::GetByTile(target); + if ((spec->ctrl_flags & OBJECT_CTRL_FLAG_USE_LAND_GROUND) && (spec->ctrl_flags & OBJECT_CTRL_FLAG_EDGE_FOUNDATION)) { + Object *obj = Object::GetByTile(target); + uint8 flags = spec->edge_foundation[obj->view]; + DiagDirection edge = (DiagDirection)GB(flags, 0, 2); + Slope incline = InclinedSlope(edge); + if (!(tileh & incline) && !(flags & OBJECT_EF_FLAG_FOUNDATION_LOWER)) { + /* object is on the lower edge with no foundation, and now underwater, clear the tile and then flood it */ + if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) { + MakeShore(target); + MarkTileDirtyByTile(target); + flooded = true; + } + break; + } + SetWaterClass(target, WATER_CLASS_SEA); + SetObjectGroundTypeDensity(target, OBJECT_GROUND_SHORE, 3); + MarkTileDirtyByTile(target, VMDF_NOT_MAP_MODE); + flooded = true; + } + break; + } + default: break; } @@ -1243,6 +1272,12 @@ static void DoDryUp(TileIndex tile) } break; + case MP_OBJECT: + SetWaterClass(tile, WATER_CLASS_INVALID); + SetObjectGroundTypeDensity(tile, OBJECT_GROUND_GRASS, 3); + MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE); + break; + default: NOT_REACHED(); } @@ -1274,6 +1309,7 @@ void TileLoop_Water(TileIndex tile) /* TREE_GROUND_SHORE is the sign of a previous flood. */ if (IsTileType(dest, MP_TREES) && GetTreeGround(dest) == TREE_GROUND_SHORE) continue; + if (IsTileType(dest, MP_OBJECT) && (!GetObjectHasNoEffectiveFoundation(dest) || GetObjectGroundType(dest) == OBJECT_GROUND_SHORE)) continue; int z_dest; Slope slope_dest = GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;