diff --git a/docs/landscape.html b/docs/landscape.html index bd6cc4b5c8..8fc837d3a2 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -1895,6 +1895,7 @@
OXX XXXXX | XXXX XXXX XXXX XXXX | XXXX XXXX | -PPPO PP PP | +PPP P PP PP | XXXX XXXX | OOOO OOPP | XXXX XXXX | diff --git a/docs/newgrf-additions-nml.html b/docs/newgrf-additions-nml.html index eda8a45b41..c7f00be5c9 100644 --- a/docs/newgrf-additions-nml.html +++ b/docs/newgrf-additions-nml.html @@ -161,6 +161,55 @@ Flood resistance is always enabled for objects which can be built on water.|||||||||||||||||||||||||||||||
map_tile_type | OBJECT_VIEWPORT_MAP_TILE_TYPE_XXX |
+ Set tile type used for display in viewport map mode and the small-map window. + The value should be one of: +
| ||||||||||||||||||||||||||||||||||||
map_tile_subtype | 0 .. 65535 |
+ This can be used to further refine the type set in map_tile_type. +Farm fields: +
Trees: +
|
Variables in the table below which are not supported by the version of OpenTTD being used return a value of 0.
diff --git a/docs/newgrf-additions.html b/docs/newgrf-additions.html index 70c28fe32e..3eef52c1e1 100644 --- a/docs/newgrf-additions.html +++ b/docs/newgrf-additions.html @@ -413,6 +413,56 @@ This property can be used to enable flood resistance without enabling the object to be built on water.This is indicated by the feature name: action0_object_flood_resistant, version 1
+This property sets how object tiles are displayed in viewport map mode and the small-map window.
+ The property length is 1 byte.
Value | Meaning | Notes |
---|---|---|
00 | Default object | |
01 | Clear/bare dirt | If object_use_land_ground is enabled, the underlying ground type will be used instead |
02 | Grass | |
03 | Rough ground | |
04 | Rocky ground | |
05 | Farm fields | The specific type of field can be set using object_viewport_map_tile_subtype |
06 | Snow | |
07 | Desert | |
08 | Trees | The specific tree count and ground type/density can be set using object_viewport_map_tile_subtype |
09 | House | |
0A | Water |
This is indicated by the feature name: action0_object_viewport_map_tile_type, version 1
+This property can be used to further refine the type set in object_viewport_map_tile_type.
+ The property length is 2 bytes.
Farm fields: +
Bit | Value | Meaning |
---|---|---|
0 - 2 | 0 - 7 | + Which field type to use + |
Trees: +
Bit | Meaning | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 - 3 |
+ Tree ground type
+
| ||||||||||||
4 - 7 | + Tree ground density (clamped to: 0 - 3) + | ||||||||||||
8 - 11 | + Number of trees on the tile (clamped to: 1 - 4) + |
This is indicated by the feature name: action0_object_viewport_map_tile_type, version 1
See Action 14 Specification and Variational Action 2 Specification for background information.
diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 734efc66ee..1c0cfe1d22 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -4327,6 +4327,17 @@ static ChangeInfoResult ObjectChangeInfo(uint id, int numinfo, int prop, const G if (buf->ReadByte() != 0) spec->ctrl_flags |= OBJECT_CTRL_FLAG_FLOOD_RESISTANT; break; + case A0RPI_OBJECT_VIEWPORT_MAP_TYPE: + if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break; + spec->vport_map_type = (ObjectViewportMapType)buf->ReadByte(); + spec->ctrl_flags |= OBJECT_CTRL_FLAG_VPORT_MAP_TYPE; + break; + + case A0RPI_OBJECT_VIEWPORT_MAP_SUBTYPE: + if (MappedPropertyLengthMismatch(buf, 2, mapping_entry)) break; + spec->vport_map_subtype = buf->ReadWord(); + break; + default: ret = HandleAction0PropertyDefault(buf, prop); break; diff --git a/src/newgrf_extension.cpp b/src/newgrf_extension.cpp index 78b3373e81..79116ff74c 100644 --- a/src/newgrf_extension.cpp +++ b/src/newgrf_extension.cpp @@ -50,6 +50,7 @@ extern const GRFFeatureInfo _grf_feature_list[] = { GRFFeatureInfo("action0_object_use_land_ground", 1), GRFFeatureInfo("action0_object_edge_foundation_mode", 2), GRFFeatureInfo("action0_object_flood_resistant", 1), + GRFFeatureInfo("action0_object_viewport_map_tile_type", 1), GRFFeatureInfo(), }; @@ -85,6 +86,8 @@ extern const GRFPropertyMapDefinition _grf_action0_remappable_properties[] = { 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(GSF_OBJECTS, A0RPI_OBJECT_FLOOD_RESISTANT, "object_flood_resistant"), + GRFPropertyMapDefinition(GSF_OBJECTS, A0RPI_OBJECT_VIEWPORT_MAP_TYPE, "object_viewport_map_tile_type"), + GRFPropertyMapDefinition(GSF_OBJECTS, A0RPI_OBJECT_VIEWPORT_MAP_SUBTYPE, "object_viewport_map_tile_subtype"), GRFPropertyMapDefinition(), }; diff --git a/src/newgrf_extension.h b/src/newgrf_extension.h index bf14d0b02b..ca21497efc 100644 --- a/src/newgrf_extension.h +++ b/src/newgrf_extension.h @@ -37,6 +37,8 @@ enum Action0RemapPropertyIds { A0RPI_OBJECT_USE_LAND_GROUND, A0RPI_OBJECT_EDGE_FOUNDATION_MODE, A0RPI_OBJECT_FLOOD_RESISTANT, + A0RPI_OBJECT_VIEWPORT_MAP_TYPE, + A0RPI_OBJECT_VIEWPORT_MAP_SUBTYPE, }; diff --git a/src/newgrf_object.h b/src/newgrf_object.h index f95a98eef4..1ab3587b60 100644 --- a/src/newgrf_object.h +++ b/src/newgrf_object.h @@ -45,6 +45,7 @@ enum ObjectCtrlFlags { OBJECT_CTRL_FLAG_USE_LAND_GROUND = 1 << 0, ///< Use land for ground sprite. OBJECT_CTRL_FLAG_EDGE_FOUNDATION = 1 << 1, ///< Use edge foundation mode. OBJECT_CTRL_FLAG_FLOOD_RESISTANT = 1 << 2, ///< Object is flood-resistant. + OBJECT_CTRL_FLAG_VPORT_MAP_TYPE = 1 << 3, ///< Viewport map type is set. }; DECLARE_ENUM_AS_BIT_SET(ObjectCtrlFlags) @@ -67,6 +68,20 @@ enum ObjectClassID { /** Allow incrementing of ObjectClassID variables */ DECLARE_POSTFIX_INCREMENT(ObjectClassID) +enum ObjectViewportMapType { + OVMT_DEFAULT = 0, + OVMT_CLEAR, + OVMT_GRASS, + OVMT_ROUGH, + OVMT_ROCKS, + OVMT_FIELDS, + OVMT_SNOW, + OVMT_DESERT, + OVMT_TREES, + OVMT_HOUSE, + OVMT_WATER, +}; + /** An object that isn't use for transport, industries or houses. * @note If you change this struct, adopt the initialization of * default objects in table/object_land.h @@ -92,6 +107,8 @@ struct ObjectSpec { uint8 views; ///< The number of views. uint8 generate_amount; ///< Number of objects which are attempted to be generated per 256^2 map during world generation. bool enabled; ///< Is this spec enabled? + ObjectViewportMapType vport_map_type; ///< Viewport map type + uint16 vport_map_subtype; ///< Viewport map subtype /** * Get the cost for building a structure of this type. diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp index 3aa8c2da17..3bb79e0ba8 100644 --- a/src/object_cmd.cpp +++ b/src/object_cmd.cpp @@ -182,6 +182,9 @@ void BuildObject(ObjectType type, TileIndex tile, CompanyID owner, Town *town, u SetObjectGroundTypeDensity(t, OBJECT_GROUND_GRASS, 0); } SetObjectFoundationType(t, SLOPE_ELEVATED, type, spec); + if (spec->ctrl_flags & OBJECT_CTRL_FLAG_VPORT_MAP_TYPE) { + SetObjectHasViewportMapViewOverride(t, true); + } MarkTileDirtyByTile(t, VMDF_NOT_MAP_MODE); } @@ -953,11 +956,11 @@ static void TileLoop_Object(TileIndex tile) } else { SetObjectGroundCounter(tile, 0); SetObjectGroundDensity(tile, GetObjectGroundDensity(tile) + 1); - MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE); + MarkTileDirtyByTile(tile, spec->vport_map_type != OVMT_CLEAR ? VMDF_NOT_MAP_MODE : VMDF_NONE); } } else { SetObjectGroundTypeDensity(tile, OBJECT_GROUND_GRASS, 3); - MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE); + MarkTileDirtyByTile(tile, spec->vport_map_type != OVMT_CLEAR ? VMDF_NOT_MAP_MODE : VMDF_NONE); } } } diff --git a/src/object_map.h b/src/object_map.h index 626f3a035a..6ffcfcb2b8 100644 --- a/src/object_map.h +++ b/src/object_map.h @@ -168,6 +168,18 @@ static inline void SetObjectEffectiveFoundationType(TileIndex t, ObjectEffective SB(_me[t].m6, 0, 2, foundation_type); } +static inline bool GetObjectHasViewportMapViewOverride(TileIndex t) +{ + assert_tile(IsTileType(t, MP_OBJECT), t); + return HasBit(_m[t].m4, 4); +} + +static inline void SetObjectHasViewportMapViewOverride(TileIndex t, bool map_view_override) +{ + assert_tile(IsTileType(t, MP_OBJECT), t); + SB(_m[t].m4, 4, 1, map_view_override ? 1 : 0); +} + /** * Make an Object tile. * @param t The tile to make and object tile. diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 8913e5cfcc..3e299e08a0 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -3989,13 +3989,20 @@ bool AfterLoadGame() } } - if (SlXvIsFeatureMissing(XSLFI_OBJECT_GROUND_TYPES, 2)) { + if (SlXvIsFeatureMissing(XSLFI_OBJECT_GROUND_TYPES, 3)) { for (TileIndex t = 0; t < map_size; t++) { if (IsTileType(t, MP_OBJECT)) { if (SlXvIsFeatureMissing(XSLFI_OBJECT_GROUND_TYPES)) _m[t].m4 = 0; - ObjectType type = GetObjectType(t); - extern void SetObjectFoundationType(TileIndex tile, Slope tileh, ObjectType type, const ObjectSpec *spec); - SetObjectFoundationType(t, SLOPE_ELEVATED, type, ObjectSpec::Get(type)); + if (SlXvIsFeatureMissing(XSLFI_OBJECT_GROUND_TYPES, 2)) { + ObjectType type = GetObjectType(t); + extern void SetObjectFoundationType(TileIndex tile, Slope tileh, ObjectType type, const ObjectSpec *spec); + SetObjectFoundationType(t, SLOPE_ELEVATED, type, ObjectSpec::Get(type)); + } + if (SlXvIsFeatureMissing(XSLFI_OBJECT_GROUND_TYPES, 3)) { + if (ObjectSpec::GetByTile(t)->ctrl_flags & OBJECT_CTRL_FLAG_VPORT_MAP_TYPE) { + SetObjectHasViewportMapViewOverride(t, true); + } + } } } } diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 4c8c4e1d29..85a945082d 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -161,7 +161,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_DEPOT_ORDER_EXTRA_FLAGS,XSCF_IGNORABLE_UNKNOWN, 1, 1, "depot_order_extra_flags", nullptr, nullptr, nullptr }, { XSLFI_EXTRA_SIGNAL_TYPES, XSCF_NULL, 1, 1, "extra_signal_types", nullptr, nullptr, nullptr }, { XSLFI_BANKRUPTCY_EXTRA, XSCF_NULL, 1, 1, "bankruptcy_extra", nullptr, nullptr, nullptr }, - { XSLFI_OBJECT_GROUND_TYPES, XSCF_NULL, 2, 2, "object_ground_types", nullptr, nullptr, nullptr }, + { XSLFI_OBJECT_GROUND_TYPES, XSCF_NULL, 3, 3, "object_ground_types", nullptr, nullptr, nullptr }, { XSLFI_LINKGRAPH_AIRCRAFT, XSCF_NULL, 1, 1, "linkgraph_aircraft", nullptr, nullptr, nullptr }, { XSLFI_COMPANY_PW, XSCF_IGNORABLE_ALL, 1, 1, "company_password", nullptr, nullptr, "PLYP" }, { XSLFI_ST_INDUSTRY_CARGO_MODE, XSCF_IGNORABLE_UNKNOWN, 1, 1, "st_industry_cargo_mode", nullptr, nullptr, nullptr }, diff --git a/src/smallmap_gui.cpp b/src/smallmap_gui.cpp index 3c416530a0..dd7192f91c 100644 --- a/src/smallmap_gui.cpp +++ b/src/smallmap_gui.cpp @@ -24,6 +24,8 @@ #include "screenshot.h" #include "guitimer_func.h" #include "zoom_func.h" +#include "object_map.h" +#include "newgrf_object.h" #include "smallmap_colours.h" #include "smallmap_gui.h" @@ -319,6 +321,41 @@ void BuildOwnerLegend() _smallmap_company_count = i; } +static TileType GetSmallMapTileType(TileIndex tile, TileType t) +{ + if (t == MP_OBJECT && GetObjectHasViewportMapViewOverride(tile)) { + ObjectViewportMapType vmtype = OVMT_DEFAULT; + const ObjectSpec *spec = ObjectSpec::GetByTile(tile); + if (spec->ctrl_flags & OBJECT_CTRL_FLAG_VPORT_MAP_TYPE) vmtype = spec->vport_map_type; + if (vmtype == OVMT_CLEAR && spec->ctrl_flags & OBJECT_CTRL_FLAG_USE_LAND_GROUND) { + if (IsTileOnWater(tile) && GetObjectGroundType(tile) != OBJECT_GROUND_SHORE) { + vmtype = OVMT_WATER; + } + } + switch (vmtype) { + case OVMT_DEFAULT: + break; + + case OVMT_TREES: + t = MP_TREES; + break; + + case OVMT_HOUSE: + t = MP_HOUSE; + break; + + case OVMT_WATER: + t = MP_WATER; + break; + + default: + t = MP_CLEAR; + break; + } + } + return t; +} + /** * Return the colour a tile would be displayed with in the small map in mode "Contour". * @param tile The tile of which we would like to get the colour. @@ -328,7 +365,7 @@ void BuildOwnerLegend() static inline uint32 GetSmallMapContoursPixels(TileIndex tile, TileType t) { const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour]; - return ApplyMask(cs->height_colours[TileHeight(tile)], &_smallmap_contours_andor[t]); + return ApplyMask(cs->height_colours[TileHeight(tile)], &_smallmap_contours_andor[GetSmallMapTileType(tile, t)]); } /** @@ -341,7 +378,7 @@ static inline uint32 GetSmallMapContoursPixels(TileIndex tile, TileType t) static inline uint32 GetSmallMapVehiclesPixels(TileIndex tile, TileType t) { const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour]; - return ApplyMask(cs->default_colour, &_smallmap_vehicles_andor[t]); + return ApplyMask(cs->default_colour, &_smallmap_vehicles_andor[GetSmallMapTileType(tile, t)]); } /** @@ -354,7 +391,7 @@ static inline uint32 GetSmallMapVehiclesPixels(TileIndex tile, TileType t) static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile, TileType t) { const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour]; - return ApplyMask(_smallmap_show_heightmap ? cs->height_colours[TileHeight(tile)] : cs->default_colour, &_smallmap_vehicles_andor[t]); + return ApplyMask(_smallmap_show_heightmap ? cs->height_colours[TileHeight(tile)] : cs->default_colour, &_smallmap_vehicles_andor[GetSmallMapTileType(tile, t)]); } /** @@ -409,7 +446,7 @@ static inline uint32 GetSmallMapRoutesPixels(TileIndex tile, TileType t) default: /* Ground colour */ const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour]; - return ApplyMask(cs->default_colour, &_smallmap_contours_andor[t]); + return ApplyMask(cs->default_colour, &_smallmap_contours_andor[GetSmallMapTileType(tile, t)]); } } @@ -451,6 +488,72 @@ static inline uint32 GetSmallMapVegetationPixels(TileIndex tile, TileType t) } return (GetTropicZone(tile) == TROPICZONE_RAINFOREST) ? MKCOLOUR_XYYX(PC_RAINFOREST, PC_TREES) : MKCOLOUR_XYYX(PC_GRASS_LAND, PC_TREES); + case MP_OBJECT: { + if (!GetObjectHasViewportMapViewOverride(tile)) return ApplyMask(MKCOLOUR_XXXX(PC_GRASS_LAND), &_smallmap_vehicles_andor[t]); + ObjectViewportMapType vmtype = OVMT_DEFAULT; + const ObjectSpec *spec = ObjectSpec::GetByTile(tile); + if (spec->ctrl_flags & OBJECT_CTRL_FLAG_VPORT_MAP_TYPE) vmtype = spec->vport_map_type; + + switch (vmtype) { + case OVMT_CLEAR: + if (spec->ctrl_flags & OBJECT_CTRL_FLAG_USE_LAND_GROUND) { + if (IsTileOnWater(tile) && GetObjectGroundType(tile) != OBJECT_GROUND_SHORE) { + t = MP_WATER; + } else { + switch (GetObjectGroundType(tile)) { + case OBJECT_GROUND_GRASS: + if (GetObjectGroundDensity(tile) < 3) return MKCOLOUR_XXXX(PC_BARE_LAND); + if (GetTropicZone(tile) == TROPICZONE_RAINFOREST) return MKCOLOUR_XXXX(PC_RAINFOREST); + return _vegetation_clear_bits[CLEAR_GRASS]; + + case OBJECT_GROUND_SNOW_DESERT: + return _vegetation_clear_bits[_settings_game.game_creation.landscape == LT_TROPIC ? CLEAR_DESERT : CLEAR_SNOW]; + + case OBJECT_GROUND_SHORE: + t = MP_WATER; + break; + + default: + /* This should never be reached, just draw as normal as a fallback */ + break; + } + } + } else { + return MKCOLOUR_XXXX(PC_BARE_LAND); + } + case OVMT_GRASS: + if (GetTropicZone(tile) == TROPICZONE_RAINFOREST) return MKCOLOUR_XXXX(PC_RAINFOREST); + return _vegetation_clear_bits[CLEAR_GRASS]; + case OVMT_ROUGH: + return _vegetation_clear_bits[CLEAR_ROUGH]; + case OVMT_ROCKS: + return _vegetation_clear_bits[CLEAR_ROCKS]; + case OVMT_FIELDS: + return _vegetation_clear_bits[CLEAR_FIELDS]; + case OVMT_SNOW: + return _vegetation_clear_bits[CLEAR_SNOW]; + case OVMT_DESERT: + return _vegetation_clear_bits[CLEAR_DESERT]; + case OVMT_TREES: { + const TreeGround tg = (TreeGround)GB(spec->vport_map_subtype, 0, 4); + if (tg == TREE_GROUND_SNOW_DESERT || tg == TREE_GROUND_ROUGH_SNOW) { + return (_settings_game.game_creation.landscape == LT_ARCTIC) ? MKCOLOUR_XYYX(PC_LIGHT_BLUE, PC_TREES) : MKCOLOUR_XYYX(PC_ORANGE, PC_TREES); + } + return (GetTropicZone(tile) == TROPICZONE_RAINFOREST) ? MKCOLOUR_XYYX(PC_RAINFOREST, PC_TREES) : MKCOLOUR_XYYX(PC_GRASS_LAND, PC_TREES); + } + case OVMT_HOUSE: + t = MP_HOUSE; + break; + case OVMT_WATER: + t = MP_WATER; + break; + + default: + break; + } + return ApplyMask(MKCOLOUR_XXXX(PC_GRASS_LAND), &_smallmap_vehicles_andor[t]); + } + default: return ApplyMask(MKCOLOUR_XXXX(PC_GRASS_LAND), &_smallmap_vehicles_andor[t]); } diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index 9a1003567d..ddd4f0d240 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -926,6 +926,7 @@ class NIHObject : public NIHelper { 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"); check_ctrl_flag(OBJECT_CTRL_FLAG_FLOOD_RESISTANT, "OBJECT_CTRL_FLAG_FLOOD_RESISTANT"); + check_ctrl_flag(OBJECT_CTRL_FLAG_VPORT_MAP_TYPE, "OBJECT_CTRL_FLAG_VPORT_MAP_TYPE"); } } } diff --git a/src/table/object_land.h b/src/table/object_land.h index 402b6a9153..1c90a459e8 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, 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, OVMT_DEFAULT, 0 } /* Climates * T = Temperate diff --git a/src/viewport.cpp b/src/viewport.cpp index 18d6b159d9..991563dbac 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -109,6 +109,8 @@ #include "scope_info.h" #include "scope.h" #include "blitter/32bpp_base.hpp" +#include "object_map.h" +#include "newgrf_object.h" #include