OpenTTD Source 20250531-master-g621c031307
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 <http://www.gnu.org/licenses/>.
6 */
7
10#include "../stdafx.h"
11#include "../debug.h"
12#include "../newgrf_airporttiles.h"
13#include "../newgrf_airport.h"
14#include "newgrf_bytereader.h"
15#include "newgrf_internal.h"
17
18#include "../safeguards.h"
19
28static ChangeInfoResult AirportChangeInfo(uint first, uint last, int prop, ByteReader &buf)
29{
31
32 if (last > NUM_AIRPORTS_PER_GRF) {
33 GrfMsg(1, "AirportChangeInfo: Too many airports, trying id ({}), max ({}). Ignoring.", last, NUM_AIRPORTS_PER_GRF);
34 return CIR_INVALID_ID;
35 }
36
37 /* Allocate industry specs if they haven't been allocated already. */
38 if (_cur_gps.grffile->airportspec.size() < last) _cur_gps.grffile->airportspec.resize(last);
39
40 for (uint id = first; id < last; ++id) {
41 auto &as = _cur_gps.grffile->airportspec[id];
42
43 if (as == nullptr && prop != 0x08 && prop != 0x09) {
44 GrfMsg(2, "AirportChangeInfo: Attempt to modify undefined airport {}, ignoring", id);
45 return CIR_INVALID_ID;
46 }
47
48 switch (prop) {
49 case 0x08: { // Modify original airport
50 uint8_t subs_id = buf.ReadByte();
51 if (subs_id == 0xFF) {
52 /* Instead of defining a new airport, an airport id
53 * of 0xFF disables the old airport with the current id. */
55 continue;
56 } else if (subs_id >= NEW_AIRPORT_OFFSET) {
57 /* The substitute id must be one of the original airports. */
58 GrfMsg(2, "AirportChangeInfo: Attempt to use new airport {} as substitute airport for {}. Ignoring.", subs_id, id);
59 continue;
60 }
61
62 /* Allocate space for this airport.
63 * Only need to do it once. If ever it is called again, it should not
64 * do anything */
65 if (as == nullptr) {
66 as = std::make_unique<AirportSpec>(*AirportSpec::GetWithoutOverride(subs_id));
67
68 as->enabled = true;
69 as->grf_prop.local_id = id;
70 as->grf_prop.subst_id = subs_id;
71 as->grf_prop.SetGRFFile(_cur_gps.grffile);
72 /* override the default airport */
73 _airport_mngr.Add(id, _cur_gps.grffile->grfid, subs_id);
74 }
75 break;
76 }
77
78 case 0x0A: { // Set airport layout
79 uint8_t num_layouts = buf.ReadByte();
80 buf.ReadDWord(); // Total size of definition, unneeded.
81 uint8_t size_x = 0;
82 uint8_t size_y = 0;
83
84 std::vector<AirportTileLayout> layouts;
85 layouts.reserve(num_layouts);
86
87 for (uint8_t j = 0; j != num_layouts; ++j) {
88 auto &layout = layouts.emplace_back();
89 layout.rotation = static_cast<Direction>(buf.ReadByte() & 6); // Rotation can only be DIR_NORTH, DIR_EAST, DIR_SOUTH or DIR_WEST.
90
91 for (;;) {
92 auto &tile = layout.tiles.emplace_back();
93 tile.ti.x = buf.ReadByte();
94 tile.ti.y = buf.ReadByte();
95 if (tile.ti.x == 0 && tile.ti.y == 0x80) {
96 /* Terminator, remove and finish up. */
97 layout.tiles.pop_back();
98 break;
99 }
100
101 tile.gfx = buf.ReadByte();
102
103 if (tile.gfx == 0xFE) {
104 /* Use a new tile from this GRF */
105 int local_tile_id = buf.ReadWord();
106
107 /* Read the ID from the _airporttile_mngr. */
108 uint16_t tempid = _airporttile_mngr.GetID(local_tile_id, _cur_gps.grffile->grfid);
109
110 if (tempid == INVALID_AIRPORTTILE) {
111 GrfMsg(2, "AirportChangeInfo: Attempt to use airport tile {} with airport id {}, not yet defined. Ignoring.", local_tile_id, id);
112 } else {
113 /* Declared as been valid, can be used */
114 tile.gfx = tempid;
115 }
116 } else if (tile.gfx == 0xFF) {
117 tile.ti.x = static_cast<int8_t>(GB(tile.ti.x, 0, 8));
118 tile.ti.y = static_cast<int8_t>(GB(tile.ti.y, 0, 8));
119 }
120
121 /* Determine largest size. */
122 if (layout.rotation == DIR_E || layout.rotation == DIR_W) {
123 size_x = std::max<uint8_t>(size_x, tile.ti.y + 1);
124 size_y = std::max<uint8_t>(size_y, tile.ti.x + 1);
125 } else {
126 size_x = std::max<uint8_t>(size_x, tile.ti.x + 1);
127 size_y = std::max<uint8_t>(size_y, tile.ti.y + 1);
128 }
129 }
130 }
131 as->layouts = std::move(layouts);
132 as->size_x = size_x;
133 as->size_y = size_y;
134 break;
135 }
136
137 case 0x0C:
138 as->min_year = TimerGameCalendar::Year{buf.ReadWord()};
139 as->max_year = TimerGameCalendar::Year{buf.ReadWord()};
140 if (as->max_year == 0xFFFF) as->max_year = CalendarTime::MAX_YEAR;
141 break;
142
143 case 0x0D:
144 as->ttd_airport_type = (TTDPAirportType)buf.ReadByte();
145 break;
146
147 case 0x0E:
148 as->catchment = Clamp(buf.ReadByte(), 1, MAX_CATCHMENT);
149 break;
150
151 case 0x0F:
152 as->noise_level = buf.ReadByte();
153 break;
154
155 case 0x10:
156 AddStringForMapping(GRFStringID{buf.ReadWord()}, &as->name);
157 break;
158
159 case 0x11: // Maintenance cost factor
160 as->maintenance_cost = buf.ReadWord();
161 break;
162
163 case 0x12: // Badge list
164 as->badges = ReadBadgeList(buf, GSF_AIRPORTS);
165 break;
166
167 default:
168 ret = CIR_UNKNOWN;
169 break;
170 }
171 }
172
173 return ret;
174}
175
176static ChangeInfoResult AirportTilesChangeInfo(uint first, uint last, int prop, ByteReader &buf)
177{
179
180 if (last > NUM_AIRPORTTILES_PER_GRF) {
181 GrfMsg(1, "AirportTileChangeInfo: Too many airport tiles loaded ({}), max ({}). Ignoring.", last, NUM_AIRPORTTILES_PER_GRF);
182 return CIR_INVALID_ID;
183 }
184
185 /* Allocate airport tile specs if they haven't been allocated already. */
186 if (_cur_gps.grffile->airtspec.size() < last) _cur_gps.grffile->airtspec.resize(last);
187
188 for (uint id = first; id < last; ++id) {
189 auto &tsp = _cur_gps.grffile->airtspec[id];
190
191 if (prop != 0x08 && tsp == nullptr) {
192 GrfMsg(2, "AirportTileChangeInfo: Attempt to modify undefined airport tile {}. Ignoring.", id);
193 return CIR_INVALID_ID;
194 }
195
196 switch (prop) {
197 case 0x08: { // Substitute airport tile type
198 uint8_t subs_id = buf.ReadByte();
199 if (subs_id >= NEW_AIRPORTTILE_OFFSET) {
200 /* The substitute id must be one of the original airport tiles. */
201 GrfMsg(2, "AirportTileChangeInfo: Attempt to use new airport tile {} as substitute airport tile for {}. Ignoring.", subs_id, id);
202 continue;
203 }
204
205 /* Allocate space for this airport tile. */
206 if (tsp == nullptr) {
207 tsp = std::make_unique<AirportTileSpec>(*AirportTileSpec::Get(subs_id));
208
209 tsp->enabled = true;
210
211 tsp->animation = {};
212
213 tsp->grf_prop.local_id = id;
214 tsp->grf_prop.subst_id = subs_id;
215 tsp->grf_prop.SetGRFFile(_cur_gps.grffile);
216 _airporttile_mngr.AddEntityID(id, _cur_gps.grffile->grfid, subs_id); // pre-reserve the tile slot
217 }
218 break;
219 }
220
221 case 0x09: { // Airport tile override
222 uint8_t override_id = buf.ReadByte();
223
224 /* The airport tile being overridden must be an original airport tile. */
225 if (override_id >= NEW_AIRPORTTILE_OFFSET) {
226 GrfMsg(2, "AirportTileChangeInfo: Attempt to override new airport tile {} with airport tile id {}. Ignoring.", override_id, id);
227 continue;
228 }
229
230 _airporttile_mngr.Add(id, _cur_gps.grffile->grfid, override_id);
231 break;
232 }
233
234 case 0x0E: // Callback mask
235 tsp->callback_mask = static_cast<AirportTileCallbackMasks>(buf.ReadByte());
236 break;
237
238 case 0x0F: // Animation information
239 tsp->animation.frames = buf.ReadByte();
240 tsp->animation.status = static_cast<AnimationStatus>(buf.ReadByte());
241 break;
242
243 case 0x10: // Animation speed
244 tsp->animation.speed = buf.ReadByte();
245 break;
246
247 case 0x11: // Animation triggers
248 tsp->animation.triggers = static_cast<AirportAnimationTriggers>(buf.ReadByte());
249 break;
250
251 case 0x12: // Badge list
252 tsp->badges = ReadBadgeList(buf, GSF_TRAMTYPES);
253 break;
254
255 default:
256 ret = CIR_UNKNOWN;
257 break;
258 }
259 }
260
261 return ret;
262}
263
265template <> ChangeInfoResult GrfChangeInfoHandler<GSF_AIRPORTS>::Activation(uint first, uint last, int prop, ByteReader &buf) { return AirportChangeInfo(first, last, prop, buf); }
266
268template <> ChangeInfoResult GrfChangeInfoHandler<GSF_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
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.
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).
uint8_t ReadByte()
Read a single byte (8 bits).
virtual uint16_t GetID(uint16_t grf_local_id, uint32_t grfid) const
Return the ID (if ever available) of a previously inserted entity.
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...
virtual uint16_t AddEntityID(uint16_t grf_local_id, uint32_t grfid, uint16_t substitute_id)
Reserves a place in the mapping array for an entity to be installed.
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,...
Direction
Defines the 8 directions on the map.
@ DIR_W
West.
@ DIR_E
East.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
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.
TTDPAirportType
Allow incrementing of AirportClassID variables.
NewGRF buffer reader definition.
NewGRF internal processing state.
ChangeInfoResult
Possible return values for the GrfChangeInfoHandler functions.
@ CIR_INVALID_ID
Attempt to modify an invalid ID.
@ CIR_UNKNOWN
Variable is unknown.
@ CIR_UNHANDLED
Variable was parsed but unread.
@ CIR_SUCCESS
Variable was parsed and read.
void AddStringForMapping(GRFStringID source, std::function< void(StringID)> &&func)
Record a static StringID for getting translated later.
NewGRF string mapping definition.
static constexpr uint MAX_CATCHMENT
Maximum catchment for airports with "modified catchment" enabled.
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.
static const AirportTileSpec * Get(StationGfx gfx)
Retrieve airport tile spec for the given airport tile.
GRF feature handler.
GRFFile * grffile
Currently processed GRF file.
Templated helper to make a type-safe 'typedef' representing a single POD value.