OpenTTD Source 20250524-master-gc366e6a48e
newgrf_act3.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 <http://www.gnu.org/licenses/>.
6 */
7
10#include "../stdafx.h"
11
12#include "../debug.h"
13#include "../house.h"
14#include "../newgrf_engine.h"
15#include "../newgrf_badge.h"
16#include "../newgrf_badge_type.h"
17#include "../newgrf_cargo.h"
18#include "../newgrf_house.h"
19#include "../newgrf_station.h"
20#include "../industrytype.h"
21#include "../newgrf_canal.h"
22#include "../newgrf_airporttiles.h"
23#include "../newgrf_airport.h"
24#include "../newgrf_object.h"
25#include "../error.h"
26#include "../vehicle_base.h"
27#include "../road.h"
28#include "../newgrf_roadstop.h"
29#include "newgrf_bytereader.h"
31#include "newgrf_internal.h"
32
33#include "../safeguards.h"
34
35
36static CargoType TranslateCargo(GrfSpecFeature feature, uint8_t ctype)
37{
38 /* Special cargo types for purchase list and stations */
39 if ((feature == GSF_STATIONS || feature == GSF_ROADSTOPS) && ctype == 0xFE) return CargoGRFFileProps::SG_DEFAULT_NA;
40 if (ctype == 0xFF) return CargoGRFFileProps::SG_PURCHASE;
41
42 auto cargo_list = GetCargoTranslationTable(*_cur_gps.grffile);
43
44 /* Check if the cargo type is out of bounds of the cargo translation table */
45 if (ctype >= cargo_list.size()) {
46 GrfMsg(1, "TranslateCargo: Cargo type {} out of range (max {}), skipping.", ctype, (unsigned int)_cur_gps.grffile->cargo_list.size() - 1);
47 return INVALID_CARGO;
48 }
49
50 /* Look up the cargo label from the translation table */
51 CargoLabel cl = cargo_list[ctype];
52 if (cl == CT_INVALID) {
53 GrfMsg(5, "TranslateCargo: Cargo type {} not available in this climate, skipping.", ctype);
54 return INVALID_CARGO;
55 }
56
57 CargoType cargo_type = GetCargoTypeByLabel(cl);
58 if (!IsValidCargoType(cargo_type)) {
59 GrfMsg(5, "TranslateCargo: Cargo '{:c}{:c}{:c}{:c}' unsupported, skipping.", GB(cl.base(), 24, 8), GB(cl.base(), 16, 8), GB(cl.base(), 8, 8), GB(cl.base(), 0, 8));
60 return INVALID_CARGO;
61 }
62
63 GrfMsg(6, "TranslateCargo: Cargo '{:c}{:c}{:c}{:c}' mapped to cargo type {}.", GB(cl.base(), 24, 8), GB(cl.base(), 16, 8), GB(cl.base(), 8, 8), GB(cl.base(), 0, 8), cargo_type);
64 return cargo_type;
65}
66
67
68static bool IsValidGroupID(uint16_t groupid, std::string_view function)
69{
70 if (groupid > MAX_SPRITEGROUP || _cur_gps.spritegroups[groupid] == nullptr) {
71 GrfMsg(1, "{}: Spritegroup 0x{:04X} out of range or empty, skipping.", function, groupid);
72 return false;
73 }
74
75 return true;
76}
77
78static void VehicleMapSpriteGroup(ByteReader &buf, GrfSpecFeature feature, uint8_t idcount)
79{
80 static std::vector<EngineID> last_engines; // Engine IDs are remembered in case the next action is a wagon override.
81 bool wagover = false;
82
83 /* Test for 'wagon override' flag */
84 if (HasBit(idcount, 7)) {
85 wagover = true;
86 /* Strip off the flag */
87 idcount = GB(idcount, 0, 7);
88
89 if (last_engines.empty()) {
90 GrfMsg(0, "VehicleMapSpriteGroup: WagonOverride: No engine to do override with");
91 return;
92 }
93
94 GrfMsg(6, "VehicleMapSpriteGroup: WagonOverride: {} engines, {} wagons", last_engines.size(), idcount);
95 } else {
96 last_engines.resize(idcount);
97 }
98
99 std::vector<EngineID> engines;
100 engines.reserve(idcount);
101 for (uint i = 0; i < idcount; i++) {
102 Engine *e = GetNewEngine(_cur_gps.grffile, (VehicleType)feature, buf.ReadExtendedByte());
103 if (e == nullptr) {
104 /* No engine could be allocated?!? Deal with it. Okay,
105 * this might look bad. Also make sure this NewGRF
106 * gets disabled, as a half loaded one is bad. */
107 HandleChangeInfoResult("VehicleMapSpriteGroup", CIR_INVALID_ID, feature, 0);
108 return;
109 }
110
111 engines.push_back(e->index);
112 if (!wagover) last_engines[i] = engines[i];
113 }
114
115 uint8_t cidcount = buf.ReadByte();
116 for (uint c = 0; c < cidcount; c++) {
117 uint8_t ctype = buf.ReadByte();
118 uint16_t groupid = buf.ReadWord();
119 if (!IsValidGroupID(groupid, "VehicleMapSpriteGroup")) continue;
120
121 GrfMsg(8, "VehicleMapSpriteGroup: * [{}] Cargo type 0x{:X}, group id 0x{:02X}", c, ctype, groupid);
122
123 CargoType cargo_type = TranslateCargo(feature, ctype);
124 if (!IsValidCargoType(cargo_type)) continue;
125
126 for (uint i = 0; i < idcount; i++) {
127 EngineID engine = engines[i];
128
129 GrfMsg(7, "VehicleMapSpriteGroup: [{}] Engine {}...", i, engine);
130
131 if (wagover) {
132 SetWagonOverrideSprites(engine, cargo_type, _cur_gps.spritegroups[groupid], last_engines);
133 } else {
134 SetCustomEngineSprites(engine, cargo_type, _cur_gps.spritegroups[groupid]);
135 }
136 }
137 }
138
139 uint16_t groupid = buf.ReadWord();
140 if (!IsValidGroupID(groupid, "VehicleMapSpriteGroup")) return;
141
142 GrfMsg(8, "-- Default group id 0x{:04X}", groupid);
143
144 for (uint i = 0; i < idcount; i++) {
145 EngineID engine = engines[i];
146
147 if (wagover) {
148 SetWagonOverrideSprites(engine, CargoGRFFileProps::SG_DEFAULT, _cur_gps.spritegroups[groupid], last_engines);
149 } else {
150 SetCustomEngineSprites(engine, CargoGRFFileProps::SG_DEFAULT, _cur_gps.spritegroups[groupid]);
151 SetEngineGRF(engine, _cur_gps.grffile);
152 }
153 }
154}
155
156
157static void CanalMapSpriteGroup(ByteReader &buf, uint8_t idcount)
158{
159 std::vector<uint16_t> cfs;
160 cfs.reserve(idcount);
161 for (uint i = 0; i < idcount; i++) {
162 cfs.push_back(buf.ReadExtendedByte());
163 }
164
165 uint8_t cidcount = buf.ReadByte();
166 buf.Skip(cidcount * 3);
167
168 uint16_t groupid = buf.ReadWord();
169 if (!IsValidGroupID(groupid, "CanalMapSpriteGroup")) return;
170
171 for (auto &cf : cfs) {
172 if (cf >= CF_END) {
173 GrfMsg(1, "CanalMapSpriteGroup: Canal subset {} out of range, skipping", cf);
174 continue;
175 }
176
177 _water_feature[cf].grffile = _cur_gps.grffile;
178 _water_feature[cf].group = _cur_gps.spritegroups[groupid];
179 }
180}
181
182
183static void StationMapSpriteGroup(ByteReader &buf, uint8_t idcount)
184{
185 if (_cur_gps.grffile->stations.empty()) {
186 GrfMsg(1, "StationMapSpriteGroup: No stations defined, skipping");
187 return;
188 }
189
190 std::vector<uint16_t> stations;
191 stations.reserve(idcount);
192 for (uint i = 0; i < idcount; i++) {
193 stations.push_back(buf.ReadExtendedByte());
194 }
195
196 uint8_t cidcount = buf.ReadByte();
197 for (uint c = 0; c < cidcount; c++) {
198 uint8_t ctype = buf.ReadByte();
199 uint16_t groupid = buf.ReadWord();
200 if (!IsValidGroupID(groupid, "StationMapSpriteGroup")) continue;
201
202 CargoType cargo_type = TranslateCargo(GSF_STATIONS, ctype);
203 if (!IsValidCargoType(cargo_type)) continue;
204
205 for (auto &station : stations) {
206 StationSpec *statspec = station >= _cur_gps.grffile->stations.size() ? nullptr : _cur_gps.grffile->stations[station].get();
207
208 if (statspec == nullptr) {
209 GrfMsg(1, "StationMapSpriteGroup: Station {} undefined, skipping", station);
210 continue;
211 }
212
213 statspec->grf_prop.SetSpriteGroup(cargo_type, _cur_gps.spritegroups[groupid]);
214 }
215 }
216
217 uint16_t groupid = buf.ReadWord();
218 if (!IsValidGroupID(groupid, "StationMapSpriteGroup")) return;
219
220 for (auto &station : stations) {
221 StationSpec *statspec = station >= _cur_gps.grffile->stations.size() ? nullptr : _cur_gps.grffile->stations[station].get();
222
223 if (statspec == nullptr) {
224 GrfMsg(1, "StationMapSpriteGroup: Station {} undefined, skipping", station);
225 continue;
226 }
227
228 if (statspec->grf_prop.HasGrfFile()) {
229 GrfMsg(1, "StationMapSpriteGroup: Station {} mapped multiple times, skipping", station);
230 continue;
231 }
232
233 statspec->grf_prop.SetSpriteGroup(CargoGRFFileProps::SG_DEFAULT, _cur_gps.spritegroups[groupid]);
234 statspec->grf_prop.SetGRFFile(_cur_gps.grffile);
235 statspec->grf_prop.local_id = station;
236 StationClass::Assign(statspec);
237 }
238}
239
240
241static void TownHouseMapSpriteGroup(ByteReader &buf, uint8_t idcount)
242{
243 if (_cur_gps.grffile->housespec.empty()) {
244 GrfMsg(1, "TownHouseMapSpriteGroup: No houses defined, skipping");
245 return;
246 }
247
248 std::vector<uint16_t> houses;
249 houses.reserve(idcount);
250 for (uint i = 0; i < idcount; i++) {
251 houses.push_back(buf.ReadExtendedByte());
252 }
253
254 auto set_sprite_group = [&houses](StandardSpriteGroup key, uint16_t groupid) {
255 if (!IsValidGroupID(groupid, "TownHouseMapSpriteGroup")) return;
256
257 for (auto &house : houses) {
258 HouseSpec *hs = house >= _cur_gps.grffile->housespec.size() ? nullptr : _cur_gps.grffile->housespec[house].get();
259 if (hs == nullptr) {
260 GrfMsg(1, "TownHouseMapSpriteGroup: House {} undefined, skipping.", house);
261 continue;
262 }
263
264 hs->grf_prop.SetSpriteGroup(key, _cur_gps.spritegroups[groupid]);
265 }
266 };
267
268 uint8_t cidcount = buf.ReadByte();
269 for (uint c = 0; c < cidcount; c++) {
270 uint8_t ctype = buf.ReadByte();
271 uint16_t groupid = buf.ReadWord();
272 if (ctype == 0xFF) {
273 set_sprite_group(StandardSpriteGroup::Purchase, groupid);
274 } else {
275 GrfMsg(1, "TownHouseMapSpriteGroup: Invalid cargo bitnum {} for houses, skipping.", ctype);
276 }
277 }
278 set_sprite_group(StandardSpriteGroup::Default, buf.ReadWord());
279}
280
281static void IndustryMapSpriteGroup(ByteReader &buf, uint8_t idcount)
282{
283 if (_cur_gps.grffile->industryspec.empty()) {
284 GrfMsg(1, "IndustryMapSpriteGroup: No industries defined, skipping");
285 return;
286 }
287
288 std::vector<uint16_t> industries;
289 industries.reserve(idcount);
290 for (uint i = 0; i < idcount; i++) {
291 industries.push_back(buf.ReadExtendedByte());
292 }
293
294 auto set_sprite_group = [&industries](StandardSpriteGroup key, uint16_t groupid) {
295 if (!IsValidGroupID(groupid, "IndustryMapSpriteGroup")) return;
296
297 for (auto &industry : industries) {
298 IndustrySpec *indsp = industry >= _cur_gps.grffile->industryspec.size() ? nullptr : _cur_gps.grffile->industryspec[industry].get();
299 if (indsp == nullptr) {
300 GrfMsg(1, "IndustryMapSpriteGroup: Industry {} undefined, skipping", industry);
301 continue;
302 }
303
304 indsp->grf_prop.SetSpriteGroup(key, _cur_gps.spritegroups[groupid]);
305 }
306 };
307
308 uint8_t cidcount = buf.ReadByte();
309 for (uint c = 0; c < cidcount; c++) {
310 uint8_t ctype = buf.ReadByte();
311 uint16_t groupid = buf.ReadWord();
312 if (ctype == 0xFF) {
313 set_sprite_group(StandardSpriteGroup::Purchase, groupid);
314 } else {
315 GrfMsg(1, "IndustryMapSpriteGroup: Invalid cargo bitnum {} for industries, skipping.", ctype);
316 }
317 }
318 set_sprite_group(StandardSpriteGroup::Default, buf.ReadWord());
319}
320
321static void IndustrytileMapSpriteGroup(ByteReader &buf, uint8_t idcount)
322{
323 if (_cur_gps.grffile->indtspec.empty()) {
324 GrfMsg(1, "IndustrytileMapSpriteGroup: No industry tiles defined, skipping");
325 return;
326 }
327
328 std::vector<uint16_t> indtiles;
329 indtiles.reserve(idcount);
330 for (uint i = 0; i < idcount; i++) {
331 indtiles.push_back(buf.ReadExtendedByte());
332 }
333
334 auto set_sprite_group = [&indtiles](StandardSpriteGroup key, uint16_t groupid) {
335 if (!IsValidGroupID(groupid, "IndustrytileMapSpriteGroup")) return;
336
337 for (auto &indtile : indtiles) {
338 IndustryTileSpec *indtsp = indtile >= _cur_gps.grffile->indtspec.size() ? nullptr : _cur_gps.grffile->indtspec[indtile].get();
339 if (indtsp == nullptr) {
340 GrfMsg(1, "IndustrytileMapSpriteGroup: Industry tile {} undefined, skipping", indtile);
341 continue;
342 }
343
344 indtsp->grf_prop.SetSpriteGroup(key, _cur_gps.spritegroups[groupid]);
345 }
346 };
347
348 uint8_t cidcount = buf.ReadByte();
349 for (uint c = 0; c < cidcount; c++) {
350 uint8_t ctype = buf.ReadByte();
351 uint16_t groupid = buf.ReadWord();
352 if (ctype == 0xFF) {
353 set_sprite_group(StandardSpriteGroup::Purchase, groupid);
354 } else {
355 GrfMsg(1, "IndustrytileMapSpriteGroup: Invalid cargo bitnum {} for industry tiles, skipping.", ctype);
356 }
357 }
358 set_sprite_group(StandardSpriteGroup::Default, buf.ReadWord());
359}
360
361static void CargoMapSpriteGroup(ByteReader &buf, uint8_t idcount)
362{
363 std::vector<uint16_t> cargoes;
364 cargoes.reserve(idcount);
365 for (uint i = 0; i < idcount; i++) {
366 cargoes.push_back(buf.ReadExtendedByte());
367 }
368
369 /* Skip the cargo type section, we only care about the default group */
370 uint8_t cidcount = buf.ReadByte();
371 buf.Skip(cidcount * 3);
372
373 uint16_t groupid = buf.ReadWord();
374 if (!IsValidGroupID(groupid, "CargoMapSpriteGroup")) return;
375
376 for (auto &cargo_type : cargoes) {
377 if (cargo_type >= NUM_CARGO) {
378 GrfMsg(1, "CargoMapSpriteGroup: Cargo type {} out of range, skipping", cargo_type);
379 continue;
380 }
381
382 CargoSpec *cs = CargoSpec::Get(cargo_type);
383 cs->grffile = _cur_gps.grffile;
384 cs->group = _cur_gps.spritegroups[groupid];
385 }
386}
387
388static void ObjectMapSpriteGroup(ByteReader &buf, uint8_t idcount)
389{
390 if (_cur_gps.grffile->objectspec.empty()) {
391 GrfMsg(1, "ObjectMapSpriteGroup: No object tiles defined, skipping");
392 return;
393 }
394
395 std::vector<uint16_t> objects;
396 objects.reserve(idcount);
397 for (uint i = 0; i < idcount; i++) {
398 objects.push_back(buf.ReadExtendedByte());
399 }
400
401 uint8_t cidcount = buf.ReadByte();
402 for (uint c = 0; c < cidcount; c++) {
403 uint8_t ctype = buf.ReadByte();
404 uint16_t groupid = buf.ReadWord();
405 if (!IsValidGroupID(groupid, "ObjectMapSpriteGroup")) continue;
406
407 /* The only valid option here is purchase list sprite groups. */
408 if (ctype != 0xFF) {
409 GrfMsg(1, "ObjectMapSpriteGroup: Invalid cargo bitnum {} for objects, skipping.", ctype);
410 continue;
411 }
412
413 for (auto &object : objects) {
414 ObjectSpec *spec = object >= _cur_gps.grffile->objectspec.size() ? nullptr : _cur_gps.grffile->objectspec[object].get();
415
416 if (spec == nullptr) {
417 GrfMsg(1, "ObjectMapSpriteGroup: Object {} undefined, skipping", object);
418 continue;
419 }
420
421 spec->grf_prop.SetSpriteGroup(StandardSpriteGroup::Purchase, _cur_gps.spritegroups[groupid]);
422 }
423 }
424
425 uint16_t groupid = buf.ReadWord();
426 if (!IsValidGroupID(groupid, "ObjectMapSpriteGroup")) return;
427
428 for (auto &object : objects) {
429 ObjectSpec *spec = object >= _cur_gps.grffile->objectspec.size() ? nullptr : _cur_gps.grffile->objectspec[object].get();
430
431 if (spec == nullptr) {
432 GrfMsg(1, "ObjectMapSpriteGroup: Object {} undefined, skipping", object);
433 continue;
434 }
435
436 if (spec->grf_prop.HasGrfFile()) {
437 GrfMsg(1, "ObjectMapSpriteGroup: Object {} mapped multiple times, skipping", object);
438 continue;
439 }
440
441 spec->grf_prop.SetSpriteGroup(StandardSpriteGroup::Default, _cur_gps.spritegroups[groupid]);
442 spec->grf_prop.SetGRFFile(_cur_gps.grffile);
443 spec->grf_prop.local_id = object;
444 }
445}
446
447static void RailTypeMapSpriteGroup(ByteReader &buf, uint8_t idcount)
448{
449 std::vector<uint8_t> railtypes;
450 railtypes.reserve(idcount);
451 for (uint i = 0; i < idcount; i++) {
452 uint16_t id = buf.ReadExtendedByte();
453 railtypes.push_back(id < RAILTYPE_END ? _cur_gps.grffile->railtype_map[id] : INVALID_RAILTYPE);
454 }
455
456 uint8_t cidcount = buf.ReadByte();
457 for (uint c = 0; c < cidcount; c++) {
458 uint8_t ctype = buf.ReadByte();
459 uint16_t groupid = buf.ReadWord();
460 if (!IsValidGroupID(groupid, "RailTypeMapSpriteGroup")) continue;
461
462 if (ctype >= RTSG_END) continue;
463
464 extern RailTypeInfo _railtypes[RAILTYPE_END];
465 for (auto &railtype : railtypes) {
466 if (railtype != INVALID_RAILTYPE) {
467 RailTypeInfo *rti = &_railtypes[railtype];
468
469 rti->grffile[ctype] = _cur_gps.grffile;
470 rti->group[ctype] = _cur_gps.spritegroups[groupid];
471 }
472 }
473 }
474
475 /* Railtypes do not use the default group. */
476 buf.ReadWord();
477}
478
479static void RoadTypeMapSpriteGroup(ByteReader &buf, uint8_t idcount, RoadTramType rtt)
480{
481 std::array<RoadType, ROADTYPE_END> &type_map = (rtt == RTT_TRAM) ? _cur_gps.grffile->tramtype_map : _cur_gps.grffile->roadtype_map;
482
483 std::vector<uint8_t> roadtypes;
484 roadtypes.reserve(idcount);
485 for (uint i = 0; i < idcount; i++) {
486 uint16_t id = buf.ReadExtendedByte();
487 roadtypes.push_back(id < ROADTYPE_END ? type_map[id] : INVALID_ROADTYPE);
488 }
489
490 uint8_t cidcount = buf.ReadByte();
491 for (uint c = 0; c < cidcount; c++) {
492 uint8_t ctype = buf.ReadByte();
493 uint16_t groupid = buf.ReadWord();
494 if (!IsValidGroupID(groupid, "RoadTypeMapSpriteGroup")) continue;
495
496 if (ctype >= ROTSG_END) continue;
497
498 extern RoadTypeInfo _roadtypes[ROADTYPE_END];
499 for (auto &roadtype : roadtypes) {
500 if (roadtype != INVALID_ROADTYPE) {
501 RoadTypeInfo *rti = &_roadtypes[roadtype];
502
503 rti->grffile[ctype] = _cur_gps.grffile;
504 rti->group[ctype] = _cur_gps.spritegroups[groupid];
505 }
506 }
507 }
508
509 /* Roadtypes do not use the default group. */
510 buf.ReadWord();
511}
512
513static void AirportMapSpriteGroup(ByteReader &buf, uint8_t idcount)
514{
515 if (_cur_gps.grffile->airportspec.empty()) {
516 GrfMsg(1, "AirportMapSpriteGroup: No airports defined, skipping");
517 return;
518 }
519
520 std::vector<uint16_t> airports;
521 airports.reserve(idcount);
522 for (uint i = 0; i < idcount; i++) {
523 airports.push_back(buf.ReadExtendedByte());
524 }
525
526 auto set_sprite_group = [&airports](StandardSpriteGroup key, uint16_t groupid) {
527 if (!IsValidGroupID(groupid, "AirportMapSpriteGroup")) return;
528
529 for (auto &airport : airports) {
530 AirportSpec *as = airport >= _cur_gps.grffile->airportspec.size() ? nullptr : _cur_gps.grffile->airportspec[airport].get();
531 if (as == nullptr) {
532 GrfMsg(1, "AirportMapSpriteGroup: Airport {} undefined, skipping", airport);
533 continue;
534 }
535
536 as->grf_prop.SetSpriteGroup(key, _cur_gps.spritegroups[groupid]);
537 }
538 };
539
540 uint8_t cidcount = buf.ReadByte();
541 for (uint c = 0; c < cidcount; c++) {
542 uint8_t ctype = buf.ReadByte();
543 uint16_t groupid = buf.ReadWord();
544 if (ctype == 0xFF) {
545 set_sprite_group(StandardSpriteGroup::Purchase, groupid);
546 } else {
547 GrfMsg(1, "AirportMapSpriteGroup: Invalid cargo bitnum {} for airports, skipping.", ctype);
548 }
549 }
550 set_sprite_group(StandardSpriteGroup::Default, buf.ReadWord());
551}
552
553static void AirportTileMapSpriteGroup(ByteReader &buf, uint8_t idcount)
554{
555 if (_cur_gps.grffile->airtspec.empty()) {
556 GrfMsg(1, "AirportTileMapSpriteGroup: No airport tiles defined, skipping");
557 return;
558 }
559
560 std::vector<uint16_t> airptiles;
561 airptiles.reserve(idcount);
562 for (uint i = 0; i < idcount; i++) {
563 airptiles.push_back(buf.ReadExtendedByte());
564 }
565
566 auto set_sprite_group = [&airptiles](StandardSpriteGroup key, uint16_t groupid) {
567 if (!IsValidGroupID(groupid, "AirportTileMapSpriteGroup")) return;
568
569 for (auto &airptile : airptiles) {
570 AirportTileSpec *airtsp = airptile >= _cur_gps.grffile->airtspec.size() ? nullptr : _cur_gps.grffile->airtspec[airptile].get();
571 if (airtsp == nullptr) {
572 GrfMsg(1, "AirportTileMapSpriteGroup: Airport tile {} undefined, skipping", airptile);
573 continue;
574 }
575
576 airtsp->grf_prop.SetSpriteGroup(key, _cur_gps.spritegroups[groupid]);
577 }
578 };
579
580 uint8_t cidcount = buf.ReadByte();
581 for (uint c = 0; c < cidcount; c++) {
582 uint8_t ctype = buf.ReadByte();
583 uint16_t groupid = buf.ReadWord();
584 if (ctype == 0xFF) {
585 set_sprite_group(StandardSpriteGroup::Purchase, groupid);
586 } else {
587 GrfMsg(1, "AirportTileMapSpriteGroup: Invalid cargo bitnum {} for airport tiles, skipping.", ctype);
588 }
589 }
590 set_sprite_group(StandardSpriteGroup::Default, buf.ReadWord());
591}
592
593static void RoadStopMapSpriteGroup(ByteReader &buf, uint8_t idcount)
594{
595 if (_cur_gps.grffile->roadstops.empty()) {
596 GrfMsg(1, "RoadStopMapSpriteGroup: No roadstops defined, skipping");
597 return;
598 }
599
600 std::vector<uint16_t> roadstops;
601 roadstops.reserve(idcount);
602 for (uint i = 0; i < idcount; i++) {
603 roadstops.push_back(buf.ReadExtendedByte());
604 }
605
606 uint8_t cidcount = buf.ReadByte();
607 for (uint c = 0; c < cidcount; c++) {
608 uint8_t ctype = buf.ReadByte();
609 uint16_t groupid = buf.ReadWord();
610 if (!IsValidGroupID(groupid, "RoadStopMapSpriteGroup")) continue;
611
612 CargoType cargo_type = TranslateCargo(GSF_ROADSTOPS, ctype);
613 if (!IsValidCargoType(cargo_type)) continue;
614
615 for (auto &roadstop : roadstops) {
616 RoadStopSpec *roadstopspec = roadstop >= _cur_gps.grffile->roadstops.size() ? nullptr : _cur_gps.grffile->roadstops[roadstop].get();
617
618 if (roadstopspec == nullptr) {
619 GrfMsg(1, "RoadStopMapSpriteGroup: Road stop {} undefined, skipping", roadstop);
620 continue;
621 }
622
623 roadstopspec->grf_prop.SetSpriteGroup(cargo_type, _cur_gps.spritegroups[groupid]);
624 }
625 }
626
627 uint16_t groupid = buf.ReadWord();
628 if (!IsValidGroupID(groupid, "RoadStopMapSpriteGroup")) return;
629
630 for (auto &roadstop : roadstops) {
631 RoadStopSpec *roadstopspec = roadstop >= _cur_gps.grffile->roadstops.size() ? nullptr : _cur_gps.grffile->roadstops[roadstop].get();
632
633 if (roadstopspec == nullptr) {
634 GrfMsg(1, "RoadStopMapSpriteGroup: Road stop {} undefined, skipping.", roadstop);
635 continue;
636 }
637
638 if (roadstopspec->grf_prop.HasGrfFile()) {
639 GrfMsg(1, "RoadStopMapSpriteGroup: Road stop {} mapped multiple times, skipping", roadstop);
640 continue;
641 }
642
643 roadstopspec->grf_prop.SetSpriteGroup(CargoGRFFileProps::SG_DEFAULT, _cur_gps.spritegroups[groupid]);
644 roadstopspec->grf_prop.SetGRFFile(_cur_gps.grffile);
645 roadstopspec->grf_prop.local_id = roadstop;
646 RoadStopClass::Assign(roadstopspec);
647 }
648}
649
650static void BadgeMapSpriteGroup(ByteReader &buf, uint8_t idcount)
651{
652 if (_cur_gps.grffile->badge_map.empty()) {
653 GrfMsg(1, "BadgeMapSpriteGroup: No badges defined, skipping");
654 return;
655 }
656
657 std::vector<uint16_t> local_ids;
658 local_ids.reserve(idcount);
659 for (uint i = 0; i < idcount; i++) {
660 local_ids.push_back(buf.ReadExtendedByte());
661 }
662
663 uint8_t cidcount = buf.ReadByte();
664 for (uint c = 0; c < cidcount; c++) {
665 uint8_t ctype = buf.ReadByte();
666 uint16_t groupid = buf.ReadWord();
667 if (!IsValidGroupID(groupid, "BadgeMapSpriteGroup")) continue;
668
669 if (ctype >= GSF_END) continue;
670
671 for (const auto &local_id : local_ids) {
672 auto found = _cur_gps.grffile->badge_map.find(local_id);
673 if (found == std::end(_cur_gps.grffile->badge_map)) {
674 GrfMsg(1, "BadgeMapSpriteGroup: Badge {} undefined, skipping", local_id);
675 continue;
676 }
677
678 auto &badge = *GetBadge(found->second);
679 badge.grf_prop.SetSpriteGroup(static_cast<GrfSpecFeature>(ctype), _cur_gps.spritegroups[groupid]);
680 }
681 }
682
683 uint16_t groupid = buf.ReadWord();
684 if (!IsValidGroupID(groupid, "BadgeMapSpriteGroup")) return;
685
686 for (auto &local_id : local_ids) {
687 auto found = _cur_gps.grffile->badge_map.find(local_id);
688 if (found == std::end(_cur_gps.grffile->badge_map)) {
689 GrfMsg(1, "BadgeMapSpriteGroup: Badge {} undefined, skipping", local_id);
690 continue;
691 }
692
693 auto &badge = *GetBadge(found->second);
694 badge.grf_prop.SetSpriteGroup(GSF_DEFAULT, _cur_gps.spritegroups[groupid]);
695 badge.grf_prop.SetGRFFile(_cur_gps.grffile);
696 badge.grf_prop.local_id = local_id;
697 }
698}
699
700/* Action 0x03 */
701static void FeatureMapSpriteGroup(ByteReader &buf)
702{
703 /* <03> <feature> <n-id> <ids>... <num-cid> [<cargo-type> <cid>]... <def-cid>
704 * id-list := [<id>] [id-list]
705 * cargo-list := <cargo-type> <cid> [cargo-list]
706 *
707 * B feature see action 0
708 * B n-id bits 0-6: how many IDs this definition applies to
709 * bit 7: if set, this is a wagon override definition (see below)
710 * E ids the IDs for which this definition applies
711 * B num-cid number of cargo IDs (sprite group IDs) in this definition
712 * can be zero, in that case the def-cid is used always
713 * B cargo-type type of this cargo type (e.g. mail=2, wood=7, see below)
714 * W cid cargo ID (sprite group ID) for this type of cargo
715 * W def-cid default cargo ID (sprite group ID) */
716
717 GrfSpecFeature feature{buf.ReadByte()};
718 uint8_t idcount = buf.ReadByte();
719
720 if (feature >= GSF_END) {
721 GrfMsg(1, "FeatureMapSpriteGroup: Unsupported feature 0x{:02X}, skipping", feature);
722 return;
723 }
724
725 /* If idcount is zero, this is a feature callback */
726 if (idcount == 0) {
727 /* Skip number of cargo ids? */
728 buf.ReadByte();
729 uint16_t groupid = buf.ReadWord();
730 if (!IsValidGroupID(groupid, "FeatureMapSpriteGroup")) return;
731
732 GrfMsg(6, "FeatureMapSpriteGroup: Adding generic feature callback for feature 0x{:02X}", feature);
733
734 AddGenericCallback(feature, _cur_gps.grffile, _cur_gps.spritegroups[groupid]);
735 return;
736 }
737
738 /* Mark the feature as used by the grf (generic callbacks do not count) */
739 _cur_gps.grffile->grf_features.Set(feature);
740
741 GrfMsg(6, "FeatureMapSpriteGroup: Feature 0x{:02X}, {} ids", feature, idcount);
742
743 switch (feature) {
744 case GSF_TRAINS:
745 case GSF_ROADVEHICLES:
746 case GSF_SHIPS:
747 case GSF_AIRCRAFT:
748 VehicleMapSpriteGroup(buf, feature, idcount);
749 return;
750
751 case GSF_CANALS:
752 CanalMapSpriteGroup(buf, idcount);
753 return;
754
755 case GSF_STATIONS:
756 StationMapSpriteGroup(buf, idcount);
757 return;
758
759 case GSF_HOUSES:
760 TownHouseMapSpriteGroup(buf, idcount);
761 return;
762
763 case GSF_INDUSTRIES:
764 IndustryMapSpriteGroup(buf, idcount);
765 return;
766
767 case GSF_INDUSTRYTILES:
768 IndustrytileMapSpriteGroup(buf, idcount);
769 return;
770
771 case GSF_CARGOES:
772 CargoMapSpriteGroup(buf, idcount);
773 return;
774
775 case GSF_AIRPORTS:
776 AirportMapSpriteGroup(buf, idcount);
777 return;
778
779 case GSF_OBJECTS:
780 ObjectMapSpriteGroup(buf, idcount);
781 break;
782
783 case GSF_RAILTYPES:
784 RailTypeMapSpriteGroup(buf, idcount);
785 break;
786
787 case GSF_ROADTYPES:
788 RoadTypeMapSpriteGroup(buf, idcount, RTT_ROAD);
789 break;
790
791 case GSF_TRAMTYPES:
792 RoadTypeMapSpriteGroup(buf, idcount, RTT_TRAM);
793 break;
794
795 case GSF_AIRPORTTILES:
796 AirportTileMapSpriteGroup(buf, idcount);
797 return;
798
799 case GSF_ROADSTOPS:
800 RoadStopMapSpriteGroup(buf, idcount);
801 return;
802
803 case GSF_BADGES:
804 BadgeMapSpriteGroup(buf, idcount);
805 break;
806
807 default:
808 GrfMsg(1, "FeatureMapSpriteGroup: Unsupported feature 0x{:02X}, skipping", feature);
809 return;
810 }
811}
812
813template <> void GrfActionHandler<0x03>::FileScan(ByteReader &) { }
814template <> void GrfActionHandler<0x03>::SafetyScan(ByteReader &buf) { GRFUnsafe(buf); }
815template <> void GrfActionHandler<0x03>::LabelScan(ByteReader &) { }
816template <> void GrfActionHandler<0x03>::Init(ByteReader &) { }
817template <> void GrfActionHandler<0x03>::Reserve(ByteReader &) { }
818template <> void GrfActionHandler<0x03>::Activation(ByteReader &buf) { FeatureMapSpriteGroup(buf); }
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
static constexpr CargoLabel CT_INVALID
Invalid cargo type.
Definition cargo_type.h:72
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:23
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:106
static const CargoType NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:75
constexpr Timpl & Set()
Set all bits.
Class to read from a NewGRF file.
uint16_t ReadWord()
Read a single Word (16 bits).
uint16_t ReadExtendedByte()
Read a single Extended Byte (8 or 16 bits).
uint8_t ReadByte()
Read a single byte (8 bits).
static void Assign(Tspec *spec)
Assign a spec to one of the classes.
This struct contains all the info that is needed to draw and construct tracks.
Definition rail.h:118
const SpriteGroup * group[RTSG_END]
Sprite groups for resolving sprites.
Definition rail.h:272
const GRFFile * grffile[RTSG_END]
NewGRF providing the Action3 for the railtype.
Definition rail.h:267
const SpriteGroup * group[ROTSG_END]
Sprite groups for resolving sprites.
Definition road.h:183
const GRFFile * grffile[ROTSG_END]
NewGRF providing the Action3 for the roadtype.
Definition road.h:178
std::span< const CargoLabel > GetCargoTranslationTable(const GRFFile &grffile)
Get the cargo translation table to use for the given GRF file.
Definition newgrf.cpp:512
void GRFUnsafe(ByteReader &)
Set the current NewGRF as unsafe for static use.
Definition newgrf.cpp:367
Engine * GetNewEngine(const GRFFile *file, VehicleType type, uint16_t internal_id, bool static_access)
Returns the engine associated to a certain internal_id, resp.
Definition newgrf.cpp:209
GrfSpecFeature
Definition newgrf.h:69
@ GSF_DEFAULT
Unspecified feature, default badge.
Definition newgrf.h:94
Badge * GetBadge(BadgeID index)
Get a badge if it exists.
NewGRF buffer reader definition.
std::array< WaterFeature, CF_END > _water_feature
Table of canal 'feature' sprite groups.
StandardSpriteGroup
Standard sprite groups.
@ Purchase
Used before an entity exists.
@ Default
Default type used when no more-specific group matches.
void SetEngineGRF(EngineID engine, const GRFFile *file)
Tie a GRFFile entry to an engine, to allow us to retrieve GRF parameters etc during a game.
void AddGenericCallback(GrfSpecFeature feature, const GRFFile *file, const SpriteGroup *group)
Add a generic feature callback sprite group to the appropriate feature list.
NewGRF internal processing state.
@ CIR_INVALID_ID
Attempt to modify an invalid ID.
static constexpr uint MAX_SPRITEGROUP
Maximum GRF-local ID for a spritegroup.
NewGRF internal processing state for vehicles.
@ RAILTYPE_END
Used for iterations.
Definition rail_type.h:31
@ INVALID_RAILTYPE
Flag for invalid railtype.
Definition rail_type.h:32
@ INVALID_ROADTYPE
flag for invalid roadtype
Definition road_type.h:28
@ ROADTYPE_END
Used for iterations.
Definition road_type.h:27
Defines the data structure for an airport.
SubstituteGRFFileProps grf_prop
Properties related to the grf file.
Defines the data structure of each individual tile of an airport.
SubstituteGRFFileProps grf_prop
properties related the the grf file
static constexpr CargoType SG_PURCHASE
Used in purchase lists before an item exists.
static constexpr CargoType SG_DEFAULT
Default type used when no more-specific cargo matches.
static constexpr CargoType SG_DEFAULT_NA
Used only by stations and roads when no more-specific cargo matches.
Specification of a cargo type.
Definition cargotype.h:74
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:137
const struct GRFFile * grffile
NewGRF where group belongs to.
Definition cargotype.h:99
void SetSpriteGroup(Tkey index, const struct SpriteGroup *spritegroup)
Set the SpriteGroup at the specified index.
uint16_t local_id
id defined by the grf file for this entity
void SetGRFFile(const struct GRFFile *grffile)
Set the NewGRF file, and its grfid, associated with grf props.
bool HasGrfFile() const
Test if this entity was introduced by NewGRF.
std::vector< CargoLabel > cargo_list
Cargo translation table (local ID -> label)
Definition newgrf.h:136
GrfSpecFeatures grf_features
Bitset of GrfSpecFeature the grf uses.
Definition newgrf.h:158
GRF action handler.
GRFFile * grffile
Currently processed GRF file.
SubstituteGRFFileProps grf_prop
Properties related the the grf file.
Definition house.h:114
Defines the data structure for constructing industry.
SubstituteGRFFileProps grf_prop
properties related to the grf file
Defines the data structure of each individual tile of an industry.
SubstituteGRFFileProps grf_prop
properties related to the grf file
Allow incrementing of ObjectClassID variables.
StandardGRFFileProps grf_prop
Properties related the the grf file.
Tindex index
Index of this pool item.
Road stop specification.
CargoGRFFileProps grf_prop
Link to NewGRF.
Station specification.
CargoGRFFileProps grf_prop
Link to NewGRF.
void SetSpriteGroup(Tkey index, const SpriteGroup *spritegroup)
Set the SpriteGroup at the specified index.
VehicleType
Available vehicle types.