diff --git a/ai_new.c b/ai_new.c index c6bcfae0e7..885c04e850 100644 --- a/ai_new.c +++ b/ai_new.c @@ -366,11 +366,11 @@ static void AiNew_State_LocateRoute(Player *p) { // increase the temp with one, and return. We will come back later here // to try again p->ainew.temp++; - if (p->ainew.from_type == AI_CITY) { - if (p->ainew.temp >= _total_towns) p->ainew.temp = 0; - } else { - if (p->ainew.temp >= _total_industries) p->ainew.temp = 0; - } + if (p->ainew.from_type == AI_CITY) { + if (p->ainew.temp >= (int)_total_towns) p->ainew.temp = 0; + } else { + if (p->ainew.temp >= _total_industries) p->ainew.temp = 0; + } // Don't do an attempt if we are trying the same id as the last time... if (p->ainew.last_id == p->ainew.temp) return; @@ -489,15 +489,15 @@ static void AiNew_State_LocateRoute(Player *p) { } } - // It was not a valid city - // increase the temp with one, and return. We will come back later here - // to try again - p->ainew.temp++; - if (p->ainew.to_type == AI_CITY) { - if (p->ainew.temp >= _total_towns) p->ainew.temp = 0; - } else { - if (p->ainew.temp >= _total_industries) p->ainew.temp = 0; - } + // It was not a valid city + // increase the temp with one, and return. We will come back later here + // to try again + p->ainew.temp++; + if (p->ainew.to_type == AI_CITY) { + if (p->ainew.temp >= (int)_total_towns) p->ainew.temp = 0; + } else { + if (p->ainew.temp >= _total_industries) p->ainew.temp = 0; + } // Don't do an attempt if we are trying the same id as the last time... if (p->ainew.last_id == p->ainew.temp) return; diff --git a/oldloader.c b/oldloader.c index 4862c14a20..1b8bca9c29 100644 --- a/oldloader.c +++ b/oldloader.c @@ -595,6 +595,9 @@ static void FixTown(OldTown *o, int num, byte town_name_type) if (o->xy == 0) continue; + if (!AddBlockIfNeeded(&_town_pool, i)) + error("Towns: failed loading savegame: too many towns"); + t = GetTown(i); t->xy = o->xy; diff --git a/saveload.c b/saveload.c index 54931a2bbe..6378ea0ff9 100644 --- a/saveload.c +++ b/saveload.c @@ -944,7 +944,11 @@ static void *IntToReference(uint r, uint t) case REF_ORDER: return GetOrder(r - 1); case REF_VEHICLE: return GetVehicle(r - 1); case REF_STATION: return GetStation(r - 1); - case REF_TOWN: return GetTown(r - 1); + case REF_TOWN: { + if (!AddBlockIfNeeded(&_town_pool, r - 1)) + error("Towns: failed loading savegame: too many towns"); + return GetTown(r - 1); + } case REF_ROADSTOPS: //return (byte*)_roadstops + (r - 1) * sizeof(_roadstops[0]); diff --git a/town.h b/town.h index a8757d5961..c5c3b574ff 100644 --- a/town.h +++ b/town.h @@ -1,6 +1,7 @@ #ifndef TOWN_H #define TOWN_H +#include "pool.h" #include "player.h" struct Town { @@ -128,20 +129,30 @@ enum { bool CheckforTownRating(uint tile, uint32 flags, Town *t, byte type); -VARDEF Town _towns[250]; -VARDEF uint _towns_size; - VARDEF uint16 *_town_sort; +extern MemoryPool _town_pool; + +/** + * Get the pointer to the town with index 'index' + */ static inline Town *GetTown(uint index) { - assert(index < _towns_size); - return &_towns[index]; + return (Town*)GetItemFromPool(&_town_pool, index); } -#define FOR_ALL_TOWNS(t) for(t = _towns; t != &_towns[_towns_size]; t++) +/** + * Get the current size of the TownPool + */ +static inline uint16 GetTownPoolSize(void) +{ + return _town_pool.total_items; +} -VARDEF int _total_towns; // For the AI: the amount of towns active +#define FOR_ALL_TOWNS_FROM(t, start) for (t = GetTown(start); t != NULL; t = (t->index + 1 < GetTownPoolSize()) ? GetTown(t->index + 1) : NULL) +#define FOR_ALL_TOWNS(t) FOR_ALL_TOWNS_FROM(t, 0) + +VARDEF uint _total_towns; // For the AI: the amount of towns active VARDEF bool _town_sort_dirty; VARDEF byte _town_sort_order; diff --git a/town_cmd.c b/town_cmd.c index eb6f5eaa6f..eeb1aff497 100644 --- a/town_cmd.c +++ b/town_cmd.c @@ -17,6 +17,27 @@ #include "gui.h" #include "network.h" +enum { + /* Max towns: 64000 (8 * 8000) */ + TOWN_POOL_BLOCK_SIZE_BITS = 3, /* In bits, so (1 << 3) == 8 */ + TOWN_POOL_MAX_BLOCKS = 8000, +}; + +/** + * Called if a new block is added to the town-pool + */ +static void TownPoolNewBlock(uint start_item) +{ + Town *t; + + FOR_ALL_TOWNS_FROM(t, start_item) + t->index = start_item++; +} + +/* Initialize the town-pool */ +MemoryPool _town_pool = { "Towns", TOWN_POOL_MAX_BLOCKS, TOWN_POOL_BLOCK_SIZE_BITS, sizeof(Town), &TownPoolNewBlock, 0, 0, NULL }; + + enum { TOWN_HAS_CHURCH = 0x02, TOWN_HAS_STADIUM = 0x04 @@ -413,13 +434,13 @@ void OnTick_Town(void) return; i = _cur_town_ctr; + if (++_cur_town_ctr >= GetTownPoolSize()) + _cur_town_ctr = 0; + t = GetTown(i); - if (++i == _towns_size) i = 0; - _cur_town_ctr = i; if (t->xy != 0) TownTickHandler(t); - } static byte GetTownRoadMask(TileIndex tile) @@ -954,10 +975,22 @@ static Town *AllocateTown(void) Town *t; FOR_ALL_TOWNS(t) { if (t->xy == 0) { - if (t->index > _total_towns) _total_towns = t->index; + uint index = t->index; + + if (t->index > _total_towns) + _total_towns = t->index; + + memset(t, 0, sizeof(Town)); + t->index = index; + return t; } } + + /* Check if we can add a block to the pool */ + if (AddBlockToPool(&_town_pool)) + return AllocateTown(); + return NULL; } @@ -1835,22 +1868,18 @@ void TownsMonthlyLoop(void) void InitializeTowns(void) { Subsidy *s; - Town *t; - int i; - memset(_towns, 0, sizeof(_towns[0]) * _towns_size); - - i = 0; - FOR_ALL_TOWNS(t) - t->index = i++; + /* Clean the town pool and create 1 block in it */ + CleanPool(&_town_pool); + AddBlockToPool(&_town_pool); memset(_subsidies, 0, sizeof(_subsidies)); for (s=_subsidies; s != endof(_subsidies); s++) s->cargo_type = 0xFF; _cur_town_ctr = 0; - _town_sort_dirty = true; _total_towns = 0; + _town_sort_dirty = true; } const TileTypeProcs _tile_type_town_procs = { @@ -1941,12 +1970,24 @@ static void Save_TOWN(void) static void Load_TOWN(void) { int index; - while ((index = SlIterateArray()) != -1) { - Town *t = GetTown(index); + while ((index = SlIterateArray()) != -1) { + Town *t; + + if (!AddBlockIfNeeded(&_town_pool, index)) + error("Towns: failed loading savegame: too many towns"); + + t = GetTown(index); SlObject(t, _town_desc); - if (index > _total_towns) _total_towns = index; + + if ((uint)index > _total_towns) + _total_towns = index; } + + /* This is to ensure all pointers are within the limits of + * the size of the TownPool */ + if (_cur_town_ctr >= GetTownPoolSize()) + _cur_town_ctr = 0; } void AfterLoadTown(void) diff --git a/town_gui.c b/town_gui.c index 96922d3579..cd08386b13 100644 --- a/town_gui.c +++ b/town_gui.c @@ -367,14 +367,18 @@ static int CDECL TownNameSorter(const void *a, const void *b) { char buf1[64]; const Town *t; - byte val; + uint16 val; int r; t = GetTown(*(const uint16*)a); SetDParam(0, t->townnameparts); GetString(buf1, t->townnametype); - if ( (val=*(const uint16*)b) != _last_town_idx) { + /* If 'b' is the same town as in the last round, use the cached value + * We do this to speed stuff up ('b' is called with the same value a lot of + * times after eachother) */ + val = *(const uint16*)b; + if (val != _last_town_idx) { _last_town_idx = val; t = GetTown(val); SetDParam(0, t->townnameparts); @@ -401,12 +405,12 @@ static void MakeSortedTownList(void) int n = 0; /* Create array for sorting */ - _town_sort = realloc(_town_sort, _towns_size * sizeof(_town_sort[0])); + _town_sort = realloc(_town_sort, GetTownPoolSize() * sizeof(_town_sort[0])); if (_town_sort == NULL) error("Could not allocate memory for the town-sorting-list"); FOR_ALL_TOWNS(t) - if(t->xy) + if (t->xy) _town_sort[n++] = t->index; _num_town_sort = n; diff --git a/ttd.c b/ttd.c index 0b15b06a38..d78164faaf 100644 --- a/ttd.c +++ b/ttd.c @@ -503,7 +503,6 @@ static void InitializeDynamicVariables(void) _vehicles_size = lengthof(_vehicles); _vehicle_sort = NULL; - _towns_size = lengthof(_towns); _town_sort = NULL; _industries_size = lengthof(_industries); @@ -516,6 +515,8 @@ static void InitializeDynamicVariables(void) static void UnInitializeDynamicVariables(void) { /* Dynamic stuff needs to be free'd somewhere... */ + CleanPool(&_town_pool); + free(_station_sort); free(_vehicle_sort);