From fef8b831a93a0fc772fc8a0b7799ec51cd86a967 Mon Sep 17 00:00:00 2001 From: Pavel Stupnikov Date: Wed, 2 May 2018 22:01:30 +0300 Subject: Change: Switch town growth rate and counter to actual game ticks (#6763) --- src/newgrf_town.cpp | 4 ++-- src/saveload/afterload.cpp | 13 ++++++++++++ src/saveload/saveload.cpp | 3 ++- src/script/api/script_town.cpp | 18 ++++++++-------- src/script/api/script_town.hpp | 2 +- src/town.h | 15 ++++++++++--- src/town_cmd.cpp | 48 ++++++++++++++++++++++++------------------ src/town_gui.cpp | 2 +- 8 files changed, 68 insertions(+), 37 deletions(-) diff --git a/src/newgrf_town.cpp b/src/newgrf_town.cpp index 820e2be68..00fcf76b6 100644 --- a/src/newgrf_town.cpp +++ b/src/newgrf_town.cpp @@ -50,7 +50,7 @@ case 0x81: return GB(this->t->xy, 8, 8); case 0x82: return ClampToU16(this->t->cache.population); case 0x83: return GB(ClampToU16(this->t->cache.population), 8, 8); - case 0x8A: return this->t->grow_counter; + case 0x8A: return this->t->grow_counter / TOWN_GROWTH_TICKS; case 0x92: return this->t->flags; // In original game, 0x92 and 0x93 are really one word. Since flags is a byte, this is to adjust case 0x93: return 0; case 0x94: return ClampToU16(this->t->cache.squared_town_zone_radius[0]); @@ -82,7 +82,7 @@ case 0xAE: return this->t->have_ratings; case 0xB2: return this->t->statues; case 0xB6: return ClampToU16(this->t->cache.num_houses); - case 0xB9: return this->t->growth_rate & (~TOWN_GROW_RATE_CUSTOM); + case 0xB9: return this->t->growth_rate / TOWN_GROWTH_TICKS; case 0xBA: return ClampToU16(this->t->supplied[CT_PASSENGERS].new_max); case 0xBB: return GB(ClampToU16(this->t->supplied[CT_PASSENGERS].new_max), 8, 8); case 0xBC: return ClampToU16(this->t->supplied[CT_MAIL].new_max); diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index f01afb425..33c49fb61 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2969,6 +2969,19 @@ bool AfterLoadGame() #endif } + if (IsSavegameVersionBefore(198)) { + /* Convert towns growth_rate and grow_counter to ticks */ + Town *t; + FOR_ALL_TOWNS(t) { + /* 0x8000 = TOWN_GROWTH_RATE_CUSTOM previously */ + if (t->growth_rate & 0x8000) SetBit(t->flags, TOWN_CUSTOM_GROWTH); + if (t->growth_rate != TOWN_GROWTH_RATE_NONE) { + t->growth_rate = TownTicksToGameTicks(t->growth_rate & ~0x8000); + } + /* Add t->index % TOWN_GROWTH_TICKS to spread growth across ticks. */ + t->grow_counter = TownTicksToGameTicks(t->grow_counter) + t->index % TOWN_GROWTH_TICKS; + } + } /* Station acceptance is some kind of cache */ if (IsSavegameVersionBefore(127)) { diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index cea80b59d..d06214e23 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -265,8 +265,9 @@ * 195 27572 1.6.x * 196 27778 1.7.x * 197 27978 1.8.x + * 198 */ -extern const uint16 SAVEGAME_VERSION = 197; ///< Current savegame version of OpenTTD. +extern const uint16 SAVEGAME_VERSION = 198; ///< Current savegame version of OpenTTD. SavegameType _savegame_type; ///< type of savegame we are loading FileToSaveLoad _file_to_saveload; ///< File to save or load in the openttd loop. diff --git a/src/script/api/script_town.cpp b/src/script/api/script_town.cpp index d81704ae3..4cdd6a9d0 100644 --- a/src/script/api/script_town.cpp +++ b/src/script/api/script_town.cpp @@ -159,24 +159,24 @@ /* static */ bool ScriptTown::SetGrowthRate(TownID town_id, uint32 days_between_town_growth) { EnforcePrecondition(false, IsValidTown(town_id)); - + uint16 growth_rate; switch (days_between_town_growth) { case TOWN_GROWTH_NORMAL: - days_between_town_growth = 0; + growth_rate = 0; break; case TOWN_GROWTH_NONE: - days_between_town_growth = TOWN_GROW_RATE_CUSTOM_NONE; + growth_rate = TOWN_GROWTH_RATE_NONE; 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 + EnforcePrecondition(false, days_between_town_growth <= MAX_TOWN_GROWTH_TICKS); + /* Don't use growth_rate 0 as it means GROWTH_NORMAL */ + growth_rate = max(days_between_town_growth * DAY_TICKS, 2u) - 1; 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) @@ -185,9 +185,9 @@ const Town *t = ::Town::Get(town_id); - if (t->growth_rate == TOWN_GROW_RATE_CUSTOM_NONE) return TOWN_GROWTH_NONE; + if (t->growth_rate == TOWN_GROWTH_RATE_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 + 1, DAY_TICKS); } /* static */ int32 ScriptTown::GetDistanceManhattanToTile(TownID town_id, TileIndex tile) diff --git a/src/script/api/script_town.hpp b/src/script/api/script_town.hpp index 03868e67a..7fdf8a6b3 100644 --- a/src/script/api/script_town.hpp +++ b/src/script/api/script_town.hpp @@ -259,7 +259,7 @@ public: * @param town_id The index of the town. * @param days_between_town_growth The amount of days between town growth, TOWN_GROWTH_NONE or TOWN_GROWTH_NORMAL. * @pre IsValidTown(town_id). - * @pre days_between_town_growth <= 30000 || days_between_town_growth == TOWN_GROWTH_NONE || days_between_town_growth == TOWN_GROWTH_NORMAL. + * @pre days_between_town_growth <= 880 || days_between_town_growth == TOWN_GROWTH_NONE || days_between_town_growth == TOWN_GROWTH_NORMAL. * @return True if the action succeeded. * @note Even when setting a growth rate, towns only grow when the conditions for growth (SetCargoCoal) are met, * and the game settings (economy.town_growth_rate) allow town growth at all. diff --git a/src/town.h b/src/town.h index 010c7c216..75deb7cca 100644 --- a/src/town.h +++ b/src/town.h @@ -35,8 +35,8 @@ static const uint INVALID_TOWN = 0xFFFF; 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_CUSTOM_NONE = 0xFFFF; ///< Special value for Town::growth_rate to disable town growth. +static const uint16 TOWN_GROWTH_RATE_NONE = 0xFFFF; ///< Special value for Town::growth_rate to disable town growth. +static const uint16 MAX_TOWN_GROWTH_TICKS = 930; ///< Max amount of original town ticks that still fit into uint16, about equal to UINT16_MAX / TOWN_GROWTH_TICKS but sligtly less to simplify calculations typedef Pool TownPool; extern TownPool _town_pool; @@ -165,6 +165,7 @@ enum TownFlags { TOWN_IS_GROWING = 0, ///< Conditions for town growth are met. Grow according to Town::growth_rate. TOWN_HAS_CHURCH = 1, ///< There can be only one church by town. TOWN_HAS_STADIUM = 2, ///< There can be only one stadium by town. + TOWN_CUSTOM_GROWTH = 3, ///< Growth rate is controlled by GS. }; CommandCost CheckforTownRating(DoCommandFlag flags, Town *t, TownRatingCheckType type); @@ -194,7 +195,6 @@ uint GetMaskOfTownActions(int *nump, CompanyID cid, const Town *t); bool GenerateTowns(TownLayout layout); const CargoSpec *FindFirstCargoWithTownEffect(TownEffect effect); - /** Town actions of a company. */ enum TownActions { TACT_NONE = 0x00, ///< Empty action set. @@ -284,6 +284,15 @@ void MakeDefaultName(T *obj) obj->town_cn = (uint16)next; // set index... } +/* + * Converts original town ticks counters to plain game ticks. Note that + * tick 0 is a valid tick so actual amount is one more than the counter value. + */ +static inline uint16 TownTicksToGameTicks(uint16 ticks) { + return (min(ticks, MAX_TOWN_GROWTH_TICKS) + 1) * TOWN_GROWTH_TICKS - 1; +} + + extern uint32 _town_cargoes_accepted; #endif /* TOWN_H */ diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 5973dfbc1..0001de00d 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -780,12 +780,13 @@ static bool GrowTown(Town *t); static void TownTickHandler(Town *t) { if (HasBit(t->flags, TOWN_IS_GROWING)) { - int i = t->grow_counter - 1; + int i = (int)t->grow_counter - 1; if (i < 0) { if (GrowTown(t)) { - i = t->growth_rate & (~TOWN_GROW_RATE_CUSTOM); + i = t->growth_rate; } else { - i = 0; + /* If growth failed wait a bit before retrying */ + i = min(t->growth_rate, TOWN_GROWTH_TICKS - 1); } } t->grow_counter = i; @@ -798,10 +799,7 @@ void OnTick_Town() Town *t; FOR_ALL_TOWNS(t) { - /* Run town tick at regular intervals, but not all at once. */ - if ((_tick_counter + t->index) % TOWN_GROWTH_TICKS == 0) { - TownTickHandler(t); - } + TownTickHandler(t); } } @@ -1596,8 +1594,10 @@ static void DoCreateTown(Town *t, TileIndex tile, uint32 townnameparts, TownSize UpdateTownRadius(t); t->flags = 0; t->cache.population = 0; - t->grow_counter = 0; - t->growth_rate = 250; + /* Spread growth across ticks so even if there are many + * similar towns they're unlikely to grow all in one tick */ + t->grow_counter = t->index % TOWN_GROWTH_TICKS; + t->growth_rate = TownTicksToGameTicks(250); /* Set the default cargo requirement for town growth */ switch (_settings_game.game_creation.landscape) { @@ -2613,14 +2613,13 @@ CommandCost CmdTownSetText(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 * @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 days between growth, or TOWN_GROWTH_RATE_NONE, or 0 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 (GB(p2, 16, 16) != 0) return CMD_ERROR; Town *t = Town::GetIfValid(p1); @@ -2628,10 +2627,10 @@ CommandCost CmdTownGrowthRate(TileIndex tile, DoCommandFlag flags, uint32 p1, ui if (flags & DC_EXEC) { if (p2 == 0) { - /* Clear TOWN_GROW_RATE_CUSTOM, UpdateTownGrowRate will determine a proper value */ - t->growth_rate = 0; + /* Just clear the flag, UpdateTownGrowRate will determine a proper growth rate */ + ClrBit(t->flags, TOWN_CUSTOM_GROWTH); } else { - uint old_rate = t->growth_rate & ~TOWN_GROW_RATE_CUSTOM; + uint old_rate = t->growth_rate; if (t->grow_counter >= old_rate) { /* This also catches old_rate == 0 */ t->grow_counter = p2; @@ -2639,7 +2638,8 @@ CommandCost CmdTownGrowthRate(TileIndex tile, DoCommandFlag flags, uint32 p1, ui /* Scale grow_counter, so half finished houses stay half finished */ t->grow_counter = t->grow_counter * p2 / old_rate; } - t->growth_rate = p2 | TOWN_GROW_RATE_CUSTOM; + t->growth_rate = p2; + SetBit(t->flags, TOWN_CUSTOM_GROWTH); } UpdateTownGrowRate(t); InvalidateWindowData(WC_TOWN_VIEW, p1); @@ -2924,14 +2924,22 @@ static CommandCost TownActionFundBuildings(Town *t, DoCommandFlag flags) if (!_settings_game.economy.fund_buildings) return CMD_ERROR; if (flags & DC_EXEC) { - /* Build next tick */ - t->grow_counter = 1; /* And grow for 3 months */ t->fund_buildings_months = 3; /* Enable growth (also checking GameScript's opinion) */ UpdateTownGrowRate(t); + /* Build a new house, but add a small delay to make sure + * that spamming funding doesn't let town grow any faster + * than 1 house per 2 * TOWN_GROWTH_TICKS ticks. + * Also emulate original behaviour when town was only growing in + * TOWN_GROWTH_TICKS intervals, to make sure that it's not too + * tick-perfect and gives player some time window where he can + * spam funding with the exact same effeciency. + */ + t->grow_counter = min(t->grow_counter, 2 * TOWN_GROWTH_TICKS - (t->growth_rate - t->grow_counter) % TOWN_GROWTH_TICKS); + SetWindowDirty(WC_TOWN_VIEW, t->index); } return CommandCost(); @@ -3148,8 +3156,8 @@ static void UpdateTownGrowRate(Town *t) } } - if ((t->growth_rate & TOWN_GROW_RATE_CUSTOM) != 0) { - if (t->growth_rate != TOWN_GROW_RATE_CUSTOM_NONE) SetBit(t->flags, TOWN_IS_GROWING); + if (HasBit(t->flags, TOWN_CUSTOM_GROWTH)) { + if (t->growth_rate != TOWN_GROWTH_RATE_NONE) SetBit(t->flags, TOWN_IS_GROWING); SetWindowDirty(WC_TOWN_VIEW, t->index); return; } @@ -3190,7 +3198,7 @@ static void UpdateTownGrowRate(Town *t) m >>= growth_multiplier; if (t->larger_town) m /= 2; - t->growth_rate = m / (t->cache.num_houses / 50 + 1); + t->growth_rate = TownTicksToGameTicks(m / (t->cache.num_houses / 50 + 1)); t->grow_counter = min(t->growth_rate, t->grow_counter); SetBit(t->flags, TOWN_IS_GROWING); diff --git a/src/town_gui.cpp b/src/town_gui.cpp index a87c29367..1d604634d 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -392,7 +392,7 @@ public: } if (HasBit(this->town->flags, TOWN_IS_GROWING)) { - SetDParam(0, ((this->town->growth_rate & (~TOWN_GROW_RATE_CUSTOM)) * TOWN_GROWTH_TICKS + DAY_TICKS) / DAY_TICKS); + SetDParam(0, RoundDivSU(this->town->growth_rate + 1, DAY_TICKS)); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_LEFT, y += FONT_HEIGHT_NORMAL, this->town->fund_buildings_months == 0 ? STR_TOWN_VIEW_TOWN_GROWS_EVERY : STR_TOWN_VIEW_TOWN_GROWS_EVERY_FUNDED); } else { DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_LEFT, y += FONT_HEIGHT_NORMAL, STR_TOWN_VIEW_TOWN_GROW_STOPPED); -- cgit v1.2.3-54-g00ecf