OpenTTD Source 20251005-master-ga617d009cc
newgrf_act0_trains.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_cargo.h"
13#include "../newgrf_engine.h"
14#include "../vehicle_base.h"
15#include "newgrf_bytereader.h"
17#include "newgrf_internal.h"
18
19#include "../safeguards.h"
20
29ChangeInfoResult RailVehicleChangeInfo(uint first, uint last, int prop, ByteReader &buf)
30{
32
33 for (uint id = first; id < last; ++id) {
34 Engine *e = GetNewEngine(_cur_gps.grffile, VEH_TRAIN, id);
35 if (e == nullptr) return CIR_INVALID_ID; // No engine could be allocated, so neither can any next vehicles
36
37 EngineInfo *ei = &e->info;
38 RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
39
40 switch (prop) {
41 case 0x05: { // Track type
42 uint8_t tracktype = buf.ReadByte();
43
44 _gted[e->index].railtypelabels.clear();
45 if (tracktype < _cur_gps.grffile->railtype_list.size()) {
46 _gted[e->index].railtypelabels.push_back(_cur_gps.grffile->railtype_list[tracktype]);
47 break;
48 }
49
50 switch (tracktype) {
51 case 0: _gted[e->index].railtypelabels.push_back(rvi->engclass >= 2 ? RAILTYPE_LABEL_ELECTRIC : RAILTYPE_LABEL_RAIL); break;
52 case 1: _gted[e->index].railtypelabels.push_back(RAILTYPE_LABEL_MONO); break;
53 case 2: _gted[e->index].railtypelabels.push_back(RAILTYPE_LABEL_MAGLEV); break;
54 default:
55 GrfMsg(1, "RailVehicleChangeInfo: Invalid track type {} specified, ignoring", tracktype);
56 break;
57 }
58 break;
59 }
60
61 case 0x08: // AI passenger service
62 /* Tells the AI that this engine is designed for
63 * passenger services and shouldn't be used for freight. */
64 rvi->ai_passenger_only = buf.ReadByte();
65 break;
66
67 case PROP_TRAIN_SPEED: { // 0x09 Speed (1 unit is 1 km-ish/h)
68 uint16_t speed = buf.ReadWord();
69 if (speed == 0xFFFF) speed = 0;
70
71 rvi->max_speed = speed;
72 break;
73 }
74
75 case PROP_TRAIN_POWER: // 0x0B Power
76 rvi->power = buf.ReadWord();
77
78 /* Set engine / wagon state based on power */
79 if (rvi->power != 0) {
80 if (rvi->railveh_type == RAILVEH_WAGON) {
81 rvi->railveh_type = RAILVEH_SINGLEHEAD;
82 }
83 } else {
84 rvi->railveh_type = RAILVEH_WAGON;
85 }
86 break;
87
88 case PROP_TRAIN_RUNNING_COST_FACTOR: // 0x0D Running cost factor
89 rvi->running_cost = buf.ReadByte();
90 break;
91
92 case 0x0E: // Running cost base
93 ConvertTTDBasePrice(buf.ReadDWord(), "RailVehicleChangeInfo", &rvi->running_cost_class);
94 break;
95
96 case 0x12: { // Sprite ID
97 uint8_t spriteid = buf.ReadByte();
98 uint8_t orig_spriteid = spriteid;
99
100 /* TTD sprite IDs point to a location in a 16bit array, but we use it
101 * as an array index, so we need it to be half the original value. */
102 if (spriteid < CUSTOM_VEHICLE_SPRITENUM) spriteid >>= 1;
103
104 if (IsValidNewGRFImageIndex<VEH_TRAIN>(spriteid)) {
105 rvi->image_index = spriteid;
106 } else {
107 GrfMsg(1, "RailVehicleChangeInfo: Invalid Sprite {} specified, ignoring", orig_spriteid);
108 rvi->image_index = 0;
109 }
110 break;
111 }
112
113 case 0x13: { // Dual-headed
114 uint8_t dual = buf.ReadByte();
115
116 if (dual != 0) {
117 rvi->railveh_type = RAILVEH_MULTIHEAD;
118 } else {
119 rvi->railveh_type = rvi->power == 0 ?
121 }
122 break;
123 }
124
125 case PROP_TRAIN_CARGO_CAPACITY: // 0x14 Cargo capacity
126 rvi->capacity = buf.ReadByte();
127 break;
128
129 case 0x15: { // Cargo type
130 _gted[e->index].defaultcargo_grf = _cur_gps.grffile;
131 uint8_t ctype = buf.ReadByte();
132
133 if (ctype == 0xFF) {
134 /* 0xFF is specified as 'use first refittable' */
135 ei->cargo_type = INVALID_CARGO;
136 } else {
137 /* Use translated cargo. Might result in INVALID_CARGO (first refittable), if cargo is not defined. */
138 ei->cargo_type = GetCargoTranslation(ctype, _cur_gps.grffile);
139 if (ei->cargo_type == INVALID_CARGO) GrfMsg(2, "RailVehicleChangeInfo: Invalid cargo type {}, using first refittable", ctype);
140 }
141 ei->cargo_label = CT_INVALID;
142 break;
143 }
144
145 case PROP_TRAIN_WEIGHT: // 0x16 Weight
146 SB(rvi->weight, 0, 8, buf.ReadByte());
147 break;
148
149 case PROP_TRAIN_COST_FACTOR: // 0x17 Cost factor
150 rvi->cost_factor = buf.ReadByte();
151 break;
152
153 case 0x18: // AI rank
154 GrfMsg(2, "RailVehicleChangeInfo: Property 0x18 'AI rank' not used by NoAI, ignored.");
155 buf.ReadByte();
156 break;
157
158 case 0x19: { // Engine traction type
159 /* What do the individual numbers mean?
160 * 0x00 .. 0x07: Steam
161 * 0x08 .. 0x27: Diesel
162 * 0x28 .. 0x31: Electric
163 * 0x32 .. 0x37: Monorail
164 * 0x38 .. 0x41: Maglev
165 */
166 uint8_t traction = buf.ReadByte();
167 EngineClass engclass;
168
169 if (traction <= 0x07) {
170 engclass = EC_STEAM;
171 } else if (traction <= 0x27) {
172 engclass = EC_DIESEL;
173 } else if (traction <= 0x31) {
174 engclass = EC_ELECTRIC;
175 } else if (traction <= 0x37) {
176 engclass = EC_MONORAIL;
177 } else if (traction <= 0x41) {
178 engclass = EC_MAGLEV;
179 } else {
180 break;
181 }
182
183 if (_cur_gps.grffile->railtype_list.empty() && !_gted[e->index].railtypelabels.empty()) {
184 /* Use traction type to select between normal and electrified
185 * rail only when no translation list is in place. */
186 if (_gted[e->index].railtypelabels[0] == RAILTYPE_LABEL_RAIL && engclass >= EC_ELECTRIC) _gted[e->index].railtypelabels[0] = RAILTYPE_LABEL_ELECTRIC;
187 if (_gted[e->index].railtypelabels[0] == RAILTYPE_LABEL_ELECTRIC && engclass < EC_ELECTRIC) _gted[e->index].railtypelabels[0] = RAILTYPE_LABEL_RAIL;
188 }
189
190 rvi->engclass = engclass;
191 break;
192 }
193
194 case 0x1A: // Alter purchase list sort order
196 break;
197
198 case 0x1B: // Powered wagons power bonus
199 rvi->pow_wag_power = buf.ReadWord();
200 break;
201
202 case 0x1C: // Refit cost
203 ei->refit_cost = buf.ReadByte();
204 break;
205
206 case 0x1D: { // Refit cargo
207 uint32_t mask = buf.ReadDWord();
208 _gted[e->index].UpdateRefittability(mask != 0);
209 ei->refit_mask = TranslateRefitMask(mask);
210 _gted[e->index].defaultcargo_grf = _cur_gps.grffile;
211 break;
212 }
213
214 case 0x1E: { // Callback
215 auto mask = ei->callback_mask.base();
216 SB(mask, 0, 8, buf.ReadByte());
218 break;
219 }
220
221 case PROP_TRAIN_TRACTIVE_EFFORT: // 0x1F Tractive effort coefficient
222 rvi->tractive_effort = buf.ReadByte();
223 break;
224
225 case 0x20: // Air drag
226 rvi->air_drag = buf.ReadByte();
227 break;
228
229 case PROP_TRAIN_SHORTEN_FACTOR: // 0x21 Shorter vehicle
230 rvi->shorten_factor = buf.ReadByte();
231 break;
232
233 case 0x22: // Visual effect
234 rvi->visual_effect = buf.ReadByte();
235 /* Avoid accidentally setting visual_effect to the default value
236 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
237 if (rvi->visual_effect == VE_DEFAULT) {
240 }
241 break;
242
243 case 0x23: // Powered wagons weight bonus
244 rvi->pow_wag_weight = buf.ReadByte();
245 break;
246
247 case 0x24: { // High byte of vehicle weight
248 uint8_t weight = buf.ReadByte();
249
250 if (weight > 4) {
251 GrfMsg(2, "RailVehicleChangeInfo: Nonsensical weight of {} tons, ignoring", weight << 8);
252 } else {
253 SB(rvi->weight, 8, 8, weight);
254 }
255 break;
256 }
257
258 case PROP_TRAIN_USER_DATA: // 0x25 User-defined bit mask to set when checking veh. var. 42
259 rvi->user_def_data = buf.ReadByte();
260 break;
261
262 case 0x26: // Retire vehicle early
263 ei->retire_early = buf.ReadByte();
264 break;
265
266 case 0x27: // Miscellaneous flags
267 ei->misc_flags = static_cast<EngineMiscFlags>(buf.ReadByte());
269 break;
270
271 case 0x28: // Cargo classes allowed
272 _gted[e->index].cargo_allowed = CargoClasses{buf.ReadWord()};
273 _gted[e->index].UpdateRefittability(_gted[e->index].cargo_allowed.Any());
274 _gted[e->index].defaultcargo_grf = _cur_gps.grffile;
275 break;
276
277 case 0x29: // Cargo classes disallowed
278 _gted[e->index].cargo_disallowed = CargoClasses{buf.ReadWord()};
279 _gted[e->index].UpdateRefittability(false);
280 break;
281
282 case 0x2A: // Long format introduction date (days since year 0)
284 break;
285
286 case PROP_TRAIN_CARGO_AGE_PERIOD: // 0x2B Cargo aging period
287 ei->cargo_age_period = buf.ReadWord();
288 break;
289
290 case 0x2C: // CTT refit include list
291 case 0x2D: { // CTT refit exclude list
292 uint8_t count = buf.ReadByte();
293 _gted[e->index].UpdateRefittability(prop == 0x2C && count != 0);
294 if (prop == 0x2C) _gted[e->index].defaultcargo_grf = _cur_gps.grffile;
295 CargoTypes &ctt = prop == 0x2C ? _gted[e->index].ctt_include_mask : _gted[e->index].ctt_exclude_mask;
296 ctt = 0;
297 while (count--) {
298 CargoType ctype = GetCargoTranslation(buf.ReadByte(), _cur_gps.grffile);
299 if (IsValidCargoType(ctype)) SetBit(ctt, ctype);
300 }
301 break;
302 }
303
304 case PROP_TRAIN_CURVE_SPEED_MOD: // 0x2E Curve speed modifier
305 rvi->curve_speed_mod = buf.ReadWord();
306 break;
307
308 case 0x2F: // Engine variant
309 ei->variant_id = static_cast<EngineID>(buf.ReadWord());
310 break;
311
312 case 0x30: // Extra miscellaneous flags
313 ei->extra_flags = static_cast<ExtraEngineFlags>(buf.ReadDWord());
314 break;
315
316 case 0x31: { // Callback additional mask
317 auto mask = ei->callback_mask.base();
318 SB(mask, 8, 8, buf.ReadByte());
320 break;
321 }
322
323 case 0x32: // Cargo classes required for a refit.
324 _gted[e->index].cargo_allowed_required = CargoClasses{buf.ReadWord()};
325 break;
326
327 case 0x33: // Badge list
328 e->badges = ReadBadgeList(buf, GSF_TRAINS);
329 break;
330
331 case 0x34: { // List of track types
332 uint8_t count = buf.ReadByte();
333
334 _gted[e->index].railtypelabels.clear();
335 while (count--) {
336 uint8_t tracktype = buf.ReadByte();
337
338 if (tracktype < _cur_gps.grffile->railtype_list.size()) {
339 _gted[e->index].railtypelabels.push_back(_cur_gps.grffile->railtype_list[tracktype]);
340 } else {
341 GrfMsg(1, "RailVehicleChangeInfo: Invalid track type {} specified, ignoring", tracktype);
342 }
343 }
344 break;
345 }
346
347 default:
348 ret = CommonVehicleChangeInfo(ei, prop, buf);
349 break;
350 }
351 }
352
353 return ret;
354}
355
357template <> ChangeInfoResult GrfChangeInfoHandler<GSF_TRAINS>::Activation(uint first, uint last, int prop, ByteReader &buf) { return RailVehicleChangeInfo(first, last, prop, buf); }
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
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.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
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
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Tstorage base() const noexcept
Retrieve the raw value behind this bit set.
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).
uint16_t ReadExtendedByte()
Read a single Extended Byte (8 or 16 bits).
uint8_t ReadByte()
Read a single byte (8 bits).
@ Uses2CC
Vehicle uses two company colours.
@ VE_TYPE_COUNT
Number of bits used for the effect type.
Definition engine_type.h:60
@ VE_TYPE_START
First bit used for the type of effect.
Definition engine_type.h:59
@ VE_DISABLE_EFFECT
Flag to disable visual effect.
Definition engine_type.h:66
@ VE_DEFAULT
Default value to indicate that visual effect should be based on engine class.
Definition engine_type.h:70
EngineClass
Type of rail engine.
Definition engine_type.h:38
@ EC_DIESEL
Diesel rail engine.
Definition engine_type.h:40
@ EC_STEAM
Steam rail engine.
Definition engine_type.h:39
@ EC_MAGLEV
Maglev engine.
Definition engine_type.h:43
@ EC_ELECTRIC
Electric rail engine.
Definition engine_type.h:41
@ EC_MONORAIL
Mono rail engine.
Definition engine_type.h:42
@ RAILVEH_SINGLEHEAD
indicates a "standalone" locomotive
Definition engine_type.h:32
@ RAILVEH_WAGON
simple wagon, not motorized
Definition engine_type.h:34
@ RAILVEH_MULTIHEAD
indicates a combination of two locomotives
Definition engine_type.h:33
void ConvertTTDBasePrice(uint32_t base_pointer, std::string_view error_location, Price *index)
Converts TTD(P) Base Price pointers into the enum used by OTTD See http://wiki.ttdpatch....
Definition newgrf.cpp:332
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:214
TypedIndexContainer< std::vector< GRFTempEngineData >, EngineID > _gted
Temporary engine data used during NewGRF loading.
Definition newgrf.cpp:78
GRFLoadedFeatures _loaded_newgrf_features
Indicates which are the newgrf features currently loaded ingame.
Definition newgrf.cpp:74
CargoTypes TranslateRefitMask(uint32_t refit_mask)
Translate the refit mask.
Definition newgrf.cpp:315
ChangeInfoResult CommonVehicleChangeInfo(EngineInfo *ei, int prop, ByteReader &buf)
Define properties common to all vehicles.
std::vector< BadgeID > ReadBadgeList(ByteReader &buf, GrfSpecFeature feature)
Read a list of badges.
ChangeInfoResult RailVehicleChangeInfo(uint first, uint last, int prop, ByteReader &buf)
Define properties for rail vehicles.
NewGRF buffer reader definition.
CargoType GetCargoTranslation(uint8_t cargo, const GRFFile *grffile, bool usebit)
Translate a GRF-local cargo slot/bitnum into a CargoType.
void AlterVehicleListOrder(EngineID engine, uint16_t target)
Record a vehicle ListOrderChange.
NewGRF internal processing state.
ChangeInfoResult
Possible return values for the GrfChangeInfoHandler functions.
@ CIR_INVALID_ID
Attempt to modify an invalid ID.
@ CIR_UNHANDLED
Variable was parsed but unread.
@ CIR_SUCCESS
Variable was parsed and read.
NewGRF internal processing state for vehicles.
@ PROP_TRAIN_SHORTEN_FACTOR
Shorter vehicles.
@ PROP_TRAIN_CURVE_SPEED_MOD
Modifier to maximum speed in curves.
@ PROP_TRAIN_COST_FACTOR
Purchase cost (if dualheaded: sum of both vehicles)
@ PROP_TRAIN_USER_DATA
User defined data for vehicle variable 0x42.
@ PROP_TRAIN_WEIGHT
Weight in t (if dualheaded: for each single vehicle)
@ PROP_TRAIN_CARGO_CAPACITY
Capacity (if dualheaded: for each single vehicle)
@ PROP_TRAIN_TRACTIVE_EFFORT
Tractive effort coefficient in 1/256.
@ PROP_TRAIN_CARGO_AGE_PERIOD
Number of ticks before carried cargo is aged.
@ PROP_TRAIN_RUNNING_COST_FACTOR
Yearly runningcost (if dualheaded: sum of both vehicles)
@ PROP_TRAIN_POWER
Power in hp (if dualheaded: sum of both vehicles)
@ PROP_TRAIN_SPEED
Max. speed: 1 unit = 1/1.6 mph = 1 km-ish/h.
Information about a vehicle.
uint16_t cargo_age_period
Number of ticks before carried cargo is aged.
EngineMiscFlags misc_flags
Miscellaneous flags.
EngineID variant_id
Engine variant ID. If set, will be treated specially in purchase lists.
VehicleCallbackMasks callback_mask
Bitmask of vehicle callbacks that have to be called.
TimerGameCalendar::Date base_intro
Basic date of engine introduction (without random parts).
int8_t retire_early
Number of years early to retire vehicle.
std::vector< RailTypeLabel > railtype_list
Railtype translation table.
Definition newgrf.h:142
bool has_2CC
Set if any vehicle is loaded which uses 2cc (two company colours).
Definition newgrf.h:189
GRF feature handler.
GRFFile * grffile
Currently processed GRF file.
Tindex index
Index of this pool item.
Information about a rail vehicle.
Definition engine_type.h:74
uint16_t power
Power of engine (hp); For multiheaded engines the sum of both engine powers.
Definition engine_type.h:82
uint8_t user_def_data
Property 0x25: "User-defined bit mask" Used only for (very few) NewGRF vehicles.
Definition engine_type.h:94
uint8_t running_cost
Running cost of engine; For multiheaded engines the sum of both running costs.
Definition engine_type.h:84
uint8_t cost_factor
Purchase cost factor; For multiheaded engines the sum of both engine prices.
Definition engine_type.h:77
uint8_t shorten_factor
length on main map for this type is 8 - shorten_factor
Definition engine_type.h:91
uint16_t pow_wag_power
Extra power applied to consist if wagon should be powered.
Definition engine_type.h:88
uint16_t max_speed
Maximum speed (1 unit = 1/1.6 mph = 1 km-ish/h)
Definition engine_type.h:81
int16_t curve_speed_mod
Modifier to maximum speed in curves (fixed-point binary with 8 fractional bits)
Definition engine_type.h:95
uint16_t weight
Weight of vehicle (tons); For multiheaded engines the weight of each single engine.
Definition engine_type.h:83
uint8_t capacity
Cargo capacity of vehicle; For multiheaded engines the capacity of each single engine.
Definition engine_type.h:87
uint8_t visual_effect
Bitstuffed NewGRF visual effect data.
Definition engine_type.h:90
uint8_t air_drag
Coefficient of air drag.
Definition engine_type.h:93
EngineClass engclass
Class of engine for this vehicle.
Definition engine_type.h:86
uint8_t ai_passenger_only
Bit value to tell AI that this engine is for passenger use only.
Definition engine_type.h:80
uint8_t tractive_effort
Tractive effort coefficient.
Definition engine_type.h:92
uint8_t pow_wag_weight
Extra weight applied to consist if wagon should be powered.
Definition engine_type.h:89
Templated helper to make a type-safe 'typedef' representing a single POD value.
@ CUSTOM_VEHICLE_SPRITENUM
Vehicle sprite from NewGRF.
@ VEH_TRAIN
Train vehicle type.