OpenTTD Source 20260903-master-ged04d336ed
newgrf_act0_airports.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
9
10#include "../stdafx.h"
11#include "../debug.h"
13#include "../newgrf_airport.h"
14#include "newgrf_bytereader.h"
15#include "newgrf_internal.h"
17
18#include "../safeguards.h"
19
25static bool ValidateAirportLayout(const AirportTileLayout &layout)
26{
27 const size_t size = layout.tiles.size();
28 if (size == 0) return false;
29
30 for (size_t i = 0; i < size - 1; i++) {
31 for (size_t j = i + 1; j < size; j++) {
32 if (layout.tiles[i].ti.x == layout.tiles[j].ti.x &&
33 layout.tiles[i].ti.y == layout.tiles[j].ti.y) {
34 return false;
35 }
36 }
37 }
38
39 return true;
40}
41
50static ChangeInfoResult AirportChangeInfo(uint first, uint last, int prop, ByteReader &buf)
51{
53
54 if (last > NUM_AIRPORTS_PER_GRF) {
55 GrfMsg(1, "AirportChangeInfo: Too many airports, trying id ({}), max ({}). Ignoring.", last, NUM_AIRPORTS_PER_GRF);
57 }
58
59 /* Allocate industry specs if they haven't been allocated already. */
60 if (_cur_gps.grffile->airportspec.size() < last) _cur_gps.grffile->airportspec.resize(last);
61
62 for (uint id = first; id < last; ++id) {
63 auto &as = _cur_gps.grffile->airportspec[id];
64
65 if (as == nullptr && prop != 0x08 && prop != 0x09) {
66 GrfMsg(2, "AirportChangeInfo: Attempt to modify undefined airport {}, ignoring", id);
68 }
69
70 switch (prop) {
71 case 0x08: { // Modify original airport
72 uint8_t subs_id = buf.ReadByte();
73 if (subs_id == 0xFF) {
74 /* Instead of defining a new airport, an airport id
75 * of 0xFF disables the old airport with the current id. */
77 continue;
78 } else if (subs_id >= NEW_AIRPORT_OFFSET) {
79 /* The substitute id must be one of the original airports. */
80 GrfMsg(2, "AirportChangeInfo: Attempt to use new airport {} as substitute airport for {}. Ignoring.", subs_id, id);
81 continue;
82 }
83
84 /* Allocate space for this airport.
85 * Only need to do it once. If ever it is called again, it should not
86 * do anything */
87 if (as == nullptr) {
88 as = std::make_unique<AirportSpec>(*AirportSpec::GetWithoutOverride(subs_id));
89
90 as->enabled = true;
91 as->grf_prop.local_id = id;
92 as->grf_prop.subst_id = subs_id;
93 as->grf_prop.SetGRFFile(_cur_gps.grffile);
94 /* override the default airport */
95 _airport_mngr.Add(id, _cur_gps.grffile->grfid, subs_id);
96 }
97 break;
98 }
99
100 case 0x0A: { // Set airport layout
101 uint8_t num_layouts = buf.ReadByte();
102 size_t definition_size = buf.ReadDWord();
103 size_t definition_end = definition_size + buf.GetBytesRead();
104 uint8_t size_x = 0;
105 uint8_t size_y = 0;
106
107 std::vector<AirportTileLayout> layouts;
108 layouts.reserve(num_layouts);
109
110 AirportTileLayout layout;
111
112 for (uint8_t j = 0; j != num_layouts; ++j) {
113 bool invalid_layout = false;
114 layout.tiles.clear();
115 layout.rotation = static_cast<Direction>(buf.ReadByte() & 6); // Rotation can only be DIR_NORTH, DIR_EAST, DIR_SOUTH or DIR_WEST.
116
117 for (;;) {
118 if (definition_end < buf.GetBytesRead()) {
119 GrfMsg(3, "AirportChangeInfo: Incorrect size for airport tile layout definition for airport {}.", id);
120 /* Avoid warning twice */
121 definition_end = SIZE_MAX;
122 }
123
124 auto &tile = layout.tiles.emplace_back();
125 tile.ti.x = buf.ReadByte();
126 tile.ti.y = buf.ReadByte();
127 if (tile.ti.x == 0 && tile.ti.y == 0x80) {
128 /* Terminator, remove and finish up. */
129 layout.tiles.pop_back();
130 break;
131 }
132
133 tile.gfx = buf.ReadByte();
134
135 if (tile.gfx == 0xFE) {
136 /* Use a new tile from this GRF */
137 int local_tile_id = buf.ReadWord();
138
139 /* Read the ID from the _airporttile_mngr. */
140 uint16_t tempid = _airporttile_mngr.GetID(local_tile_id, _cur_gps.grffile->grfid);
141
142 if (tempid == INVALID_AIRPORTTILE) {
143 GrfMsg(2, "AirportChangeInfo: Attempt to use airport tile {} with airport id {}, not yet defined. Ignoring.", local_tile_id, id);
144 invalid_layout = true;
145 } else {
146 /* Declared as been valid, can be used */
147 tile.gfx = tempid;
148 }
149 } else if (tile.gfx == 0xFF) {
150 tile.ti.x = static_cast<int8_t>(GB(tile.ti.x, 0, 8));
151 tile.ti.y = static_cast<int8_t>(GB(tile.ti.y, 0, 8));
152 } else if (tile.gfx >= NEW_AIRPORTTILE_OFFSET) {
153 GrfMsg(2, "AirportChangeInfo: Attempt to use invalid airport tile {} with airport id {}. Ignoring.", tile.gfx, id);
154 invalid_layout = true;
155 }
156
157 /* Determine largest size. */
158 if (layout.rotation == Direction::E || layout.rotation == Direction::W) {
159 size_x = std::max<uint8_t>(size_x, tile.ti.y + 1);
160 size_y = std::max<uint8_t>(size_y, tile.ti.x + 1);
161 } else {
162 size_x = std::max<uint8_t>(size_x, tile.ti.x + 1);
163 size_y = std::max<uint8_t>(size_y, tile.ti.y + 1);
164 }
165 }
166
167 if (invalid_layout) continue;
168
169 if (!ValidateAirportLayout(layout)) {
170 /* The airport layout was not valid, so skip this one. */
171 GrfMsg(1, "AirportChangeInfo: Invalid airport layout for airport id {}. Ignoring", id);
172 } else {
173 layouts.push_back(layout);
174 }
175 }
176 as->layouts = std::move(layouts);
177 as->size_x = size_x;
178 as->size_y = size_y;
179 break;
180 }
181
182 case 0x0C:
183 as->min_year = TimerGameCalendar::Year{buf.ReadWord()};
184 as->max_year = TimerGameCalendar::Year{buf.ReadWord()};
185 if (as->max_year == 0xFFFF) as->max_year = CalendarTime::MAX_YEAR;
186 break;
187
188 case 0x0D:
189 as->ttd_airport_type = (TTDPAirportType)buf.ReadByte();
190 break;
191
192 case 0x0E:
193 as->catchment = Clamp(buf.ReadByte(), 1, MAX_CATCHMENT);
194 break;
195
196 case 0x0F:
197 as->noise_level = buf.ReadByte();
198 break;
199
200 case 0x10:
201 AddStringForMapping(GRFStringID{buf.ReadWord()}, &as->name);
202 break;
203
204 case 0x11: // Maintenance cost factor
205 as->maintenance_cost = buf.ReadWord();
206 break;
207
208 case 0x12: // Badge list
209 as->badges = ReadBadgeList(buf, GrfSpecFeature::Airports);
210 break;
211
212 default:
214 break;
215 }
216 }
217
218 return ret;
219}
220
221static ChangeInfoResult AirportTilesChangeInfo(uint first, uint last, int prop, ByteReader &buf)
222{
224
225 if (last > NUM_AIRPORTTILES_PER_GRF) {
226 GrfMsg(1, "AirportTileChangeInfo: Too many airport tiles loaded ({}), max ({}). Ignoring.", last, NUM_AIRPORTTILES_PER_GRF);
228 }
229
230 /* Allocate airport tile specs if they haven't been allocated already. */
231 if (_cur_gps.grffile->airtspec.size() < last) _cur_gps.grffile->airtspec.resize(last);
232
233 for (uint id = first; id < last; ++id) {
234 auto &tsp = _cur_gps.grffile->airtspec[id];
235
236 if (prop != 0x08 && tsp == nullptr) {
237 GrfMsg(2, "AirportTileChangeInfo: Attempt to modify undefined airport tile {}. Ignoring.", id);
239 }
240
241 switch (prop) {
242 case 0x08: { // Substitute airport tile type
243 uint8_t subs_id = buf.ReadByte();
244 if (subs_id >= NEW_AIRPORTTILE_OFFSET) {
245 /* The substitute id must be one of the original airport tiles. */
246 GrfMsg(2, "AirportTileChangeInfo: Attempt to use new airport tile {} as substitute airport tile for {}. Ignoring.", subs_id, id);
247 continue;
248 }
249
250 /* Allocate space for this airport tile. */
251 if (tsp == nullptr) {
252 tsp = std::make_unique<AirportTileSpec>(*AirportTileSpec::Get(subs_id));
253
254 tsp->enabled = true;
255
256 tsp->animation = {};
257
258 tsp->grf_prop.local_id = id;
259 tsp->grf_prop.subst_id = subs_id;
260 tsp->grf_prop.SetGRFFile(_cur_gps.grffile);
261 _airporttile_mngr.AddEntityID(id, _cur_gps.grffile->grfid, subs_id); // pre-reserve the tile slot
262 }
263 break;
264 }
265
266 case 0x09: { // Airport tile override
267 uint8_t override_id = buf.ReadByte();
268
269 /* The airport tile being overridden must be an original airport tile. */
270 if (override_id >= NEW_AIRPORTTILE_OFFSET) {
271 GrfMsg(2, "AirportTileChangeInfo: Attempt to override new airport tile {} with airport tile id {}. Ignoring.", override_id, id);
272 continue;
273 }
274
275 _airporttile_mngr.Add(id, _cur_gps.grffile->grfid, override_id);
276 break;
277 }
278
279 case 0x0E: // Callback mask
280 tsp->callback_mask = static_cast<AirportTileCallbackMasks>(buf.ReadByte());
281 break;
282
283 case 0x0F: // Animation information
284 tsp->animation.frames = buf.ReadByte();
285 tsp->animation.status = static_cast<AnimationStatus>(buf.ReadByte());
286 break;
287
288 case 0x10: // Animation speed
289 tsp->animation.speed = buf.ReadByte();
290 break;
291
292 case 0x11: // Animation triggers
293 tsp->animation.triggers = static_cast<AirportAnimationTriggers>(buf.ReadByte());
294 break;
295
296 case 0x12: // Badge list
297 tsp->badges = ReadBadgeList(buf, GrfSpecFeature::TramTypes);
298 break;
299
300 default:
302 break;
303 }
304 }
305
306 return ret;
307}
308
312template <> ChangeInfoResult GrfChangeInfoHandler<GrfSpecFeature::Airports>::Activation(uint first, uint last, int prop, ByteReader &buf) { return AirportChangeInfo(first, last, prop, buf); }
313
317template <> ChangeInfoResult GrfChangeInfoHandler<GrfSpecFeature::AirportTiles>::Activation(uint first, uint last, int prop, ByteReader &buf) { return AirportTilesChangeInfo(first, last, prop, buf); }
static const uint INVALID_AIRPORTTILE
id for an invalid airport tile
Definition airport.h:25
static const uint NEW_AIRPORTTILE_OFFSET
offset of first newgrf airport tile
Definition airport.h:24
static const uint NUM_AIRPORTTILES_PER_GRF
Number of airport tiles per NewGRF; limited to 255 to allow extending Action3 with an extended byte l...
Definition airport.h:21
@ NEW_AIRPORT_OFFSET
Number of the first newgrf airport.
Definition airport.h:39
@ NUM_AIRPORTS_PER_GRF
Maximal number of airports per NewGRF.
Definition airport.h:40
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
Class to read from a NewGRF file.
uint32_t ReadDWord()
Read a single DWord (32 bits).
uint16_t ReadWord()
Read a single Word (16 bits).
size_t GetBytesRead() const
Get number of already read bytes.
uint8_t ReadByte()
Read a single byte (8 bits).
static constexpr TimerGame< struct Calendar >::Year MAX_YEAR
StrongType::Typedef< int32_t, struct YearTag< struct Calendar >, StrongType::Compare, StrongType::Integer > Year
Functions related to debugging.
Direction
Defines the 8 directions on the map.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
@ TramTypes
Tram types feature.
Definition newgrf.h:98
@ Airports
Airports feature.
Definition newgrf.h:92
std::vector< BadgeID > ReadBadgeList(ByteReader &buf, GrfSpecFeature feature)
Read a list of badges.
static ChangeInfoResult AirportChangeInfo(uint first, uint last, int prop, ByteReader &buf)
Define properties for airports.
static bool ValidateAirportLayout(const AirportTileLayout &layout)
Validate the airport layout; e.g.
NewGRF handling of airports.
TTDPAirportType
TTDP airport types.
NewGRF handling of airport tiles.
AnimationStatus
Statuses of animation within NewGRFs.
NewGRF buffer reader definition.
EnumBitSet< AirportTileCallbackMask, uint8_t > AirportTileCallbackMasks
Bitset of AirportTileCallbackMask elements.
NewGRF internal processing state.
ChangeInfoResult
Possible return values for the GrfChangeInfoHandler functions.
@ Success
Variable was parsed and read.
@ Unhandled
Variable was parsed but unread.
@ Unknown
Variable is unknown.
@ InvalidId
Attempt to modify an invalid ID.
void AddStringForMapping(GRFStringID source, std::function< void(StringID)> &&func)
Record a static StringID for getting translated later.
NewGRF string mapping definition.
StrongType::Typedef< uint32_t, struct GRFStringIDTag, StrongType::Compare, StrongType::Integer > GRFStringID
Type for GRF-internal string IDs.
A number of safeguards to prevent using unsafe methods.
EnumBitSet< AirportAnimationTrigger, uint8_t > AirportAnimationTriggers
Bitset of AirportAnimationTrigger elements.
static constexpr uint MAX_CATCHMENT
Maximum catchment for airports with "modified catchment" enabled.
Definition of base types and functions in a cross-platform compatible way.
static AirportSpec * GetWithoutOverride(uint8_t type)
Retrieve airport spec for the given airport.
bool enabled
Entity still available (by default true). Newgrf can disable it, though.
std::vector< AirportTileTable > tiles
List of all tiles in this layout.
Direction rotation
The rotation of this layout.
static const AirportTileSpec * Get(StationGfx gfx)
Retrieve airport tile spec for the given airport tile.
static ChangeInfoResult Reserve(uint first, uint last, int prop, ByteReader &buf)
Implementation of the GrfLoadingStage::Reserve stage of this feature.
static ChangeInfoResult Activation(uint first, uint last, int prop, ByteReader &buf)
Implementation of the GrfLoadingStage::Activation stage of this feature.