diff -r 2b6e3fc5cd1c src/script/api/script_town.cpp --- a/src/script/api/script_town.cpp Sun Nov 01 20:21:24 2015 +0300 +++ b/src/script/api/script_town.cpp Mon Nov 16 18:08:40 2015 +0300 @@ -160,23 +160,24 @@ { EnforcePrecondition(false, IsValidTown(town_id)); + uint16 growth_rate; switch (days_between_town_growth) { - case TOWN_GROWTH_NORMAL: - days_between_town_growth = 0; - break; - - case TOWN_GROWTH_NONE: - days_between_town_growth = TOWN_GROW_RATE_CUSTOM_NONE; + case TOWN_GROW_RATE_NORMAL: + case TOWN_GROW_RATE_CUSTOM_NONE: + growth_rate = days_between_town_growth; break; default: - days_between_town_growth = days_between_town_growth * DAY_TICKS / TOWN_GROWTH_TICKS; - EnforcePrecondition(false, days_between_town_growth < TOWN_GROW_RATE_CUSTOM); - if (days_between_town_growth == 0) days_between_town_growth = 1; // as fast as possible + // We are asked for days_between_town_growth * DAY_TICKS, + // but town expands every (t->growth_rate + 1) * TOWN_GROWTH_TICKS + // Thus selecting growth_rate to make it as close as possible + growth_rate = RoundDivSU(days_between_town_growth * DAY_TICKS, TOWN_GROWTH_TICKS); + if (growth_rate > 0) growth_rate--; + EnforcePrecondition(false, growth_rate < TOWN_GROW_RATE_NORMAL); break; } - return ScriptObject::DoCommand(::Town::Get(town_id)->xy, town_id, days_between_town_growth, CMD_TOWN_GROWTH_RATE); + return ScriptObject::DoCommand(::Town::Get(town_id)->xy, town_id, growth_rate, CMD_TOWN_GROWTH_RATE); } /* static */ int32 ScriptTown::GetGrowthRate(TownID town_id) @@ -187,7 +188,7 @@ if (t->growth_rate == TOWN_GROW_RATE_CUSTOM_NONE) return TOWN_GROWTH_NONE; - return ((t->growth_rate & ~TOWN_GROW_RATE_CUSTOM) * TOWN_GROWTH_TICKS + DAY_TICKS) / DAY_TICKS; + return RoundDivSU(((t->growth_rate & ~TOWN_GROW_RATE_CUSTOM) + 1) * TOWN_GROWTH_TICKS, DAY_TICKS); } /* static */ int32 ScriptTown::GetDistanceManhattanToTile(TownID town_id, TileIndex tile) diff -r 2b6e3fc5cd1c src/script/api/script_town.hpp --- a/src/script/api/script_town.hpp Sun Nov 01 20:21:24 2015 +0300 +++ b/src/script/api/script_town.hpp Mon Nov 16 18:08:40 2015 +0300 @@ -120,8 +120,8 @@ * Special values for SetGrowthRate. */ enum TownGrowth { + TOWN_GROWTH_NORMAL = 0xFFFE, ///< Use default town growth algorithm instead of custom growth rate. TOWN_GROWTH_NONE = 0xFFFF, ///< Town does not grow at all. - TOWN_GROWTH_NORMAL = 0x10000, ///< Use default town growth algorithm instead of custom growth rate. }; /** diff -r 2b6e3fc5cd1c src/town.h --- a/src/town.h Sun Nov 01 20:21:24 2015 +0300 +++ b/src/town.h Mon Nov 16 18:08:40 2015 +0300 @@ -36,6 +36,7 @@ static const uint TOWN_GROWTH_WINTER = 0xFFFFFFFE; ///< The town only needs this cargo in the winter (any amount) static const uint TOWN_GROWTH_DESERT = 0xFFFFFFFF; ///< The town needs the cargo for growth when on desert (any amount) static const uint16 TOWN_GROW_RATE_CUSTOM = 0x8000; ///< If this mask is applied to Town::growth_rate, the grow_counter will not be calculated by the system (but assumed to be set by scripts) +static const uint16 TOWN_GROW_RATE_NORMAL = 0xFFFE; ///< Special value for CmdTownGrowthRate to switch to normal growth rate calculation static const uint16 TOWN_GROW_RATE_CUSTOM_NONE = 0xFFFF; ///< Special value for Town::growth_rate to disable town growth. typedef Pool TownPool; diff -r 2b6e3fc5cd1c src/town_cmd.cpp --- a/src/town_cmd.cpp Sun Nov 01 20:21:24 2015 +0300 +++ b/src/town_cmd.cpp Mon Nov 16 18:08:40 2015 +0300 @@ -2522,7 +2522,10 @@ return NULL; } -static void UpdateTownGrowRate(Town *t); +static void UpdateTownGrowCounter(Town *t, uint prev_growth_rate); +static uint GetNormalGrowthRate(Town *t); +static void UpdateTownGrowthRate(Town *t); +static void UpdateTownGrowth(Town *t); /** * Change the cargo goal of a town. @@ -2552,7 +2555,7 @@ if (flags & DC_EXEC) { t->goal[te] = p2; - UpdateTownGrowRate(t); + UpdateTownGrowth(t); InvalidateWindowData(WC_TOWN_VIEW, index); } @@ -2588,35 +2591,31 @@ * @param tile Unused. * @param flags Type of operation. * @param p1 Town ID to cargo game of. - * @param p2 Amount of days between growth, or TOWN_GROW_RATE_CUSTOM_NONE, or 0 to reset custom growth rate. + * @param p2 Amount of TOWN_GROWTH_TICKS intervals between growth, or TOWN_GROW_RATE_CUSTOM_NONE, or TOWN_GROW_RATE_NORMAL to reset custom growth rate. * @param text Unused. * @return Empty cost or an error. */ CommandCost CmdTownGrowthRate(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (_current_company != OWNER_DEITY) return CMD_ERROR; - if ((p2 & TOWN_GROW_RATE_CUSTOM) != 0 && p2 != TOWN_GROW_RATE_CUSTOM_NONE) return CMD_ERROR; + if ((p2 & TOWN_GROW_RATE_CUSTOM) != 0 && p2 != TOWN_GROW_RATE_CUSTOM_NONE && + p2 != TOWN_GROW_RATE_NORMAL) return CMD_ERROR; if (GB(p2, 16, 16) != 0) return CMD_ERROR; Town *t = Town::GetIfValid(p1); if (t == NULL) return CMD_ERROR; if (flags & DC_EXEC) { - if (p2 == 0) { - /* Clear TOWN_GROW_RATE_CUSTOM, UpdateTownGrowRate will determine a proper value */ - t->growth_rate = 0; - } else { - uint old_rate = t->growth_rate & ~TOWN_GROW_RATE_CUSTOM; - if (t->grow_counter >= old_rate) { - /* This also catches old_rate == 0 */ - t->grow_counter = p2; - } else { - /* Scale grow_counter, so half finished houses stay half finished */ - t->grow_counter = t->grow_counter * p2 / old_rate; - } + uint old_rate = t->growth_rate; + if (p2 == TOWN_GROW_RATE_NORMAL) + t->growth_rate = GetNormalGrowthRate(t); + else t->growth_rate = p2 | TOWN_GROW_RATE_CUSTOM; - } - UpdateTownGrowRate(t); + UpdateTownGrowCounter(t, old_rate); + if (old_rate == TOWN_GROW_RATE_CUSTOM_NONE || + t->growth_rate == TOWN_GROW_RATE_CUSTOM_NONE) + UpdateTownGrowth(t); + InvalidateWindowData(WC_TOWN_VIEW, p1); } @@ -2905,7 +2904,7 @@ t->fund_buildings_months = 3; /* Enable growth (also checking GameScript's opinion) */ - UpdateTownGrowRate(t); + UpdateTownGrowth(t); SetWindowDirty(WC_TOWN_VIEW, t->index); } @@ -3099,10 +3098,81 @@ SetWindowDirty(WC_TOWN_AUTHORITY, t->index); } -static void UpdateTownGrowRate(Town *t) -{ +static void UpdateTownGrowCounter(Town *t, uint prev_growth_rate) { + if (t->growth_rate == TOWN_GROW_RATE_CUSTOM_NONE) + return; + if (prev_growth_rate == TOWN_GROW_RATE_CUSTOM_NONE) { + t->grow_counter = min(t->growth_rate & ~TOWN_GROW_RATE_CUSTOM, t->grow_counter); + return; + } + t->grow_counter = RoundDivSU(t->grow_counter * ((t->growth_rate & ~TOWN_GROW_RATE_CUSTOM) + 1), (prev_growth_rate & ~TOWN_GROW_RATE_CUSTOM) + 1); // left + round +} + +/** + * Calculates amount of active stations in Tz0. + * @param town The town to calculate stations for + * @returns Amount of active stations + */ +static int CountActiveStations(Town *t) { + int n = 0; + const Station *st; + FOR_ALL_STATIONS(st) { + if (DistanceSquare(st->xy, t->xy) <= t->cache.squared_town_zone_radius[0]) { + if (st->time_since_load <= 20 || st->time_since_unload <= 20) { + n++; + } + } + } + return n; +} + +/** + * Calculates town growth rate in normal conditions (custom growth rate not set). + * @param town The town to calculate growth rate for + * @returns Calculated growth rate + */ +static uint GetNormalGrowthRate(Town *t) { + /** + * Towns are processed every TOWN_GROWTH_TICKS ticks, and this is the + * number of times towns are processed before a new building is built. + */ + static const uint16 _grow_count_values[2][6] = { + { 120, 120, 120, 100, 80, 60 }, // Fund new buildings has been activated + { 320, 420, 300, 220, 160, 100 } // Normal values + }; + + int n = CountActiveStations(t); + uint16 m = _grow_count_values[t->fund_buildings_months != 0 ? 0 : 1][min(n, 5)]; + + /* Use the normal growth rate values if new buildings have been funded in + * this town and the growth rate is set to none. */ + uint growth_multiplier = _settings_game.economy.town_growth_rate != 0 ? _settings_game.economy.town_growth_rate - 1 : 1; + + m >>= growth_multiplier; + if (t->larger_town) m /= 2; + + return m / (t->cache.num_houses / 50 + 1); +} + +static void UpdateTownGrowthRate(Town *t) { + if ((t->growth_rate & TOWN_GROW_RATE_CUSTOM) != 0) + return; + SetWindowDirty(WC_TOWN_VIEW, t->index); + uint old_rate = t->growth_rate; + t->growth_rate = GetNormalGrowthRate(t); + UpdateTownGrowCounter(t, old_rate); +} + +/** + * Updates town growth state (whether it is growing or not). + * @param town The town to update growth for + */ +static void UpdateTownGrowth(Town *t) { + UpdateTownGrowthRate(t); + + SetWindowDirty(WC_TOWN_VIEW, t->index); + ClrBit(t->flags, TOWN_IS_GROWING); - SetWindowDirty(WC_TOWN_VIEW, t->index); if (_settings_game.economy.town_growth_rate == 0 && t->fund_buildings_months == 0) return; @@ -3125,51 +3195,14 @@ if ((t->growth_rate & TOWN_GROW_RATE_CUSTOM) != 0) { if (t->growth_rate != TOWN_GROW_RATE_CUSTOM_NONE) SetBit(t->flags, TOWN_IS_GROWING); - SetWindowDirty(WC_TOWN_VIEW, t->index); return; } - /** - * Towns are processed every TOWN_GROWTH_TICKS ticks, and this is the - * number of times towns are processed before a new building is built. - */ - static const uint16 _grow_count_values[2][6] = { - { 120, 120, 120, 100, 80, 60 }, // Fund new buildings has been activated - { 320, 420, 300, 220, 160, 100 } // Normal values - }; - - int n = 0; - - const Station *st; - FOR_ALL_STATIONS(st) { - if (DistanceSquare(st->xy, t->xy) <= t->cache.squared_town_zone_radius[0]) { - if (st->time_since_load <= 20 || st->time_since_unload <= 20) { - n++; - } - } - } - - uint16 m; - - if (t->fund_buildings_months != 0) { - m = _grow_count_values[0][min(n, 5)]; - } else { - m = _grow_count_values[1][min(n, 5)]; - if (n == 0 && !Chance16(1, 12)) return; - } - - /* Use the normal growth rate values if new buildings have been funded in - * this town and the growth rate is set to none. */ - uint growth_multiplier = _settings_game.economy.town_growth_rate != 0 ? _settings_game.economy.town_growth_rate - 1 : 1; - - m >>= growth_multiplier; - if (t->larger_town) m /= 2; - - t->growth_rate = m / (t->cache.num_houses / 50 + 1); - t->grow_counter = min(t->growth_rate, t->grow_counter); + if (t->fund_buildings_months == 0 && CountActiveStations(t) == 0 && + !Chance16(1, 12)) + return; SetBit(t->flags, TOWN_IS_GROWING); - SetWindowDirty(WC_TOWN_VIEW, t->index); } static void UpdateTownAmounts(Town *t) @@ -3401,7 +3434,7 @@ UpdateTownAmounts(t); UpdateTownRating(t); - UpdateTownGrowRate(t); + UpdateTownGrowth(t); UpdateTownUnwanted(t); UpdateTownCargoes(t); }