diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 86718b715c..494340fa35 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -4301,6 +4301,14 @@ static ChangeInfoResult ObjectChangeInfo(uint id, int numinfo, int prop, const G SB(spec->ctrl_flags, OBJECT_CTRL_FLAG_USE_LAND_GROUND, 1, (buf->ReadByte() != 0 ? 1 : 0)); break; + case A0RPI_OBJECT_EDGE_FOUNDATION_MODE: + if (MappedPropertyLengthMismatch(buf, 4, mapping_entry)) break; + SetBit(spec->ctrl_flags, OBJECT_CTRL_FLAG_EDGE_FOUNDATION); + for (int i = 0; i < 4; i++) { + spec->edge_foundation[i] = buf->ReadByte(); + } + break; + default: ret = HandleAction0PropertyDefault(buf, prop); break; diff --git a/src/newgrf_extension.cpp b/src/newgrf_extension.cpp index 55f1499ff2..7833f25e3a 100644 --- a/src/newgrf_extension.cpp +++ b/src/newgrf_extension.cpp @@ -68,6 +68,7 @@ extern const GRFPropertyMapDefinition _grf_action0_remappable_properties[] = { GRFPropertyMapDefinition(GSF_SIGNALS, A0RPI_SIGNALS_ENABLE_SIGNAL_RECOLOUR, "signals_enable_signal_recolour"), GRFPropertyMapDefinition(GSF_SIGNALS, A0RPI_SIGNALS_EXTRA_ASPECTS, "signals_extra_aspects"), GRFPropertyMapDefinition(GSF_OBJECTS, A0RPI_OBJECT_USE_LAND_GROUND, "object_use_land_ground"), + GRFPropertyMapDefinition(GSF_OBJECTS, A0RPI_OBJECT_EDGE_FOUNDATION_MODE, "object_edge_foundation_mode"), GRFPropertyMapDefinition(), }; diff --git a/src/newgrf_extension.h b/src/newgrf_extension.h index fd479cd5e3..0604ce2f7d 100644 --- a/src/newgrf_extension.h +++ b/src/newgrf_extension.h @@ -33,6 +33,7 @@ enum Action0RemapPropertyIds { A0RPI_SIGNALS_ENABLE_SIGNAL_RECOLOUR, A0RPI_SIGNALS_EXTRA_ASPECTS, A0RPI_OBJECT_USE_LAND_GROUND, + A0RPI_OBJECT_EDGE_FOUNDATION_MODE, }; diff --git a/src/newgrf_object.cpp b/src/newgrf_object.cpp index 59f31f5d96..c4101c483f 100644 --- a/src/newgrf_object.cpp +++ b/src/newgrf_object.cpp @@ -418,7 +418,7 @@ uint16 GetObjectCallback(CallbackID callback, uint32 param1, uint32 param2, cons * @param group The group of sprites to draw. * @param spec Object spec to draw. */ -static void DrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *group, const ObjectSpec *spec) +static void DrawTileLayout(TileInfo *ti, const TileLayoutSpriteGroup *group, const ObjectSpec *spec, int building_z_offset) { const DrawTileSprites *dts = group->ProcessRegisters(nullptr); PaletteID palette = ((spec->flags & OBJECT_FLAG_2CC_COLOUR) ? SPR_2CCMAP_BASE : PALETTE_RECOLOUR_START) + Object::GetByTile(ti->tile)->colour; @@ -455,7 +455,9 @@ static void DrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *grou } } + if (building_z_offset) ti->z += building_z_offset; DrawNewGRFTileSeq(ti, dts, TO_STRUCTURES, 0, palette); + if (building_z_offset) ti->z -= building_z_offset; } /** @@ -463,7 +465,7 @@ static void DrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *grou * @param ti Information about the tile to draw on. * @param spec Object spec to draw. */ -void DrawNewObjectTile(TileInfo *ti, const ObjectSpec *spec) +void DrawNewObjectTile(TileInfo *ti, const ObjectSpec *spec, int building_z_offset) { Object *o = Object::GetByTile(ti->tile); ObjectResolverObject object(spec, o, ti->tile); @@ -471,7 +473,7 @@ void DrawNewObjectTile(TileInfo *ti, const ObjectSpec *spec) const SpriteGroup *group = object.Resolve(); if (group == nullptr || group->type != SGT_TILELAYOUT) return; - DrawTileLayout(ti, (const TileLayoutSpriteGroup *)group, spec); + DrawTileLayout(ti, (const TileLayoutSpriteGroup *)group, spec, building_z_offset); } /** diff --git a/src/newgrf_object.h b/src/newgrf_object.h index e06cd84a10..8174bd065b 100644 --- a/src/newgrf_object.h +++ b/src/newgrf_object.h @@ -43,9 +43,17 @@ DECLARE_ENUM_AS_BIT_SET(ObjectFlags) enum ObjectCtrlFlags { OBJECT_CTRL_FLAG_NONE = 0, ///< Just nothing. OBJECT_CTRL_FLAG_USE_LAND_GROUND = 1 << 0, ///< Use land for ground sprite. + OBJECT_CTRL_FLAG_EDGE_FOUNDATION = 1 << 2, ///< Use edge foundation mode. }; DECLARE_ENUM_AS_BIT_SET(ObjectCtrlFlags) +enum ObjectEdgeFoundationFlags { + /* Bits 0 and 1 use for edge DiagDirection */ + OBJECT_EF_FLAG_ADJUST_Z = 1 << 2, ///< Adjust sprite z position to z at edge. + OBJECT_EF_FLAG_FOUNDATION_LOWER = 1 << 3, ///< If edge is lower than tile max z, add foundation. +}; +DECLARE_ENUM_AS_BIT_SET(ObjectEdgeFoundationFlags) + void ResetObjects(); /** Class IDs for objects. */ @@ -75,6 +83,7 @@ struct ObjectSpec { Date end_of_life_date; ///< When can't this object be built anymore. ObjectFlags flags; ///< Flags/settings related to the object. ObjectCtrlFlags ctrl_flags; ///< Extra control flags. + uint8 edge_foundation[4]; ///< Edge foundation flags AnimationInfo animation; ///< Information about the animation. uint16 callback_mask; ///< Bitmask of requested/allowed callbacks. uint8 height; ///< The height of this structure, in heightlevels; max MAX_TILE_HEIGHT. @@ -167,7 +176,7 @@ static const CargoID CT_PURCHASE_OBJECT = 1; uint16 GetObjectCallback(CallbackID callback, uint32 param1, uint32 param2, const ObjectSpec *spec, Object *o, TileIndex tile, uint8 view = 0); -void DrawNewObjectTile(TileInfo *ti, const ObjectSpec *spec); +void DrawNewObjectTile(TileInfo *ti, const ObjectSpec *spec, int building_z_offset); void DrawNewObjectTileInGUI(int x, int y, const ObjectSpec *spec, uint8 view); void AnimateNewObjectTile(TileIndex tile); uint8 GetNewObjectTileAnimationSpeed(TileIndex tile); diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp index 02da697d6a..2d341d6e66 100644 --- a/src/object_cmd.cpp +++ b/src/object_cmd.cpp @@ -517,13 +517,34 @@ static Foundation GetFoundation_Object(TileIndex tile, Slope tileh); static void DrawTile_Object(TileInfo *ti, DrawTileProcParams params) { - ObjectType type = GetObjectType(ti->tile); + const Object *obj = Object::GetByTile(ti->tile); + ObjectType type = obj->type; const ObjectSpec *spec = ObjectSpec::Get(type); - /* Fall back for when the object doesn't exist anymore. */ - if (!spec->enabled) type = OBJECT_TRANSMITTER; + int building_z_offset = 0; - if ((spec->flags & OBJECT_FLAG_HAS_NO_FOUNDATION) == 0) DrawFoundation(ti, GetFoundation_Object(ti->tile, ti->tileh)); + /* Fall back for when the object doesn't exist anymore. */ + if (!spec->enabled) { + type = OBJECT_TRANSMITTER; + } else if ((spec->flags & OBJECT_FLAG_HAS_NO_FOUNDATION) == 0) { + if (spec->ctrl_flags & OBJECT_CTRL_FLAG_EDGE_FOUNDATION) { + uint8 flags = spec->edge_foundation[obj->view]; + DiagDirection edge = (DiagDirection)GB(flags, 0, 2); + Slope incline = InclinedSlope(edge); + if (IsSteepSlope(ti->tileh) || IsOddParity(incline & ti->tileh)) { + /* Steep slope, or odd number of matching bits indicating that edge is not level */ + DrawFoundation(ti, GetFoundation_Object(ti->tile, ti->tileh)); + } else if (flags & OBJECT_EF_FLAG_FOUNDATION_LOWER && !(ti->tileh & incline)) { + /* The edge is the lower edge of an inclined slope */ + DrawFoundation(ti, GetFoundation_Object(ti->tile, ti->tileh)); + } else if (flags & OBJECT_EF_FLAG_ADJUST_Z && ti->tileh & incline) { + /* The edge is elevated relative to the lowest tile height, adjust z */ + building_z_offset = TILE_HEIGHT; + } + } else { + DrawFoundation(ti, GetFoundation_Object(ti->tile, ti->tileh)); + } + } if (type < NEW_OBJECT_OFFSET) { const DrawTileSprites *dts = nullptr; @@ -564,7 +585,7 @@ static void DrawTile_Object(TileInfo *ti, DrawTileProcParams params) } } } else { - DrawNewObjectTile(ti, spec); + DrawNewObjectTile(ti, spec, building_z_offset); } DrawBridgeMiddle(ti); @@ -1065,6 +1086,16 @@ static void ChangeTileOwner_Object(TileIndex tile, Owner old_owner, Owner new_ow } } +static int GetObjectEffectiveZ(TileIndex tile, const ObjectSpec *spec, int z, Slope tileh) +{ + if ((spec->ctrl_flags & OBJECT_CTRL_FLAG_EDGE_FOUNDATION) && !(spec->flags & OBJECT_FLAG_HAS_NO_FOUNDATION)) { + uint8 flags = spec->edge_foundation[Object::GetByTile(tile)->view]; + DiagDirection edge = (DiagDirection)GB(flags, 0, 2); + if (!(flags & OBJECT_EF_FLAG_FOUNDATION_LOWER) && !(tileh & InclinedSlope(edge))) return z; + } + return z + GetSlopeMaxZ(tileh); +} + static CommandCost TerraformTile_Object(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new) { ObjectType type = GetObjectType(tile); @@ -1080,10 +1111,11 @@ static CommandCost TerraformTile_Object(TileIndex tile, DoCommandFlag flags, int * - Allow autoslope by default. * - Disallow autoslope if callback succeeds and returns non-zero. */ - Slope tileh_old = GetTileSlope(tile); - /* TileMaxZ must not be changed. Slopes must not be steep. */ - if (!IsSteepSlope(tileh_old) && !IsSteepSlope(tileh_new) && (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new))) { - const ObjectSpec *spec = ObjectSpec::Get(type); + 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))) { /* Call callback 'disable autosloping for objects'. */ if (HasBit(spec->callback_mask, CBM_OBJ_AUTOSLOPE)) { diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index ab47791b58..826359790f 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -897,6 +897,7 @@ class NIHObject : public NIHelper { check_flag(OBJECT_FLAG_ANIM_RANDOM_BITS, "OBJECT_FLAG_ANIM_RANDOM_BITS"); check_flag(OBJECT_FLAG_SCALE_BY_WATER, "OBJECT_FLAG_SCALE_BY_WATER"); check_ctrl_flag(OBJECT_CTRL_FLAG_USE_LAND_GROUND, "OBJECT_CTRL_FLAG_USE_LAND_GROUND"); + check_ctrl_flag(OBJECT_CTRL_FLAG_EDGE_FOUNDATION, "OBJECT_CTRL_FLAG_EDGE_FOUNDATION"); } } } diff --git a/src/table/object_land.h b/src/table/object_land.h index 0f6c6a0ffc..402b6a9153 100644 --- a/src/table/object_land.h +++ b/src/table/object_land.h @@ -121,7 +121,7 @@ static const DrawTileSprites _object_hq[] = { #undef TILE_SPRITE_LINE -#define M(name, size, build_cost_multiplier, clear_cost_multiplier, height, climate, gen_amount, flags) { GRFFilePropsBase<2>(), INVALID_OBJECT_CLASS, name, climate, size, build_cost_multiplier, clear_cost_multiplier, 0, MAX_DAY + 1, flags, OBJECT_CTRL_FLAG_NONE, {0, 0, 0, 0}, 0, height, 1, gen_amount, true } +#define M(name, size, build_cost_multiplier, clear_cost_multiplier, height, climate, gen_amount, flags) { GRFFilePropsBase<2>(), INVALID_OBJECT_CLASS, name, climate, size, build_cost_multiplier, clear_cost_multiplier, 0, MAX_DAY + 1, flags, OBJECT_CTRL_FLAG_NONE, {0, 0, 0, 0}, {0, 0, 0, 0}, 0, height, 1, gen_amount, true } /* Climates * T = Temperate