diff --git a/src/newgrf_airporttiles.cpp b/src/newgrf_airporttiles.cpp index bc2520718a..489357a34b 100644 --- a/src/newgrf_airporttiles.cpp +++ b/src/newgrf_airporttiles.cpp @@ -193,6 +193,7 @@ static uint32_t GetAirportTileIDAtOffset(TileIndex tile, const Station *st, uint /* Get airport tile ID at offset */ case 0x62: return GetAirportTileIDAtOffset(GetNearbyTile(parameter, this->tile), this->st, this->ro.grffile->grfid); + case 0x79: return GetNearbyBadgeVariableResult(*this->ro.grffile, GetNearbyTile(parameter, this->tile), this->ro); case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, this->ats->badges, parameter); } diff --git a/src/newgrf_badge.cpp b/src/newgrf_badge.cpp index 59a9a4a1ca..081789d36d 100644 --- a/src/newgrf_badge.cpp +++ b/src/newgrf_badge.cpp @@ -8,13 +8,25 @@ /** @file newgrf_badge.cpp Functionality for NewGRF badges. */ #include "stdafx.h" +#include "house.h" +#include "industry_map.h" #include "newgrf.h" +#include "newgrf_airporttiles.h" #include "newgrf_badge.h" #include "newgrf_badge_type.h" +#include "newgrf_object.h" +#include "newgrf_roadstop.h" +#include "newgrf_station.h" #include "newgrf_spritegroup.h" +#include "rail.h" +#include "rail_map.h" +#include "station_map.h" #include "stringfilter_type.h" #include "strings_func.h" +#include "tile_map.h" #include "timer/timer_game_calendar.h" +#include "town_map.h" +#include "tunnelbridge_map.h" #include "table/strings.h" @@ -216,7 +228,173 @@ BadgeResolverObject::BadgeResolverObject(const Badge &badge, GrfSpecFeature feat } /** - * Test for a matching badge in a list of badges, returning the number of matching bits. + * Test if a list of badges contains a badge. + * @param badges List of badges. + * @param badge Badge to find. + * @returns true iff the badge appears in the list. + */ +static bool BadgesContains(std::span badges, BadgeID badge) +{ + return std::ranges::find(badges, badge) != std::end(badges); +} + +/** + * Test if a rail type has a badge. + * @param rt Rail type to test. + * @param badge Badge to find. + * @returns true iff the rail type has the badge. + */ +static bool RailTypeHasBadge(RailType rt, BadgeID badge) +{ + return rt != INVALID_RAILTYPE && BadgesContains(GetRailTypeInfo(rt)->badges, badge); +} + +/** + * Test if a road type has a badge. + * @param rt Road type to test. + * @param badge Badge to find. + * @returns true iff the road type has the badge. + */ +static bool RoadTypeHasBadge(RoadType rt, BadgeID badge) +{ + return rt != INVALID_ROADTYPE && BadgesContains(GetRoadTypeInfo(rt)->badges, badge); +} + +using TileHasBadgeProc = bool(*)(TileIndex tile, BadgeID badge, GrfSpecFeatures features); + +static bool TileHasBadge_Rail(TileIndex tile, BadgeID badge, GrfSpecFeatures features) +{ + if (features.Test(GrfSpecFeature::GSF_RAILTYPES) && RailTypeHasBadge(GetRailType(tile), badge)) return true; + return false; +} + +static bool TileHasBadge_Road(TileIndex tile, BadgeID badge, GrfSpecFeatures features) +{ + if (features.Test(GrfSpecFeature::GSF_ROADTYPES) && RoadTypeHasBadge(GetRoadTypeRoad(tile), badge)) return true; + if (features.Test(GrfSpecFeature::GSF_TRAMTYPES) && RoadTypeHasBadge(GetRoadTypeTram(tile), badge)) return true; + if (features.Test(GrfSpecFeature::GSF_RAILTYPES) && IsLevelCrossing(tile) && RailTypeHasBadge(GetRailType(tile), badge)) return true; + return false; +} + +static bool TileHasBadge_Town(TileIndex tile, BadgeID badge, GrfSpecFeatures features) +{ + if (features.Test(GrfSpecFeature::GSF_HOUSES) && BadgesContains(HouseSpec::Get(GetHouseType(tile))->badges, badge)) return true; + return false; +} + +static bool TileHasBadge_Station(TileIndex tile, BadgeID badge, GrfSpecFeatures features) +{ + switch (GetStationType(tile)) { + case StationType::Rail: + case StationType::RailWaypoint: + if (features.Test(GrfSpecFeature::GSF_STATIONS)) { + if (const StationSpec *spec = GetStationSpec(tile); spec != nullptr && BadgesContains(spec->badges, badge)) return true; + } + if (features.Test(GrfSpecFeature::GSF_RAILTYPES) && RailTypeHasBadge(GetRailType(tile), badge)) return true; + return false; + + case StationType::Bus: + case StationType::Truck: + case StationType::RoadWaypoint: + if (features.Test(GrfSpecFeature::GSF_ROADSTOPS)) { + if (const RoadStopSpec *spec = GetRoadStopSpec(tile); spec != nullptr && BadgesContains(spec->badges, badge)) return true; + } + if (features.Test(GrfSpecFeature::GSF_ROADTYPES) && RoadTypeHasBadge(GetRoadTypeRoad(tile), badge)) return true; + if (features.Test(GrfSpecFeature::GSF_TRAMTYPES) && RoadTypeHasBadge(GetRoadTypeTram(tile), badge)) return true; + return false; + + case StationType::Airport: + if (features.Test(GrfSpecFeature::GSF_AIRPORTTILES) && BadgesContains(AirportTileSpec::GetByTile(tile)->badges, badge)) return true; + return false; + + default: + return false; + } +} + +static bool TileHasBadge_Industry(TileIndex tile, BadgeID badge, GrfSpecFeatures features) +{ + if (features.Test(GrfSpecFeature::GSF_INDUSTRYTILES) && BadgesContains(GetIndustryTileSpec(GetIndustryGfx(tile))->badges, badge)) return true; + if (features.Test(GrfSpecFeature::GSF_INDUSTRIES) && BadgesContains(GetIndustrySpec(GetIndustryType(tile))->badges, badge)) return true; + return false; +} + +static bool TileHasBadge_TunnelBridge(TileIndex tile, BadgeID badge, GrfSpecFeatures features) +{ + switch (GetTunnelBridgeTransportType(tile)) { + case TransportType::TRANSPORT_RAIL: + if (features.Test(GrfSpecFeature::GSF_RAILTYPES) && RailTypeHasBadge(GetRailType(tile), badge)) return true; + return false; + + case TransportType::TRANSPORT_ROAD: + if (features.Test(GrfSpecFeature::GSF_ROADTYPES) && RoadTypeHasBadge(GetRoadTypeRoad(tile), badge)) return true; + if (features.Test(GrfSpecFeature::GSF_TRAMTYPES) && RoadTypeHasBadge(GetRoadTypeTram(tile), badge)) return true; + return false; + + default: + return false; + } +} + +static bool TileHasBadge_Object(TileIndex tile, BadgeID badge, GrfSpecFeatures features) +{ + if (features.Test(GrfSpecFeature::GSF_OBJECTS) && BadgesContains(ObjectSpec::GetByTile(tile)->badges, badge)) return true; + return false; +} + +/** + * Test if a tile has an item containing the specified badge. + * @param tile Tile to query. + * @param badge Badge to search for. + * @param features GRF features to include in the test. + * @returns true iff the badge 'is on' the tile. + */ +static bool TileHasBadge(TileIndex tile, BadgeID badge, GrfSpecFeatures features) +{ + /* Per-tiletype functions for badge testing. Like _tile_type_procs, this is 16 entries as GetTileType() reads 4 bits. */ + static constexpr std::array tile_procs = { + nullptr, + TileHasBadge_Rail, + TileHasBadge_Road, + TileHasBadge_Town, + nullptr, + TileHasBadge_Station, + nullptr, + nullptr, + TileHasBadge_Industry, + TileHasBadge_TunnelBridge, + TileHasBadge_Object, + }; + + TileHasBadgeProc proc = tile_procs[GetTileType(tile)]; + return proc != nullptr && proc(tile, badge, features); +} + +/** + * Test for a matching badge 'on' a specific map tile. + * @param grffile GRF file of the current varaction. + * @param tile Tile to test. + * @param parameter GRF-local badge index. + * @param features GRF features to include in the test. + * @returns true iff the badge is present. + */ +uint32_t GetNearbyBadgeVariableResult(const GRFFile &grffile, TileIndex tile, const ResolverObject &object) +{ + GrfSpecFeatures features = static_cast(object.GetRegister(0x101)); + if (features.None()) return 0; + + uint32_t parameter = object.GetRegister(0x100); + if (parameter >= std::size(grffile.badge_list)) return UINT_MAX; + + /* NewGRF cannot be expected to know the bounds of the map. If the tile is invalid it doesn't have the queried badge. */ + if (!IsValidTile(tile)) return 0; + + BadgeID index = grffile.badge_list[parameter]; + return TileHasBadge(tile, index, features); +} + +/** + * Test for a matching badge in a list of badges. * @param grffile GRF file of the current varaction. * @param badges List of badges to test. * @param parameter GRF-local badge index. @@ -227,7 +405,7 @@ uint32_t GetBadgeVariableResult(const GRFFile &grffile, std::span if (parameter >= std::size(grffile.badge_list)) return UINT_MAX; BadgeID index = grffile.badge_list[parameter]; - return std::ranges::find(badges, index) != std::end(badges); + return BadgesContains(badges, index); } /** diff --git a/src/newgrf_badge.h b/src/newgrf_badge.h index f08882351a..899683be1a 100644 --- a/src/newgrf_badge.h +++ b/src/newgrf_badge.h @@ -64,6 +64,7 @@ Badge *GetBadgeByLabel(std::string_view label); Badge *GetClassBadge(BadgeClassID class_index); std::span GetClassBadges(); +uint32_t GetNearbyBadgeVariableResult(const GRFFile &grffile, TileIndex tile, const ResolverObject &object); uint32_t GetBadgeVariableResult(const struct GRFFile &grffile, std::span badges, uint32_t parameter); PalSpriteID GetBadgeSprite(const Badge &badge, GrfSpecFeature feature, std::optional introduction_date, PaletteID remap); diff --git a/src/newgrf_house.cpp b/src/newgrf_house.cpp index 565927aaa7..4a37cf1eec 100644 --- a/src/newgrf_house.cpp +++ b/src/newgrf_house.cpp @@ -444,6 +444,7 @@ static uint32_t GetDistanceFromNearbyHouse(uint8_t parameter, TileIndex start_ti return _house_mngr.GetGRFID(house_id); } + case 0x79: return GetNearbyBadgeVariableResult(*this->ro.grffile, GetNearbyTile(parameter, this->tile), this->ro); case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, HouseSpec::Get(this->house_id)->badges, parameter); } diff --git a/src/newgrf_industries.cpp b/src/newgrf_industries.cpp index 4f532ff1b2..1778698381 100644 --- a/src/newgrf_industries.cpp +++ b/src/newgrf_industries.cpp @@ -168,6 +168,7 @@ static uint32_t GetCountAndDistanceOfClosestInstance(const ResolverObject &objec /* Variables available during construction check. */ switch (variable) { + case 0x79: return GetNearbyBadgeVariableResult(*this->ro.grffile, GetNearbyTile(parameter, this->tile), this->ro); case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, GetIndustrySpec(this->type)->badges, parameter); case 0x80: return this->tile.base(); @@ -354,6 +355,7 @@ static uint32_t GetCountAndDistanceOfClosestInstance(const ResolverObject &objec NOT_REACHED(); } + case 0x79: return GetNearbyBadgeVariableResult(*this->ro.grffile, GetNearbyTile(parameter, this->tile), this->ro); case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, GetIndustrySpec(this->type)->badges, parameter); /* Get a variable from the persistent storage */ diff --git a/src/newgrf_industrytiles.cpp b/src/newgrf_industrytiles.cpp index 1c73b14b62..5229525612 100644 --- a/src/newgrf_industrytiles.cpp +++ b/src/newgrf_industrytiles.cpp @@ -93,6 +93,7 @@ uint32_t GetRelativePosition(TileIndex tile, TileIndex ind_tile) /* Get industry tile ID at offset */ case 0x62: return GetIndustryIDAtOffset(GetNearbyTile(parameter, this->tile), this->industry, this->ro.grffile->grfid); + case 0x79: return GetNearbyBadgeVariableResult(*this->ro.grffile, GetNearbyTile(parameter, this->tile), this->ro); case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, GetIndustryTileSpec(GetIndustryGfx(this->tile))->badges, parameter); } diff --git a/src/newgrf_object.cpp b/src/newgrf_object.cpp index 52963dc007..10feffef3c 100644 --- a/src/newgrf_object.cpp +++ b/src/newgrf_object.cpp @@ -291,6 +291,7 @@ static uint32_t GetCountAndDistanceOfClosestInstance(const ResolverObject &objec /* Object view */ case 0x48: return this->view; + case 0x79: return GetNearbyBadgeVariableResult(*this->ro.grffile, GetNearbyTile(parameter, this->tile), this->ro); case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, this->spec->badges, parameter); /* @@ -364,6 +365,7 @@ static uint32_t GetCountAndDistanceOfClosestInstance(const ResolverObject &objec /* Count of object, distance of closest instance */ case 0x64: return GetCountAndDistanceOfClosestInstance(this->ro, parameter, this->ro.grffile->grfid, this->tile, this->obj); + case 0x79: return GetNearbyBadgeVariableResult(*this->ro.grffile, GetNearbyTile(parameter, this->tile), this->ro); case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, this->spec->badges, parameter); } diff --git a/src/newgrf_roadstop.cpp b/src/newgrf_roadstop.cpp index 02db3fa33d..4f16f4ccd8 100644 --- a/src/newgrf_roadstop.cpp +++ b/src/newgrf_roadstop.cpp @@ -205,6 +205,7 @@ uint32_t RoadStopScopeResolver::GetVariable(uint8_t variable, [[maybe_unused]] u return 0xFFFE; } + case 0x79: return GetNearbyBadgeVariableResult(*this->ro.grffile, GetNearbyTile(parameter, this->tile), this->ro); case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, this->roadstopspec->badges, parameter); case 0xF0: return this->st == nullptr ? 0 : this->st->facilities.base(); // facilities diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index 4c16bb7155..2483cbd0d1 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -295,6 +295,12 @@ TownScopeResolver *StationResolverObject::GetTown() } break; + case 0x79: + if (this->axis != INVALID_AXIS && this->tile != INVALID_TILE) { + return GetNearbyBadgeVariableResult(*this->ro.grffile, GetNearbyTile(parameter, this->tile, true, this->axis), this->ro); + } + break; + case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, this->statspec->badges, parameter); case 0xFA: return ClampTo(TimerGameCalendar::date - CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR); // Build date, clamped to a 16 bit value @@ -398,6 +404,7 @@ TownScopeResolver *StationResolverObject::GetTown() return 0xFFFE; } + case 0x79: return GetNearbyBadgeVariableResult(*this->ro.grffile, GetNearbyTile(parameter, this->tile), this->ro); case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, this->statspec->badges, parameter); /* General station variables */