OpenTTD Source: src/newgrf/newgrf_act0_houses.cpp Source File

OpenTTD Source 15.0-RC2

newgrf_act0_houses.cpp
Go to the documentation of this file.
1/*
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
10#include "../stdafx.h"
11#include "../debug.h"
12#include "../town.h"
13#include "../newgrf_cargo.h"
14#include "../newgrf_house.h"
15#include "newgrf_bytereader.h"
16#include "newgrf_internal.h"
17#include "newgrf_stringmapping.h"
18
19#include "table/strings.h"
20
21#include "../safeguards.h"
22
29 static ChangeInfoResult IgnoreTownHouseProperty(int prop, ByteReader &buf)
30{
31 ChangeInfoResult ret = CIR_SUCCESS;
32
33 switch (prop) {
34 case 0x09:
35 case 0x0B:
36 case 0x0C:
37 case 0x0D:
38 case 0x0E:
39 case 0x0F:
40 case 0x11:
41 case 0x14:
42 case 0x15:
43 case 0x16:
44 case 0x18:
45 case 0x19:
46 case 0x1A:
47 case 0x1B:
48 case 0x1C:
49 case 0x1D:
50 case 0x1F:
51 buf.ReadByte();
52 break;
53
54 case 0x0A:
55 case 0x10:
56 case 0x12:
57 case 0x13:
58 case 0x21:
59 case 0x22:
60 buf.ReadWord();
61 break;
62
63 case 0x1E:
64 buf.ReadDWord();
65 break;
66
67 case 0x17:
68 for (uint j = 0; j < 4; j++) buf.ReadByte();
69 break;
70
71 case 0x20: {
72 uint8_t count = buf.ReadByte();
73 for (uint8_t j = 0; j < count; j++) buf.ReadByte();
74 break;
75 }
76
77 case 0x23:
78 buf.Skip(buf.ReadByte() * 2);
79 break;
80
81 default:
82 ret = CIR_UNKNOWN;
83 break;
84 }
85 return ret;
86}
87
96 static ChangeInfoResult TownHouseChangeInfo(uint first, uint last, int prop, ByteReader &buf)
97{
98 ChangeInfoResult ret = CIR_SUCCESS;
99
100 if (last > NUM_HOUSES_PER_GRF) {
101 GrfMsg(1, "TownHouseChangeInfo: Too many houses loaded ({}), max ({}). Ignoring.", last, NUM_HOUSES_PER_GRF);
102 return CIR_INVALID_ID;
103 }
104
105 /* Allocate house specs if they haven't been allocated already. */
106 if (_cur_gps.grffile->housespec.size() < last) _cur_gps.grffile->housespec.resize(last);
107
108 for (uint id = first; id < last; ++id) {
109 auto &housespec = _cur_gps.grffile->housespec[id];
110
111 if (prop != 0x08 && housespec == nullptr) {
112 /* If the house property 08 is not yet set, ignore this property */
113 ChangeInfoResult cir = IgnoreTownHouseProperty(prop, buf);
114 if (cir > ret) ret = cir;
115 continue;
116 }
117
118 switch (prop) {
119 case 0x08: { // Substitute building type, and definition of a new house
120 uint8_t subs_id = buf.ReadByte();
121 if (subs_id == 0xFF) {
122 /* Instead of defining a new house, a substitute house id
123 * of 0xFF disables the old house with the current id. */
124 if (id < NEW_HOUSE_OFFSET) HouseSpec::Get(id)->enabled = false;
125 continue;
126 } else if (subs_id >= NEW_HOUSE_OFFSET) {
127 /* The substitute id must be one of the original houses. */
128 GrfMsg(2, "TownHouseChangeInfo: Attempt to use new house {} as substitute house for {}. Ignoring.", subs_id, id);
129 continue;
130 }
131
132 /* Allocate space for this house. */
133 if (housespec == nullptr) {
134 /* Only the first property 08 setting copies properties; if you later change it, properties will stay. */
135 housespec = std::make_unique<HouseSpec>(*HouseSpec::Get(subs_id));
136
137 housespec->enabled = true;
138 housespec->grf_prop.local_id = id;
139 housespec->grf_prop.subst_id = subs_id;
140 housespec->grf_prop.SetGRFFile(_cur_gps.grffile);
141 /* Set default colours for randomization, used if not overridden. */
142 housespec->random_colour[0] = COLOUR_RED;
143 housespec->random_colour[1] = COLOUR_BLUE;
144 housespec->random_colour[2] = COLOUR_ORANGE;
145 housespec->random_colour[3] = COLOUR_GREEN;
146
147 /* House flags 40 and 80 are exceptions; these flags are never set automatically. */
148 housespec->building_flags.Reset({BuildingFlag::IsChurch, BuildingFlag::IsStadium});
149
150 /* Make sure that the third cargo type is valid in this
151 * climate. This can cause problems when copying the properties
152 * of a house that accepts food, where the new house is valid
153 * in the temperate climate. */
154 CargoType cargo_type = housespec->accepts_cargo[2];
155 if (!IsValidCargoType(cargo_type)) cargo_type = GetCargoTypeByLabel(housespec->accepts_cargo_label[2]);
156 if (!IsValidCargoType(cargo_type)) {
157 housespec->cargo_acceptance[2] = 0;
158 }
159 }
160 break;
161 }
162
163 case 0x09: // Building flags
164 housespec->building_flags = (BuildingFlags)buf.ReadByte();
165 break;
166
167 case 0x0A: { // Availability years
168 uint16_t years = buf.ReadWord();
169 housespec->min_year = GB(years, 0, 8) > 150 ? CalendarTime::MAX_YEAR : CalendarTime::ORIGINAL_BASE_YEAR + GB(years, 0, 8);
170 housespec->max_year = GB(years, 8, 8) > 150 ? CalendarTime::MAX_YEAR : CalendarTime::ORIGINAL_BASE_YEAR + GB(years, 8, 8);
171 break;
172 }
173
174 case 0x0B: // Population
175 housespec->population = buf.ReadByte();
176 break;
177
178 case 0x0C: // Mail generation multiplier
179 housespec->mail_generation = buf.ReadByte();
180 break;
181
182 case 0x0D: // Passenger acceptance
183 case 0x0E: // Mail acceptance
184 housespec->cargo_acceptance[prop - 0x0D] = buf.ReadByte();
185 break;
186
187 case 0x0F: { // Goods/candy, food/fizzy drinks acceptance
188 int8_t goods = buf.ReadByte();
189
190 /* If value of goods is negative, it means in fact food or, if in toyland, fizzy_drink acceptance.
191 * Else, we have "standard" 3rd cargo type, goods or candy, for toyland once more */
192 CargoType cargo_type = (goods >= 0) ? ((_settings_game.game_creation.landscape == LandscapeType::Toyland) ? GetCargoTypeByLabel(CT_CANDY) : GetCargoTypeByLabel(CT_GOODS)) :
193 ((_settings_game.game_creation.landscape == LandscapeType::Toyland) ? GetCargoTypeByLabel(CT_FIZZY_DRINKS) : GetCargoTypeByLabel(CT_FOOD));
194
195 /* Make sure the cargo type is valid in this climate. */
196 if (!IsValidCargoType(cargo_type)) goods = 0;
197
198 housespec->accepts_cargo[2] = cargo_type;
199 housespec->accepts_cargo_label[2] = CT_INVALID;
200 housespec->cargo_acceptance[2] = abs(goods); // but we do need positive value here
201 break;
202 }
203
204 case 0x10: // Local authority rating decrease on removal
205 housespec->remove_rating_decrease = buf.ReadWord();
206 break;
207
208 case 0x11: // Removal cost multiplier
209 housespec->removal_cost = buf.ReadByte();
210 break;
211
212 case 0x12: // Building name ID
213 AddStringForMapping(GRFStringID{buf.ReadWord()}, &housespec->building_name);
214 break;
215
216 case 0x13: // Building availability mask
217 housespec->building_availability = (HouseZones)buf.ReadWord();
218 break;
219
220 case 0x14: { // House callback mask
221 auto mask = housespec->callback_mask.base();
222 SB(mask, 0, 8, buf.ReadByte());
223 housespec->callback_mask = HouseCallbackMasks{mask};
224 break;
225 }
226
227 case 0x15: { // House override byte
228 uint8_t override_id = buf.ReadByte();
229
230 /* The house being overridden must be an original house. */
231 if (override_id >= NEW_HOUSE_OFFSET) {
232 GrfMsg(2, "TownHouseChangeInfo: Attempt to override new house {} with house id {}. Ignoring.", override_id, id);
233 continue;
234 }
235
236 _house_mngr.Add(id, _cur_gps.grffile->grfid, override_id);
237 break;
238 }
239
240 case 0x16: // Periodic refresh multiplier
241 housespec->processing_time = std::min<uint8_t>(buf.ReadByte(), 63u);
242 break;
243
244 case 0x17: // Four random colours to use
245 for (uint j = 0; j < 4; j++) housespec->random_colour[j] = static_cast<Colours>(GB(buf.ReadByte(), 0, 4));
246 break;
247
248 case 0x18: // Relative probability of appearing
249 housespec->probability = buf.ReadByte();
250 break;
251
252 case 0x19: // Extra flags
253 housespec->extra_flags = static_cast<HouseExtraFlags>(buf.ReadByte());
254 break;
255
256 case 0x1A: { // Animation frames
257 uint8_t info = buf.ReadByte();
258 housespec->animation.frames = GB(info, 0, 7);
259 housespec->animation.status = HasBit(info, 7) ? AnimationStatus::Looping : AnimationStatus::NonLooping;
260 break;
261 }
262
263 case 0x1B: // Animation speed
264 housespec->animation.speed = Clamp(buf.ReadByte(), 2, 16);
265 break;
266
267 case 0x1C: // Class of the building type
268 housespec->class_id = AllocateHouseClassID(buf.ReadByte(), _cur_gps.grffile->grfid);
269 break;
270
271 case 0x1D: { // Callback mask part 2
272 auto mask = housespec->callback_mask.base();
273 SB(mask, 8, 8, buf.ReadByte());
274 housespec->callback_mask = HouseCallbackMasks{mask};
275 break;
276 }
277
278 case 0x1E: { // Accepted cargo types
279 uint32_t cargotypes = buf.ReadDWord();
280
281 /* Check if the cargo types should not be changed */
282 if (cargotypes == 0xFFFFFFFF) break;
283
284 for (uint j = 0; j < HOUSE_ORIGINAL_NUM_ACCEPTS; j++) {
285 /* Get the cargo number from the 'list' */
286 uint8_t cargo_part = GB(cargotypes, 8 * j, 8);
287 CargoType cargo = GetCargoTranslation(cargo_part, _cur_gps.grffile);
288
289 if (!IsValidCargoType(cargo)) {
290 /* Disable acceptance of invalid cargo type */
291 housespec->cargo_acceptance[j] = 0;
292 } else {
293 housespec->accepts_cargo[j] = cargo;
294 }
295 housespec->accepts_cargo_label[j] = CT_INVALID;
296 }
297 break;
298 }
299
300 case 0x1F: // Minimum life span
301 housespec->minimum_life = buf.ReadByte();
302 break;
303
304 case 0x20: { // Cargo acceptance watch list
305 uint8_t count = buf.ReadByte();
306 for (uint8_t j = 0; j < count; j++) {
307 CargoType cargo = GetCargoTranslation(buf.ReadByte(), _cur_gps.grffile);
308 if (IsValidCargoType(cargo)) SetBit(housespec->watched_cargoes, cargo);
309 }
310 break;
311 }
312
313 case 0x21: // long introduction year
314 housespec->min_year = TimerGameCalendar::Year{buf.ReadWord()};
315 break;
316
317 case 0x22: // long maximum year
318 housespec->max_year = TimerGameCalendar::Year{buf.ReadWord()};
319 if (housespec->max_year == UINT16_MAX) housespec->max_year = CalendarTime::MAX_YEAR;
320 break;
321
322 case 0x23: { // variable length cargo types accepted
323 uint count = buf.ReadByte();
324 if (count > lengthof(housespec->accepts_cargo)) {
325 GRFError *error = DisableGrf(STR_NEWGRF_ERROR_LIST_PROPERTY_TOO_LONG);
326 error->param_value[1] = prop;
327 return CIR_DISABLED;
328 }
329 /* Always write the full accepts_cargo array, and check each index for being inside the
330 * provided data. This ensures all values are properly initialized, and also avoids
331 * any risks of array overrun. */
332 for (uint i = 0; i < lengthof(housespec->accepts_cargo); i++) {
333 if (i < count) {
334 housespec->accepts_cargo[i] = GetCargoTranslation(buf.ReadByte(), _cur_gps.grffile);
335 housespec->cargo_acceptance[i] = buf.ReadByte();
336 } else {
337 housespec->accepts_cargo[i] = INVALID_CARGO;
338 housespec->cargo_acceptance[i] = 0;
339 }
340 if (i < std::size(housespec->accepts_cargo_label)) housespec->accepts_cargo_label[i] = CT_INVALID;
341 }
342 break;
343 }
344
345 case 0x24: // Badge list
346 housespec->badges = ReadBadgeList(buf, GSF_HOUSES);
347 break;
348
349 default:
350 ret = CIR_UNKNOWN;
351 break;
352 }
353 }
354
355 return ret;
356}
357
358template <> ChangeInfoResult GrfChangeInfoHandler<GSF_HOUSES>::Reserve(uint, uint, int, ByteReader &) { return CIR_UNHANDLED; }
359template <> ChangeInfoResult GrfChangeInfoHandler<GSF_HOUSES>::Activation(uint first, uint last, int prop, ByteReader &buf) { return TownHouseChangeInfo(first, last, prop, buf); }
SB
constexpr T SB(T &x, const uint8_t s, const uint8_t n, const U d)
Set n bits in x starting at bit s to d.
Definition bitmath_func.hpp:58
GB
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
Definition bitmath_func.hpp:32
SetBit
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
Definition bitmath_func.hpp:121
HasBit
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
Definition bitmath_func.hpp:103
CT_INVALID
static constexpr CargoLabel CT_INVALID
Invalid cargo type.
Definition cargo_type.h:70
CargoType
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:21
IsValidCargoType
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:104
ByteReader
Class to read from a NewGRF file.
Definition newgrf_bytereader.h:18
ByteReader::ReadDWord
uint32_t ReadDWord()
Read a single DWord (32 bits).
Definition newgrf_bytereader.h:66
ByteReader::ReadWord
uint16_t ReadWord()
Read a single Word (16 bits).
Definition newgrf_bytereader.h:45
ByteReader::ReadByte
uint8_t ReadByte()
Read a single byte (8 bits).
Definition newgrf_bytereader.h:34
EnumBitSet< BuildingFlag, uint8_t >
OverrideManagerBase::Add
void Add(uint16_t local_id, uint32_t grfid, uint entity_type)
Since the entity IDs defined by the GRF file does not necessarily correlate to those used by the game...
Definition newgrf_commons.cpp:63
TimerGameConst< struct Calendar >::ORIGINAL_BASE_YEAR
static constexpr TimerGame< struct Calendar >::Year ORIGINAL_BASE_YEAR
The minimum starting year/base year of the original TTD.
Definition timer_game_common.h:163
TimerGameConst< struct Calendar >::MAX_YEAR
static constexpr TimerGame< struct Calendar >::Year MAX_YEAR
MAX_YEAR, nicely rounded value of the number of years that can be encoded in a single 32 bits date,...
Definition timer_game_common.h:173
NEW_HOUSE_OFFSET
static const HouseID NEW_HOUSE_OFFSET
Offset for new houses.
Definition house.h:28
NUM_HOUSES_PER_GRF
static const HouseID NUM_HOUSES_PER_GRF
Number of supported houses per NewGRF.
Definition house.h:32
HOUSE_ORIGINAL_NUM_ACCEPTS
static const uint HOUSE_ORIGINAL_NUM_ACCEPTS
Original number of accepted cargo types.
Definition house.h:35
abs
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition math_func.hpp:23
Clamp
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
DisableGrf
GRFError * DisableGrf(StringID message, GRFConfig *config)
Disable a GRF.
Definition newgrf.cpp:135
ReadBadgeList
std::vector< BadgeID > ReadBadgeList(ByteReader &buf, GrfSpecFeature feature)
Read a list of badges.
Definition newgrf_act0.cpp:86
IgnoreTownHouseProperty
static ChangeInfoResult IgnoreTownHouseProperty(int prop, ByteReader &buf)
Ignore a house property.
Definition newgrf_act0_houses.cpp:29
TownHouseChangeInfo
static ChangeInfoResult TownHouseChangeInfo(uint first, uint last, int prop, ByteReader &buf)
Define properties for houses.
Definition newgrf_act0_houses.cpp:96
AnimationStatus::NonLooping
@ NonLooping
Animation is not looping.
AnimationStatus::Looping
@ Looping
Animation is looping.
newgrf_bytereader.h
NewGRF buffer reader definition.
GetCargoTranslation
CargoType GetCargoTranslation(uint8_t cargo, const GRFFile *grffile, bool usebit)
Translate a GRF-local cargo slot/bitnum into a CargoType.
Definition newgrf_cargo.cpp:80
newgrf_internal.h
NewGRF internal processing state.
ChangeInfoResult
ChangeInfoResult
Possible return values for the GrfChangeInfoHandler functions.
Definition newgrf_internal.h:20
CIR_INVALID_ID
@ CIR_INVALID_ID
Attempt to modify an invalid ID.
Definition newgrf_internal.h:25
CIR_DISABLED
@ CIR_DISABLED
GRF was disabled due to error.
Definition newgrf_internal.h:22
CIR_UNKNOWN
@ CIR_UNKNOWN
Variable is unknown.
Definition newgrf_internal.h:24
CIR_UNHANDLED
@ CIR_UNHANDLED
Variable was parsed but unread.
Definition newgrf_internal.h:23
CIR_SUCCESS
@ CIR_SUCCESS
Variable was parsed and read.
Definition newgrf_internal.h:21
AddStringForMapping
void AddStringForMapping(GRFStringID source, std::function< void(StringID)> &&func)
Record a static StringID for getting translated later.
Definition newgrf_stringmapping.cpp:42
newgrf_stringmapping.h
NewGRF string mapping definition.
_settings_game
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
lengthof
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:271
GRFError
Information about why GRF had problems during initialisation.
Definition newgrf_config.h:109
GRFError::param_value
std::array< uint32_t, 2 > param_value
Values of GRF parameters to show for message and custom_message.
Definition newgrf_config.h:118
GameCreationSettings::landscape
LandscapeType landscape
the landscape we're currently in
Definition settings_type.h:395
GameSettings::game_creation
GameCreationSettings game_creation
settings used during the creation of a game (map)
Definition settings_type.h:634
GrfChangeInfoHandler
GRF feature handler.
Definition newgrf_internal.h:30
GrfProcessingState::grffile
GRFFile * grffile
Currently processed GRF file.
Definition newgrf_internal.h:67
HouseSpec::enabled
bool enabled
the house is available to build (true by default, but can be disabled by newgrf)
Definition house.h:111
HouseSpec::Get
static HouseSpec * Get(size_t house_id)
Get the spec for a house ID.
Definition newgrf_house.cpp:70
StrongType::Typedef< uint32_t, struct GRFStringIDTag, StrongType::Compare, StrongType::Integer >