2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 /** @file town_sl.cpp Code handling saving and loading of towns and houses */
10 #include "../stdafx.h"
13 #include "compat/town_sl_compat.h"
15 #include "newgrf_sl.h"
16 #include "../newgrf_house.h"
18 #include "../landscape.h"
19 #include "../subsidy_func.h"
20 #include "../strings_func.h"
22 #include "../safeguards.h"
25 * Rebuild all the cached variables of towns.
27 void RebuildTownCaches()
29 InitializeBuildingCounts();
32 /* Reset town population and num_houses */
33 for (Town
*town
: Town::Iterate()) {
34 town
->cache
.population
= 0;
35 town
->cache
.num_houses
= 0;
38 for (TileIndex t
= 0; t
< Map::Size(); t
++) {
39 if (!IsTileType(t
, MP_HOUSE
)) continue;
41 HouseID house_id
= GetHouseType(t
);
42 Town
*town
= Town::GetByTile(t
);
43 IncreaseBuildingCount(town
, house_id
);
44 if (IsHouseCompleted(t
)) town
->cache
.population
+= HouseSpec::Get(house_id
)->population
;
46 /* Increase the number of houses for every house, but only once. */
47 if (GetHouseNorthPart(house_id
) == TileDiffXY(0, 0)) town
->cache
.num_houses
++;
50 /* Update the population and num_house dependent values */
51 for (Town
*town
: Town::Iterate()) {
52 UpdateTownRadius(town
);
57 * Check and update town and house values.
59 * Checked are the HouseIDs. Updated are the
60 * town population the number of houses per
61 * town, the town radius and the max passengers
64 void UpdateHousesAndTowns()
66 for (TileIndex t
= 0; t
< Map::Size(); t
++) {
67 if (!IsTileType(t
, MP_HOUSE
)) continue;
69 HouseID house_id
= GetCleanHouseType(t
);
70 if (!HouseSpec::Get(house_id
)->enabled
&& house_id
>= NEW_HOUSE_OFFSET
) {
71 /* The specs for this type of house are not available any more, so
72 * replace it with the substitute original house type. */
73 house_id
= _house_mngr
.GetSubstituteID(house_id
);
74 SetHouseType(t
, house_id
);
78 /* Check for cases when a NewGRF has set a wrong house substitute type. */
79 for (TileIndex t
= 0; t
< Map::Size(); t
++) {
80 if (!IsTileType(t
, MP_HOUSE
)) continue;
82 HouseID house_type
= GetCleanHouseType(t
);
83 TileIndex north_tile
= t
+ GetHouseNorthPart(house_type
); // modifies 'house_type'!
84 if (t
== north_tile
) {
85 const HouseSpec
*hs
= HouseSpec::Get(house_type
);
86 bool valid_house
= true;
87 if (hs
->building_flags
& TILE_SIZE_2x1
) {
88 TileIndex tile
= t
+ TileDiffXY(1, 0);
89 if (!IsTileType(tile
, MP_HOUSE
) || GetCleanHouseType(tile
) != house_type
+ 1) valid_house
= false;
90 } else if (hs
->building_flags
& TILE_SIZE_1x2
) {
91 TileIndex tile
= t
+ TileDiffXY(0, 1);
92 if (!IsTileType(tile
, MP_HOUSE
) || GetCleanHouseType(tile
) != house_type
+ 1) valid_house
= false;
93 } else if (hs
->building_flags
& TILE_SIZE_2x2
) {
94 TileIndex tile
= t
+ TileDiffXY(0, 1);
95 if (!IsTileType(tile
, MP_HOUSE
) || GetCleanHouseType(tile
) != house_type
+ 1) valid_house
= false;
96 tile
= t
+ TileDiffXY(1, 0);
97 if (!IsTileType(tile
, MP_HOUSE
) || GetCleanHouseType(tile
) != house_type
+ 2) valid_house
= false;
98 tile
= t
+ TileDiffXY(1, 1);
99 if (!IsTileType(tile
, MP_HOUSE
) || GetCleanHouseType(tile
) != house_type
+ 3) valid_house
= false;
101 /* If not all tiles of this house are present remove the house.
102 * The other tiles will get removed later in this loop because
103 * their north tile is not the correct type anymore. */
104 if (!valid_house
) DoClearSquare(t
);
105 } else if (!IsTileType(north_tile
, MP_HOUSE
) || GetCleanHouseType(north_tile
) != house_type
) {
106 /* This tile should be part of a multi-tile building but the
107 * north tile of this house isn't on the map. */
115 class SlTownSupplied
: public DefaultSaveLoadHandler
<SlTownSupplied
, Town
> {
117 inline static const SaveLoad description
[] = {
118 SLE_CONDVAR(TransportedCargoStat
<uint32_t>, old_max
, SLE_UINT32
, SLV_165
, SL_MAX_VERSION
),
119 SLE_CONDVAR(TransportedCargoStat
<uint32_t>, new_max
, SLE_UINT32
, SLV_165
, SL_MAX_VERSION
),
120 SLE_CONDVAR(TransportedCargoStat
<uint32_t>, old_act
, SLE_UINT32
, SLV_165
, SL_MAX_VERSION
),
121 SLE_CONDVAR(TransportedCargoStat
<uint32_t>, new_act
, SLE_UINT32
, SLV_165
, SL_MAX_VERSION
),
123 inline const static SaveLoadCompatTable compat_description
= _town_supplied_sl_compat
;
126 * Get the number of cargoes used by this savegame version.
127 * @return The number of cargoes used by this savegame version.
129 size_t GetNumCargo() const
131 if (IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES
)) return 32;
132 if (IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH
)) return NUM_CARGO
;
133 /* Read from the savegame how long the list is. */
134 return SlGetStructListLength(NUM_CARGO
);
137 void Save(Town
*t
) const override
139 SlSetStructListLength(std::size(t
->supplied
));
140 for (auto &supplied
: t
->supplied
) {
141 SlObject(&supplied
, this->GetDescription());
145 void Load(Town
*t
) const override
147 size_t num_cargo
= this->GetNumCargo();
148 for (size_t i
= 0; i
< num_cargo
; i
++) {
149 SlObject(&t
->supplied
[i
], this->GetLoadDescription());
154 class SlTownReceived
: public DefaultSaveLoadHandler
<SlTownReceived
, Town
> {
156 inline static const SaveLoad description
[] = {
157 SLE_CONDVAR(TransportedCargoStat
<uint16_t>, old_max
, SLE_UINT16
, SLV_165
, SL_MAX_VERSION
),
158 SLE_CONDVAR(TransportedCargoStat
<uint16_t>, new_max
, SLE_UINT16
, SLV_165
, SL_MAX_VERSION
),
159 SLE_CONDVAR(TransportedCargoStat
<uint16_t>, old_act
, SLE_UINT16
, SLV_165
, SL_MAX_VERSION
),
160 SLE_CONDVAR(TransportedCargoStat
<uint16_t>, new_act
, SLE_UINT16
, SLV_165
, SL_MAX_VERSION
),
162 inline const static SaveLoadCompatTable compat_description
= _town_received_sl_compat
;
164 void Save(Town
*t
) const override
166 SlSetStructListLength(std::size(t
->received
));
167 for (auto &received
: t
->received
) {
168 SlObject(&received
, this->GetDescription());
172 void Load(Town
*t
) const override
174 size_t length
= IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH
) ? static_cast<size_t>(TAE_END
) : SlGetStructListLength(TAE_END
);
175 for (size_t i
= 0; i
< length
; i
++) {
176 SlObject(&t
->received
[i
], this->GetLoadDescription());
181 class SlTownAcceptanceMatrix
: public DefaultSaveLoadHandler
<SlTownAcceptanceMatrix
, Town
> {
183 /** Compatibility struct with just enough of TileMatrix to facilitate loading. */
184 struct AcceptanceMatrix
{
186 static const uint GRID
= 4;
189 inline static const SaveLoad description
[] = {
190 SLE_VAR(AcceptanceMatrix
, area
.tile
, SLE_UINT32
),
191 SLE_VAR(AcceptanceMatrix
, area
.w
, SLE_UINT16
),
192 SLE_VAR(AcceptanceMatrix
, area
.h
, SLE_UINT16
),
194 inline const static SaveLoadCompatTable compat_description
= _town_acceptance_matrix_sl_compat
;
196 void Load(Town
*) const override
198 /* Discard now unused acceptance matrix. */
199 AcceptanceMatrix dummy
;
200 SlObject(&dummy
, this->GetLoadDescription());
201 if (dummy
.area
.w
!= 0) {
202 uint arr_len
= dummy
.area
.w
/ AcceptanceMatrix::GRID
* dummy
.area
.h
/ AcceptanceMatrix::GRID
;
203 SlSkipBytes(4 * arr_len
);
208 static const SaveLoad _town_desc
[] = {
209 SLE_CONDVAR(Town
, xy
, SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_6
),
210 SLE_CONDVAR(Town
, xy
, SLE_UINT32
, SLV_6
, SL_MAX_VERSION
),
212 SLE_CONDVAR(Town
, townnamegrfid
, SLE_UINT32
, SLV_66
, SL_MAX_VERSION
),
213 SLE_VAR(Town
, townnametype
, SLE_UINT16
),
214 SLE_VAR(Town
, townnameparts
, SLE_UINT32
),
215 SLE_CONDSSTR(Town
, name
, SLE_STR
| SLF_ALLOW_CONTROL
, SLV_84
, SL_MAX_VERSION
),
217 SLE_VAR(Town
, flags
, SLE_UINT8
),
218 SLE_CONDVAR(Town
, statues
, SLE_FILE_U8
| SLE_VAR_U16
, SL_MIN_VERSION
, SLV_104
),
219 SLE_CONDVAR(Town
, statues
, SLE_UINT16
, SLV_104
, SL_MAX_VERSION
),
221 SLE_CONDVAR(Town
, have_ratings
, SLE_FILE_U8
| SLE_VAR_U16
, SL_MIN_VERSION
, SLV_104
),
222 SLE_CONDVAR(Town
, have_ratings
, SLE_UINT16
, SLV_104
, SL_MAX_VERSION
),
223 SLE_CONDARR(Town
, ratings
, SLE_INT16
, 8, SL_MIN_VERSION
, SLV_104
),
224 SLE_CONDARR(Town
, ratings
, SLE_INT16
, MAX_COMPANIES
, SLV_104
, SL_MAX_VERSION
),
225 SLE_CONDARR(Town
, unwanted
, SLE_INT8
, 8, SLV_4
, SLV_104
),
226 SLE_CONDARR(Town
, unwanted
, SLE_INT8
, MAX_COMPANIES
, SLV_104
, SL_MAX_VERSION
),
228 /* Slots 0 and 2 are passengers and mail respectively for old saves. */
229 SLE_CONDVARNAME(Town
, supplied
[0].old_max
, "supplied[CT_PASSENGERS].old_max", SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_9
),
230 SLE_CONDVARNAME(Town
, supplied
[0].old_max
, "supplied[CT_PASSENGERS].old_max", SLE_UINT32
, SLV_9
, SLV_165
),
231 SLE_CONDVARNAME(Town
, supplied
[2].old_max
, "supplied[CT_MAIL].old_max", SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_9
),
232 SLE_CONDVARNAME(Town
, supplied
[2].old_max
, "supplied[CT_MAIL].old_max", SLE_UINT32
, SLV_9
, SLV_165
),
233 SLE_CONDVARNAME(Town
, supplied
[0].new_max
, "supplied[CT_PASSENGERS].new_max", SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_9
),
234 SLE_CONDVARNAME(Town
, supplied
[0].new_max
, "supplied[CT_PASSENGERS].new_max", SLE_UINT32
, SLV_9
, SLV_165
),
235 SLE_CONDVARNAME(Town
, supplied
[2].new_max
, "supplied[CT_MAIL].new_max", SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_9
),
236 SLE_CONDVARNAME(Town
, supplied
[2].new_max
, "supplied[CT_MAIL].new_max", SLE_UINT32
, SLV_9
, SLV_165
),
237 SLE_CONDVARNAME(Town
, supplied
[0].old_act
, "supplied[CT_PASSENGERS].old_act", SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_9
),
238 SLE_CONDVARNAME(Town
, supplied
[0].old_act
, "supplied[CT_PASSENGERS].old_act", SLE_UINT32
, SLV_9
, SLV_165
),
239 SLE_CONDVARNAME(Town
, supplied
[2].old_act
, "supplied[CT_MAIL].old_act", SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_9
),
240 SLE_CONDVARNAME(Town
, supplied
[2].old_act
, "supplied[CT_MAIL].old_act", SLE_UINT32
, SLV_9
, SLV_165
),
241 SLE_CONDVARNAME(Town
, supplied
[0].new_act
, "supplied[CT_PASSENGERS].new_act", SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_9
),
242 SLE_CONDVARNAME(Town
, supplied
[0].new_act
, "supplied[CT_PASSENGERS].new_act", SLE_UINT32
, SLV_9
, SLV_165
),
243 SLE_CONDVARNAME(Town
, supplied
[2].new_act
, "supplied[CT_MAIL].new_act", SLE_FILE_U16
| SLE_VAR_U32
, SL_MIN_VERSION
, SLV_9
),
244 SLE_CONDVARNAME(Town
, supplied
[2].new_act
, "supplied[CT_MAIL].new_act", SLE_UINT32
, SLV_9
, SLV_165
),
246 SLE_CONDVARNAME(Town
, received
[TAE_FOOD
].old_act
, "received[TE_FOOD].old_act", SLE_UINT16
, SL_MIN_VERSION
, SLV_165
),
247 SLE_CONDVARNAME(Town
, received
[TAE_WATER
].old_act
, "received[TE_WATER].old_act", SLE_UINT16
, SL_MIN_VERSION
, SLV_165
),
248 SLE_CONDVARNAME(Town
, received
[TAE_FOOD
].new_act
, "received[TE_FOOD].new_act", SLE_UINT16
, SL_MIN_VERSION
, SLV_165
),
249 SLE_CONDVARNAME(Town
, received
[TAE_WATER
].new_act
, "received[TE_WATER].new_act", SLE_UINT16
, SL_MIN_VERSION
, SLV_165
),
251 SLE_CONDARR(Town
, goal
, SLE_UINT32
, NUM_TAE
, SLV_165
, SL_MAX_VERSION
),
253 SLE_CONDSSTR(Town
, text
, SLE_STR
| SLF_ALLOW_CONTROL
, SLV_168
, SL_MAX_VERSION
),
255 SLE_CONDVAR(Town
, time_until_rebuild
, SLE_FILE_U8
| SLE_VAR_U16
, SL_MIN_VERSION
, SLV_54
),
256 SLE_CONDVAR(Town
, time_until_rebuild
, SLE_UINT16
, SLV_54
, SL_MAX_VERSION
),
257 SLE_CONDVAR(Town
, grow_counter
, SLE_FILE_U8
| SLE_VAR_U16
, SL_MIN_VERSION
, SLV_54
),
258 SLE_CONDVAR(Town
, grow_counter
, SLE_UINT16
, SLV_54
, SL_MAX_VERSION
),
259 SLE_CONDVAR(Town
, growth_rate
, SLE_FILE_U8
| SLE_VAR_I16
, SL_MIN_VERSION
, SLV_54
),
260 SLE_CONDVAR(Town
, growth_rate
, SLE_FILE_I16
| SLE_VAR_U16
, SLV_54
, SLV_165
),
261 SLE_CONDVAR(Town
, growth_rate
, SLE_UINT16
, SLV_165
, SL_MAX_VERSION
),
263 SLE_VAR(Town
, fund_buildings_months
, SLE_UINT8
),
264 SLE_VAR(Town
, road_build_months
, SLE_UINT8
),
266 SLE_CONDVAR(Town
, exclusivity
, SLE_UINT8
, SLV_2
, SL_MAX_VERSION
),
267 SLE_CONDVAR(Town
, exclusive_counter
, SLE_UINT8
, SLV_2
, SL_MAX_VERSION
),
269 SLE_CONDVAR(Town
, larger_town
, SLE_BOOL
, SLV_56
, SL_MAX_VERSION
),
270 SLE_CONDVAR(Town
, layout
, SLE_UINT8
, SLV_113
, SL_MAX_VERSION
),
272 SLE_CONDREFLIST(Town
, psa_list
, REF_STORAGE
, SLV_161
, SL_MAX_VERSION
),
274 SLEG_CONDSTRUCTLIST("supplied", SlTownSupplied
, SLV_165
, SL_MAX_VERSION
),
275 SLEG_CONDSTRUCTLIST("received", SlTownReceived
, SLV_165
, SL_MAX_VERSION
),
276 SLEG_CONDSTRUCTLIST("acceptance_matrix", SlTownAcceptanceMatrix
, SLV_166
, SLV_REMOVE_TOWN_CARGO_CACHE
),
279 struct HIDSChunkHandler
: NewGRFMappingChunkHandler
{
280 HIDSChunkHandler() : NewGRFMappingChunkHandler('HIDS', _house_mngr
) {}
283 struct CITYChunkHandler
: ChunkHandler
{
284 CITYChunkHandler() : ChunkHandler('CITY', CH_TABLE
) {}
286 void Save() const override
288 SlTableHeader(_town_desc
);
290 for (Town
*t
: Town::Iterate()) {
291 SlSetArrayIndex(t
->index
);
292 SlObject(t
, _town_desc
);
296 void Load() const override
298 const std::vector
<SaveLoad
> slt
= SlCompatTableHeader(_town_desc
, _town_sl_compat
);
302 while ((index
= SlIterateArray()) != -1) {
303 Town
*t
= new (index
) Town();
306 if (t
->townnamegrfid
== 0 && !IsInsideMM(t
->townnametype
, SPECSTR_TOWNNAME_START
, SPECSTR_TOWNNAME_LAST
+ 1) && GetStringTab(t
->townnametype
) != TEXT_TAB_OLD_CUSTOM
) {
307 SlErrorCorrupt("Invalid town name generator");
312 void FixPointers() const override
314 if (IsSavegameVersionBefore(SLV_161
)) return;
316 for (Town
*t
: Town::Iterate()) {
317 SlObject(t
, _town_desc
);
322 static const HIDSChunkHandler HIDS
;
323 static const CITYChunkHandler CITY
;
324 static const ChunkHandlerRef town_chunk_handlers
[] = {
329 extern const ChunkHandlerTable
_town_chunk_handlers(town_chunk_handlers
);