diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index d231f6912f..800f816471 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2700,6 +2700,19 @@ bool AfterLoadGame() * which is done by StartupEngines(). */ if (gcf_res != GLC_ALL_GOOD) StartupEngines(); + if (IsSavegameVersionBefore(166)) { + /* Update cargo acceptance map of towns. */ + for (TileIndex t = 0; t < map_size; t++) { + if (!IsTileType(t, MP_HOUSE)) continue; + Town::Get(GetTownIndex(t))->cargo_accepted.Add(t); + } + + Town *town; + FOR_ALL_TOWNS(town) { + UpdateTownCargos(town); + } + } + /* Road stops is 'only' updating some caches */ AfterLoadRoadStops(); AfterLoadLabelMaps(); diff --git a/src/saveload/town_sl.cpp b/src/saveload/town_sl.cpp index c88754ca89..71dc4cd5b9 100644 --- a/src/saveload/town_sl.cpp +++ b/src/saveload/town_sl.cpp @@ -97,6 +97,7 @@ void UpdateHousesAndTowns() /* Update the population and num_house dependant values */ FOR_ALL_TOWNS(town) { UpdateTownRadius(town); + UpdateTownCargos(town); } } @@ -176,6 +177,8 @@ static const SaveLoad _town_desc[] = { SLE_CONDLST(Town, psa_list, REF_STORAGE, 161, SL_MAX_VERSION), + SLE_CONDVAR(Town, cargo_produced, SLE_UINT32, 166, SL_MAX_VERSION), + /* reserve extra space in savegame here. (currently 30 bytes) */ SLE_CONDNULL(30, 2, SL_MAX_VERSION), @@ -210,6 +213,19 @@ static void Load_HIDS() Load_NewGRFMapping(_house_mngr); } +const SaveLoad *GetTileMatrixDesc() +{ + /* Here due to private member vars. */ + static const SaveLoad _tilematrix_desc[] = { + SLE_VAR(AcceptanceMatrix, area.tile, SLE_UINT32), + SLE_VAR(AcceptanceMatrix, area.w, SLE_UINT16), + SLE_VAR(AcceptanceMatrix, area.h, SLE_UINT16), + SLE_END() + }; + + return _tilematrix_desc; +} + static void RealSave_Town(Town *t) { SlObject(t, _town_desc); @@ -220,6 +236,14 @@ static void RealSave_Town(Town *t) for (int i = TE_BEGIN; i < NUM_TE; i++) { SlObject(&t->received[i], _town_received_desc); } + + if (IsSavegameVersionBefore(166)) return; + + SlObject(&t->cargo_accepted, GetTileMatrixDesc()); + if (t->cargo_accepted.area.w != 0) { + uint arr_len = t->cargo_accepted.area.w / AcceptanceMatrix::GRID * t->cargo_accepted.area.h / AcceptanceMatrix::GRID; + SlArray(t->cargo_accepted.data, arr_len, SLE_UINT32); + } } static void Save_TOWN() @@ -250,6 +274,18 @@ static void Load_TOWN() if (t->townnamegrfid == 0 && !IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST)) { SlErrorCorrupt("Invalid town name generator"); } + + if (IsSavegameVersionBefore(166)) continue; + + SlObject(&t->cargo_accepted, GetTileMatrixDesc()); + if (t->cargo_accepted.area.w != 0) { + uint arr_len = t->cargo_accepted.area.w / AcceptanceMatrix::GRID * t->cargo_accepted.area.h / AcceptanceMatrix::GRID; + t->cargo_accepted.data = MallocT(arr_len); + SlArray(t->cargo_accepted.data, arr_len, SLE_UINT32); + + /* Rebuild total cargo acceptance. */ + UpdateTownCargoTotal(t); + } } } diff --git a/src/town.h b/src/town.h index c0c3567358..c276be26b9 100644 --- a/src/town.h +++ b/src/town.h @@ -19,6 +19,7 @@ #include "subsidy_type.h" #include "newgrf_storage.h" #include "cargotype.h" +#include "tilematrix_type.hpp" #include template @@ -27,6 +28,8 @@ struct BuildingCounts { T class_count[HOUSE_CLASS_MAX]; }; +typedef TileMatrix AcceptanceMatrix; + static const uint CUSTOM_TOWN_NUMBER_DIFFICULTY = 4; ///< value for custom town number in difficulty settings static const uint CUSTOM_TOWN_MAX_NUMBER = 5000; ///< this is the maximum number of towns a user can specify in customisation @@ -76,6 +79,11 @@ struct Town : TownPool::PoolItem<&_town_pool> { inline byte GetPercentTransported(CargoID cid) const { return this->supplied[cid].old_act * 256 / (this->supplied[cid].old_max + 1); } + /* Cargo production and acceptance stats. */ + uint32 cargo_produced; ///< Bitmap of all cargos produced by houses in this town. + AcceptanceMatrix cargo_accepted; ///< Bitmap of cargos accepted by houses for each 4*4 map square of the town. + uint32 cargo_accepted_total; ///< NOSAVE: Bitmap of all cargos accepted by houses in this town. + uint16 time_until_rebuild; ///< time until we rebuild a house uint16 grow_counter; ///< counter to count when to grow @@ -175,6 +183,8 @@ void ResetHouses(); void ClearTownHouse(Town *t, TileIndex tile); void UpdateTownMaxPass(Town *t); void UpdateTownRadius(Town *t); +void UpdateTownCargos(Town *t); +void UpdateTownCargoTotal(Town *t); CommandCost CheckIfAuthorityAllowsNewStation(TileIndex tile, DoCommandFlag flags); Town *ClosestTownFromTile(TileIndex tile, uint threshold); void ChangeTownRating(Town *t, int add, int max, DoCommandFlag flags); diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 184d00de93..39e4ebc3e5 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -673,6 +673,75 @@ static void ChangeTileOwner_Town(TileIndex tile, Owner old_owner, Owner new_owne /* not used */ } +/** Update the total cargo acceptance of the whole town. + * @param t The town to update. + */ +void UpdateTownCargoTotal(Town *t) +{ + t->cargo_accepted_total = 0; + + const TileArea &area = t->cargo_accepted.GetArea(); + TILE_AREA_LOOP(tile, area) { + if (TileX(tile) % AcceptanceMatrix::GRID == 0 && TileY(tile) % AcceptanceMatrix::GRID == 0) { + t->cargo_accepted_total |= t->cargo_accepted[tile]; + } + } +} + +/** + * Update accepted town cargos around a specific tile. + * @param t The town to update. + * @param start Update the values around this tile. + * @param update_total Set to true if the total cargo acceptance should be updated. + */ +static void UpdateTownCargos(Town *t, TileIndex start, bool update_total = true) +{ + CargoArray accepted, produced; + uint32 dummy; + + /* Gather acceptance for all houses in an area around the start tile. + * The area is composed of the square the tile is in, extended one square in all + * directions as the coverage area of a single station is bigger than just one square. */ + TileArea area = AcceptanceMatrix::GetAreaForTile(start, 1); + TILE_AREA_LOOP(tile, area) { + if (!IsTileType(tile, MP_HOUSE) || GetTownIndex(tile) != t->index) continue; + + AddAcceptedCargo_Town(tile, accepted, &dummy); + AddProducedCargo_Town(tile, produced); + } + + /* Create bitmap of produced and accepted cargos. */ + uint32 acc = 0; + for (uint cid = 0; cid < NUM_CARGO; cid++) { + if (accepted[cid] >= 8) SetBit(acc, cid); + if (produced[cid] > 0) SetBit(t->cargo_produced, cid); + } + t->cargo_accepted[start] = acc; + + if (update_total) UpdateTownCargoTotal(t); +} + +/** Update cargo acceptance for the complete town. + * @param t The town to update. + */ +void UpdateTownCargos(Town *t) +{ + t->cargo_produced = 0; + + const TileArea &area = t->cargo_accepted.GetArea(); + if (area.tile == INVALID_TILE) return; + + /* Update acceptance for each grid square. */ + TILE_AREA_LOOP(tile, area) { + if (TileX(tile) % AcceptanceMatrix::GRID == 0 && TileY(tile) % AcceptanceMatrix::GRID == 0) { + UpdateTownCargos(t, tile, false); + } + } + + /* Update the total acceptance. */ + UpdateTownCargoTotal(t); +} + static bool GrowTown(Town *t); static void TownTickHandler(Town *t) @@ -2221,6 +2290,7 @@ static bool BuildTownHouse(Town *t, TileIndex tile) } MakeTownHouse(tile, t, construction_counter, construction_stage, house, random_bits); + UpdateTownCargos(t, tile); return true; } @@ -2302,6 +2372,9 @@ void ClearTownHouse(Town *t, TileIndex tile) if (eflags & BUILDING_2_TILES_Y) DoClearTownHouseHelper(tile + TileDiffXY(0, 1), t, ++house); if (eflags & BUILDING_2_TILES_X) DoClearTownHouseHelper(tile + TileDiffXY(1, 0), t, ++house); if (eflags & BUILDING_HAS_4_TILES) DoClearTownHouseHelper(tile + TileDiffXY(1, 1), t, ++house); + + /* Update cargo acceptance. */ + UpdateTownCargos(t, tile); } /** @@ -3068,6 +3141,7 @@ void TownsMonthlyLoop() UpdateTownRating(t); UpdateTownGrowRate(t); UpdateTownUnwanted(t); + UpdateTownCargos(t); } }