diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 27462a69df..f927760b09 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -6194,6 +6194,10 @@ static void NewSpriteGroup(ByteReader *buf) break; } + case GSF_FAKE_TOWNS: + act_group = NewCallbackResultSpriteGroupNoTransform(CALLBACK_FAILED); + break; + /* Loading of Tile Layout and Production Callback groups would happen here */ default: grfmsg(1, "NewSpriteGroup: Unsupported feature %s, skipping", GetFeatureString(feature)); } diff --git a/src/newgrf_callbacks.h b/src/newgrf_callbacks.h index 08fd8604c0..b4988bb42b 100644 --- a/src/newgrf_callbacks.h +++ b/src/newgrf_callbacks.h @@ -282,6 +282,11 @@ enum CallbackID { /** Called to determine the engine name to show. */ CBID_VEHICLE_NAME = 0x161, // 15 bit callback + + /** Extended/non-standard callbacks follow */ + + /** Called to set town zones */ + XCBID_TOWN_ZONES = 0xEC008001, }; /** diff --git a/src/newgrf_debug_gui.cpp b/src/newgrf_debug_gui.cpp index 0a23823e7c..91db2e0af1 100644 --- a/src/newgrf_debug_gui.cpp +++ b/src/newgrf_debug_gui.cpp @@ -1642,6 +1642,7 @@ const char *GetNewGRFCallbackName(CallbackID cbid) CBID(CBID_INDUSTRY_PROD_CHANGE_BUILD) CBID(CBID_VEHICLE_SPAWN_VISUAL_EFFECT) CBID(CBID_VEHICLE_NAME) + CBID(XCBID_TOWN_ZONES) default: return nullptr; } } diff --git a/src/newgrf_extension.cpp b/src/newgrf_extension.cpp index 9389ffed26..a8d9d7f7d2 100644 --- a/src/newgrf_extension.cpp +++ b/src/newgrf_extension.cpp @@ -64,6 +64,7 @@ extern const GRFFeatureInfo _grf_feature_list[] = { GRFFeatureInfo("more_action2_ids", 1, GFTOF_MORE_ACTION2_IDS), GRFFeatureInfo("town_feature", 1), GRFFeatureInfo("town_uncapped_variables", 1), + GRFFeatureInfo("town_zone_callback", 1, GFTOF_TOWN_ZONE_CALLBACK), GRFFeatureInfo(), }; diff --git a/src/newgrf_extension.h b/src/newgrf_extension.h index ef8f009356..a1e94a3d0e 100644 --- a/src/newgrf_extension.h +++ b/src/newgrf_extension.h @@ -99,6 +99,7 @@ enum Action2VariableRemapIds { enum GRFFeatureTestObservationFlag : uint8 { GFTOF_MORE_OBJECTS_PER_GRF = 0, GFTOF_MORE_ACTION2_IDS, + GFTOF_TOWN_ZONE_CALLBACK, GFTOF_INVALID = 0xFF, }; diff --git a/src/newgrf_generic.cpp b/src/newgrf_generic.cpp index df8739c07d..8dd96cbc6f 100644 --- a/src/newgrf_generic.cpp +++ b/src/newgrf_generic.cpp @@ -13,7 +13,10 @@ #include "industrytype.h" #include "core/random_func.hpp" #include "newgrf_sound.h" +#include "newgrf_town.h" +#include "newgrf_extension.h" #include "water_map.h" +#include "string_func.h" #include #include "safeguards.h" @@ -261,3 +264,38 @@ void AmbientSoundEffectCallback(TileIndex tile) if (callback != CALLBACK_FAILED) PlayTileSound(grf_file, callback, tile); } + +uint16 GetTownZonesCallback(Town *t) +{ + TownResolverObject object(nullptr, t, true); + object.callback = XCBID_TOWN_ZONES; + + const uint16 MAX_RETURN_VERSION = 0; + + for (GenericCallbackList::const_iterator it = _gcl[GSF_FAKE_TOWNS].begin(); it != _gcl[GSF_FAKE_TOWNS].end(); ++it) { + if (!HasBit(it->file->observed_feature_tests, GFTOF_TOWN_ZONE_CALLBACK)) continue; + object.grffile = it->file; + object.root_spritegroup = it->group; + uint16 result = object.ResolveCallback(); + if (result == CALLBACK_FAILED || result > MAX_RETURN_VERSION) continue; + + return result; + } + + return CALLBACK_FAILED; +} + +void DumpGenericCallbackSpriteGroups(GrfSpecFeature feature, DumpSpriteGroupPrinter print) +{ + SpriteGroupDumper dumper(print); + bool first = true; + for (GenericCallbackList::const_iterator it = _gcl[feature].begin(); it != _gcl[feature].end(); ++it) { + if (!first) print(nullptr, DSGPO_PRINT, 0, ""); + char buffer[64]; + seprintf(buffer, lastof(buffer), "GRF: %08X, town zone cb enabled: %s", + BSWAP32(it->file->grfid), HasBit(it->file->observed_feature_tests, GFTOF_TOWN_ZONE_CALLBACK) ? "yes" : "no"); + print(nullptr, DSGPO_PRINT, 0, buffer); + first = false; + dumper.DumpSpriteGroup(it->group, 0); + } +} diff --git a/src/newgrf_generic.h b/src/newgrf_generic.h index 6b9444e291..f5e16877bb 100644 --- a/src/newgrf_generic.h +++ b/src/newgrf_generic.h @@ -49,6 +49,7 @@ void AddGenericCallback(GrfSpecFeature feature, const GRFFile *file, const Sprit uint16 GetAiPurchaseCallbackResult(GrfSpecFeature feature, CargoID cargo_type, uint8 default_selection, IndustryType src_industry, IndustryType dst_industry, uint8 distance, AIConstructionEvent event, uint8 count, uint8 station_size, const GRFFile **file); void AmbientSoundEffectCallback(TileIndex tile); +uint16 GetTownZonesCallback(Town *t); /** Play an ambient sound effect for an empty tile. */ static inline void AmbientSoundEffect(TileIndex tile) diff --git a/src/newgrf_town.cpp b/src/newgrf_town.cpp index e45fd09714..77e70cb50f 100644 --- a/src/newgrf_town.cpp +++ b/src/newgrf_town.cpp @@ -11,6 +11,7 @@ #include "debug.h" #include "town.h" #include "newgrf_town.h" +#include "newgrf_extension.h" #include "safeguards.h" diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index 18c09e427b..90f3dd8690 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -1593,6 +1593,7 @@ static const NIVariable _niv_towns[] = { class NIHTown : public NIHelper { bool IsInspectable(uint index) const override { return Town::IsValidID(index); } + bool ShowSpriteDumpButton(uint index) const override { return true; } uint GetParent(uint index) const override { return UINT32_MAX; } const void *GetInstance(uint index)const override { return Town::Get(index); } const void *GetSpec(uint index) const override { return nullptr; } @@ -1660,6 +1661,12 @@ class NIHTown : public NIHelper { } } } + + /* virtual */ void SpriteDump(uint index, DumpSpriteGroupPrinter print) const override + { + extern void DumpGenericCallbackSpriteGroups(GrfSpecFeature feature, DumpSpriteGroupPrinter print); + DumpGenericCallbackSpriteGroups(GSF_FAKE_TOWNS, std::move(print)); + } }; static const NIFeature _nif_town = { diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 8bf23314ee..7ed8414e51 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -2052,21 +2052,37 @@ void UpdateTownRadius(Town *t) {121, 81, 0, 49, 36}, // 88 }; - if (_settings_game.economy.town_zone_calc_mode && t->larger_town) { + if (_settings_game.economy.town_zone_calc_mode) { int mass = t->cache.num_houses / 8; - t->cache.squared_town_zone_radius[0] = mass * _settings_game.economy.city_zone_0_mult; - t->cache.squared_town_zone_radius[1] = mass * _settings_game.economy.city_zone_1_mult; - t->cache.squared_town_zone_radius[2] = mass * _settings_game.economy.city_zone_2_mult; - t->cache.squared_town_zone_radius[3] = mass * _settings_game.economy.city_zone_3_mult; - t->cache.squared_town_zone_radius[4] = mass * _settings_game.economy.city_zone_4_mult; - } else if (_settings_game.economy.town_zone_calc_mode) { - int mass = t->cache.num_houses / 8; - t->cache.squared_town_zone_radius[0] = mass * _settings_game.economy.town_zone_0_mult; - t->cache.squared_town_zone_radius[1] = mass * _settings_game.economy.town_zone_1_mult; - t->cache.squared_town_zone_radius[2] = mass * _settings_game.economy.town_zone_2_mult; - t->cache.squared_town_zone_radius[3] = mass * _settings_game.economy.town_zone_3_mult; - t->cache.squared_town_zone_radius[4] = mass * _settings_game.economy.town_zone_4_mult; - } else if (t->cache.num_houses < 92) { + if (t->larger_town) { + t->cache.squared_town_zone_radius[0] = mass * _settings_game.economy.city_zone_0_mult; + t->cache.squared_town_zone_radius[1] = mass * _settings_game.economy.city_zone_1_mult; + t->cache.squared_town_zone_radius[2] = mass * _settings_game.economy.city_zone_2_mult; + t->cache.squared_town_zone_radius[3] = mass * _settings_game.economy.city_zone_3_mult; + t->cache.squared_town_zone_radius[4] = mass * _settings_game.economy.city_zone_4_mult; + } else { + t->cache.squared_town_zone_radius[0] = mass * _settings_game.economy.town_zone_0_mult; + t->cache.squared_town_zone_radius[1] = mass * _settings_game.economy.town_zone_1_mult; + t->cache.squared_town_zone_radius[2] = mass * _settings_game.economy.town_zone_2_mult; + t->cache.squared_town_zone_radius[3] = mass * _settings_game.economy.town_zone_3_mult; + t->cache.squared_town_zone_radius[4] = mass * _settings_game.economy.town_zone_4_mult; + } + return; + } + + MemSetT(t->cache.squared_town_zone_radius, 0, lengthof(t->cache.squared_town_zone_radius)); + + uint16 cb_result = GetTownZonesCallback(t); + if (cb_result == 0) { + t->cache.squared_town_zone_radius[0] = GetRegister(0x100); + t->cache.squared_town_zone_radius[1] = GetRegister(0x101); + t->cache.squared_town_zone_radius[2] = GetRegister(0x102); + t->cache.squared_town_zone_radius[3] = GetRegister(0x103); + t->cache.squared_town_zone_radius[4] = GetRegister(0x104); + return; + } + + if (t->cache.num_houses < 92) { memcpy(t->cache.squared_town_zone_radius, _town_squared_town_zone_radius_data[t->cache.num_houses / 4], sizeof(t->cache.squared_town_zone_radius)); } else { int mass = t->cache.num_houses / 8;