diff --git a/src/newgrf_industries.cpp b/src/newgrf_industries.cpp index 455501d557..6279165900 100644 --- a/src/newgrf_industries.cpp +++ b/src/newgrf_industries.cpp @@ -88,40 +88,21 @@ uint32 GetIndustryIDAtOffset(TileIndex tile, const Industry *i, uint32 cur_grfid return 0xFF << 8 | indtsp->grf_prop.subst_id; // so just give it the substitute } -struct IndustryLocationDistanceCache { - uint16 distances[NUM_INDUSTRYTYPES]; - bool initialised = false; - - static IndustryLocationDistanceCache *instance; -}; -IndustryLocationDistanceCache *IndustryLocationDistanceCache::instance = nullptr; - -static uint32 GetClosestIndustry(TileIndex tile, IndustryType type, const Industry *current) +uint32 IndustriesScopeResolver::GetClosestIndustry(IndustryType type) const { if (type >= NUM_INDUSTRYTYPES) return UINT32_MAX; - if (IndustryLocationDistanceCache::instance != nullptr) { - IndustryLocationDistanceCache *cache = IndustryLocationDistanceCache::instance; - if (!cache->initialised) { - MemSetT(cache->distances, 0xFF, NUM_INDUSTRYTYPES); - for (const Industry *i : Industry::Iterate()) { - if (i == current || i->type >= NUM_INDUSTRYTYPES) continue; - uint dist = DistanceManhattan(tile, i->location.tile); - if (dist < (uint)cache->distances[i->type]) cache->distances[i->type] = (uint16)dist; - } - cache->initialised = true; + if (this->location_distance_cache == nullptr) { + this->location_distance_cache = std::make_unique(); + MemSetT(this->location_distance_cache->distances, 0xFF, NUM_INDUSTRYTYPES); + for (const Industry *i : Industry::Iterate()) { + if (i == this->industry || i->type >= NUM_INDUSTRYTYPES) continue; + + uint dist = DistanceManhattan(this->tile, i->location.tile); + if (dist < (uint)this->location_distance_cache->distances[i->type]) this->location_distance_cache->distances[i->type] = (uint16)dist; } - return cache->distances[type]; } - - uint32 best_dist = UINT32_MAX; - for (const Industry *i : Industry::Iterate()) { - if (i->type != type || i == current) continue; - - best_dist = std::min(best_dist, DistanceManhattan(tile, i->location.tile)); - } - - return best_dist; + return this->location_distance_cache->distances[type]; } /** @@ -131,10 +112,9 @@ static uint32 GetClosestIndustry(TileIndex tile, IndustryType type, const Indust * @param param_setID parameter given to the callback, which is the set id, or the local id, in our terminology * @param layout_filter on what layout do we filter? * @param town_filter Do we filter on the same town as the current industry? - * @param current Industry for which the inquiry is made * @return the formatted answer to the callback : rr(reserved) cc(count) dddd(manhattan distance of closest sister) */ -static uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout_filter, bool town_filter, const Industry *current, uint32 mask) +uint32 IndustriesScopeResolver::GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout_filter, bool town_filter, uint32 mask) const { uint32 GrfID = GetRegister(0x100); ///< Get the GRFID of the definition to look for in register 100h IndustryType ind_index; @@ -148,7 +128,7 @@ static uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout break; case 0xFFFFFFFF: // current grf - GrfID = GetIndustrySpec(current->type)->grf_prop.grffile->grfid; + GrfID = GetIndustrySpec(this->industry->type)->grf_prop.grffile->grfid; FALLTHROUGH; default: // use the grfid specified in register 100h @@ -163,14 +143,14 @@ static uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout if (layout_filter == 0 && !town_filter) { /* If the filter is 0, it could be because none was specified as well as being really a 0. * In either case, just do the regular var67 */ - if (mask & 0xFFFF) closest_dist = GetClosestIndustry(current->location.tile, ind_index, current); + if (mask & 0xFFFF) closest_dist = this->GetClosestIndustry(ind_index); if (mask & 0xFF0000) count = ClampTo(Industry::GetIndustryTypeCount(ind_index)); } else { /* Count only those who match the same industry type and layout filter * Unfortunately, we have to do it manually */ for (const Industry *i : Industry::Iterate()) { - if (i->type == ind_index && i != current && (i->selected_layout == layout_filter || layout_filter == 0) && (!town_filter || i->town == current->town)) { - closest_dist = std::min(closest_dist, DistanceManhattan(current->location.tile, i->location.tile)); + if (i->type == ind_index && i != this->industry && (i->selected_layout == layout_filter || layout_filter == 0) && (!town_filter || i->town == this->industry->town)) { + closest_dist = std::min(closest_dist, DistanceManhattan(this->tile, i->location.tile)); count++; } } @@ -309,7 +289,7 @@ static uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout /* Distance of nearest industry of given type */ case 0x64: if (this->tile == INVALID_TILE) break; - return GetClosestIndustry(this->tile, MapNewGRFIndustryType(parameter, indspec->grf_prop.grffile->grfid), this->industry); + return this->GetClosestIndustry(MapNewGRFIndustryType(parameter, indspec->grf_prop.grffile->grfid)); /* Get town zone and Manhattan distance of closest town */ case 0x65: { if (this->tile == INVALID_TILE) break; @@ -334,7 +314,7 @@ static uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout layout_filter = GB(reg, 0, 8); town_filter = HasBit(reg, 8); } - return GetCountAndDistanceOfClosestInstance(parameter, layout_filter, town_filter, this->industry, extra->mask); + return this->GetCountAndDistanceOfClosestInstance(parameter, layout_filter, town_filter, extra->mask); } case 0x69: @@ -580,14 +560,9 @@ CommandCost CheckIfCallBackAllowsCreation(TileIndex tile, IndustryType type, siz ind.founder = founder; ind.psa = nullptr; - IndustryLocationDistanceCache distance_cache; - IndustryLocationDistanceCache::instance = &distance_cache; - IndustriesResolverObject object(tile, &ind, type, seed, CBID_INDUSTRY_LOCATION, 0, creation_type); uint16 result = object.ResolveCallback(); - IndustryLocationDistanceCache::instance = nullptr; - /* Unlike the "normal" cases, not having a valid result means we allow * the building of the industry, as that's how it's done in TTDP. */ if (result == CALLBACK_FAILED) return CommandCost(); diff --git a/src/newgrf_industries.h b/src/newgrf_industries.h index 6c0cd4ba2b..65af1e90dc 100644 --- a/src/newgrf_industries.h +++ b/src/newgrf_industries.h @@ -12,12 +12,18 @@ #include "newgrf_town.h" +struct IndustryLocationDistanceCache { + uint16 distances[NUM_INDUSTRYTYPES]; +}; + /** Resolver for industry scopes. */ struct IndustriesScopeResolver : public ScopeResolver { TileIndex tile; ///< Tile owned by the industry. + uint32 random_bits; ///< Random bits of the new industry. Industry *industry; ///< %Industry being resolved. IndustryType type; ///< Type of the industry. - uint32 random_bits; ///< Random bits of the new industry. + + mutable std::unique_ptr location_distance_cache; /** * Scope resolver for industries. @@ -28,7 +34,7 @@ struct IndustriesScopeResolver : public ScopeResolver { * @param random_bits Random bits of the new industry. */ IndustriesScopeResolver(ResolverObject &ro, TileIndex tile, Industry *industry, IndustryType type, uint32 random_bits = 0) - : ScopeResolver(ro), tile(tile), industry(industry), type(type), random_bits(random_bits) + : ScopeResolver(ro), tile(tile), random_bits(random_bits), industry(industry), type(type) { } @@ -36,6 +42,9 @@ struct IndustriesScopeResolver : public ScopeResolver { uint32 GetVariable(uint16 variable, uint32 parameter, GetVariableExtra *extra) const override; uint32 GetTriggers() const override; void StorePSA(uint pos, int32 value) override; + + uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout_filter, bool town_filter, uint32 mask) const; + uint32 GetClosestIndustry(IndustryType type) const; }; /** Resolver for industries. */