OpenTTD Source 20260531-master-g0e951f3528
train_cmd.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 "error.h"
13#include "command_func.h"
14#include "error_func.h"
16#include "news_func.h"
17#include "company_func.h"
18#include "newgrf_sound.h"
19#include "newgrf_text.h"
20#include "strings_func.h"
21#include "viewport_func.h"
22#include "vehicle_func.h"
23#include "sound_func.h"
24#include "ai/ai.hpp"
25#include "game/game.hpp"
26#include "newgrf_station.h"
27#include "effectvehicle_func.h"
28#include "network/network.h"
29#include "core/random_func.hpp"
30#include "company_base.h"
31#include "newgrf.h"
32#include "order_backup.h"
33#include "zoom_func.h"
34#include "newgrf_debug.h"
35#include "framerate_type.h"
36#include "train_cmd.h"
37#include "misc_cmd.h"
40
41#include "table/strings.h"
42#include "table/train_sprites.h"
43
44#include "safeguards.h"
45
46static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck);
47static bool TrainCheckIfLineEnds(Train *v, bool reverse = true);
48bool TrainController(Train *v, Vehicle *nomove, bool reverse = true); // Also used in vehicle_sl.cpp.
50static void CheckIfTrainNeedsService(Train *v);
51static void CheckNextTrainTile(Train *v);
52
57
59template <>
61{
62 return image_index < lengthof(_engine_sprite_base);
63}
64
65
72{
73 if (!CargoSpec::Get(cargo)->is_freight) return 1;
74 return _settings_game.vehicle.freight_trains;
75}
76
79{
80 bool first = true;
81
82 for (const Train *v : Train::Iterate()) {
83 if (v->First() == v && !v->vehstatus.Test(VehState::Crashed)) {
84 for (const Train *u = v->GetMovingFront(), *w = v->GetMovingNext(); w != nullptr; u = w, w = w->GetMovingNext()) {
85 if (u->track != TRACK_BIT_DEPOT) {
86 if ((w->track != TRACK_BIT_DEPOT &&
87 std::max(abs(u->x_pos - w->x_pos), abs(u->y_pos - w->y_pos)) != u->CalcNextVehicleOffset()) ||
88 (w->track == TRACK_BIT_DEPOT && TicksToLeaveDepot(u) <= 0)) {
89 ShowErrorMessage(GetEncodedString(STR_BROKEN_VEHICLE_LENGTH, v->index, v->owner), {}, WarningLevel::Critical);
90
91 if (!_networking && first) {
92 first = false;
93 Command<Commands::Pause>::Post(PauseMode::Error, true);
94 }
95 /* Break so we warn only once for each train. */
96 break;
97 }
98 }
99 }
100 }
101 }
102}
103
111{
112 uint16_t max_speed = UINT16_MAX;
113
114 assert(this->IsFrontEngine() || this->IsFreeWagon());
115
116 const RailVehicleInfo *rvi_v = RailVehInfo(this->engine_type);
117 EngineID first_engine = this->IsFrontEngine() ? this->engine_type : EngineID::Invalid();
118 this->gcache.cached_total_length = 0;
119 this->compatible_railtypes = {};
120
121 bool train_can_tilt = true;
122 int16_t min_curve_speed_mod = INT16_MAX;
123
124 for (Train *u = this; u != nullptr; u = u->Next()) {
125 const RailVehicleInfo *rvi_u = RailVehInfo(u->engine_type);
126
127 /* Check the this->first cache. */
128 assert(u->First() == this);
129
130 /* update the 'first engine' */
131 u->gcache.first_engine = this == u ? EngineID::Invalid() : first_engine;
132 u->railtypes = rvi_u->railtypes;
133
134 if (u->IsEngine()) first_engine = u->engine_type;
135
136 /* Set user defined data to its default value */
137 u->tcache.user_def_data = rvi_u->user_def_data;
138 this->InvalidateNewGRFCache();
139 u->InvalidateNewGRFCache();
140 }
141
142 for (Train *u = this; u != nullptr; u = u->Next()) {
143 /* Update user defined data (must be done before other properties) */
144 u->tcache.user_def_data = GetVehicleProperty(u, PROP_TRAIN_USER_DATA, u->tcache.user_def_data);
145 this->InvalidateNewGRFCache();
146 u->InvalidateNewGRFCache();
147 }
148
149 for (Train *u = this; u != nullptr; u = u->Next()) {
150 const Engine *e_u = u->GetEngine();
151 const RailVehicleInfo *rvi_u = &e_u->VehInfo<RailVehicleInfo>();
152
153 if (!e_u->info.misc_flags.Test(EngineMiscFlag::RailTilts)) train_can_tilt = false;
154 min_curve_speed_mod = std::min(min_curve_speed_mod, u->GetCurveSpeedModifier());
155
156 /* Cache wagon override sprite group. nullptr is returned if there is none */
157 u->tcache.cached_override = GetWagonOverrideSpriteSet(u->engine_type, u->cargo_type, u->gcache.first_engine);
158
159 /* Reset colour map */
160 u->colourmap = PAL_NONE;
161
162 /* Update powered-wagon-status and visual effect */
163 u->UpdateVisualEffect(true);
164
165 if (rvi_v->pow_wag_power != 0 && rvi_u->railveh_type == RailVehicleType::Wagon &&
166 UsesWagonOverride(u) && !HasBit(u->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
167 /* wagon is powered */
168 u->flags.Set(VehicleRailFlag::PoweredWagon); // cache 'powered' status
169 } else {
170 u->flags.Reset(VehicleRailFlag::PoweredWagon);
171 }
172
173 if (!u->IsArticulatedPart()) {
174 /* Do not count powered wagons for the compatible railtypes, as wagons always
175 have railtype normal */
176 if (rvi_u->power > 0) {
177 this->compatible_railtypes.Set(GetAllPoweredRailTypes(u->railtypes));
178 }
179
180 /* Some electric engines can be allowed to run on normal rail. It happens to all
181 * existing electric engines when elrails are disabled and then re-enabled */
182 if (u->flags.Test(VehicleRailFlag::AllowedOnNormalRail)) {
183 u->railtypes.Set(RAILTYPE_RAIL);
184 u->compatible_railtypes.Set(RAILTYPE_RAIL);
185 }
186
187 /* max speed is the minimum of the speed limits of all vehicles in the consist */
188 if ((rvi_u->railveh_type != RailVehicleType::Wagon || _settings_game.vehicle.wagon_speed_limits) && !UsesWagonOverride(u)) {
189 uint16_t speed = GetVehicleProperty(u, PROP_TRAIN_SPEED, rvi_u->max_speed);
190 if (speed != 0) max_speed = std::min(speed, max_speed);
191 }
192 }
193
194 uint16_t new_cap = e_u->DetermineCapacity(u);
195 if (allowed_changes.Test(ConsistChangeFlag::Capacity)) {
196 /* Update vehicle capacity. */
197 if (u->cargo_cap > new_cap) u->cargo.Truncate(new_cap);
198 u->refit_cap = std::min(new_cap, u->refit_cap);
199 u->cargo_cap = new_cap;
200 } else {
201 /* Verify capacity hasn't changed. */
202 if (new_cap != u->cargo_cap) ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_CAPACITY, GRFBug::VehCapacity, true);
203 }
204 u->vcache.cached_cargo_age_period = GetVehicleProperty(u, PROP_TRAIN_CARGO_AGE_PERIOD, e_u->info.cargo_age_period);
205
206 /* check the vehicle length (callback) */
207 uint16_t veh_len = CALLBACK_FAILED;
208 if (e_u->GetGRF() != nullptr && e_u->GetGRF()->grf_version >= 8) {
209 /* Use callback 36 */
210 veh_len = GetVehicleProperty(u, PROP_TRAIN_SHORTEN_FACTOR, CALLBACK_FAILED);
211
212 if (veh_len != CALLBACK_FAILED && veh_len >= VEHICLE_LENGTH) {
214 }
215 } else if (e_u->info.callback_mask.Test(VehicleCallbackMask::Length)) {
216 /* Use callback 11 */
217 veh_len = GetVehicleCallback(CBID_VEHICLE_LENGTH, 0, 0, u->engine_type, u);
218 }
219 if (veh_len == CALLBACK_FAILED) veh_len = rvi_u->shorten_factor;
220 veh_len = VEHICLE_LENGTH - Clamp(veh_len, 0, VEHICLE_LENGTH - 1);
221
222 if (allowed_changes.Test(ConsistChangeFlag::Length)) {
223 /* Update vehicle length. */
224 u->gcache.cached_veh_length = veh_len;
225 } else {
226 /* Verify length hasn't changed. */
227 if (veh_len != u->gcache.cached_veh_length) VehicleLengthChanged(u);
228 }
229
230 this->gcache.cached_total_length += u->gcache.cached_veh_length;
231 this->InvalidateNewGRFCache();
232 u->InvalidateNewGRFCache();
233 }
234
235 /* store consist weight/max speed in cache */
236 this->vcache.cached_max_speed = max_speed;
237 this->tcache.cached_tilt = train_can_tilt;
238 this->tcache.cached_curve_speed_mod = min_curve_speed_mod;
239 this->tcache.cached_max_curve_speed = this->GetCurveSpeedLimit();
240
241 /* recalculate cached weights and power too (we do this *after* the rest, so it is known which wagons are powered and need extra weight added) */
242 this->CargoChanged();
243
244 if (this->IsFrontEngine()) {
245 this->UpdateAcceleration();
246 SetWindowDirty(WindowClass::VehicleDetails, this->index);
247 InvalidateWindowData(WindowClass::VehicleRefit, this->index, VIWD_CONSIST_CHANGED);
248 InvalidateWindowData(WindowClass::VehicleOrders, this->index, VIWD_CONSIST_CHANGED);
250
251 /* If the consist is changed while in a depot, the vehicle view window must be invalidated to update the availability of refitting. */
252 InvalidateWindowData(WindowClass::VehicleView, this->index, VIWD_CONSIST_CHANGED);
253 }
254}
255
266int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *moving_front, int *station_ahead, int *station_length)
267{
268 const Train *consist = moving_front->First();
269 const Station *st = Station::Get(station_id);
270 *station_ahead = st->GetPlatformLength(tile, DirToDiagDir(moving_front->GetMovingDirection())) * TILE_SIZE;
271 *station_length = st->GetPlatformLength(tile) * TILE_SIZE;
272
273 /* Default to the middle of the station for stations stops that are not in
274 * the order list like intermediate stations when non-stop is disabled */
276 if (consist->gcache.cached_total_length >= *station_length) {
277 /* The train is longer than the station, make it stop at the far end of the platform */
279 } else if (consist->current_order.IsType(OT_GOTO_STATION) && consist->current_order.GetDestination() == station_id) {
280 osl = consist->current_order.GetStopLocation();
281 }
282
283 /* The stop location of the FRONT! of the train */
284 int stop;
285 switch (osl) {
286 default: NOT_REACHED();
287
289 stop = consist->gcache.cached_total_length;
290 break;
291
293 stop = *station_length - (*station_length - consist->gcache.cached_total_length) / 2;
294 break;
295
297 stop = *station_length;
298 break;
299 }
300
301 /* Subtract half the front vehicle length of the train so we get the real
302 * stop location of the train. */
303 uint8_t rounding = consist->IsDrivingBackwards() ? 2 : 1;
304 return stop - (consist->gcache.cached_veh_length + rounding) / 2;
305}
306
307
313{
314 assert(this->First() == this);
315
316 static const int absolute_max_speed = UINT16_MAX;
317 int max_speed = absolute_max_speed;
318
319 if (_settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) return max_speed;
320
321 int curvecount[2] = {0, 0};
322
323 /* first find the curve speed limit */
324 int numcurve = 0;
325 int sum = 0;
326 int pos = 0;
327 int lastpos = -1;
328 for (const Train *u = this; u->Next() != nullptr; u = u->Next(), pos += u->gcache.cached_veh_length) {
329 Direction this_dir = u->direction;
330 Direction next_dir = u->Next()->direction;
331
332 DirDiff dirdiff = DirDifference(this_dir, next_dir);
333 if (dirdiff == DirDiff::Same) continue;
334
335 if (dirdiff == DirDiff::Left45) curvecount[0]++;
336 if (dirdiff == DirDiff::Right45) curvecount[1]++;
337 if (dirdiff == DirDiff::Left45 || dirdiff == DirDiff::Right45) {
338 if (lastpos != -1) {
339 numcurve++;
340 sum += pos - lastpos;
341 if (pos - lastpos <= static_cast<int>(VEHICLE_LENGTH) && max_speed > 88) {
342 max_speed = 88;
343 }
344 }
345 lastpos = pos;
346 }
347
348 /* if we have a 90 degree turn, fix the speed limit to 60 */
349 if (dirdiff == DirDiff::Left90 || dirdiff == DirDiff::Right90) {
350 max_speed = 61;
351 }
352 }
353
354 if (numcurve > 0 && max_speed > 88) {
355 if (curvecount[0] == 1 && curvecount[1] == 1) {
356 max_speed = absolute_max_speed;
357 } else {
358 sum = CeilDiv(sum, VEHICLE_LENGTH);
359 sum /= numcurve;
360 max_speed = 232 - (13 - Clamp(sum, 1, 12)) * (13 - Clamp(sum, 1, 12));
361 }
362 }
363
364 if (max_speed != absolute_max_speed) {
365 /* Apply the current railtype's curve speed advantage */
366 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(this->tile));
367 max_speed += (max_speed / 2) * rti->curve_speed;
368
369 if (this->tcache.cached_tilt) {
370 /* Apply max_speed bonus of 20% for a tilting train */
371 max_speed += max_speed / 5;
372 }
373
374 /* Apply max_speed modifier (cached value is fixed-point binary with 8 fractional bits)
375 * and clamp the result to an acceptable range. */
376 max_speed += (max_speed * this->tcache.cached_curve_speed_mod) / 256;
377 max_speed = Clamp(max_speed, 2, absolute_max_speed);
378 }
379
380 return static_cast<uint16_t>(max_speed);
381}
382
388{
389 const Train *moving_front = this->GetMovingFront();
390 int max_speed = _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL ?
391 this->gcache.cached_max_track_speed :
392 this->tcache.cached_max_curve_speed;
393
394 if (_settings_game.vehicle.train_acceleration_model == AM_REALISTIC && IsRailStationTile(moving_front->tile)) {
395 StationID sid = GetStationIndex(moving_front->tile);
396 if (this->current_order.ShouldStopAtStation(this, sid)) {
397 int station_ahead;
398 int station_length;
399 int stop_at = GetTrainStopLocation(sid, moving_front->tile, moving_front, &station_ahead, &station_length);
400
401 /* The distance to go is whatever is still ahead of the train minus the
402 * distance from the train's stop location to the end of the platform */
403 int distance_to_go = station_ahead / TILE_SIZE - (station_length - stop_at) / TILE_SIZE;
404
405 if (distance_to_go > 0) {
406 int st_max_speed = 120;
407
408 int delta_v = this->cur_speed / (distance_to_go + 1);
409 if (max_speed > (this->cur_speed - delta_v)) {
410 st_max_speed = this->cur_speed - (delta_v / 10);
411 }
412
413 st_max_speed = std::max(st_max_speed, 25 * distance_to_go);
414 max_speed = std::min(max_speed, st_max_speed);
415 }
416 }
417 }
418
419 for (const Train *u = this; u != nullptr; u = u->Next()) {
420 if (_settings_game.vehicle.train_acceleration_model == AM_REALISTIC && u->track == TRACK_BIT_DEPOT) {
421 constexpr int DEPOT_SPEED_LIMIT = 61;
422 max_speed = std::min(max_speed, DEPOT_SPEED_LIMIT);
423 break;
424 }
425
426 /* Vehicle is on the middle part of a bridge. */
427 if (u->track == TRACK_BIT_WORMHOLE && !u->vehstatus.Test(VehState::Hidden)) {
428 max_speed = std::min<int>(max_speed, GetBridgeSpec(GetBridgeType(u->tile))->speed);
429 }
430 }
431
432 max_speed = std::min<int>(max_speed, this->current_order.GetMaxSpeed());
433
434 /* If the train is going backwards, without a leading cab, restrict its speed. */
435 if (!moving_front->CanLeadTrain()) {
436 constexpr int BACKWARDS_NO_CAB_SPEED_LIMIT = 32;
437 max_speed = std::min<int>(max_speed, BACKWARDS_NO_CAB_SPEED_LIMIT);
438 }
439
440 return std::min<int>(max_speed, this->gcache.cached_max_track_speed);
441}
442
445{
446 assert(this->IsFrontEngine() || this->IsFreeWagon());
447
448 uint power = this->gcache.cached_power;
449 uint weight = this->gcache.cached_weight;
450 assert(weight != 0);
451 this->acceleration = Clamp(power / weight * 4, 1, 255);
452}
453
459{
460 if (this->gcache.cached_veh_length != 8 && this->flags.Test(VehicleRailFlag::Flipped) && !EngInfo(this->engine_type)->misc_flags.Test(EngineMiscFlag::RailFlips)) {
461 int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
462
463 const Engine *e = this->GetEngine();
464 if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->VehInfo<RailVehicleInfo>().image_index)) {
465 reference_width = e->GetGRF()->traininfo_vehicle_width;
466 }
467
468 return ScaleSpriteTrad((this->gcache.cached_veh_length - (int)VEHICLE_LENGTH) * reference_width / (int)VEHICLE_LENGTH);
469 }
470 return 0;
471}
472
479{
480 int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
481 int vehicle_pitch = 0;
482
483 const Engine *e = this->GetEngine();
484 if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->VehInfo<RailVehicleInfo>().image_index)) {
485 reference_width = e->GetGRF()->traininfo_vehicle_width;
486 vehicle_pitch = e->GetGRF()->traininfo_vehicle_pitch;
487 }
488
489 if (offset != nullptr) {
490 if (this->flags.Test(VehicleRailFlag::Flipped) && !EngInfo(this->engine_type)->misc_flags.Test(EngineMiscFlag::RailFlips)) {
491 offset->x = ScaleSpriteTrad(((int)this->gcache.cached_veh_length - (int)VEHICLE_LENGTH / 2) * reference_width / (int)VEHICLE_LENGTH);
492 } else {
493 offset->x = ScaleSpriteTrad(reference_width) / 2;
494 }
495 offset->y = ScaleSpriteTrad(vehicle_pitch);
496 }
497 return ScaleSpriteTrad(this->gcache.cached_veh_length * reference_width / VEHICLE_LENGTH);
498}
499
500static SpriteID GetDefaultTrainSprite(uint8_t spritenum, Direction direction)
501{
502 assert(IsValidImageIndex<VehicleType::Train>(spritenum));
503 return ((to_underlying(direction) + _engine_sprite_add[spritenum]) & _engine_sprite_and[spritenum]) + _engine_sprite_base[spritenum];
504}
505
513{
514 uint8_t spritenum = this->spritenum;
515
517
518 if (IsCustomVehicleSpriteNum(spritenum)) {
520 GetCustomVehicleSprite(this, direction, image_type, result);
521 if (result->IsValid()) return;
522
524 }
525
527 SpriteID sprite = GetDefaultTrainSprite(spritenum, direction);
528
529 if (this->cargo.StoredCount() >= this->cargo_cap / 2U) sprite += _wagon_full_adder[spritenum];
530
531 result->Set(sprite);
532}
533
534static void GetRailIcon(EngineID engine, bool rear_head, int &y, EngineImageType image_type, VehicleSpriteSeq *result)
535{
536 const Engine *e = Engine::Get(engine);
537 Direction dir = rear_head ? Direction::E : Direction::W;
538 uint8_t spritenum = e->VehInfo<RailVehicleInfo>().image_index;
539
540 if (IsCustomVehicleSpriteNum(spritenum)) {
541 GetCustomVehicleIcon(engine, dir, image_type, result);
542 if (result->IsValid()) {
543 if (e->GetGRF() != nullptr) {
545 }
546 return;
547 }
548
549 spritenum = Engine::Get(engine)->original_image_index;
550 }
551
552 if (rear_head) spritenum++;
553
554 result->Set(GetDefaultTrainSprite(spritenum, Direction::W));
555}
556
557void DrawTrainEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
558{
559 const GRFFile *grf = Engine::Get(engine)->GetGRF();
560 int vehicle_width = ScaleSpriteTrad(grf == nullptr ? TRAININFO_DEFAULT_VEHICLE_WIDTH : grf->traininfo_vehicle_width);
561
562 if (RailVehInfo(engine)->railveh_type == RailVehicleType::Multihead) {
563 int yf = y;
564 int yr = y;
565
566 VehicleSpriteSeq seqf, seqr;
567 GetRailIcon(engine, false, yf, image_type, &seqf);
568 GetRailIcon(engine, true, yr, image_type, &seqr);
569
570 Rect rectf, rectr;
571 seqf.GetBounds(&rectf);
572 seqr.GetBounds(&rectr);
573
574 preferred_x = Clamp(preferred_x,
575 left - UnScaleGUI(rectf.left) + vehicle_width / 2,
576 right - UnScaleGUI(rectr.right) - (vehicle_width - vehicle_width / 2));
577
578 seqf.Draw(preferred_x - vehicle_width / 2, yf, pal, pal == PALETTE_CRASH);
579 seqr.Draw(preferred_x + (vehicle_width - vehicle_width / 2), yr, pal, pal == PALETTE_CRASH);
580 } else {
582 GetRailIcon(engine, false, y, image_type, &seq);
583
584 Rect rect;
585 seq.GetBounds(&rect);
586 preferred_x = Clamp(preferred_x,
587 left - UnScaleGUI(rect.left),
588 right - UnScaleGUI(rect.right));
589
590 seq.Draw(preferred_x, y, pal, pal == PALETTE_CRASH);
591 }
592}
593
603void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type)
604{
605 int y = 0;
606
608 GetRailIcon(engine, false, y, image_type, &seq);
609
610 Rect rect;
611 seq.GetBounds(&rect);
612
613 width = UnScaleGUI(rect.Width());
614 height = UnScaleGUI(rect.Height());
615 xoffs = UnScaleGUI(rect.left);
616 yoffs = UnScaleGUI(rect.top);
617
618 if (RailVehInfo(engine)->railveh_type == RailVehicleType::Multihead) {
619 const GRFFile *grf = Engine::Get(engine)->GetGRF();
620 int vehicle_width = ScaleSpriteTrad(grf == nullptr ? TRAININFO_DEFAULT_VEHICLE_WIDTH : grf->traininfo_vehicle_width);
621
622 GetRailIcon(engine, true, y, image_type, &seq);
623 seq.GetBounds(&rect);
624
625 /* Calculate values relative to an imaginary center between the two sprites. */
626 width = vehicle_width + UnScaleGUI(rect.right) - xoffs;
627 height = std::max<uint>(height, UnScaleGUI(rect.Height()));
628 xoffs = xoffs - vehicle_width / 2;
629 yoffs = std::min(yoffs, UnScaleGUI(rect.top));
630 }
631}
632
638static std::vector<VehicleID> GetFreeWagonsInDepot(TileIndex tile)
639{
640 std::vector<VehicleID> free_wagons;
641
642 for (Vehicle *v : VehiclesOnTile(tile)) {
643 if (v->type != VehicleType::Train) continue;
644 if (v->vehstatus.Test(VehState::Crashed)) continue;
645 if (!Train::From(v)->IsFreeWagon()) continue;
646
647 free_wagons.push_back(v->index);
648 }
649
650 /* Sort by vehicle index for consistency across clients. */
651 std::ranges::sort(free_wagons);
652 return free_wagons;
653}
654
663static CommandCost CmdBuildRailWagon(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
664{
665 const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
666
667 /* Check that the wagon can drive on the track in question */
668 if (!IsCompatibleRail(rvi->railtypes, GetRailType(tile))) return CMD_ERROR;
669
670 if (flags.Test(DoCommandFlag::Execute)) {
671 Train *v = Train::Create();
672 *ret = v;
673 v->spritenum = rvi->image_index;
674
675 v->engine_type = e->index;
676 v->gcache.first_engine = EngineID::Invalid(); // needs to be set before first callback
677
679
680 v->direction = DiagDirToDir(dir);
681 v->tile = tile;
682
683 int x = TileX(tile) * TILE_SIZE | _vehicle_initial_x_fract[dir];
684 int y = TileY(tile) * TILE_SIZE | _vehicle_initial_y_fract[dir];
685
686 v->x_pos = x;
687 v->y_pos = y;
688 v->z_pos = GetSlopePixelZ(x, y, true);
692
693 v->SetWagon();
694
695 v->SetFreeWagon();
696 InvalidateWindowData(WindowClass::VehicleDepot, v->tile);
697
699 assert(IsValidCargoType(v->cargo_type));
700 v->cargo_cap = rvi->capacity;
701 v->refit_cap = 0;
702
703 v->railtypes = rvi->railtypes;
704
708 v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
709 v->random_bits = Random();
710
712
714 if (prob.has_value()) v->flags.Set(VehicleRailFlag::Flipped, prob.value());
716
717 v->UpdatePosition();
720
722
723 /* Try to connect the vehicle to one of free chains of wagons. */
724 for (VehicleID vehicle : GetFreeWagonsInDepot(tile)) {
725 if (vehicle == v->index) continue;
726
727 const Train *w = Train::Get(vehicle);
728 if (w->engine_type != v->engine_type) continue;
729 if (w->First() == v) continue;
730
731 if (Command<Commands::MoveRailVehicle>::Do(DoCommandFlag::Execute, v->index, w->Last()->index, true).Succeeded()) {
732 break;
733 }
734 }
735 }
736
737 return CommandCost();
738}
739
745{
746 assert(u->IsEngine());
747 for (VehicleID vehicle : GetFreeWagonsInDepot(u->tile)) {
748 if (Command<Commands::MoveRailVehicle>::Do(DoCommandFlag::Execute, vehicle, u->index, true).Failed()) {
749 break;
750 }
751 }
752}
753
754static void AddRearEngineToMultiheadedTrain(Train *v)
755{
756 Train *u = Train::Create();
757 v->value >>= 1;
758 u->value = v->value;
759 u->direction = v->direction;
760 u->owner = v->owner;
761 u->tile = v->tile;
762 u->x_pos = v->x_pos;
763 u->y_pos = v->y_pos;
764 u->z_pos = v->z_pos;
766 u->vehstatus = v->vehstatus;
768 u->spritenum = v->spritenum + 1;
769 u->cargo_type = v->cargo_type;
771 u->cargo_cap = v->cargo_cap;
772 u->refit_cap = v->refit_cap;
773 u->railtypes = v->railtypes;
774 u->engine_type = v->engine_type;
777 u->build_year = v->build_year;
778 u->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
779 u->random_bits = Random();
780 v->SetMultiheaded();
781 u->SetMultiheaded();
782 v->SetNext(u);
784 if (prob.has_value()) u->flags.Set(VehicleRailFlag::Flipped, prob.value());
785 u->UpdatePosition();
786
787 /* Now we need to link the front and rear engines together */
790}
791
800CommandCost CmdBuildRailVehicle(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
801{
802 const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
803
804 if (rvi->railveh_type == RailVehicleType::Wagon) return CmdBuildRailWagon(flags, tile, e, ret);
805
806 /* Check if depot and new engine uses the same kind of tracks *
807 * We need to see if the engine got power on the tile to avoid electric engines in non-electric depots */
808 if (!HasPowerOnRail(rvi->railtypes, GetRailType(tile))) return CMD_ERROR;
809
810 if (flags.Test(DoCommandFlag::Execute)) {
812 int x = TileX(tile) * TILE_SIZE + _vehicle_initial_x_fract[dir];
813 int y = TileY(tile) * TILE_SIZE + _vehicle_initial_y_fract[dir];
814
815 Train *v = Train::Create();
816 *ret = v;
817 v->direction = DiagDirToDir(dir);
818 v->tile = tile;
820 v->x_pos = x;
821 v->y_pos = y;
822 v->z_pos = GetSlopePixelZ(x, y, true);
825 v->spritenum = rvi->image_index;
827 assert(IsValidCargoType(v->cargo_type));
828 v->cargo_cap = rvi->capacity;
829 v->refit_cap = 0;
830 v->last_station_visited = StationID::Invalid();
831 v->last_loading_station = StationID::Invalid();
832
833 v->engine_type = e->index;
834 v->gcache.first_engine = EngineID::Invalid(); // needs to be set before first callback
835
836 v->reliability = e->reliability;
839
840 v->railtypes = rvi->railtypes;
841
842 v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_trains);
846 v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
847 v->random_bits = Random();
848
850 v->SetServiceIntervalIsPercent(Company::Get(_current_company)->settings.vehicle.servint_ispercent);
851
853
854 v->SetFrontEngine();
855 v->SetEngine();
856
858 if (prob.has_value()) v->flags.Set(VehicleRailFlag::Flipped, prob.value());
859 v->UpdatePosition();
860
862 AddRearEngineToMultiheadedTrain(v);
863 } else {
865 }
866
869
871 }
872
873 return CommandCost();
874}
875
876static Train *FindGoodVehiclePos(const Train *src)
877{
878 EngineID eng = src->engine_type;
879
880 for (VehicleID vehicle : GetFreeWagonsInDepot(src->tile)) {
881 Train *dst = Train::Get(vehicle);
882
883 /* check so all vehicles in the line have the same engine. */
884 Train *t = dst;
885 while (t->engine_type == eng) {
886 t = t->Next();
887 if (t == nullptr) return dst;
888 }
889 }
890
891 return nullptr;
892}
893
895typedef std::vector<Train *> TrainList;
896
902static void MakeTrainBackup(TrainList &list, Train *t)
903{
904 for (; t != nullptr; t = t->Next()) list.push_back(t);
905}
906
912{
913 /* No train, nothing to do. */
914 if (list.empty()) return;
915
916 Train *prev = nullptr;
917 /* Iterate over the list and rebuild it. */
918 for (Train *t : list) {
919 if (prev != nullptr) {
920 prev->SetNext(t);
921 } else if (t->Previous() != nullptr) {
922 /* Make sure the head of the train is always the first in the chain. */
923 t->Previous()->SetNext(nullptr);
924 }
925 prev = t;
926 }
927}
928
934static void RemoveFromConsist(Train *part, bool chain = false)
935{
936 Train *tail;
937
938 if (chain) {
939 /* We're moving several vehicles, find the last one in the chain. */
940 tail = part;
941 while (tail->Next() != nullptr) tail = tail->Next();
942 } else {
943 /* We're just moving one vehicle, but make sure we get all the articulated parts. */
944 tail = part->GetLastEnginePart();
945 }
946
947 /* Unlink at the front, but make it point to the next
948 * vehicle after the to be remove part. */
949 if (part->Previous() != nullptr) part->Previous()->SetNext(tail->Next());
950
951 /* Unlink at the back */
952 tail->SetNext(nullptr);
953}
954
960static void InsertInConsist(Train *dst, Train *chain)
961{
962 /* We do not want to add something in the middle of an articulated part. */
963 assert(dst != nullptr && (dst->Next() == nullptr || !dst->Next()->IsArticulatedPart()));
964
965 chain->Last()->SetNext(dst->Next());
966 dst->SetNext(chain);
967}
968
975{
976 for (; t != nullptr; t = t->GetNextVehicle()) {
977 if (!t->IsMultiheaded() || !t->IsEngine()) continue;
978
979 /* Make sure that there are no free cars before next engine */
980 Train *u;
981 for (u = t; u->Next() != nullptr && !u->Next()->IsEngine(); u = u->Next()) {}
982
983 if (u == t->other_multiheaded_part) continue;
984
985 /* Remove the part from the 'wrong' train */
987 /* And add it to the 'right' train */
989 }
990}
991
996static void NormaliseSubtypes(Train *chain)
997{
998 /* Nothing to do */
999 if (chain == nullptr) return;
1000
1001 /* We must be the first in the chain. */
1002 assert(chain->Previous() == nullptr);
1003
1004 /* Set the appropriate bits for the first in the chain. */
1005 if (chain->IsWagon()) {
1006 chain->SetFreeWagon();
1007 } else {
1008 assert(chain->IsEngine());
1009 chain->SetFrontEngine();
1010 }
1011
1012 /* Now clear the bits for the rest of the chain */
1013 for (Train *t = chain->Next(); t != nullptr; t = t->Next()) {
1014 t->ClearFreeWagon();
1015 t->ClearFrontEngine();
1016 }
1017}
1018
1028static CommandCost CheckNewTrain(Train *original_dst, Train *dst, Train *original_src, Train *src)
1029{
1030 /* Just add 'new' engines and subtract the original ones.
1031 * If that's less than or equal to 0 we can be sure we did
1032 * not add any engines (read: trains) along the way. */
1033 if ((src != nullptr && src->IsEngine() ? 1 : 0) +
1034 (dst != nullptr && dst->IsEngine() ? 1 : 0) -
1035 (original_src != nullptr && original_src->IsEngine() ? 1 : 0) -
1036 (original_dst != nullptr && original_dst->IsEngine() ? 1 : 0) <= 0) {
1037 return CommandCost();
1038 }
1039
1040 /* Get a free unit number and check whether it's within the bounds.
1041 * There will always be a maximum of one new train. */
1042 if (GetFreeUnitNumber(VehicleType::Train) <= _settings_game.vehicle.max_trains) return CommandCost();
1043
1044 return CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);
1045}
1046
1053{
1054 /* No multi-part train, no need to check. */
1055 if (t == nullptr || t->Next() == nullptr) return CommandCost();
1056
1057 /* The maximum length for a train. For each part we decrease this by one
1058 * and if the result is negative the train is simply too long. */
1059 int allowed_len = _settings_game.vehicle.max_train_length * TILE_SIZE - t->gcache.cached_veh_length;
1060
1061 /* For free-wagon chains, check if they are within the max_train_length limit. */
1062 if (!t->IsEngine()) {
1063 t = t->Next();
1064 while (t != nullptr) {
1065 allowed_len -= t->gcache.cached_veh_length;
1066
1067 t = t->Next();
1068 }
1069
1070 if (allowed_len < 0) return CommandCost(STR_ERROR_TRAIN_TOO_LONG);
1071 return CommandCost();
1072 }
1073
1074 Train *head = t;
1075 Train *prev = t;
1076
1077 /* Break the prev -> t link so it always holds within the loop. */
1078 t = t->Next();
1079 prev->SetNext(nullptr);
1080
1081 /* Make sure the cache is cleared. */
1082 head->InvalidateNewGRFCache();
1083
1084 while (t != nullptr) {
1085 allowed_len -= t->gcache.cached_veh_length;
1086
1087 Train *next = t->Next();
1088
1089 /* Unlink the to-be-added piece; it is already unlinked from the previous
1090 * part due to the fact that the prev -> t link is broken. */
1091 t->SetNext(nullptr);
1092
1093 /* Don't check callback for articulated or rear dual headed parts */
1094 if (!t->IsArticulatedPart() && !t->IsRearDualheaded()) {
1095 /* Back up and clear the first_engine data to avoid using wagon override group */
1096 EngineID first_engine = t->gcache.first_engine;
1097 t->gcache.first_engine = EngineID::Invalid();
1098
1099 /* We don't want the cache to interfere. head's cache is cleared before
1100 * the loop and after each callback does not need to be cleared here. */
1102
1103 std::array<int32_t, 1> regs100;
1104 uint16_t callback = GetVehicleCallbackParent(CBID_TRAIN_ALLOW_WAGON_ATTACH, 0, 0, head->engine_type, t, head, regs100);
1105
1106 /* Restore original first_engine data */
1107 t->gcache.first_engine = first_engine;
1108
1109 /* We do not want to remember any cached variables from the test run */
1111 head->InvalidateNewGRFCache();
1112
1113 if (callback != CALLBACK_FAILED) {
1114 /* A failing callback means everything is okay */
1115 StringID error = STR_NULL;
1116
1117 if (head->GetGRF()->grf_version < 8) {
1118 if (callback == 0xFD) error = STR_ERROR_INCOMPATIBLE_RAIL_TYPES;
1119 if (callback < 0xFD) error = GetGRFStringID(head->GetGRFID(), GRFSTR_MISC_GRF_TEXT + callback);
1120 if (callback >= 0x100) ErrorUnknownCallbackResult(head->GetGRFID(), CBID_TRAIN_ALLOW_WAGON_ATTACH, callback);
1121 } else {
1122 if (callback < 0x400) {
1123 error = GetGRFStringID(head->GetGRFID(), GRFSTR_MISC_GRF_TEXT + callback);
1124 } else {
1125 switch (callback) {
1126 case 0x400: // allow if railtypes match (always the case for OpenTTD)
1127 case 0x401: // allow
1128 break;
1129
1130 case 0x40F:
1131 error = GetGRFStringID(head->GetGRFID(), static_cast<GRFStringID>(regs100[0]));
1132 break;
1133
1134 default: // unknown reason -> disallow
1135 case 0x402: // disallow attaching
1136 error = STR_ERROR_INCOMPATIBLE_RAIL_TYPES;
1137 break;
1138 }
1139 }
1140 }
1141
1142 if (error != STR_NULL) return CommandCost(error);
1143 }
1144 }
1145
1146 /* And link it to the new part. */
1147 prev->SetNext(t);
1148 prev = t;
1149 t = next;
1150 }
1151
1152 if (allowed_len < 0) return CommandCost(STR_ERROR_TRAIN_TOO_LONG);
1153 return CommandCost();
1154}
1155
1166static CommandCost ValidateTrains(Train *original_dst, Train *dst, Train *original_src, Train *src, bool check_limit)
1167{
1168 /* Check whether we may actually construct the trains. */
1170 if (ret.Failed()) return ret;
1171 ret = CheckTrainAttachment(dst);
1172 if (ret.Failed()) return ret;
1173
1174 /* Check whether we need to build a new train. */
1175 return check_limit ? CheckNewTrain(original_dst, dst, original_src, src) : CommandCost();
1176}
1177
1186static void ArrangeTrains(Train **dst_head, Train *dst, Train **src_head, Train *src, bool move_chain)
1187{
1188 /* First determine the front of the two resulting trains */
1189 if (*src_head == *dst_head) {
1190 /* If we aren't moving part(s) to a new train, we are just moving the
1191 * front back and there is not destination head. */
1192 *dst_head = nullptr;
1193 } else if (*dst_head == nullptr) {
1194 /* If we are moving to a new train the head of the move train would become
1195 * the head of the new vehicle. */
1196 *dst_head = src;
1197 }
1198
1199 if (src == *src_head) {
1200 /* If we are moving the front of a train then we are, in effect, creating
1201 * a new head for the train. Point to that. Unless we are moving the whole
1202 * train in which case there is not 'source' train anymore.
1203 * In case we are a multiheaded part we want the complete thing to come
1204 * with us, so src->GetNextUnit(), however... when we are e.g. a wagon
1205 * that is followed by a rear multihead we do not want to include that. */
1206 *src_head = move_chain ? nullptr :
1207 (src->IsMultiheaded() ? src->GetNextUnit() : src->GetNextVehicle());
1208 }
1209
1210 /* Now it's just simply removing the part that we are going to move from the
1211 * source train and *if* the destination is a not a new train add the chain
1212 * at the destination location. */
1213 RemoveFromConsist(src, move_chain);
1214 if (*dst_head != src) InsertInConsist(dst, src);
1215
1216 /* Now normalise the dual heads, that is move the dual heads around in such
1217 * a way that the head and rear of a dual head are in the same train */
1218 NormaliseDualHeads(*src_head);
1219 NormaliseDualHeads(*dst_head);
1220}
1221
1227static void NormaliseTrainHead(Train *head)
1228{
1229 /* Not much to do! */
1230 if (head == nullptr) return;
1231
1232 /* Tell the 'world' the train changed. */
1234 UpdateTrainGroupID(head);
1235
1236 /* Not a front engine, i.e. a free wagon chain. No need to do more. */
1237 if (!head->IsFrontEngine()) return;
1238
1239 /* Update the refit button and window */
1240 InvalidateWindowData(WindowClass::VehicleRefit, head->index, VIWD_CONSIST_CHANGED);
1241 SetWindowWidgetDirty(WindowClass::VehicleView, head->index, WID_VV_REFIT);
1242
1243 /* If we don't have a unit number yet, set one. */
1244 if (head->unitnumber != 0) return;
1245 head->unitnumber = Company::Get(head->owner)->freeunits[head->type].UseID(GetFreeUnitNumber(VehicleType::Train));
1246}
1247
1257CommandCost CmdMoveRailVehicle(DoCommandFlags flags, VehicleID src_veh, VehicleID dest_veh, bool move_chain)
1258{
1259 Train *src = Train::GetIfValid(src_veh);
1260 if (src == nullptr) return CMD_ERROR;
1261
1262 CommandCost ret = CheckOwnership(src->owner);
1263 if (ret.Failed()) return ret;
1264
1265 /* Do not allow moving crashed vehicles inside the depot, it is likely to cause asserts later */
1266 if (src->vehstatus.Test(VehState::Crashed)) return CMD_ERROR;
1267
1268 /* if nothing is selected as destination, try and find a matching vehicle to drag to. */
1269 Train *dst;
1270 if (dest_veh == VehicleID::Invalid()) {
1271 dst = (src->IsEngine() || flags.Test(DoCommandFlag::AutoReplace)) ? nullptr : FindGoodVehiclePos(src);
1272 } else {
1273 dst = Train::GetIfValid(dest_veh);
1274 if (dst == nullptr) return CMD_ERROR;
1275
1276 ret = CheckOwnership(dst->owner);
1277 if (ret.Failed()) return ret;
1278
1279 /* Do not allow appending to crashed vehicles, too */
1280 if (dst->vehstatus.Test(VehState::Crashed)) return CMD_ERROR;
1281 }
1282
1283 /* if an articulated part is being handled, deal with its parent vehicle */
1284 src = src->GetFirstEnginePart();
1285 if (dst != nullptr) {
1286 dst = dst->GetFirstEnginePart();
1287 }
1288
1289 /* don't move the same vehicle.. */
1290 if (src == dst) return CommandCost();
1291
1292 /* locate the head of the two chains */
1293 Train *src_head = src->First();
1294 Train *dst_head;
1295 if (dst != nullptr) {
1296 dst_head = dst->First();
1297 if (dst_head->tile != src_head->tile) return CMD_ERROR;
1298 /* Now deal with articulated part of destination wagon */
1299 dst = dst->GetLastEnginePart();
1300 } else {
1301 dst_head = nullptr;
1302 }
1303
1304 if (src->IsRearDualheaded()) return CommandCost(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT);
1305
1306 /* When moving all wagons, we can't have the same src_head and dst_head */
1307 if (move_chain && src_head == dst_head) return CommandCost();
1308
1309 /* When moving a multiheaded part to be place after itself, bail out. */
1310 if (!move_chain && dst != nullptr && dst->IsRearDualheaded() && src == dst->other_multiheaded_part) return CommandCost();
1311
1312 /* Check if all vehicles in the source train are stopped inside a depot. */
1313 if (!src_head->IsStoppedInDepot()) return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
1314
1315 /* Check if all vehicles in the destination train are stopped inside a depot. */
1316 if (dst_head != nullptr && !dst_head->IsStoppedInDepot()) return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
1317
1318 /* First make a backup of the order of the trains. That way we can do
1319 * whatever we want with the order and later on easily revert. */
1320 TrainList original_src;
1321 TrainList original_dst;
1322
1323 MakeTrainBackup(original_src, src_head);
1324 MakeTrainBackup(original_dst, dst_head);
1325
1326 /* Also make backup of the original heads as ArrangeTrains can change them.
1327 * For the destination head we do not care if it is the same as the source
1328 * head because in that case it's just a copy. */
1329 Train *original_src_head = src_head;
1330 Train *original_dst_head = (dst_head == src_head ? nullptr : dst_head);
1331
1332 /* We want this information from before the rearrangement, but execute this after the validation.
1333 * original_src_head can't be nullptr; src is by definition != nullptr, so src_head can't be nullptr as
1334 * src->GetFirst() always yields non-nullptr, so eventually original_src_head != nullptr as well. */
1335 bool original_src_head_front_engine = original_src_head->IsFrontEngine();
1336 bool original_dst_head_front_engine = original_dst_head != nullptr && original_dst_head->IsFrontEngine();
1337
1338 /* (Re)arrange the trains in the wanted arrangement. */
1339 ArrangeTrains(&dst_head, dst, &src_head, src, move_chain);
1340
1341 if (!flags.Test(DoCommandFlag::AutoReplace)) {
1342 /* If the autoreplace flag is set we do not need to test for the validity
1343 * because we are going to revert the train to its original state. As we
1344 * assume the original state was correct autoreplace can skip this. */
1345 ret = ValidateTrains(original_dst_head, dst_head, original_src_head, src_head, true);
1346 if (ret.Failed()) {
1347 /* Restore the train we had. */
1348 RestoreTrainBackup(original_src);
1349 RestoreTrainBackup(original_dst);
1350 return ret;
1351 }
1352 }
1353
1354 /* do it? */
1355 if (flags.Test(DoCommandFlag::Execute)) {
1356 /* Remove old heads from the statistics */
1357 if (original_src_head_front_engine) GroupStatistics::CountVehicle(original_src_head, -1);
1358 if (original_dst_head_front_engine) GroupStatistics::CountVehicle(original_dst_head, -1);
1359
1360 /* First normalise the sub types of the chains. */
1361 NormaliseSubtypes(src_head);
1362 NormaliseSubtypes(dst_head);
1363
1364 /* There are 14 different cases:
1365 * 1) front engine gets moved to a new train, it stays a front engine.
1366 * a) the 'next' part is a wagon that becomes a free wagon chain.
1367 * b) the 'next' part is an engine that becomes a front engine.
1368 * c) there is no 'next' part, nothing else happens
1369 * 2) front engine gets moved to another train, it is not a front engine anymore
1370 * a) the 'next' part is a wagon that becomes a free wagon chain.
1371 * b) the 'next' part is an engine that becomes a front engine.
1372 * c) there is no 'next' part, nothing else happens
1373 * 3) front engine gets moved to later in the current train, it is not a front engine anymore.
1374 * a) the 'next' part is a wagon that becomes a free wagon chain.
1375 * b) the 'next' part is an engine that becomes a front engine.
1376 * 4) free wagon gets moved
1377 * a) the 'next' part is a wagon that becomes a free wagon chain.
1378 * b) the 'next' part is an engine that becomes a front engine.
1379 * c) there is no 'next' part, nothing else happens
1380 * 5) non front engine gets moved and becomes a new train, nothing else happens
1381 * 6) non front engine gets moved within a train / to another train, nothing happens
1382 * 7) wagon gets moved, nothing happens
1383 */
1384 if (src == original_src_head && src->IsEngine() && !src->IsFrontEngine()) {
1385 /* Cases #2 and #3: the front engine gets trashed. */
1386 CloseWindowById(WindowClass::VehicleView, src->index);
1387 CloseWindowById(WindowClass::VehicleOrders, src->index);
1388 CloseWindowById(WindowClass::VehicleRefit, src->index);
1389 CloseWindowById(WindowClass::VehicleDetails, src->index);
1390 CloseWindowById(WindowClass::VehicleTimetable, src->index);
1392 SetWindowDirty(WindowClass::Company, _current_company);
1393
1394 if (src_head != nullptr && src_head->IsFrontEngine()) {
1395 /* Cases #?b: Transfer order, unit number and other stuff
1396 * to the new front engine. */
1397 src_head->orders = src->orders;
1398 if (src_head->orders != nullptr) src_head->AddToShared(src);
1399 src_head->CopyVehicleConfigAndStatistics(src);
1400 }
1401 /* Remove stuff not valid anymore for non-front engines. */
1403 src->ReleaseUnitNumber();
1404 src->name.clear();
1405 }
1406
1407 /* We weren't a front engine but are becoming one. So
1408 * we should be put in the default group. */
1409 if (original_src_head != src && dst_head == src) {
1411 SetWindowDirty(WindowClass::Company, _current_company);
1412 }
1413
1414 /* Handle 'new engine' part of cases #1b, #2b, #3b, #4b and #5 in NormaliseTrainHead. */
1415 NormaliseTrainHead(src_head);
1416 NormaliseTrainHead(dst_head);
1417
1418 /* Add new heads to statistics.
1419 * This should be done after NormaliseTrainHead due to engine total limit checks in GetFreeUnitNumber. */
1420 if (src_head != nullptr && src_head->IsFrontEngine()) GroupStatistics::CountVehicle(src_head, 1);
1421 if (dst_head != nullptr && dst_head->IsFrontEngine()) GroupStatistics::CountVehicle(dst_head, 1);
1422
1424 CheckCargoCapacity(src_head);
1425 CheckCargoCapacity(dst_head);
1426 }
1427
1428 if (src_head != nullptr) src_head->First()->MarkDirty();
1429 if (dst_head != nullptr) dst_head->First()->MarkDirty();
1430
1431 /* We are undoubtedly changing something in the depot and train list. */
1432 InvalidateWindowData(WindowClass::VehicleDepot, src->tile);
1433 InvalidateWindowClassesData(WindowClass::TrainList, 0);
1434 } else {
1435 /* We don't want to execute what we're just tried. */
1436 RestoreTrainBackup(original_src);
1437 RestoreTrainBackup(original_dst);
1438 }
1439
1440 return CommandCost();
1441}
1442
1455CommandCost CmdSellRailWagon(DoCommandFlags flags, Vehicle *t, bool sell_chain, bool backup_order, ClientID user)
1456{
1458 Train *first = v->First();
1459
1460 if (v->IsRearDualheaded()) return CommandCost(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT);
1461
1462 /* First make a backup of the order of the train. That way we can do
1463 * whatever we want with the order and later on easily revert. */
1464 TrainList original;
1465 MakeTrainBackup(original, first);
1466
1467 /* We need to keep track of the new head and the head of what we're going to sell. */
1468 Train *new_head = first;
1469 Train *sell_head = nullptr;
1470
1471 /* Split the train in the wanted way. */
1472 ArrangeTrains(&sell_head, nullptr, &new_head, v, sell_chain);
1473
1474 /* We don't need to validate the second train; it's going to be sold. */
1475 CommandCost ret = ValidateTrains(nullptr, nullptr, first, new_head, !flags.Test(DoCommandFlag::AutoReplace));
1476 if (ret.Failed()) {
1477 /* Restore the train we had. */
1478 RestoreTrainBackup(original);
1479 return ret;
1480 }
1481
1482 if (first->orders == nullptr && !OrderList::CanAllocateItem()) {
1483 /* Restore the train we had. */
1484 RestoreTrainBackup(original);
1485 return CommandCost(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS);
1486 }
1487
1489 for (Train *part = sell_head; part != nullptr; part = part->Next()) cost.AddCost(-part->value);
1490
1491 /* do it? */
1492 if (flags.Test(DoCommandFlag::Execute)) {
1493 /* First normalise the sub types of the chain. */
1494 NormaliseSubtypes(new_head);
1495
1496 if (v == first && !sell_chain && new_head != nullptr && new_head->IsFrontEngine()) {
1497 if (v->IsEngine()) {
1498 /* We are selling the front engine. In this case we want to
1499 * 'give' the order, unit number and such to the new head. */
1500 new_head->orders = first->orders;
1501 new_head->AddToShared(first);
1502 DeleteVehicleOrders(first);
1503
1504 /* Copy other important data from the front engine */
1505 new_head->CopyVehicleConfigAndStatistics(first);
1506 }
1507 GroupStatistics::CountVehicle(new_head, 1); // after copying over the profit, if required
1508 } else if (v->IsPrimaryVehicle() && backup_order) {
1509 OrderBackup::Backup(v, user);
1510 }
1511
1512 /* We need to update the information about the train. */
1513 NormaliseTrainHead(new_head);
1514
1515 /* We are undoubtedly changing something in the depot and train list. */
1516 InvalidateWindowData(WindowClass::VehicleDepot, v->tile);
1517 InvalidateWindowClassesData(WindowClass::TrainList, 0);
1518
1519 /* Actually delete the sold 'goods' */
1520 delete sell_head;
1521 } else {
1522 /* We don't want to execute what we're just tried. */
1523 RestoreTrainBackup(original);
1524 }
1525
1526 return cost;
1527}
1528
1530{
1531 /* Set common defaults. */
1532 this->bounds = {{-1, -1, 0}, {3, 3, 6}, {}};
1533
1534 /* Set if flipped and engine is NOT flagged with custom flip handling. */
1535 int flipped = this->flags.Test(VehicleRailFlag::Flipped) && !EngInfo(this->engine_type)->misc_flags.Test(EngineMiscFlag::RailFlips);
1536 /* If flipped and vehicle length is odd, we need to adjust the bounding box offset slightly. */
1537 int flip_offs = flipped && (this->gcache.cached_veh_length & 1);
1538
1539 Direction dir = this->direction;
1540 if (flipped) dir = ReverseDir(dir);
1541
1542 if (!IsDiagonalDirection(dir)) {
1543 static constexpr DiagDirectionIndexArray<Point> _sign_table{{{
1544 /* x, y */
1545 {-1, -1}, // DiagDirection::N
1546 {-1, 1}, // DiagDirection::E
1547 { 1, 1}, // DiagDirection::S
1548 { 1, -1}, // DiagDirection::W
1549 }}};
1550
1551 int half_shorten = (VEHICLE_LENGTH - this->gcache.cached_veh_length + flipped) / 2;
1552
1553 /* For all straight directions, move the bound box to the centre of the vehicle, but keep the size. */
1554 this->bounds.offset.x -= half_shorten * _sign_table[DirToDiagDir(dir)].x;
1555 this->bounds.offset.y -= half_shorten * _sign_table[DirToDiagDir(dir)].y;
1556 } else {
1557 switch (dir) {
1558 /* Shorten southern corner of the bounding box according the vehicle length
1559 * and center the bounding box on the vehicle. */
1560 case Direction::NE:
1561 this->bounds.origin.x = -(this->gcache.cached_veh_length + 1) / 2 + flip_offs;
1562 this->bounds.extent.x = this->gcache.cached_veh_length;
1563 this->bounds.offset.x = 1;
1564 break;
1565
1566 case Direction::NW:
1567 this->bounds.origin.y = -(this->gcache.cached_veh_length + 1) / 2 + flip_offs;
1568 this->bounds.extent.y = this->gcache.cached_veh_length;
1569 this->bounds.offset.y = 1;
1570 break;
1571
1572 /* Move northern corner of the bounding box down according to vehicle length
1573 * and center the bounding box on the vehicle. */
1574 case Direction::SW:
1575 this->bounds.origin.x = -(this->gcache.cached_veh_length) / 2 - flip_offs;
1576 this->bounds.extent.x = this->gcache.cached_veh_length;
1577 this->bounds.offset.x = 1 - (VEHICLE_LENGTH - this->gcache.cached_veh_length);
1578 break;
1579
1580 case Direction::SE:
1581 this->bounds.origin.y = -(this->gcache.cached_veh_length) / 2 - flip_offs;
1582 this->bounds.extent.y = this->gcache.cached_veh_length;
1583 this->bounds.offset.y = 1 - (VEHICLE_LENGTH - this->gcache.cached_veh_length);
1584 break;
1585
1586 default:
1587 NOT_REACHED();
1588 }
1589 }
1590}
1591
1596static void MarkTrainAsStuck(Train *consist)
1597{
1598 if (!consist->flags.Test(VehicleRailFlag::Stuck)) {
1599 /* It is the first time the problem occurred, set the "train stuck" flag. */
1601
1602 consist->wait_counter = 0;
1603
1604 /* Stop train */
1605 consist->cur_speed = 0;
1606 consist->subspeed = 0;
1607 consist->SetLastSpeed();
1608
1609 SetWindowWidgetDirty(WindowClass::VehicleView, consist->index, WID_VV_START_STOP);
1610 }
1611}
1612
1620static void SwapTrainFlags(uint16_t *swap_flag1, uint16_t *swap_flag2)
1621{
1622 uint16_t flag1 = *swap_flag1;
1623 uint16_t flag2 = *swap_flag2;
1624
1625 /* Clear the flags */
1626 ClrBit(*swap_flag1, GVF_GOINGUP_BIT);
1627 ClrBit(*swap_flag1, GVF_GOINGDOWN_BIT);
1628 ClrBit(*swap_flag2, GVF_GOINGUP_BIT);
1629 ClrBit(*swap_flag2, GVF_GOINGDOWN_BIT);
1630
1631 /* Reverse the rail-flags (if needed) */
1632 if (HasBit(flag1, GVF_GOINGUP_BIT)) {
1633 SetBit(*swap_flag2, GVF_GOINGDOWN_BIT);
1634 } else if (HasBit(flag1, GVF_GOINGDOWN_BIT)) {
1635 SetBit(*swap_flag2, GVF_GOINGUP_BIT);
1636 }
1637 if (HasBit(flag2, GVF_GOINGUP_BIT)) {
1638 SetBit(*swap_flag1, GVF_GOINGDOWN_BIT);
1639 } else if (HasBit(flag2, GVF_GOINGDOWN_BIT)) {
1640 SetBit(*swap_flag1, GVF_GOINGUP_BIT);
1641 }
1642}
1643
1649static void UpdateStatusAfterSwap(Train *v, bool reverse = true)
1650{
1651 /* Maybe reverse the direction. */
1652 if (reverse) v->direction = ReverseDir(v->direction);
1653
1654 /* Call the proper EnterTile function unless we are in a wormhole. */
1655 if (v->track != TRACK_BIT_WORMHOLE) {
1656 VehicleEnterTile(v, v->tile, v->x_pos, v->y_pos);
1657 } else {
1658 /* VehicleEnterTile_TunnelBridge() sets TRACK_BIT_WORMHOLE when the vehicle
1659 * is on the last bit of the bridge head (frame == TILE_SIZE - 1).
1660 * If we were swapped with such a vehicle, we have set TRACK_BIT_WORMHOLE,
1661 * when we shouldn't have. Check if this is the case. */
1662 TileIndex vt = TileVirtXY(v->x_pos, v->y_pos);
1664 VehicleEnterTile(v, vt, v->x_pos, v->y_pos);
1665 if (v->track != TRACK_BIT_WORMHOLE && IsBridgeTile(v->tile)) {
1666 /* We have just left the wormhole, possibly set the
1667 * "goingdown" bit. UpdateInclination() can be used
1668 * because we are at the border of the tile. */
1669 v->UpdatePosition();
1670 v->UpdateInclination(true, true);
1671 return;
1672 }
1673 }
1674 }
1675
1676 v->UpdatePosition();
1677 v->UpdateViewport(true, true);
1678}
1679
1686void ReverseTrainSwapVeh(Train *v, int l, int r)
1687{
1688 Train *a, *b;
1689
1690 /* locate vehicles to swap */
1691 for (a = v; l != 0; l--) a = a->Next();
1692 for (b = v; r != 0; r--) b = b->Next();
1693
1694 if (a != b) {
1695 /* swap the hidden bits */
1696 {
1697 bool a_hidden = a->vehstatus.Test(VehState::Hidden);
1698 bool b_hidden = b->vehstatus.Test(VehState::Hidden);
1699 b->vehstatus.Set(VehState::Hidden, a_hidden);
1700 a->vehstatus.Set(VehState::Hidden, b_hidden);
1701 }
1702
1703 std::swap(a->track, b->track);
1704 std::swap(a->direction, b->direction);
1705 std::swap(a->x_pos, b->x_pos);
1706 std::swap(a->y_pos, b->y_pos);
1707 std::swap(a->tile, b->tile);
1708 std::swap(a->z_pos, b->z_pos);
1709
1711
1714 } else {
1715 /* Swap GVF_GOINGUP_BIT/GVF_GOINGDOWN_BIT.
1716 * This is a little bit redundant way, a->gv_flags will
1717 * be (re)set twice, but it reduces code duplication */
1720 }
1721}
1722
1728static bool IsTrain(const Vehicle *v)
1729{
1730 return v->type == VehicleType::Train;
1731}
1732
1740{
1741 assert(IsLevelCrossingTile(tile));
1742
1743 return HasVehicleOnTile(tile, IsTrain);
1744}
1745
1753{
1754 if (v->type != VehicleType::Train || v->vehstatus.Test(VehState::Crashed)) return false;
1755
1756 const Train *t = Train::From(v);
1757 if (!t->IsMovingFront()) return false;
1758
1759 return TrainApproachingCrossingTile(t) == tile;
1760}
1761
1762
1770{
1771 assert(IsLevelCrossingTile(tile));
1772
1774 TileIndex tile_from = tile + TileOffsByDiagDir(dir);
1775
1776 if (HasVehicleOnTile(tile_from, [&](const Vehicle *v) {
1777 return TrainApproachingCrossingEnum(v, tile);
1778 })) return true;
1779
1780 dir = ReverseDiagDir(dir);
1781 tile_from = tile + TileOffsByDiagDir(dir);
1782
1783 return HasVehicleOnTile(tile_from, [&](const Vehicle *v) {
1784 return TrainApproachingCrossingEnum(v, tile);
1785 });
1786}
1787
1793static inline bool CheckLevelCrossing(TileIndex tile)
1794{
1795 /* reserved || train on crossing || train approaching crossing */
1797}
1798
1806static void UpdateLevelCrossingTile(TileIndex tile, bool sound, bool force_barred)
1807{
1808 assert(IsLevelCrossingTile(tile));
1809 bool set_barred;
1810
1811 /* We force the crossing to be barred when an adjacent crossing is barred, otherwise let it decide for itself. */
1812 set_barred = force_barred || CheckLevelCrossing(tile);
1813
1814 /* The state has changed */
1815 if (set_barred != IsCrossingBarred(tile)) {
1816 if (set_barred && sound && _settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, tile);
1817 SetCrossingBarred(tile, set_barred);
1818 MarkTileDirtyByTile(tile);
1819 }
1820}
1821
1828void UpdateLevelCrossing(TileIndex tile, bool sound, bool force_bar)
1829{
1830 if (!IsLevelCrossingTile(tile)) return;
1831
1832 bool forced_state = force_bar;
1833
1834 Axis axis = GetCrossingRoadAxis(tile);
1835 DiagDirections diagdirs = AxisToDiagDirs(axis);
1836
1837 /* Check if an adjacent crossing is barred. */
1838 for (DiagDirection dir : diagdirs) {
1839 for (TileIndex t = tile; !forced_state && t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, dir)) {
1840 forced_state |= CheckLevelCrossing(t);
1841 }
1842 }
1843
1844 /* Now that we know whether all tiles in this crossing should be barred or open,
1845 * we need to update those tiles. We start with the tile itself, then look along the road axis. */
1846 UpdateLevelCrossingTile(tile, sound, forced_state);
1847 for (DiagDirection dir : diagdirs) {
1848 for (TileIndex t = TileAddByDiagDir(tile, dir); t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, dir)) {
1849 UpdateLevelCrossingTile(t, sound, forced_state);
1850 }
1851 }
1852}
1853
1860{
1861 for (DiagDirection dir : AxisToDiagDirs(road_axis)) {
1862 const TileIndex t = TileAddByDiagDir(tile, dir);
1863 if (t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis) {
1865 }
1866 }
1867}
1868
1875{
1876 for (DiagDirection dir : AxisToDiagDirs(road_axis)) {
1877 const TileIndexDiff diff = TileOffsByDiagDir(dir);
1878 bool occupied = false;
1879 for (TileIndex t = tile + diff; t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis; t += diff) {
1880 occupied |= CheckLevelCrossing(t);
1881 }
1882 if (occupied) {
1883 /* Mark the immediately adjacent tile dirty */
1884 const TileIndex t = tile + diff;
1885 if (t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis) {
1887 }
1888 } else {
1889 /* Unbar the crossing tiles in this direction as necessary */
1890 for (TileIndex t = tile + diff; t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis; t += diff) {
1891 if (IsCrossingBarred(t)) {
1892 /* The crossing tile is barred, unbar it and continue to check the next tile */
1893 SetCrossingBarred(t, false);
1895 } else {
1896 /* The crossing tile is already unbarred, mark the tile dirty and stop checking */
1898 break;
1899 }
1900 }
1901 }
1902 }
1903}
1904
1910static inline void MaybeBarCrossingWithSound(TileIndex tile)
1911{
1912 if (!IsCrossingBarred(tile)) {
1913 SetCrossingReservation(tile, true);
1914 UpdateLevelCrossing(tile, true);
1915 }
1916}
1917
1918
1924static void AdvanceWagonsBeforeSwap(Train *moving_front)
1925{
1926 Train *base = moving_front;
1927 Train *first = base; // first vehicle to move
1928 Train *last = moving_front->GetMovingBack(); // last vehicle to move
1929 uint length = CountVehiclesInChain(moving_front->First());
1930
1931 while (length > 2) {
1932 last = last->GetMovingPrev();
1933 first = first->GetMovingNext();
1934
1935 int differential = base->CalcNextVehicleOffset() - last->CalcNextVehicleOffset();
1936
1937 /* do not update images now
1938 * negative differential will be handled in AdvanceWagonsAfterSwap() */
1939 for (int i = 0; i < differential; i++) TrainController(first, last->GetMovingNext());
1940
1941 base = first; // == base->GetMovingNext()
1942 length -= 2;
1943 }
1944}
1945
1946
1952static void AdvanceWagonsAfterSwap(Train *moving_front)
1953{
1954 /* first of all, fix the situation when the train was entering a depot */
1955 Train *dep = moving_front; // last vehicle in front of just left depot
1956 while (dep->GetMovingNext() != nullptr && (dep->track == TRACK_BIT_DEPOT || dep->GetMovingNext()->track != TRACK_BIT_DEPOT)) {
1957 dep = dep->GetMovingNext(); // find first vehicle outside of a depot, with next vehicle inside a depot
1958 }
1959
1960 Train *leave = dep->GetMovingNext(); // first vehicle in a depot we are leaving now
1961
1962 if (leave != nullptr) {
1963 /* 'pull' next wagon out of the depot, so we won't miss it (it could stay in depot forever) */
1964 int d = TicksToLeaveDepot(dep);
1965
1966 if (d <= 0) {
1967 leave->vehstatus.Reset(VehState::Hidden); // move it out of the depot
1968 leave->track = TrackToTrackBits(GetRailDepotTrack(leave->tile));
1969 for (int i = 0; i >= d; i--) TrainController(leave, nullptr); // maybe move it, and maybe let another wagon leave
1970 }
1971 } else {
1972 dep = nullptr; // no vehicle in a depot, so no vehicle leaving a depot
1973 }
1974
1975 Train *base = moving_front;
1976 Train *first = base; // first vehicle to move
1977 Train *last = moving_front->GetMovingBack(); // last vehicle to move
1978 uint length = CountVehiclesInChain(moving_front->First());
1979
1980 /* We have to make sure all wagons that leave a depot because of train reversing are moved correctly
1981 * they have already correct spacing, so we have to make sure they are moved how they should */
1982 bool nomove = (dep == nullptr); // If there is no vehicle leaving a depot, limit the number of wagons moved immediately.
1983
1984 while (length > 2) {
1985 /* we reached vehicle (originally) in front of a depot, stop now
1986 * (we would move wagons that are already moved with new wagon length). */
1987 if (base == dep) break;
1988
1989 /* the last wagon was that one leaving a depot, so do not move it anymore */
1990 if (last == dep) nomove = true;
1991
1992 last = last->GetMovingPrev();
1993 first = first->GetMovingNext();
1994
1995 int differential = last->CalcNextVehicleOffset() - base->CalcNextVehicleOffset();
1996
1997 /* do not update images now */
1998 for (int i = 0; i < differential; i++) TrainController(first, (nomove ? last->GetMovingNext() : nullptr));
1999
2000 base = first; // == base->GetMovingNext()
2001 length -= 2;
2002 }
2003}
2004
2005static bool IsWholeTrainInsideDepot(const Train *v)
2006{
2007 for (const Train *u = v; u != nullptr; u = u->Next()) {
2008 if (u->track != TRACK_BIT_DEPOT || u->tile != v->tile) return false;
2009 }
2010 return true;
2011}
2012
2017static void ReverseTrainDirection(Train *consist)
2018{
2019 Train *moving_front = consist->GetMovingFront();
2020 if (IsRailDepotTile(moving_front->tile)) {
2021 if (IsWholeTrainInsideDepot(consist)) return;
2022 InvalidateWindowData(WindowClass::VehicleDepot, moving_front->tile);
2023 }
2024
2025 /* Clear path reservation in front if train is not stuck. */
2027
2028 /* Check if we were approaching a rail/road-crossing */
2029 TileIndex crossing = TrainApproachingCrossingTile(moving_front);
2030
2031 /* Check if we should back up or flip the train. */
2032 if (consist->vehicle_flags.Test(VehicleFlag::DrivingBackwards) || _settings_game.difficulty.train_flip_reverse_allowed == TrainFlipReversingAllowed::None || consist->Last()->CanLeadTrain()) {
2033 /* The train will back up. */
2035
2036 for (Train *u = consist; u != nullptr; u = u->Next()) {
2037 /* Invert going up/down */
2041 }
2042 UpdateStatusAfterSwap(u, false);
2043 }
2044 /* We may have entered a depot and stopped driving backwards. */
2045 moving_front = consist->GetMovingFront();
2046 } else {
2047 /* The train will flip. */
2048 int r = CountVehiclesInChain(consist) - 1; // number of vehicles - 1
2049
2050 AdvanceWagonsBeforeSwap(moving_front);
2051
2052 /* swap start<>end, start+1<>end-1, ... */
2053 int l = 0;
2054 do {
2055 ReverseTrainSwapVeh(consist, l++, r--);
2056 } while (l <= r);
2057
2058 AdvanceWagonsAfterSwap(moving_front);
2059 }
2060
2061 if (IsRailDepotTile(moving_front->tile)) {
2062 InvalidateWindowData(WindowClass::VehicleDepot, moving_front->tile);
2063 }
2064
2067
2068 /* recalculate cached data */
2069 consist->ConsistChanged(CCF_TRACK);
2070
2071 /* update all images */
2072 for (Train *u = consist; u != nullptr; u = u->Next()) u->UpdateViewport(false, false);
2073
2074 /* update crossing we were approaching */
2075 if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing);
2076
2077 /* maybe we are approaching crossing now, after reversal */
2078 crossing = TrainApproachingCrossingTile(moving_front);
2079 if (crossing != INVALID_TILE) MaybeBarCrossingWithSound(crossing);
2080
2081 /* If we are inside a depot after reversing, don't bother with path reserving. */
2082 if (moving_front->track == TRACK_BIT_DEPOT) {
2083 /* Can't be stuck here as inside a depot is always a safe tile. */
2084 if (consist->flags.Test(VehicleRailFlag::Stuck)) SetWindowWidgetDirty(WindowClass::VehicleView, consist->index, WID_VV_START_STOP);
2086 return;
2087 }
2088
2089 /* VehicleExitDir does not always produce the desired dir for depots and
2090 * tunnels/bridges that is needed for UpdateSignalsOnSegment. */
2091 DiagDirection dir = VehicleExitDir(moving_front->GetMovingDirection(), moving_front->track);
2092 if (IsRailDepotTile(moving_front->tile) || IsTileType(moving_front->tile, TileType::TunnelBridge)) dir = DiagDirection::Invalid;
2093
2094 if (UpdateSignalsOnSegment(moving_front->tile, dir, consist->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
2095 /* If we are currently on a tile with conventional signals, we can't treat the
2096 * current tile as a safe tile or we would enter a PBS block without a reservation. */
2097 bool first_tile_okay = !HasBlockSignalOnTrackdir(moving_front->tile, moving_front->GetVehicleTrackdir());
2098
2099 /* If we are on a depot tile facing outwards, do not treat the current tile as safe. */
2100 if (IsRailDepotTile(moving_front->tile) && TrackdirToExitdir(moving_front->GetVehicleTrackdir()) == GetRailDepotDirection(moving_front->tile)) first_tile_okay = false;
2101
2102 if (IsRailStationTile(moving_front->tile)) SetRailStationPlatformReservation(moving_front->tile, TrackdirToExitdir(moving_front->GetVehicleTrackdir()), true);
2103 if (TryPathReserve(consist, false, first_tile_okay)) {
2104 /* Do a look-ahead now in case our current tile was already a safe tile. */
2105 CheckNextTrainTile(consist);
2106 } else if (consist->current_order.GetType() != OT_LOADING) {
2107 /* Do not wait for a way out when we're still loading */
2108 MarkTrainAsStuck(consist);
2109 }
2110 } else if (consist->flags.Test(VehicleRailFlag::Stuck)) {
2111 /* A train not inside a PBS block can't be stuck. */
2113 consist->wait_counter = 0;
2114 }
2115}
2116
2124CommandCost CmdReverseTrainDirection(DoCommandFlags flags, VehicleID veh_id, bool reverse_single_veh)
2125{
2126 Train *v = Train::GetIfValid(veh_id);
2127 if (v == nullptr) return CMD_ERROR;
2128
2130 if (ret.Failed()) return ret;
2131
2132 if (reverse_single_veh) {
2133 /* turn a single unit around */
2134
2135 if (v->IsMultiheaded() || EngInfo(v->engine_type)->callback_mask.Test(VehicleCallbackMask::ArticEngine)) {
2136 return CommandCost(STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE_MULTIPLE_UNITS);
2137 }
2138
2139 Train *front = v->First();
2140 /* make sure the vehicle is stopped in the depot */
2141 if (!front->IsStoppedInDepot()) {
2142 return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
2143 }
2144
2145 if (flags.Test(DoCommandFlag::Execute)) {
2147
2149 SetWindowDirty(WindowClass::VehicleDepot, front->tile);
2150 SetWindowDirty(WindowClass::VehicleDetails, front->index);
2151 InvalidateWindowData(WindowClass::VehicleView, front->index);
2152 SetWindowClassesDirty(WindowClass::TrainList);
2153 }
2154 } else {
2155 /* turn the whole train around */
2156 if (!v->IsPrimaryVehicle()) return CMD_ERROR;
2157 if (v->vehstatus.Test(VehState::Crashed) || v->breakdown_ctr != 0) return CMD_ERROR;
2158
2159 if (flags.Test(DoCommandFlag::Execute)) {
2160 /* Properly leave the station if we are loading and won't be loading anymore */
2161 if (v->current_order.IsType(OT_LOADING)) {
2162 const Vehicle *moving_back = v->GetMovingBack();
2163
2164 /* not a station || different station --> leave the station */
2165 if (!IsTileType(moving_back->tile, TileType::Station) || GetStationIndex(moving_back->tile) != GetStationIndex(v->GetMovingFront()->tile)) {
2166 v->LeaveStation();
2167 }
2168 }
2169
2170 /* We cancel any 'skip signal at dangers' here */
2172 InvalidateWindowData(WindowClass::VehicleView, v->index);
2173
2174 if (_settings_game.vehicle.train_acceleration_model != AM_ORIGINAL && v->cur_speed != 0) {
2176 } else {
2177 v->cur_speed = 0;
2178 v->SetLastSpeed();
2181 }
2182
2183 /* Unbunching data is no longer valid. */
2185 }
2186 }
2187 return CommandCost();
2188}
2189
2201{
2204
2205 const Train *moving_front = t->GetMovingFront();
2206 TileIndex next_tile = TileAddByDiagDir(moving_front->tile, TrackdirToExitdir(moving_front->GetVehicleTrackdir()));
2207 if (next_tile == INVALID_TILE || !IsTileType(next_tile, TileType::Railway) || !HasSignals(next_tile)) return TFP_STUCK;
2208 TrackBits new_tracks = DiagdirReachesTracks(TrackdirToExitdir(moving_front->GetVehicleTrackdir())) & GetTrackBits(next_tile);
2209 return new_tracks != TRACK_BIT_NONE && HasSignalOnTrack(next_tile, FindFirstTrack(new_tracks)) ? TFP_SIGNAL : TFP_STUCK;
2210}
2211
2218CommandCost CmdForceTrainProceed(DoCommandFlags flags, VehicleID veh_id)
2219{
2220 Train *t = Train::GetIfValid(veh_id);
2221 if (t == nullptr) return CMD_ERROR;
2222
2223 if (!t->IsPrimaryVehicle()) return CMD_ERROR;
2224
2226 if (ret.Failed()) return ret;
2227
2228
2229 if (flags.Test(DoCommandFlag::Execute)) {
2231 InvalidateWindowData(WindowClass::VehicleView, t->index);
2232
2233 /* Unbunching data is no longer valid. */
2235 }
2236
2237 return CommandCost();
2238}
2239
2247static FindDepotData FindClosestTrainDepot(Train *v, int max_distance)
2248{
2249 assert(!v->vehstatus.Test(VehState::Crashed));
2250
2251 return YapfTrainFindNearestDepot(v, max_distance);
2252}
2253
2255{
2256 FindDepotData tfdd = FindClosestTrainDepot(this, 0);
2257 if (tfdd.best_length == UINT_MAX) return ClosestDepot();
2258
2259 return ClosestDepot(tfdd.tile, GetDepotIndex(tfdd.tile), tfdd.reverse);
2260}
2261
2262void Train::PlayLeaveStationSound(bool force) const
2263{
2264 static const SoundFx sfx[] = {
2270 };
2271
2272 if (PlayVehicleSound(this, VSE_START, force)) return;
2273
2274 SndPlayVehicleFx(sfx[to_underlying(RailVehInfo(this->engine_type)->engclass)], this);
2275}
2276
2281static void CheckNextTrainTile(Train *consist)
2282{
2283 /* Don't do any look-ahead if path_backoff_interval is 255. */
2284 if (_settings_game.pf.path_backoff_interval == 255) return;
2285
2286 const Train *moving_front = consist->GetMovingFront();
2287
2288 /* Exit if we are inside a depot. */
2289 if (moving_front->track == TRACK_BIT_DEPOT) return;
2290
2291 switch (consist->current_order.GetType()) {
2292 /* Exit if we reached our destination depot. */
2293 case OT_GOTO_DEPOT:
2294 if (moving_front->tile == consist->dest_tile) return;
2295 break;
2296
2297 case OT_GOTO_WAYPOINT:
2298 /* If we reached our waypoint, make sure we see that. */
2299 if (IsRailWaypointTile(moving_front->tile) && GetStationIndex(moving_front->tile) == consist->current_order.GetDestination()) ProcessOrders(consist);
2300 break;
2301
2302 case OT_NOTHING:
2303 case OT_LEAVESTATION:
2304 case OT_LOADING:
2305 /* Exit if the current order doesn't have a destination, but the train has orders. */
2306 if (consist->GetNumOrders() > 0) return;
2307 break;
2308
2309 default:
2310 break;
2311 }
2312 /* Exit if we are on a station tile and are going to stop. */
2313 if (IsRailStationTile(moving_front->tile) && consist->current_order.ShouldStopAtStation(consist, GetStationIndex(moving_front->tile))) return;
2314
2315 Trackdir td = moving_front->GetVehicleTrackdir();
2316
2317 /* On a tile with a red non-pbs signal, don't look ahead. */
2318 if (HasBlockSignalOnTrackdir(moving_front->tile, td) && GetSignalStateByTrackdir(moving_front->tile, td) == SIGNAL_STATE_RED) return;
2319
2320 CFollowTrackRail ft(consist);
2321 if (!ft.Follow(moving_front->tile, td)) return;
2322
2324 /* Next tile is not reserved. */
2327 /* If the next tile is a PBS signal, try to make a reservation. */
2331 }
2332 ChooseTrainTrack(consist, ft.new_tile, ft.exitdir, tracks, false, nullptr, false);
2333 }
2334 }
2335 }
2336}
2337
2344{
2345 /* bail out if not all wagons are in the same depot or not in a depot at all */
2346 for (const Train *u = v; u != nullptr; u = u->Next()) {
2347 if (u->track != TRACK_BIT_DEPOT || u->tile != v->tile) return false;
2348 }
2349
2350 /* if the train got no power, then keep it in the depot */
2351 if (v->gcache.cached_power == 0) {
2353 SetWindowDirty(WindowClass::VehicleDepot, v->tile);
2354 return true;
2355 }
2356
2357 /* Check if we should wait here for unbunching. */
2358 if (v->IsWaitingForUnbunching()) return true;
2359
2360 SigSegState seg_state;
2361
2362 if (v->force_proceed == TFP_NONE) {
2363 /* force proceed was not pressed */
2364 if (++v->wait_counter < 37) {
2365 SetWindowClassesDirty(WindowClass::TrainList);
2366 return true;
2367 }
2368
2369 v->wait_counter = 0;
2370
2371 seg_state = _settings_game.pf.reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment(v->tile, DiagDirection::Invalid, v->owner);
2372 if (seg_state == SIGSEG_FULL || HasDepotReservation(v->tile)) {
2373 /* Full and no PBS signal in block or depot reserved, can't exit. */
2374 SetWindowClassesDirty(WindowClass::TrainList);
2375 return true;
2376 }
2377 } else {
2378 seg_state = _settings_game.pf.reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment(v->tile, DiagDirection::Invalid, v->owner);
2379 }
2380
2381 /* We are leaving a depot, but have to go to the exact same one; re-enter. */
2382 if (v->current_order.IsType(OT_GOTO_DEPOT) && v->tile == v->dest_tile) {
2383 /* Service when depot has no reservation. */
2385 return true;
2386 }
2387
2388 /* Only leave when we can reserve a path to our destination. */
2389 if (seg_state == SIGSEG_PBS && !TryPathReserve(v) && v->force_proceed == TFP_NONE) {
2390 /* No path and no force proceed. */
2391 SetWindowClassesDirty(WindowClass::TrainList);
2393 return true;
2394 }
2395
2396 SetDepotReservation(v->tile, true);
2397 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
2398
2402 SetWindowClassesDirty(WindowClass::TrainList);
2403
2405
2407 v->cur_speed = 0;
2408
2409 v->UpdateViewport(true, true);
2410 v->UpdatePosition();
2412 v->UpdateAcceleration();
2413 InvalidateWindowData(WindowClass::VehicleDepot, v->tile);
2414
2415 return false;
2416}
2417
2424static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_dir)
2425{
2426 DiagDirection dir = TrackdirToExitdir(track_dir);
2427
2429 /* Are we just leaving a tunnel/bridge? */
2430 if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) {
2432
2433 if (TunnelBridgeIsFree(tile, end, v).Succeeded()) {
2434 /* Free the reservation only if no other train is on the tiles. */
2435 SetTunnelBridgeReservation(tile, false);
2436 SetTunnelBridgeReservation(end, false);
2437
2438 if (_settings_client.gui.show_track_reservation) {
2439 if (IsBridge(tile)) {
2440 MarkBridgeDirty(tile);
2441 } else {
2442 MarkTileDirtyByTile(tile);
2444 }
2445 }
2446 }
2447 }
2448 } else if (IsRailStationTile(tile)) {
2449 TileIndex new_tile = TileAddByDiagDir(tile, dir);
2450 /* If the new tile is not a further tile of the same station, we
2451 * clear the reservation for the whole platform. */
2452 if (!IsCompatibleTrainStationTile(new_tile, tile)) {
2454 }
2455 } else {
2456 /* Any other tile */
2457 UnreserveRailTrack(tile, TrackdirToTrack(track_dir));
2458 }
2459}
2460
2466{
2467 assert(consist->IsFrontEngine());
2468
2469 const Train *moving_front = consist->GetMovingFront();
2470 TileIndex tile = moving_front->tile;
2471 Trackdir td = moving_front->GetVehicleTrackdir();
2472 bool free_tile = tile != moving_front->tile || !(IsRailStationTile(moving_front->tile) || IsTileType(moving_front->tile, TileType::TunnelBridge));
2473 StationID station_id = IsRailStationTile(moving_front->tile) ? GetStationIndex(moving_front->tile) : StationID::Invalid();
2474
2475 /* Can't be holding a reservation if we enter a depot. */
2476 if (IsRailDepotTile(tile) && TrackdirToExitdir(td) != GetRailDepotDirection(tile)) return;
2477 if (moving_front->track == TRACK_BIT_DEPOT) {
2478 /* Front engine is in a depot. We enter if some part is not in the depot. */
2479 for (const Train *u = consist; u != nullptr; u = u->Next()) {
2480 if (u->track != TRACK_BIT_DEPOT || u->tile != consist->tile) return;
2481 }
2482 }
2483 /* Don't free reservation if it's not ours. */
2485
2486 CFollowTrackRail ft(consist, GetAllCompatibleRailTypes(consist->railtypes));
2487 while (ft.Follow(tile, td)) {
2488 tile = ft.new_tile;
2490 td = RemoveFirstTrackdir(&bits);
2491 assert(bits == TRACKDIR_BIT_NONE);
2492
2493 if (!IsValidTrackdir(td)) break;
2494
2495 if (IsTileType(tile, TileType::Railway)) {
2496 if (HasSignalOnTrackdir(tile, td) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(td)))) {
2497 /* Conventional signal along trackdir: remove reservation and stop. */
2499 break;
2500 }
2501 if (HasPbsSignalOnTrackdir(tile, td)) {
2502 if (GetSignalStateByTrackdir(tile, td) == SIGNAL_STATE_RED) {
2503 /* Red PBS signal? Can't be our reservation, would be green then. */
2504 break;
2505 } else {
2506 /* Turn the signal back to red. */
2508 MarkTileDirtyByTile(tile);
2509 }
2510 } else if (HasPbsSignalOnTrackdir(tile, ReverseTrackdir(td))) {
2511 /* Reservation passes an opposing path signal. Mark signal for update to re-establish the proper default state. */
2513 } else if (HasSignalOnTrackdir(tile, ReverseTrackdir(td)) && IsOnewaySignal(tile, TrackdirToTrack(td))) {
2514 break;
2515 }
2516 }
2517
2518 /* Don't free first station/bridge/tunnel if we are on it. */
2519 if (free_tile || (!(ft.is_station && GetStationIndex(ft.new_tile) == station_id) && !ft.is_tunnel && !ft.is_bridge)) ClearPathReservation(consist, tile, td);
2520
2521 free_tile = true;
2522 }
2523
2525}
2526
2540static Track DoTrainPathfind(const Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found, bool do_track_reservation, PBSTileInfo *dest, TileIndex *final_dest)
2541{
2542 if (final_dest != nullptr) *final_dest = INVALID_TILE;
2543 return YapfTrainChooseTrack(v, tile, enterdir, tracks, path_found, do_track_reservation, dest, final_dest);
2544}
2545
2554static PBSTileInfo ExtendTrainReservation(const Train *v, TrackBits *new_tracks, DiagDirection *enterdir)
2555{
2557
2558 CFollowTrackRail ft(v);
2559
2560 std::vector<std::pair<TileIndex, Trackdir>> signals_set_to_red;
2561
2562 TileIndex tile = origin.tile;
2563 Trackdir cur_td = origin.trackdir;
2564 while (ft.Follow(tile, cur_td)) {
2566 /* Possible signal tile. */
2568 }
2569
2572 if (ft.new_td_bits == TRACKDIR_BIT_NONE) break;
2573 }
2574
2575 /* Station, depot or waypoint are a possible target. */
2576 bool target_seen = ft.is_station || (IsTileType(ft.new_tile, TileType::Railway) && !IsPlainRail(ft.new_tile));
2577 if (target_seen || KillFirstBit(ft.new_td_bits) != TRACKDIR_BIT_NONE) {
2578 /* Choice found or possible target encountered.
2579 * On finding a possible target, we need to stop and let the pathfinder handle the
2580 * remaining path. This is because we don't know if this target is in one of our
2581 * orders, so we might cause pathfinding to fail later on if we find a choice.
2582 * This failure would cause a bogus call to TryReserveSafePath which might reserve
2583 * a wrong path not leading to our next destination. */
2585
2586 /* If we did skip some tiles, backtrack to the first skipped tile so the pathfinder
2587 * actually starts its search at the first unreserved tile. */
2588 if (ft.tiles_skipped != 0) ft.new_tile -= TileOffsByDiagDir(ft.exitdir) * ft.tiles_skipped;
2589
2590 /* Choice found, path valid but not okay. Save info about the choice tile as well. */
2591 if (new_tracks != nullptr) *new_tracks = TrackdirBitsToTrackBits(ft.new_td_bits);
2592 if (enterdir != nullptr) *enterdir = ft.exitdir;
2593 return PBSTileInfo(ft.new_tile, ft.old_td, false);
2594 }
2595
2596 tile = ft.new_tile;
2597 cur_td = FindFirstTrackdir(ft.new_td_bits);
2598
2599 Trackdir rev_td = ReverseTrackdir(cur_td);
2600 if (IsSafeWaitingPosition(v, tile, cur_td, true, _settings_game.pf.forbid_90_deg)) {
2601 bool wp_free = IsWaitingPositionFree(v, tile, cur_td, _settings_game.pf.forbid_90_deg);
2602 if (!(wp_free && TryReserveRailTrack(tile, TrackdirToTrack(cur_td)))) break;
2603 /* Green path signal opposing the path? Turn to red. */
2604 if (HasPbsSignalOnTrackdir(tile, rev_td) && GetSignalStateByTrackdir(tile, rev_td) == SIGNAL_STATE_GREEN) {
2605 signals_set_to_red.emplace_back(tile, rev_td);
2607 MarkTileDirtyByTile(tile);
2608 }
2609 /* Safe position is all good, path valid and okay. */
2610 return PBSTileInfo(tile, cur_td, true);
2611 }
2612
2613 if (!TryReserveRailTrack(tile, TrackdirToTrack(cur_td))) break;
2614
2615 /* Green path signal opposing the path? Turn to red. */
2616 if (HasPbsSignalOnTrackdir(tile, rev_td) && GetSignalStateByTrackdir(tile, rev_td) == SIGNAL_STATE_GREEN) {
2617 signals_set_to_red.emplace_back(tile, rev_td);
2619 MarkTileDirtyByTile(tile);
2620 }
2621 }
2622
2623 if (ft.err == CFollowTrackRail::EC_OWNER || ft.err == CFollowTrackRail::EC_NO_WAY) {
2624 /* End of line, path valid and okay. */
2625 return PBSTileInfo(ft.old_tile, ft.old_td, true);
2626 }
2627
2628 /* Sorry, can't reserve path, back out. */
2629 tile = origin.tile;
2630 cur_td = origin.trackdir;
2631 TileIndex stopped = ft.old_tile;
2632 Trackdir stopped_td = ft.old_td;
2633 while (tile != stopped || cur_td != stopped_td) {
2634 if (!ft.Follow(tile, cur_td)) break;
2635
2638 assert(ft.new_td_bits != TRACKDIR_BIT_NONE);
2639 }
2641
2642 tile = ft.new_tile;
2643 cur_td = FindFirstTrackdir(ft.new_td_bits);
2644
2645 UnreserveRailTrack(tile, TrackdirToTrack(cur_td));
2646 }
2647
2648 /* Re-instate green signals we turned to red. */
2649 for (auto [sig_tile, td] : signals_set_to_red) {
2651 }
2652
2653 /* Path invalid. */
2654 return PBSTileInfo();
2655}
2656
2667static bool TryReserveSafeTrack(const Train *v, TileIndex tile, Trackdir td, bool override_railtype)
2668{
2669 return YapfTrainFindNearestSafeTile(v, tile, td, override_railtype);
2670}
2671
2673class VehicleOrderSaver {
2674private:
2675 Train *v;
2676 Order old_order;
2677 TileIndex old_dest_tile;
2678 StationID old_last_station_visited;
2679 VehicleOrderID index;
2680 bool suppress_implicit_orders;
2681 bool restored;
2682
2683public:
2684 VehicleOrderSaver(Train *_v) :
2685 v(_v),
2686 old_order(_v->current_order),
2687 old_dest_tile(_v->dest_tile),
2688 old_last_station_visited(_v->last_station_visited),
2689 index(_v->cur_real_order_index),
2690 suppress_implicit_orders(HasBit(_v->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)),
2691 restored(false)
2692 {
2693 }
2694
2698 void Restore()
2699 {
2700 this->v->current_order = this->old_order;
2701 this->v->dest_tile = this->old_dest_tile;
2702 this->v->last_station_visited = this->old_last_station_visited;
2703 AssignBit(this->v->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS, suppress_implicit_orders);
2704 this->restored = true;
2705 }
2706
2711 {
2712 if (!this->restored) this->Restore();
2713 }
2714
2720 bool SwitchToNextOrder(bool skip_first)
2721 {
2722 if (this->v->GetNumOrders() == 0) return false;
2723
2724 if (skip_first) ++this->index;
2725
2726 int depth = 0;
2727
2728 do {
2729 /* Wrap around. */
2730 if (this->index >= this->v->GetNumOrders()) this->index = 0;
2731
2732 Order *order = this->v->GetOrder(this->index);
2733 assert(order != nullptr);
2734
2735 switch (order->GetType()) {
2736 case OT_GOTO_DEPOT:
2737 /* Skip service in depot orders when the train doesn't need service. */
2738 if (order->GetDepotOrderType().Test(OrderDepotTypeFlag::Service) && !this->v->NeedsServicing()) break;
2739 [[fallthrough]];
2740 case OT_GOTO_STATION:
2741 case OT_GOTO_WAYPOINT:
2742 this->v->current_order = *order;
2743 return UpdateOrderDest(this->v, order, 0, true);
2744 case OT_CONDITIONAL: {
2745 VehicleOrderID next = ProcessConditionalOrder(order, this->v);
2746 if (next != INVALID_VEH_ORDER_ID) {
2747 depth++;
2748 this->index = next;
2749 /* Don't increment next, so no break here. */
2750 continue;
2751 }
2752 break;
2753 }
2754 default:
2755 break;
2756 }
2757 /* Don't increment inside the while because otherwise conditional
2758 * orders can lead to an infinite loop. */
2759 ++this->index;
2760 depth++;
2761 } while (this->index != this->v->cur_real_order_index && depth < this->v->GetNumOrders());
2762
2763 return false;
2764 }
2765};
2766
2767/* choose a track */
2768static Track ChooseTrainTrack(Train *consist, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck)
2769{
2770 Track best_track = INVALID_TRACK;
2771 bool do_track_reservation = _settings_game.pf.reserve_paths || force_res;
2772 bool changed_signal = false;
2773 TileIndex final_dest = INVALID_TILE;
2774
2775 assert((tracks & ~TRACK_BIT_MASK) == 0);
2776
2777 if (got_reservation != nullptr) *got_reservation = false;
2778
2779 /* Don't use tracks here as the setting to forbid 90 deg turns might have been switched between reservation and now. */
2780 TrackBits res_tracks = (TrackBits)(GetReservedTrackbits(tile) & DiagdirReachesTracks(enterdir));
2781 /* Do we have a suitable reserved track? */
2782 if (res_tracks != TRACK_BIT_NONE) return FindFirstTrack(res_tracks);
2783
2784 /* Quick return in case only one possible track is available */
2785 if (KillFirstBit(tracks) == TRACK_BIT_NONE) {
2786 Track track = FindFirstTrack(tracks);
2787 /* We need to check for signals only here, as a junction tile can't have signals. */
2788 if (track != INVALID_TRACK && HasPbsSignalOnTrackdir(tile, TrackEnterdirToTrackdir(track, enterdir))) {
2789 do_track_reservation = true;
2790 changed_signal = true;
2792 } else if (!do_track_reservation) {
2793 return track;
2794 }
2795 best_track = track;
2796 }
2797
2798 const Train *moving_front = consist->GetMovingFront();
2799
2800 PBSTileInfo res_dest(tile, INVALID_TRACKDIR, false);
2801 DiagDirection dest_enterdir = enterdir;
2802 if (do_track_reservation) {
2803 res_dest = ExtendTrainReservation(consist, &tracks, &dest_enterdir);
2804 if (res_dest.tile == INVALID_TILE) {
2805 /* Reservation failed? */
2806 if (mark_stuck) MarkTrainAsStuck(consist);
2807 if (changed_signal) SetSignalStateByTrackdir(tile, TrackEnterdirToTrackdir(best_track, enterdir), SIGNAL_STATE_RED);
2808 return FindFirstTrack(tracks);
2809 }
2810 if (res_dest.okay) {
2811 /* Got a valid reservation that ends at a safe target, quick exit. */
2812 if (got_reservation != nullptr) *got_reservation = true;
2813 if (changed_signal) MarkTileDirtyByTile(tile);
2814 TryReserveRailTrack(moving_front->tile, TrackdirToTrack(moving_front->GetVehicleTrackdir()));
2815 return best_track;
2816 }
2817
2818 /* Check if the train needs service here, so it has a chance to always find a depot.
2819 * Also check if the current order is a service order so we don't reserve a path to
2820 * the destination but instead to the next one if service isn't needed. */
2821 CheckIfTrainNeedsService(consist);
2822 if (consist->current_order.IsType(OT_DUMMY) || consist->current_order.IsType(OT_CONDITIONAL) || consist->current_order.IsType(OT_GOTO_DEPOT)) ProcessOrders(consist);
2823 }
2824
2825 /* Save the current train order. The destructor will restore the old order on function exit. */
2826 VehicleOrderSaver orders(consist);
2827
2828 /* If the current tile is the destination of the current order and
2829 * a reservation was requested, advance to the next order.
2830 * Don't advance on a depot order as depots are always safe end points
2831 * for a path and no look-ahead is necessary. This also avoids a
2832 * problem with depot orders not part of the order list when the
2833 * order list itself is empty. */
2834 if (consist->current_order.IsType(OT_LEAVESTATION)) {
2835 orders.SwitchToNextOrder(false);
2836 } else if (consist->current_order.IsType(OT_LOADING) || (!consist->current_order.IsType(OT_GOTO_DEPOT) && (
2837 consist->current_order.IsType(OT_GOTO_STATION) ?
2838 IsRailStationTile(moving_front->tile) && consist->current_order.GetDestination() == GetStationIndex(moving_front->tile) :
2839 moving_front->tile == consist->dest_tile))) {
2840 orders.SwitchToNextOrder(true);
2841 }
2842
2843 if (res_dest.tile != INVALID_TILE && !res_dest.okay) {
2844 /* Pathfinders are able to tell that route was only 'guessed'. */
2845 bool path_found = true;
2846 TileIndex new_tile = res_dest.tile;
2847
2848 Track next_track = DoTrainPathfind(consist, new_tile, dest_enterdir, tracks, path_found, do_track_reservation, &res_dest, &final_dest);
2849 if (new_tile == tile) best_track = next_track;
2850 consist->HandlePathfindingResult(path_found);
2851 }
2852
2853 /* No track reservation requested -> finished. */
2854 if (!do_track_reservation) return best_track;
2855
2856 /* A path was found, but could not be reserved. */
2857 if (res_dest.tile != INVALID_TILE && !res_dest.okay) {
2858 if (mark_stuck) MarkTrainAsStuck(consist);
2860 return best_track;
2861 }
2862
2863 /* No possible reservation target found, we are probably lost. */
2864 if (res_dest.tile == INVALID_TILE) {
2865 /* Try to find any safe destination. */
2866 PBSTileInfo origin = FollowTrainReservation(consist);
2867 if (TryReserveSafeTrack(consist, origin.tile, origin.trackdir, false)) {
2868 TrackBits res = GetReservedTrackbits(tile) & DiagdirReachesTracks(enterdir);
2869 best_track = FindFirstTrack(res);
2870 TryReserveRailTrack(moving_front->tile, TrackdirToTrack(moving_front->GetVehicleTrackdir()));
2871 if (got_reservation != nullptr) *got_reservation = true;
2872 if (changed_signal) MarkTileDirtyByTile(tile);
2873 } else {
2875 if (mark_stuck) MarkTrainAsStuck(consist);
2876 }
2877 return best_track;
2878 }
2879
2880 if (got_reservation != nullptr) *got_reservation = true;
2881
2882 /* Reservation target found and free, check if it is safe. */
2883 while (!IsSafeWaitingPosition(consist, res_dest.tile, res_dest.trackdir, true, _settings_game.pf.forbid_90_deg)) {
2884 /* Extend reservation until we have found a safe position. */
2885 DiagDirection exitdir = TrackdirToExitdir(res_dest.trackdir);
2886 TileIndex next_tile = TileAddByDiagDir(res_dest.tile, exitdir);
2888 if (Rail90DegTurnDisallowed(GetTileRailType(res_dest.tile), GetTileRailType(next_tile))) {
2889 reachable &= ~TrackCrossesTracks(TrackdirToTrack(res_dest.trackdir));
2890 }
2891
2892 /* Get next order with destination. */
2893 if (orders.SwitchToNextOrder(true)) {
2894 PBSTileInfo cur_dest;
2895 bool path_found;
2896 DoTrainPathfind(consist, next_tile, exitdir, reachable, path_found, true, &cur_dest, nullptr);
2897 if (cur_dest.tile != INVALID_TILE) {
2898 res_dest = cur_dest;
2899 if (res_dest.okay) continue;
2900 /* Path found, but could not be reserved. */
2902 if (mark_stuck) MarkTrainAsStuck(consist);
2903 if (got_reservation != nullptr) *got_reservation = false;
2904 changed_signal = false;
2905 break;
2906 }
2907 }
2908 /* No order or no safe position found, try any position. */
2909 if (!TryReserveSafeTrack(consist, res_dest.tile, res_dest.trackdir, true)) {
2911 if (mark_stuck) MarkTrainAsStuck(consist);
2912 if (got_reservation != nullptr) *got_reservation = false;
2913 changed_signal = false;
2914 }
2915 break;
2916 }
2917
2918 TryReserveRailTrack(moving_front->tile, TrackdirToTrack(moving_front->GetVehicleTrackdir()));
2919
2920 if (changed_signal) MarkTileDirtyByTile(tile);
2921
2922 orders.Restore();
2923 if (consist->current_order.IsType(OT_GOTO_DEPOT) &&
2925 final_dest != INVALID_TILE && IsRailDepotTile(final_dest)) {
2926 consist->current_order.SetDestination(GetDepotIndex(final_dest));
2927 consist->dest_tile = final_dest;
2928 SetWindowWidgetDirty(WindowClass::VehicleView, consist->index, WID_VV_START_STOP);
2929 }
2930
2931 return best_track;
2932}
2933
2942bool TryPathReserve(Train *consist, bool mark_as_stuck, bool first_tile_okay)
2943{
2944 assert(consist->IsFrontEngine());
2945
2946 const Train *moving_front = consist->GetMovingFront();
2947
2948 /* We have to handle depots specially as the track follower won't look
2949 * at the depot tile itself but starts from the next tile. If we are still
2950 * inside the depot, a depot reservation can never be ours. */
2951 if (moving_front->track == TRACK_BIT_DEPOT) {
2952 if (HasDepotReservation(moving_front->tile)) {
2953 if (mark_as_stuck) MarkTrainAsStuck(consist);
2954 return false;
2955 } else {
2956 /* Depot not reserved, but the next tile might be. */
2957 TileIndex next_tile = TileAddByDiagDir(moving_front->tile, GetRailDepotDirection(moving_front->tile));
2958 if (HasReservedTracks(next_tile, DiagdirReachesTracks(GetRailDepotDirection(moving_front->tile)))) return false;
2959 }
2960 }
2961
2962 Vehicle *other_train = nullptr;
2963 PBSTileInfo origin = FollowTrainReservation(consist, &other_train);
2964 /* The path we are driving on is already blocked by some other train.
2965 * This can only happen in certain situations when mixing path and
2966 * block signals or when changing tracks and/or signals.
2967 * Exit here as doing any further reservations will probably just
2968 * make matters worse. */
2969 if (other_train != nullptr && other_train->index != consist->index) {
2970 if (mark_as_stuck) MarkTrainAsStuck(consist);
2971 return false;
2972 }
2973 /* If we have a reserved path and the path ends at a safe tile, we are finished already. */
2974 if (origin.okay && (moving_front->tile != origin.tile || first_tile_okay)) {
2975 /* Can't be stuck then. */
2976 if (consist->flags.Test(VehicleRailFlag::Stuck)) SetWindowWidgetDirty(WindowClass::VehicleView, consist->index, WID_VV_START_STOP);
2978 return true;
2979 }
2980
2981 /* If we are in a depot, tentatively reserve the depot. */
2982 if (moving_front->track == TRACK_BIT_DEPOT) {
2983 SetDepotReservation(moving_front->tile, true);
2984 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(moving_front->tile);
2985 }
2986
2987 DiagDirection exitdir = TrackdirToExitdir(origin.trackdir);
2988 TileIndex new_tile = TileAddByDiagDir(origin.tile, exitdir);
2990
2992
2993 bool res_made = false;
2994 ChooseTrainTrack(consist, new_tile, exitdir, reachable, true, &res_made, mark_as_stuck);
2995
2996 if (!res_made) {
2997 /* Free the depot reservation as well. */
2998 if (moving_front->track == TRACK_BIT_DEPOT) SetDepotReservation(moving_front->tile, false);
2999 return false;
3000 }
3001
3002 if (consist->flags.Test(VehicleRailFlag::Stuck)) {
3003 consist->wait_counter = 0;
3004 SetWindowWidgetDirty(WindowClass::VehicleView, consist->index, WID_VV_START_STOP);
3005 }
3007 return true;
3008}
3009
3015static bool CheckReverseTrain(const Train *consist)
3016{
3017 const Train *moving_front = consist->GetMovingFront();
3018 if (_settings_game.difficulty.train_flip_reverse_allowed == TrainFlipReversingAllowed::EndOfLineOnly ||
3019 moving_front->track == TRACK_BIT_DEPOT || moving_front->track == TRACK_BIT_WORMHOLE ||
3020 !IsDiagonalDirection(moving_front->GetMovingDirection())) {
3021 return false;
3022 }
3023
3024 assert(moving_front->track != TRACK_BIT_NONE);
3025
3026 return YapfTrainCheckReverse(consist);
3027}
3028
3035{
3036 if (station == this->last_station_visited) this->last_station_visited = StationID::Invalid();
3037
3038 const Station *st = Station::Get(station);
3040 /* The destination station has no trainstation tiles. */
3042 return TileIndex{};
3043 }
3044
3045 return st->xy;
3046}
3047
3050{
3051 Train *v = this;
3052 do {
3053 v->colourmap = PAL_NONE;
3054 v->UpdateViewport(true, false);
3055 } while ((v = v->Next()) != nullptr);
3056
3057 /* need to update acceleration and cached values since the goods on the train changed. */
3058 this->CargoChanged();
3059 this->UpdateAcceleration();
3060}
3061
3070{
3071 switch (_settings_game.vehicle.train_acceleration_model) {
3072 default: NOT_REACHED();
3073 case AM_ORIGINAL:
3074 return this->DoUpdateSpeed(this->acceleration * (this->GetAccelerationStatus() == AS_BRAKE ? -4 : 2), 0, this->GetCurrentMaxSpeed());
3075
3076 case AM_REALISTIC:
3077 return this->DoUpdateSpeed(this->GetAcceleration(), this->GetAccelerationStatus() == AS_BRAKE ? 0 : 2, this->GetCurrentMaxSpeed());
3078 }
3079}
3080
3086static void TrainEnterStation(Train *consist, StationID station)
3087{
3088 consist->last_station_visited = station;
3089
3090 /* check if a train ever visited this station before */
3091 Station *st = Station::Get(station);
3092 if (!(st->had_vehicle_of_type & HVOT_TRAIN)) {
3093 st->had_vehicle_of_type |= HVOT_TRAIN;
3095 GetEncodedString(STR_NEWS_FIRST_TRAIN_ARRIVAL, st->index),
3097 consist->index,
3098 st->index
3099 );
3100 AI::NewEvent(consist->owner, new ScriptEventStationFirstVehicle(st->index, consist->index));
3101 Game::NewEvent(new ScriptEventStationFirstVehicle(st->index, consist->index));
3102 }
3103
3104 consist->force_proceed = TFP_NONE;
3105 InvalidateWindowData(WindowClass::VehicleView, consist->index);
3106
3107 consist->BeginLoading();
3108
3109 TileIndex tile = consist->GetMovingFront()->tile;
3111 TriggerStationAnimation(st, tile, StationAnimationTrigger::VehicleArrives);
3112}
3113
3121static inline bool CheckCompatibleRail(const Train *v, TileIndex tile, bool check_railtype)
3122{
3123 return IsTileOwner(tile, v->owner) &&
3124 (!check_railtype || !v->IsFrontEngine() || v->compatible_railtypes.Test(GetRailType(tile)));
3125}
3126
3129 uint8_t small_turn;
3130 uint8_t large_turn;
3131 uint8_t z_up;
3132 uint8_t z_down;
3133};
3134
3137 /* normal accel */
3138 {256 / 4, 256 / 2, 256 / 4, 2},
3139 {256 / 4, 256 / 2, 256 / 4, 2},
3140 {0, 256 / 2, 256 / 4, 2},
3141};
3142
3148static inline void AffectSpeedByZChange(Train *consist, int z_diff)
3149{
3150 if (z_diff == 0 || _settings_game.vehicle.train_acceleration_model != AM_ORIGINAL) return;
3151
3152 const AccelerationSlowdownParams *asp = &_accel_slowdown[static_cast<int>(consist->GetAccelerationType())];
3153
3154 if (z_diff > 0) {
3155 consist->cur_speed -= (consist->cur_speed * asp->z_up >> 8);
3156 } else {
3157 uint16_t spd = consist->cur_speed + asp->z_down;
3158 if (spd <= consist->gcache.cached_max_track_speed) consist->cur_speed = spd;
3159 }
3160}
3161
3162static bool TrainMovedChangeSignals(TileIndex tile, DiagDirection dir)
3163{
3164 if (IsTileType(tile, TileType::Railway) &&
3167 Trackdir trackdir = FindFirstTrackdir(tracks);
3168 if (UpdateSignalsOnSegment(tile, TrackdirToExitdir(trackdir), GetTileOwner(tile)) == SIGSEG_PBS && HasSignalOnTrackdir(tile, trackdir)) {
3169 /* A PBS block with a non-PBS signal facing us? */
3170 if (!IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) return true;
3171 }
3172 }
3173 return false;
3174}
3175
3178{
3179 for (const Train *u = this; u != nullptr; u = u->Next()) {
3180 switch (u->track) {
3181 case TRACK_BIT_WORMHOLE:
3183 break;
3184 case TRACK_BIT_DEPOT:
3185 break;
3186 default:
3188 break;
3189 }
3190 }
3191}
3192
3199uint Train::Crash(bool flooded)
3200{
3201 uint victims = 0;
3202 if (this->IsFrontEngine()) {
3203 victims += 2; // driver
3204
3205 /* Remove the reserved path in front of the train if it is not stuck.
3206 * Also clear all reserved tracks the train is currently on. */
3208 for (const Train *v = this; v != nullptr; v = v->Next()) {
3211 /* ClearPathReservation will not free the wormhole exit
3212 * if the train has just entered the wormhole. */
3214 }
3215 }
3216
3217 /* we may need to update crossing we were approaching,
3218 * but must be updated after the train has been marked crashed */
3220 if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing);
3221
3222 /* Remove the loading indicators (if any) */
3224 }
3225
3226 victims += this->GroundVehicleBase::Crash(flooded);
3227
3228 this->crash_anim_pos = flooded ? 4000 : 1; // max 4440, disappear pretty fast when flooded
3229 return victims;
3230}
3231
3238static uint TrainCrashed(Train *v)
3239{
3240 uint victims = 0;
3241
3242 /* do not crash train twice */
3243 if (!v->vehstatus.Test(VehState::Crashed)) {
3244 victims = v->Crash();
3245 TileIndex tile = v->GetMovingFront()->tile;
3246 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, tile, ScriptEventVehicleCrashed::CRASH_TRAIN, victims, v->owner));
3247 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, tile, ScriptEventVehicleCrashed::CRASH_TRAIN, victims, v->owner));
3248 }
3249
3250 /* Try to re-reserve track under already crashed train too.
3251 * Crash() clears the reservation! */
3253
3254 return victims;
3255}
3256
3263static uint CheckTrainCollision(Vehicle *v, Train *moving_front)
3264{
3265 /* Make sure we are a train, and are not in a depot. */
3266 if (v->type != VehicleType::Train) return 0;
3267
3268 /* We can't crash into trains in a depot. */
3269 if (Train::From(v)->track == TRACK_BIT_DEPOT) return 0;
3270
3271 /* Do not crash into trains of another company. */
3272 if (v->owner != moving_front->First()->owner) return 0;
3273
3274 /* Do not collide with our own wagons */
3275 if (v->First() == moving_front->First()) return 0;
3276
3277 int x_diff = v->x_pos - moving_front->x_pos;
3278 int y_diff = v->y_pos - moving_front->y_pos;
3279
3280 /* Do fast calculation to check whether trains are not in close vicinity
3281 * and quickly reject trains distant enough for any collision.
3282 * Differences are shifted by 7, mapping range [-7 .. 8] into [0 .. 15]
3283 * Differences are then ORed and then we check for any higher bits */
3284 uint hash = (y_diff + 7) | (x_diff + 7);
3285 if (hash & ~15) return 0;
3286
3287 /* Slower check using multiplication */
3288 int min_diff = (Train::From(v)->gcache.cached_veh_length + 1) / 2 + (moving_front->gcache.cached_veh_length + 1) / 2 - 1;
3289 if (x_diff * x_diff + y_diff * y_diff > min_diff * min_diff) return 0;
3290
3291 /* Happens when there is a train under bridge next to bridge head */
3292 if (abs(v->z_pos - moving_front->z_pos) > 5) return 0;
3293
3294 /* Crash both trains. Two statements required to guarantee execution
3295 * order because RandomRange() is involved. */
3296 uint num_victims = TrainCrashed(moving_front->First());
3297 return num_victims + TrainCrashed(Train::From(v)->First());
3298}
3299
3308static bool CheckTrainCollision(Train *moving_front)
3309{
3310 /* can't collide in depot */
3311 if (moving_front->track == TRACK_BIT_DEPOT) return false;
3312
3313 assert(moving_front->track == TRACK_BIT_WORMHOLE || TileVirtXY(moving_front->x_pos, moving_front->y_pos) == moving_front->tile);
3314
3315 uint num_victims = 0;
3316
3317 /* find colliding vehicles */
3318 if (moving_front->track == TRACK_BIT_WORMHOLE) {
3319 for (Vehicle *u : VehiclesOnTile(moving_front->tile)) {
3320 num_victims += CheckTrainCollision(u, moving_front);
3321 }
3322 for (Vehicle *u : VehiclesOnTile(GetOtherTunnelBridgeEnd(moving_front->tile))) {
3323 num_victims += CheckTrainCollision(u, moving_front);
3324 }
3325 } else {
3326 for (Vehicle *u : VehiclesNearTileXY(moving_front->x_pos, moving_front->y_pos, 7)) {
3327 num_victims += CheckTrainCollision(u, moving_front);
3328 }
3329 }
3330
3331 /* any dead -> no crash */
3332 if (num_victims == 0) return false;
3333
3334 AddTileNewsItem(GetEncodedString(STR_NEWS_TRAIN_CRASH, num_victims), NewsType::Accident, moving_front->tile);
3335
3336 ModifyStationRatingAround(moving_front->tile, moving_front->First()->owner, -160, 30);
3337 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_13_TRAIN_COLLISION, moving_front);
3338 return true;
3339}
3340
3348bool TrainController(Train *v, Vehicle *nomove, bool reverse)
3349{
3350 Train *first = v->First();
3351 Train *prev;
3352 bool direction_changed = false; // has direction of any part changed?
3353
3354 /* For every vehicle after and including the given vehicle */
3355 for (prev = v->GetMovingPrev(); v != nomove; prev = v, v = v->GetMovingNext()) {
3357 bool update_signals_crossing = false; // will we update signals or crossing state?
3358
3360 if (v->track != TRACK_BIT_WORMHOLE) {
3361 /* Not inside tunnel */
3362 if (gp.old_tile == gp.new_tile) {
3363 /* Staying in the old tile */
3364 if (v->track == TRACK_BIT_DEPOT) {
3365 /* Inside depot */
3366 gp.x = v->x_pos;
3367 gp.y = v->y_pos;
3368 } else {
3369 /* Not inside depot */
3370
3371 /* Reverse when we are at the end of the track already, do not move to the new position */
3372 if (v->IsMovingFront() && !TrainCheckIfLineEnds(v, reverse)) return false;
3373
3374 auto vets = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
3375 if (vets.Test(VehicleEnterTileState::CannotEnter)) {
3376 goto invalid_rail;
3377 }
3379 /* The new position is the end of the platform */
3381 }
3382 }
3383 } else {
3384 /* A new tile is about to be entered. */
3385
3386 /* Determine what direction we're entering the new tile from */
3387 enterdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile);
3388 assert(IsValidDiagDirection(enterdir));
3389
3390 /* Get the status of the tracks in the new tile and mask
3391 * away the bits that aren't reachable. */
3393 TrackdirBits reachable_trackdirs = DiagdirReachesTrackdirs(enterdir);
3394
3395 TrackdirBits trackdirbits = ts.trackdirs & reachable_trackdirs;
3396 TrackBits red_signals = TrackdirBitsToTrackBits(ts.signals & reachable_trackdirs);
3397
3398 TrackBits bits = TrackdirBitsToTrackBits(trackdirbits);
3399 if (Rail90DegTurnDisallowed(GetTileRailType(gp.old_tile), GetTileRailType(gp.new_tile)) && prev == nullptr) {
3400 /* We allow wagons to make 90 deg turns, because forbid_90_deg
3401 * can be switched on halfway a turn */
3403 }
3404
3405 if (bits == TRACK_BIT_NONE) goto invalid_rail;
3406
3407 /* Check if the new tile constrains tracks that are compatible
3408 * with the current train, if not, bail out. */
3409 if (!CheckCompatibleRail(v->First(), gp.new_tile, v->IsMovingFront())) goto invalid_rail;
3410
3411 TrackBits chosen_track;
3412 if (v->IsMovingFront()) {
3413 /* Currently the locomotive is active. Determine which one of the
3414 * available tracks to choose */
3415 chosen_track = TrackToTrackBits(ChooseTrainTrack(first, gp.new_tile, enterdir, bits, false, nullptr, true));
3416 assert(chosen_track & (bits | GetReservedTrackbits(gp.new_tile)));
3417
3418 if (first->force_proceed != TFP_NONE && IsPlainRailTile(gp.new_tile) && HasSignals(gp.new_tile)) {
3419 /* For each signal we find decrease the counter by one.
3420 * We start at two, so the first signal we pass decreases
3421 * this to one, then if we reach the next signal it is
3422 * decreased to zero and we won't pass that new signal. */
3423 Trackdir dir = FindFirstTrackdir(trackdirbits);
3424 if (HasSignalOnTrackdir(gp.new_tile, dir) ||
3427 /* However, we do not want to be stopped by PBS signals
3428 * entered via the back. */
3429 first->force_proceed = (first->force_proceed == TFP_SIGNAL) ? TFP_STUCK : TFP_NONE;
3430 InvalidateWindowData(WindowClass::VehicleView, first->index);
3431 }
3432 }
3433
3434 /* Check if it's a red signal and that force proceed is not clicked. */
3435 if ((red_signals & chosen_track) && first->force_proceed == TFP_NONE) {
3436 /* In front of a red signal */
3437 Trackdir i = FindFirstTrackdir(trackdirbits);
3438
3439 /* Don't handle stuck trains here. */
3440 if (first->flags.Test(VehicleRailFlag::Stuck)) return false;
3441
3443 first->cur_speed = 0;
3444 first->subspeed = 0;
3445 first->progress = 255; // make sure that every bit of acceleration will hit the signal again, so speed stays 0.
3446 if (!_settings_game.pf.reverse_at_signals || ++first->wait_counter < _settings_game.pf.wait_oneway_signal * Ticks::DAY_TICKS * 2) return false;
3447 } else if (HasSignalOnTrackdir(gp.new_tile, i)) {
3448 first->cur_speed = 0;
3449 first->subspeed = 0;
3450 first->progress = 255; // make sure that every bit of acceleration will hit the signal again, so speed stays 0.
3451 if (!_settings_game.pf.reverse_at_signals || ++first->wait_counter < _settings_game.pf.wait_twoway_signal * Ticks::DAY_TICKS * 2) {
3452 DiagDirection exitdir = TrackdirToExitdir(i);
3453 TileIndex o_tile = TileAddByDiagDir(gp.new_tile, exitdir);
3454
3455 exitdir = ReverseDiagDir(exitdir);
3456
3457 /* check if a train is waiting on the other side */
3458 if (!HasVehicleOnTile(o_tile, [&exitdir](const Vehicle *u) {
3459 if (u->type != VehicleType::Train || u->vehstatus.Test(VehState::Crashed)) return false;
3460 const Train *t = Train::From(u);
3461
3462 /* not front engine of a train, inside wormhole or depot, crashed */
3463 if (!t->IsFrontEngine() || !(t->track & TRACK_BIT_MASK)) return false;
3464
3465 if (t->cur_speed > 5 || VehicleExitDir(t->direction, t->track) != exitdir) return false;
3466
3467 return true;
3468 })) return false;
3469 }
3470 }
3471
3472 /* If we would reverse but are currently in a PBS block and
3473 * reversing of stuck trains is disabled, don't reverse.
3474 * This does not apply if the reason for reversing is a one-way
3475 * signal blocking us, because a train would then be stuck forever. */
3476 if (!_settings_game.pf.reverse_at_signals && !HasOnewaySignalBlockingTrackdir(gp.new_tile, i) &&
3477 UpdateSignalsOnSegment(v->tile, enterdir, v->owner) == SIGSEG_PBS) {
3478 first->wait_counter = 0;
3479 return false;
3480 }
3481 goto reverse_train_direction;
3482 } else {
3483 TryReserveRailTrack(gp.new_tile, TrackBitsToTrack(chosen_track), false);
3484 }
3485 } else {
3486 /* The wagon is active, simply follow the prev vehicle. */
3487 if (prev->tile == gp.new_tile) {
3488 /* Choose the same track as prev */
3489 if (prev->track == TRACK_BIT_WORMHOLE) {
3490 /* Vehicles entering tunnels enter the wormhole earlier than for bridges.
3491 * However, just choose the track into the wormhole. */
3492 assert(IsTunnel(prev->tile));
3493 chosen_track = bits;
3494 } else {
3495 chosen_track = prev->track;
3496 }
3497 } else {
3498 /* Choose the track that leads to the tile where prev is.
3499 * This case is active if 'prev' is already on the second next tile, when 'v' just enters the next tile.
3500 * I.e. when the tile between them has only space for a single vehicle like
3501 * 1) horizontal/vertical track tiles and
3502 * 2) some orientations of tunnel entries, where the vehicle is already inside the wormhole at 8/16 from the tile edge.
3503 * Is also the train just reversing, the wagon inside the tunnel is 'on' the tile of the opposite tunnel entry.
3504 */
3505 static const DiagDirectionIndexArray<DiagDirectionIndexArray<TrackBits>> _connecting_track{{{
3510 }}};
3511 DiagDirection exitdir = DiagdirBetweenTiles(gp.new_tile, prev->tile);
3512 assert(IsValidDiagDirection(exitdir));
3513 chosen_track = _connecting_track[enterdir][exitdir];
3514 }
3515 chosen_track &= bits;
3516 }
3517
3518 /* Update XY to reflect the entrance to the new tile, and select the direction to use */
3519 Direction chosen_dir = VehicleEnterTileCoordinates(gp, enterdir, TrackBitsToTrack(chosen_track));
3520
3521 /* Call the landscape function and tell it that the vehicle entered the tile */
3522 auto vets = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
3523 if (vets.Test(VehicleEnterTileState::CannotEnter)) {
3524 goto invalid_rail;
3525 }
3526
3528 Track track = FindFirstTrack(chosen_track);
3529 Trackdir tdir = TrackDirectionToTrackdir(track, chosen_dir);
3530 if (v->IsMovingFront() && HasPbsSignalOnTrackdir(gp.new_tile, tdir)) {
3533 }
3534
3535 /* Clear any track reservation when the last vehicle leaves the tile */
3536 if (v->GetMovingNext() == nullptr) ClearPathReservation(v, v->tile, v->GetVehicleTrackdir());
3537
3538 v->tile = gp.new_tile;
3539
3541 first->ConsistChanged(CCF_TRACK);
3542 }
3543
3544 v->track = chosen_track;
3545 assert(v->track);
3546 }
3547
3548 /* We need to update signal status, but after the vehicle position hash
3549 * has been updated by UpdateInclination() */
3550 update_signals_crossing = true;
3551
3552 if (chosen_dir != v->GetMovingDirection()) {
3553 if (prev == nullptr && _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) {
3554 const AccelerationSlowdownParams *asp = &_accel_slowdown[static_cast<int>(v->GetAccelerationType())];
3555 DirDiff diff = DirDifference(v->direction, chosen_dir);
3556 v->cur_speed -= (diff == DirDiff::Right45 || diff == DirDiff::Left45 ? asp->small_turn : asp->large_turn) * v->cur_speed >> 8;
3557 }
3558 direction_changed = true;
3559 v->SetMovingDirection(chosen_dir);
3560 }
3561
3562 if (v->IsMovingFront()) {
3563 first->wait_counter = 0;
3564
3565 /* If we are approaching a crossing that is reserved, play the sound now. */
3566 TileIndex crossing = TrainApproachingCrossingTile(v); // We know we are the moving front, so we can check v.
3567 if (crossing != INVALID_TILE && HasCrossingReservation(crossing) && _settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, crossing);
3568
3569 /* Always try to extend the reservation when entering a tile. */
3570 CheckNextTrainTile(first);
3571 }
3572
3574 /* The new position is the location where we want to stop */
3576 }
3577 }
3578 } else {
3580 /* Perform look-ahead on tunnel exit. */
3581 if (v->IsMovingFront()) {
3583 CheckNextTrainTile(first);
3584 }
3585 /* Prevent v->UpdateInclination() being called with wrong parameters.
3586 * This could happen if the train was reversed inside the tunnel/bridge. */
3587 if (gp.old_tile == gp.new_tile) {
3589 }
3590 } else {
3591 v->x_pos = gp.x;
3592 v->y_pos = gp.y;
3593 v->UpdatePosition();
3594 if (!v->vehstatus.Test(VehState::Hidden)) v->Vehicle::UpdateViewport(true);
3595 continue;
3596 }
3597 }
3598
3599 /* update image of train, as well as delta XY */
3600 v->UpdateDeltaXY();
3601
3602 v->x_pos = gp.x;
3603 v->y_pos = gp.y;
3604 v->UpdatePosition();
3605
3606 /* update the Z position of the vehicle */
3607 int old_z = v->UpdateInclination(gp.new_tile != gp.old_tile, false);
3608
3609 if (prev == nullptr) {
3610 /* This is the first vehicle in the train */
3611 AffectSpeedByZChange(first, v->z_pos - old_z);
3612 }
3613
3614 if (update_signals_crossing) {
3615 if (v->IsMovingFront()) {
3616 if (TrainMovedChangeSignals(gp.new_tile, enterdir)) {
3617 /* We are entering a block with PBS signals right now, but
3618 * not through a PBS signal. This means we don't have a
3619 * reservation right now. As a conventional signal will only
3620 * ever be green if no other train is in the block, getting
3621 * a path should always be possible. If the player built
3622 * such a strange network that it is not possible, the train
3623 * will be marked as stuck and the player has to deal with
3624 * the problem. */
3625 if ((!HasReservedTracks(gp.new_tile, v->track) &&
3627 !TryPathReserve(first)) {
3628 MarkTrainAsStuck(first);
3629 }
3630 }
3631 }
3632
3633 /* Signals can only change when the first
3634 * (above) or the last vehicle moves. */
3635 if (v->GetMovingNext() == nullptr) {
3636 TrainMovedChangeSignals(gp.old_tile, ReverseDiagDir(enterdir));
3638 }
3639 }
3640
3641 /* Do not check on every tick to save some computing time. */
3642 if (v->IsMovingFront() && first->tick_counter % _settings_game.pf.path_backoff_interval == 0) CheckNextTrainTile(first);
3643 }
3644
3645 if (direction_changed) first->tcache.cached_max_curve_speed = first->GetCurveSpeedLimit();
3646
3647 return true;
3648
3649invalid_rail:
3650 /* We've reached end of line?? */
3651 if (prev != nullptr) FatalError("Disconnecting train");
3652
3653reverse_train_direction:
3654 if (reverse) {
3655 first->wait_counter = 0;
3656 first->cur_speed = 0;
3657 first->subspeed = 0;
3658 ReverseTrainDirection(first);
3659 }
3660
3661 return false;
3662}
3663
3664static bool IsRailStationPlatformOccupied(TileIndex tile)
3665{
3667
3668 for (TileIndex t = tile; IsCompatibleTrainStationTile(t, tile); t -= delta) {
3669 if (HasVehicleOnTile(t, IsTrain)) return true;
3670 }
3671 for (TileIndex t = tile + delta; IsCompatibleTrainStationTile(t, tile); t += delta) {
3672 if (HasVehicleOnTile(t, IsTrain)) return true;
3673 }
3674
3675 return false;
3676}
3677
3685static void DeleteLastWagon(Train *v)
3686{
3687 Train *first = v->First();
3688
3689 /* Go to the last wagon and delete the link pointing there
3690 * new_last is then the one-before-last wagon, and v the last
3691 * one which will physically be removed */
3692 Train *new_last = v;
3693 for (; v->Next() != nullptr; v = v->Next()) new_last = v;
3694 new_last->SetNext(nullptr);
3695
3696 if (first != v) {
3697 /* Recalculate cached train properties */
3699 /* Update the depot window in case a part of the consist is in a depot. */
3700 SetWindowDirty(WindowClass::VehicleDepot, first->tile);
3701 SetWindowDirty(WindowClass::VehicleDepot, v->tile);
3702 }
3703
3704 /* 'v' shouldn't be accessed after it has been deleted */
3705 TrackBits trackbits = v->track;
3706 TileIndex tile = v->tile;
3707 Owner owner = v->owner;
3708
3709 delete v;
3710 v = nullptr; // make sure nobody will try to read 'v' anymore
3711
3712 if (trackbits == TRACK_BIT_WORMHOLE) {
3713 /* Vehicle is inside a wormhole, v->track contains no useful value then. */
3715 }
3716
3717 Track track = TrackBitsToTrack(trackbits);
3718 if (HasReservedTracks(tile, trackbits)) {
3719 UnreserveRailTrack(tile, track);
3720
3721 /* If there are still crashed vehicles on the tile, give the track reservation to them */
3722 TrackBits remaining_trackbits = TRACK_BIT_NONE;
3723 for (const Vehicle *u : VehiclesOnTile(tile)) {
3724 if (u->type != VehicleType::Train || !u->vehstatus.Test(VehState::Crashed)) continue;
3725 TrackBits train_tbits = Train::From(u)->track;
3726 if (train_tbits == TRACK_BIT_WORMHOLE) {
3727 /* Vehicle is inside a wormhole, u->track contains no useful value then. */
3728 remaining_trackbits |= DiagDirToDiagTrackBits(GetTunnelBridgeDirection(u->tile));
3729 } else if (train_tbits != TRACK_BIT_DEPOT) {
3730 remaining_trackbits |= train_tbits;
3731 }
3732 }
3733
3734 /* It is important that these two are the first in the loop, as reservation cannot deal with every trackbit combination */
3735 assert(TRACK_BEGIN == TRACK_X && TRACK_Y == TRACK_BEGIN + 1);
3736 for (Track t : SetTrackBitIterator(remaining_trackbits)) TryReserveRailTrack(tile, t);
3737 }
3738
3739 /* check if the wagon was on a road/rail-crossing */
3741
3742 if (IsRailStationTile(tile)) {
3743 bool occupied = IsRailStationPlatformOccupied(tile);
3745 SetRailStationPlatformReservation(tile, dir, occupied);
3747 }
3748
3749 /* Update signals */
3752 } else {
3753 SetSignalsOnBothDir(tile, track, owner);
3754 }
3755}
3756
3762{
3763 static const DirDiff delta[] = {
3765 };
3766
3767 do {
3768 /* We don't need to twist around vehicles if they're not visible */
3769 if (!v->vehstatus.Test(VehState::Hidden)) {
3770 v->direction = ChangeDir(v->direction, delta[GB(Random(), 0, 2)]);
3771 /* Refrain from updating the z position of the vehicle when on
3772 * a bridge, because UpdateInclination() will put the vehicle under
3773 * the bridge in that case */
3774 if (v->track != TRACK_BIT_WORMHOLE) {
3775 v->UpdatePosition();
3776 v->UpdateInclination(false, true);
3777 } else {
3778 v->UpdateViewport(false, true);
3779 }
3780 }
3781 } while ((v = v->Next()) != nullptr);
3782}
3783
3790{
3791 int state = ++v->crash_anim_pos;
3792
3793 if (state == 4 && !v->vehstatus.Test(VehState::Hidden)) {
3795 }
3796
3797 uint32_t r;
3798 if (state <= 200 && Chance16R(1, 7, r)) {
3799 int index = (r * 10 >> 16);
3800
3801 Vehicle *u = v;
3802 do {
3803 if (--index < 0) {
3804 r = Random();
3805
3807 GB(r, 8, 3) + 2,
3808 GB(r, 16, 3) + 2,
3809 GB(r, 0, 3) + 5,
3811 break;
3812 }
3813 } while ((u = u->Next()) != nullptr);
3814 }
3815
3816 if (state <= 240 && !(v->tick_counter & 3)) ChangeTrainDirRandomly(v);
3817
3818 if (state >= 4440 && !(v->tick_counter & 0x1F)) {
3819 bool ret = v->Next() != nullptr;
3820 DeleteLastWagon(v);
3821 return ret;
3822 }
3823
3824 return true;
3825}
3826
3828static const uint16_t _breakdown_speeds[16] = {
3829 225, 210, 195, 180, 165, 150, 135, 120, 105, 90, 75, 60, 45, 30, 15, 15
3830};
3831
3832
3841static bool TrainApproachingLineEnd(Train *moving_front, bool signal, bool reverse)
3842{
3843 /* Calc position within the current tile */
3844 uint x = moving_front->x_pos & 0xF;
3845 uint y = moving_front->y_pos & 0xF;
3846
3847 Direction vdir = moving_front->GetMovingDirection();
3848
3849 /* for diagonal directions, 'x' will be 0..15 -
3850 * for other directions, it will be 1, 3, 5, ..., 15 */
3851 switch (vdir) {
3852 case Direction::N : x = ~x + ~y + 25; break;
3853 case Direction::NW: x = y; [[fallthrough]];
3854 case Direction::NE: x = ~x + 16; break;
3855 case Direction::E : x = ~x + y + 9; break;
3856 case Direction::SE: x = y; break;
3857 case Direction::S : x = x + y - 7; break;
3858 case Direction::W : x = ~y + x + 9; break;
3859 default: break;
3860 }
3861
3862 Train *consist = moving_front->First();
3863
3864 /* Do not reverse when approaching red signal. Make sure the vehicle's front
3865 * does not cross the tile boundary when we do reverse, but as the vehicle's
3866 * location is based on their center, use half a vehicle's length as offset.
3867 * Multiply the half-length by two for straight directions to compensate that
3868 * we only get odd x offsets there. */
3869 uint8_t rounding = moving_front->IsDrivingBackwards() ? 0 : 1;
3870 if (!signal && x + (moving_front->gcache.cached_veh_length + rounding) / 2 * (IsDiagonalDirection(vdir) ? 1 : 2) >= TILE_SIZE) {
3871 /* we are too near the tile end, reverse now */
3872 consist->cur_speed = 0;
3873 if (reverse) ReverseTrainDirection(consist);
3874 return false;
3875 }
3876
3877 /* slow down */
3879 uint16_t break_speed = _breakdown_speeds[x & 0xF];
3880 if (break_speed < consist->cur_speed) consist->cur_speed = break_speed;
3881
3882 return true;
3883}
3884
3885
3891static bool TrainCanLeaveTile(const Train *moving_front)
3892{
3893 /* Exit if inside a tunnel/bridge or a depot */
3894 if (moving_front->track == TRACK_BIT_WORMHOLE || moving_front->track == TRACK_BIT_DEPOT) return false;
3895
3896 TileIndex tile = moving_front->tile;
3897
3898 /* entering a tunnel/bridge? */
3901 if (DiagDirToDir(dir) == moving_front->GetMovingDirection()) return false;
3902 }
3903
3904 /* entering a depot? */
3905 if (IsRailDepotTile(tile)) {
3907 if (DiagDirToDir(dir) == moving_front->GetMovingDirection()) return false;
3908 }
3909
3910 return true;
3911}
3912
3913
3922{
3923 assert(moving_front->IsMovingFront());
3924 assert(!moving_front->First()->vehstatus.Test(VehState::Crashed));
3925
3926 if (!TrainCanLeaveTile(moving_front)) return INVALID_TILE;
3927
3928 DiagDirection dir = VehicleExitDir(moving_front->GetMovingDirection(), moving_front->track);
3929 TileIndex tile = moving_front->tile + TileOffsByDiagDir(dir);
3930
3931 /* not a crossing || wrong axis || unusable rail (wrong type or owner) */
3932 if (!IsLevelCrossingTile(tile) || DiagDirToAxis(dir) == GetCrossingRoadAxis(tile) ||
3933 !CheckCompatibleRail(moving_front->First(), tile, true)) {
3934 return INVALID_TILE;
3935 }
3936
3937 return tile;
3938}
3939
3940
3948static bool TrainCheckIfLineEnds(Train *moving_front, bool reverse)
3949{
3950 /* First, handle broken down train */
3951
3952 Train *consist = moving_front->First();
3953 int t = consist->breakdown_ctr;
3954 if (t > 1) {
3956
3957 uint16_t break_speed = _breakdown_speeds[GB(~t, 4, 4)];
3958 if (break_speed < consist->cur_speed) consist->cur_speed = break_speed;
3959 } else {
3961 }
3962
3963 if (!TrainCanLeaveTile(moving_front)) return true;
3964
3965 /* Determine the non-diagonal direction in which we will exit this tile */
3966 DiagDirection dir = VehicleExitDir(moving_front->GetMovingDirection(), moving_front->track);
3967 /* Calculate next tile */
3968 TileIndex tile = moving_front->tile + TileOffsByDiagDir(dir);
3969
3970 /* Determine the track status on the next tile */
3972 TrackdirBits reachable_trackdirs = DiagdirReachesTrackdirs(dir);
3973
3974 TrackdirBits trackdirbits = ts.trackdirs & reachable_trackdirs;
3975 TrackdirBits red_signals = ts.signals & reachable_trackdirs;
3976
3977 /* We are sure the train is not entering a depot, it is detected above */
3978
3979 /* mask unreachable track bits if we are forbidden to do 90deg turns */
3980 TrackBits bits = TrackdirBitsToTrackBits(trackdirbits);
3981 if (Rail90DegTurnDisallowed(GetTileRailType(moving_front->tile), GetTileRailType(tile))) {
3982 bits &= ~TrackCrossesTracks(FindFirstTrack(moving_front->track));
3983 }
3984
3985 /* no suitable trackbits at all || unusable rail (wrong type or owner) */
3986 if (bits == TRACK_BIT_NONE || !CheckCompatibleRail(consist, tile, true)) {
3987 return TrainApproachingLineEnd(moving_front, false, reverse);
3988 }
3989
3990 /* approaching red signal */
3991 if ((trackdirbits & red_signals) != 0) return TrainApproachingLineEnd(moving_front, true, reverse);
3992
3993 /* approaching a rail/road crossing? then make it red */
3995
3996 return true;
3997}
3998
4005static bool TrainLocoHandler(Train *consist, bool mode)
4006{
4007 /* train has crashed? */
4008 if (consist->vehstatus.Test(VehState::Crashed)) {
4009 return mode ? true : HandleCrashedTrain(consist); // 'this' can be deleted here
4010 }
4011
4012 if (consist->force_proceed != TFP_NONE) {
4014 SetWindowWidgetDirty(WindowClass::VehicleView, consist->index, WID_VV_START_STOP);
4015 }
4016
4017 /* train is broken down? */
4018 if (consist->HandleBreakdown()) return true;
4019
4020 if (consist->flags.Test(VehicleRailFlag::Reversing) && consist->cur_speed == 0) {
4021 ReverseTrainDirection(consist);
4022 }
4023
4024 /* exit if train is stopped */
4025 if (consist->vehstatus.Test(VehState::Stopped) && consist->cur_speed == 0) return true;
4026
4027 bool valid_order = !consist->current_order.IsType(OT_NOTHING) && consist->current_order.GetType() != OT_CONDITIONAL;
4028 if (ProcessOrders(consist) && CheckReverseTrain(consist)) {
4029 consist->wait_counter = 0;
4030 consist->cur_speed = 0;
4031 consist->subspeed = 0;
4033 ReverseTrainDirection(consist);
4034 return true;
4035 } else if (consist->flags.Test(VehicleRailFlag::LeavingStation)) {
4036 /* Try to reserve a path when leaving the station as we
4037 * might not be marked as wanting a reservation, e.g.
4038 * when an overlength train gets turned around in a station. */
4039 const Train *moving_front = consist->GetMovingFront();
4040 DiagDirection dir = VehicleExitDir(moving_front->GetMovingDirection(), moving_front->track);
4041 if (IsRailDepotTile(moving_front->tile) || IsTileType(moving_front->tile, TileType::TunnelBridge)) dir = DiagDirection::Invalid;
4042
4043 if (UpdateSignalsOnSegment(moving_front->tile, dir, consist->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
4044 TryPathReserve(consist, true, true);
4045 }
4047 }
4048
4049 consist->HandleLoading(mode);
4050
4051 if (consist->current_order.IsType(OT_LOADING)) return true;
4052
4053 if (CheckTrainStayInDepot(consist)) return true;
4054
4055 if (!mode) consist->ShowVisualEffect();
4056
4057 /* We had no order but have an order now, do look ahead. */
4058 if (!valid_order && !consist->current_order.IsType(OT_NOTHING)) {
4059 CheckNextTrainTile(consist);
4060 }
4061
4062 /* Handle stuck trains. */
4063 if (!mode && consist->flags.Test(VehicleRailFlag::Stuck)) {
4064 ++consist->wait_counter;
4065
4066 /* Should we try reversing this tick if still stuck? */
4067 bool turn_around = consist->wait_counter % (_settings_game.pf.wait_for_pbs_path * Ticks::DAY_TICKS) == 0 && _settings_game.pf.reverse_at_signals;
4068
4069 if (!turn_around && consist->wait_counter % _settings_game.pf.path_backoff_interval != 0 && consist->force_proceed == TFP_NONE) return true;
4070 if (!TryPathReserve(consist)) {
4071 /* Still stuck. */
4072 if (turn_around) ReverseTrainDirection(consist);
4073
4074 if (consist->flags.Test(VehicleRailFlag::Stuck) && consist->wait_counter > 2 * _settings_game.pf.wait_for_pbs_path * Ticks::DAY_TICKS) {
4075 /* Show message to player. */
4076 if (_settings_client.gui.lost_vehicle_warn && consist->owner == _local_company) {
4077 AddVehicleAdviceNewsItem(AdviceType::TrainStuck, GetEncodedString(STR_NEWS_TRAIN_IS_STUCK, consist->index), consist->index);
4078 }
4079 consist->wait_counter = 0;
4080 }
4081 /* Exit if force proceed not pressed, else reset stuck flag anyway. */
4082 if (consist->force_proceed == TFP_NONE) return true;
4084 consist->wait_counter = 0;
4085 SetWindowWidgetDirty(WindowClass::VehicleView, consist->index, WID_VV_START_STOP);
4086 }
4087 }
4088
4089 if (consist->current_order.IsType(OT_LEAVESTATION)) {
4090 consist->current_order.Free();
4091 SetWindowWidgetDirty(WindowClass::VehicleView, consist->index, WID_VV_START_STOP);
4092 return true;
4093 }
4094
4095 int j = consist->UpdateSpeed();
4096
4097 /* we need to invalidate the widget if we are stopping from 'Stopping 0 km/h' to 'Stopped' */
4098 if (consist->cur_speed == 0 && consist->vehstatus.Test(VehState::Stopped)) {
4099 /* If we manually stopped, we're not force-proceeding anymore. */
4100 consist->force_proceed = TFP_NONE;
4101 InvalidateWindowData(WindowClass::VehicleView, consist->index);
4102 }
4103
4104 Train* moving_front = consist->GetMovingFront();
4105 int adv_spd = moving_front->GetAdvanceDistance();
4106 if (j < adv_spd) {
4107 /* if the vehicle has speed 0, update the last_speed field. */
4108 if (consist->cur_speed == 0) consist->SetLastSpeed();
4109 } else {
4110 TrainCheckIfLineEnds(moving_front);
4111 moving_front = moving_front->GetMovingFront();
4112 /* Loop until the train has finished moving. */
4113 for (;;) {
4114 j -= adv_spd;
4115 TrainController(moving_front, nullptr);
4116 moving_front = moving_front->GetMovingFront();
4117 /* Don't continue to move if the train crashed. */
4118 if (CheckTrainCollision(moving_front)) break;
4119 /* Determine distance to next map position */
4120 adv_spd = moving_front->GetAdvanceDistance();
4121
4122 /* No more moving this tick */
4123 if (j < adv_spd || consist->cur_speed == 0) break;
4124
4125 OrderType order_type = consist->current_order.GetType();
4126 /* Do not skip waypoints (incl. 'via' stations) when passing through at full speed. */
4127 if ((order_type == OT_GOTO_WAYPOINT || order_type == OT_GOTO_STATION) &&
4129 IsTileType(moving_front->tile, TileType::Station) &&
4130 consist->current_order.GetDestination() == GetStationIndex(moving_front->tile)) {
4131 ProcessOrders(consist);
4132 }
4133 }
4134 consist->SetLastSpeed();
4135 }
4136
4137 for (Train *u = consist; u != nullptr; u = u->Next()) {
4138 if (u->vehstatus.Test(VehState::Hidden)) continue;
4139
4140 u->UpdateViewport(false, false);
4141 }
4142
4143 if (consist->progress == 0) consist->progress = j; // Save unused spd for next time, if TrainController didn't set progress
4144
4145 return true;
4146}
4147
4153{
4154 Money cost = 0;
4155 const Train *v = this;
4156
4157 do {
4158 const Engine *e = v->GetEngine();
4159 if (e->VehInfo<RailVehicleInfo>().running_cost_class == Price::Invalid) continue;
4160
4161 uint cost_factor = GetVehicleProperty(v, PROP_TRAIN_RUNNING_COST_FACTOR, e->VehInfo<RailVehicleInfo>().running_cost);
4162 if (cost_factor == 0) continue;
4163
4164 /* Halve running cost for multiheaded parts */
4165 if (v->IsMultiheaded()) cost_factor /= 2;
4166
4167 cost += GetPrice(e->VehInfo<RailVehicleInfo>().running_cost_class, cost_factor, e->GetGRF());
4168 } while ((v = v->GetNextVehicle()) != nullptr);
4169
4170 return cost;
4171}
4172
4178{
4179 this->tick_counter++;
4180
4181 if (this->IsFrontEngine()) {
4183
4184 if (!this->vehstatus.Test(VehState::Stopped) || this->cur_speed > 0) this->running_ticks++;
4185
4186 this->current_order_time++;
4187
4188 if (!TrainLocoHandler(this, false)) return false;
4189
4190 return TrainLocoHandler(this, true);
4191 } else if (this->IsFreeWagon() && this->vehstatus.Test(VehState::Crashed)) {
4192 /* Delete flooded standalone wagon chain */
4193 if (++this->crash_anim_pos >= 4400) {
4194 delete this;
4195 return false;
4196 }
4197 }
4198
4199 return true;
4200}
4201
4207{
4208 if (Company::Get(v->owner)->settings.vehicle.servint_trains == 0 || !v->NeedsAutomaticServicing()) return;
4209 if (v->IsChainInDepot()) {
4211 return;
4212 }
4213
4214 uint max_penalty = _settings_game.pf.yapf.maximum_go_to_depot_penalty;
4215
4216 FindDepotData tfdd = FindClosestTrainDepot(v, max_penalty);
4217 /* Only go to the depot if it is not too far out of our way. */
4218 if (tfdd.best_length == UINT_MAX || tfdd.best_length > max_penalty) {
4219 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
4220 /* If we were already heading for a depot but it has
4221 * suddenly moved farther away, we continue our normal
4222 * schedule? */
4224 SetWindowWidgetDirty(WindowClass::VehicleView, v->index, WID_VV_START_STOP);
4225 }
4226 return;
4227 }
4228
4229 DepotID depot = GetDepotIndex(tfdd.tile);
4230
4231 if (v->current_order.IsType(OT_GOTO_DEPOT) &&
4232 v->current_order.GetDestination() != depot &&
4233 !Chance16(3, 16)) {
4234 return;
4235 }
4236
4239 v->dest_tile = tfdd.tile;
4240 SetWindowWidgetDirty(WindowClass::VehicleView, v->index, WID_VV_START_STOP);
4241}
4242
4245{
4246 AgeVehicle(this);
4247}
4248
4251{
4252 EconomyAgeVehicle(this);
4253
4254 if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
4255
4256 if (this->IsFrontEngine()) {
4258
4260
4261 CheckOrders(this);
4262
4263 /* update destination */
4264 if (this->current_order.IsType(OT_GOTO_STATION)) {
4265 TileIndex tile = Station::Get(this->current_order.GetDestination().ToStationID())->train_station.tile;
4266 if (tile != INVALID_TILE) this->dest_tile = tile;
4267 }
4268
4269 if (this->running_ticks != 0) {
4270 /* running costs */
4272
4273 this->profit_this_year -= cost.GetCost();
4274 this->running_ticks = 0;
4275
4277
4278 SetWindowDirty(WindowClass::VehicleDetails, this->index);
4279 SetWindowClassesDirty(WindowClass::TrainList);
4280 }
4281 }
4282}
4283
4289{
4290 if (this->vehstatus.Test(VehState::Crashed)) return INVALID_TRACKDIR;
4291
4292 if (this->track == TRACK_BIT_DEPOT) {
4293 /* We'll assume the train is facing outwards */
4294 return DiagDirToDiagTrackdir(GetRailDepotDirection(this->tile)); // Train in depot
4295 }
4296
4297 if (this->track == TRACK_BIT_WORMHOLE) {
4298 /* train in tunnel or on bridge, so just use its direction and assume a diagonal track */
4300 }
4301
4302 return TrackDirectionToTrackdir(FindFirstTrack(this->track), this->GetMovingDirection());
4303}
4304
4305uint16_t Train::GetMaxWeight() const
4306{
4307 uint16_t weight = CargoSpec::Get(this->cargo_type)->WeightOfNUnitsInTrain(this->GetEngine()->DetermineCapacity(this));
4308
4309 /* Vehicle weight is not added for articulated parts. */
4310 if (!this->IsArticulatedPart()) {
4311 weight += GetVehicleProperty(this, PROP_TRAIN_WEIGHT, RailVehInfo(this->engine_type)->weight);
4312 }
4313
4314 /* Powered wagons have extra weight added. */
4315 if (this->flags.Test(VehicleRailFlag::PoweredWagon)) {
4316 weight += RailVehInfo(this->gcache.first_engine)->pow_wag_weight;
4317 }
4318
4319 return weight;
4320}
Base functions for all AIs.
void AddArticulatedParts(Vehicle *first)
Add the remaining articulated parts to the given vehicle.
void CheckConsistencyOfArticulatedVehicle(const Vehicle *v)
Checks whether the specs of freshly build articulated vehicles are consistent with the information sp...
Functions related to articulated vehicles.
void CheckCargoCapacity(Vehicle *v)
Check the capacity of all vehicles in a chain and spread cargo if needed.
@ BuiltAsPrototype
Vehicle is a prototype (accepted as exclusive preview).
@ DrivingBackwards
Vehicle is driving backwards.
constexpr T AssignBit(T &x, const uint8_t y, bool value)
Assigns a bit in a variable.
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T ToggleBit(T &x, const uint8_t y)
Toggles a bit in a variable.
constexpr T KillFirstBit(T value)
Clear the first bit in an integer.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
const BridgeSpec * GetBridgeSpec(BridgeType i)
Get the specification of a bridge type.
Definition bridge.h:60
bool IsBridgeTile(Tile t)
checks if there is a bridge on this tile
Definition bridge_map.h:35
BridgeType GetBridgeType(Tile t)
Determines the type of bridge on a tile.
Definition bridge_map.h:56
bool IsBridge(Tile t)
Checks if this is a bridge, instead of a tunnel.
Definition bridge_map.h:24
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:110
CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:22
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition ai_core.cpp:221
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Flip()
Flip all bits.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Set()
Set all bits.
Common return value for all commands.
void AddCost(const Money &cost)
Adds the given cost to the cost of the command.
Money GetCost() const
The costs as made up to this moment.
bool Failed() const
Did this command fail?
uint32_t GetGRFID() const
Retrieve the GRF ID of the NewGRF the engine is tied to.
Definition engine.cpp:182
uint16_t reliability_spd_dec
Speed of reliability decay between services (per day).
Definition engine_base.h:50
const GRFFile * GetGRF() const
Retrieve the NewGRF the engine is tied to.
uint DetermineCapacity(const Vehicle *v, uint16_t *mail_capacity=nullptr) const
Determines capacity of a given vehicle from scratch.
Definition engine.cpp:226
EngineFlags flags
Flags of the engine.
Definition engine_base.h:57
uint8_t original_image_index
Original vehicle image index, thus the image index of the overridden vehicle.
Definition engine_base.h:61
TimerGameCalendar::Date GetLifeLengthInDays() const
Returns the vehicle's (not model's!) life length in days.
Definition engine.cpp:468
CargoType GetDefaultCargoType() const
Determines the default cargo type of an engine.
Definition engine_base.h:94
uint16_t reliability
Current reliability of the engine.
Definition engine_base.h:49
static void NewEvent(class ScriptEvent *event)
Queue a new event for the game script.
RAII class for measuring multi-step elements of performance.
This struct contains all the info that is needed to draw and construct tracks.
Definition rail.h:115
uint8_t curve_speed
Multiplier for curve maximum speed advantage.
Definition rail.h:195
static constexpr TimerGameTick::Ticks DAY_TICKS
1 day is 74 ticks; TimerGameCalendar::date_fract used to be uint16_t and incremented by 885.
static Date date
Current date in days (day counter).
static Year year
Current year, starting at 0.
static Date date
Current date in days (day counter).
This class will save the current order of a vehicle and restore it on destruction.
void Restore()
Restore the saved order to the vehicle.
~VehicleOrderSaver()
Restore the saved order to the vehicle, if Restore() has not already been called.
bool SwitchToNextOrder(bool skip_first)
Set the current vehicle order to the next order in the order list.
Iterate over all vehicles near a given world coordinate.
Iterate over all vehicles on a tile.
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Execute
execute the given command
@ NoCargoCapacityCheck
when autoreplace/autorenew is in progress, this shall prevent truncating the amount of cargo in the v...
@ AutoReplace
autoreplace/autorenew is in progress, this shall disable vehicle limits when building,...
Definition of stuff that is very close to a company, like the company struct itself.
CommandCost CheckOwnership(Owner owner, TileIndex tile)
Check whether the current owner owns something.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
void SubtractMoneyFromCompanyFract(CompanyID company, const CommandCost &cst)
Subtract money from a company, including the money fraction.
Functions related to companies.
DepotID GetDepotIndex(Tile t)
Get the index of which depot is attached to the tile.
Definition depot_map.h:56
PoolID< uint16_t, struct DepotIDTag, 64000, 0xFFFF > DepotID
Type for the unique identifier of depots.
Definition depot_type.h:15
DirDiff DirDifference(Direction d0, Direction d1)
Calculate the difference between two directions.
Direction DiagDirToDir(DiagDirection dir)
Convert a DiagDirection to a Direction.
DiagDirection ReverseDiagDir(DiagDirection d)
Returns the reverse direction of the given DiagDirection.
DiagDirections AxisToDiagDirs(Axis a)
Converts an Axis to DiagDirections.
Direction ReverseDir(Direction d)
Return the reverse of a direction.
bool IsValidDiagDirection(DiagDirection d)
Checks if an integer value is a valid DiagDirection.
Direction ChangeDir(Direction d, DirDiff delta)
Change a direction by a given difference.
DiagDirection AxisToDiagDir(Axis a)
Converts an Axis to a DiagDirection.
bool IsDiagonalDirection(Direction dir)
Checks if a given Direction is diagonal.
Axis DiagDirToAxis(DiagDirection d)
Convert a DiagDirection to the axis.
DiagDirection DirToDiagDir(Direction dir)
Convert a Direction to a DiagDirection.
DirDiff
Enumeration for the difference between two directions.
@ Left45
Angle of 45 degrees left.
@ Left90
Angle of 90 degrees left.
@ Same
Both directions faces to the same direction.
@ Right45
Angle of 45 degrees right.
@ Right90
Angle of 90 degrees right.
EnumIndexArray< T, DiagDirection, DiagDirection::End > DiagDirectionIndexArray
Array with DiagDirection as index.
Direction
Defines the 8 directions on the map.
@ SW
Southwest.
@ NW
Northwest.
@ NE
Northeast.
@ SE
Southeast.
Axis
Enumeration for the two axis X and Y.
DiagDirection
Enumeration for diagonal directions.
@ Begin
Used for iterations.
@ Invalid
Flag for an invalid DiagDirection.
EnumBitSet< DiagDirection, uint8_t > DiagDirections
Bitset of DiagDirection elements.
Money GetPrice(Price index, uint cost_factor, const GRFFile *grf_file, int shift)
Determine a certain price.
Definition economy.cpp:938
@ TrainRun
Running costs trains.
@ NewVehicles
New vehicles.
@ Invalid
Invalid base price.
EffectVehicle * CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicleType type)
Create an effect vehicle above a particular vehicle.
Functions related to effect vehicles.
@ EV_EXPLOSION_SMALL
Various explosions.
@ EV_EXPLOSION_LARGE
Various explosions.
@ RailFlips
Rail vehicle has old depot-flip handling.
@ RailTilts
Rail vehicle tilts in curves.
@ VE_DISABLE_WAGON_POWER
Flag to disable wagon power.
Definition engine_type.h:68
PoolID< uint16_t, struct EngineIDTag, 64000, 0xFFFF > EngineID
Unique identification number of an engine.
Definition engine_type.h:26
@ ExclusivePreview
This vehicle is in the exclusive preview stage, either being used or being offered to a company.
@ Multihead
indicates a combination of two locomotives
Definition engine_type.h:33
@ Wagon
simple wagon, not motorized
Definition engine_type.h:34
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
Functions related to errors.
@ Critical
Critical errors, the MessageBox is shown in all cases.
Definition error.h:27
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
Error reporting related functions.
fluid_settings_t * settings
FluidSynth settings handle.
Types for recording game performance data.
@ PFE_GL_TRAINS
Time spent processing trains.
Base functions for all Games.
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
@ AS_BRAKE
We want to stop.
@ GVF_SUPPRESS_IMPLICIT_ORDERS
Disable insertion and removal of automatic orders until the vehicle completes the real order.
@ GVF_GOINGDOWN_BIT
Vehicle is currently going downhill. (Cached track information for acceleration).
@ GVF_GOINGUP_BIT
Vehicle is currently going uphill. (Cached track information for acceleration).
void UpdateTrainGroupID(Train *v)
Recalculates the groupID of a train.
void SetTrainGroupID(Train *v, GroupID grp)
Affect the groupID of a train to new_g.
void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
Mark a tile given by its index dirty for repaint.
static constexpr GroupID DEFAULT_GROUP
Ungrouped vehicles are in this group.
Definition group_type.h:18
TrackStatus GetTileTrackStatus(TileIndex tile, TransportType mode, RoadTramType sub_mode, DiagDirection side)
Returns information about trackdirs and signal states.
int GetSlopePixelZ(int x, int y, bool ground_vehicle)
Return world Z coordinate of a given point of a tile.
#define Rect
Macro that prevents name conflicts between included headers.
#define Point
Macro that prevents name conflicts between included headers.
DiagDirection DiagdirBetweenTiles(TileIndex tile_from, TileIndex tile_to)
Determines the DiagDirection to get from one tile to another.
Definition map_func.h:627
static TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:407
TileIndex TileAddByDiagDir(TileIndex tile, DiagDirection dir)
Adds a DiagDir to a tile.
Definition map_func.h:615
TileIndexDiff TileOffsByAxis(Axis axis)
Convert an Axis to a TileIndexDiff.
Definition map_func.h:559
static uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:429
static uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:419
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition map_func.h:574
int32_t TileIndexDiff
An offset value between two tiles.
Definition map_type.h:23
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition math_func.hpp:23
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
Miscellaneous command definitions.
void HideFillingPercent(TextEffectID *te_id)
Hide vehicle loading indicators.
Definition misc_gui.cpp:582
bool _networking
are we in networking mode?
Definition network.cpp:67
Basic functions/variables used all over the place.
ClientID
'Unique' identifier to be given to clients
Base for the NewGRF implementation.
@ Trains
Trains feature.
Definition newgrf.h:74
@ ArticEngine
Add articulated engines (trains and road vehicles).
@ Length
Vehicle length (trains and road vehicles).
@ CBID_VEHICLE_LENGTH
Vehicle length, returns the amount of 1/8's the vehicle is shorter for trains and RVs.
@ CBID_TRAIN_ALLOW_WAGON_ATTACH
Determine whether a wagon can be attached to an already existing train.
static const uint CALLBACK_FAILED
Different values for Callback result evaluations.
void ErrorUnknownCallbackResult(uint32_t grfid, uint16_t cbid, uint16_t cb_res)
Record that a NewGRF returned an unknown/invalid callback result.
@ VehCapacity
Capacity of vehicle changes when not refitting or arranging.
Functions/types related to NewGRF debugging.
void InvalidateNewGRFInspectWindow(GrfSpecFeature feature, uint index)
Invalidate the inspect window for a given feature and index.
void DeleteNewGRFInspectWindow(GrfSpecFeature feature, uint index)
Delete inspect window for a given feature and index.
uint16_t GetVehicleCallbackParent(CallbackID callback, uint32_t param1, uint32_t param2, EngineID engine, const Vehicle *v, const Vehicle *parent, std::span< int32_t > regs100)
Evaluate a newgrf callback for vehicles with a different vehicle for parent scope.
bool UsesWagonOverride(const Vehicle *v)
Check if a wagon is currently using a wagon override.
uint16_t GetVehicleCallback(CallbackID callback, uint32_t param1, uint32_t param2, EngineID engine, const Vehicle *v, std::span< int32_t > regs100)
Evaluate a newgrf callback for vehicles.
std::optional< bool > TestVehicleBuildProbability(Vehicle *v, BuildProbabilityType type)
Test for vehicle build probability type.
@ Reversed
Change the rail vehicle should be reversed when purchased.
@ PROP_TRAIN_SHORTEN_FACTOR
Shorter 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_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_SPEED
Max. speed: 1 unit = 1/1.6 mph = 1 km-ish/h.
bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event, bool force)
Checks whether a NewGRF wants to play a different vehicle sound effect.
Functions related to NewGRF provided sounds.
@ VSE_START
Vehicle starting, i.e. leaving, the station.
void TriggerStationRandomisation(BaseStation *st, TileIndex trigger_tile, StationRandomTrigger trigger, CargoType cargo_type)
Trigger station randomisation.
Header file for NewGRF stations.
StringID GetGRFStringID(uint32_t grfid, GRFStringID stringid)
Returns the index for this stringid associated with its grfID.
Header of Action 04 "universal holder" structure and functions.
StrongType::Typedef< uint32_t, struct GRFStringIDTag, StrongType::Compare, StrongType::Integer > GRFStringID
Type for GRF-internal string IDs.
static constexpr GRFStringID GRFSTR_MISC_GRF_TEXT
Miscellaneous GRF text range.
Functions related to news.
void AddVehicleNewsItem(EncodedString &&headline, NewsType type, VehicleID vehicle, StationID station=StationID::Invalid())
Adds a newsitem referencing a vehicle.
Definition news_func.h:32
void AddVehicleAdviceNewsItem(AdviceType advice_type, EncodedString &&headline, VehicleID vehicle)
Adds a vehicle-advice news item.
Definition news_func.h:43
@ ArrivalCompany
First vehicle arrived for company.
Definition news_type.h:30
@ ArrivalOther
First vehicle arrived for competitor.
Definition news_type.h:31
@ Accident
An accident or disaster has occurred.
Definition news_type.h:32
@ TrainStuck
The train got stuck and needs to be unstuck manually.
Definition news_type.h:56
@ Error
A game paused because a (critical) error.
Definition openttd.h:75
Functions related to order backups.
bool ProcessOrders(Vehicle *v)
Handle the orders of a vehicle and determine the next place to go to if needed.
bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool pbs_look_ahead)
Update the vehicle's destination tile from an order.
void CheckOrders(const Vehicle *v)
Check the orders of a vehicle, to see if there are invalid orders and stuff.
void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist, bool reset_order_indices)
Delete all orders from a vehicle.
VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v)
Process a conditional order and determine the next order.
OrderStopLocation
Where to stop the trains.
Definition order_type.h:97
@ NearEnd
Stop at the near end of the platform.
Definition order_type.h:98
@ FarEnd
Stop at the far end of the platform.
Definition order_type.h:100
@ Middle
Stop at the middle of the platform.
Definition order_type.h:99
@ NonStop
The vehicle will not stop at any stations it passes except the destination, aka non-stop.
Definition order_type.h:88
@ GoVia
The vehicle will stop at any station it passes except the destination, aka via.
Definition order_type.h:89
uint8_t VehicleOrderID
The index of an order within its current vehicle (not pool related).
Definition order_type.h:18
@ NearestDepot
Send the vehicle to the nearest depot.
Definition order_type.h:119
@ Service
This depot order is because of the servicing limit.
Definition order_type.h:108
static const VehicleOrderID INVALID_VEH_ORDER_ID
Invalid vehicle order index (sentinel).
Definition order_type.h:39
OrderType
Order types.
Definition order_type.h:50
void SetRailStationPlatformReservation(TileIndex start, DiagDirection dir, bool b)
Set the reservation for a complete station platform.
Definition pbs.cpp:57
TrackBits GetReservedTrackbits(TileIndex t)
Get the reserved trackbits for any tile, regardless of type.
Definition pbs.cpp:24
bool TryReserveRailTrack(TileIndex tile, Track t, bool trigger_stations)
Try to reserve a specific track on a tile.
Definition pbs.cpp:80
bool IsWaitingPositionFree(const Train *v, TileIndex tile, Trackdir trackdir, bool forbid_90deg)
Check if a safe position is free.
Definition pbs.cpp:439
void UnreserveRailTrack(TileIndex tile, Track t)
Lift the reservation of a specific track on a tile.
Definition pbs.cpp:144
PBSTileInfo FollowTrainReservation(const Train *consist, Vehicle **train_on_res)
Follow a train reservation to the last tile.
Definition pbs.cpp:301
bool IsSafeWaitingPosition(const Train *v, TileIndex tile, Trackdir trackdir, bool include_line_end, bool forbid_90deg)
Determine whether a certain track on a tile is a safe position to end a path.
Definition pbs.cpp:395
bool HasReservedTracks(TileIndex tile, TrackBits tracks)
Check whether some of tracks is reserved on a tile.
Definition pbs.h:58
RailType GetTileRailType(Tile tile)
Return the rail type of tile, or INVALID_RAILTYPE if this is no rail tile.
Definition rail.cpp:39
int TicksToLeaveDepot(const Train *v)
Compute number of ticks when next wagon will leave a depot.
RailTypes GetAllPoweredRailTypes(RailTypes railtypes)
Returns all powered railtypes for a set of railtypes.
Definition rail.h:325
bool IsCompatibleRail(RailType enginetype, RailType tiletype)
Checks if an engine of the given RailType can drive on a tile with a given RailType.
Definition rail.h:352
bool HasPowerOnRail(RailType enginetype, RailType tiletype)
Checks if an engine of the given RailType got power on a tile with a given RailType.
Definition rail.h:377
bool Rail90DegTurnDisallowed(RailType rt1, RailType rt2, bool def=_settings_game.pf.forbid_90_deg)
Test if 90 degree turns are disallowed between two railtypes.
Definition rail.h:411
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:301
RailTypes GetAllCompatibleRailTypes(RailTypes railtypes)
Returns all compatible railtypes for a set of railtypes.
Definition rail.h:313
std::vector< Train * > TrainList
Helper type for lists/vectors of trains.
Definition rail_cmd.cpp:44
bool HasOnewaySignalBlockingTrackdir(Tile tile, Trackdir td)
Is a one-way signal blocking the trackdir?
Definition rail_map.h:548
static RailTileType GetRailTileType(Tile t)
Returns the RailTileType (normal with or without signals, waypoint or depot).
Definition rail_map.h:36
static bool IsPlainRail(Tile t)
Returns whether this is plain rails, with or without signals.
Definition rail_map.h:49
RailType GetRailType(Tile t)
Gets the rail type of the given tile.
Definition rail_map.h:115
bool HasSignalOnTrackdir(Tile tile, Trackdir trackdir)
Checks for the presence of signals along the given trackdir on the given rail tile.
Definition rail_map.h:491
TrackBits GetTrackBits(Tile tile)
Gets the track bits of the given tile.
Definition rail_map.h:136
static bool IsPlainRailTile(Tile t)
Checks whether the tile is a rail tile or rail tile with signals.
Definition rail_map.h:60
bool IsPbsSignal(SignalType s)
Checks whether the given signal is a path based signal.
Definition rail_map.h:292
@ Signals
Normal rail tile with signals.
Definition rail_map.h:25
Track GetRailDepotTrack(Tile t)
Returns the track of a depot, ignoring direction.
Definition rail_map.h:182
DiagDirection GetRailDepotDirection(Tile t)
Returns the direction the depot is facing to.
Definition rail_map.h:171
void SetSignalStateByTrackdir(Tile tile, Trackdir trackdir, SignalState state)
Sets the state of the signal along the given trackdir.
Definition rail_map.h:520
bool HasSignalOnTrack(Tile tile, Track track)
Checks for the presence of signals (either way) on the given track on the given rail tile.
Definition rail_map.h:475
bool HasPbsSignalOnTrackdir(Tile tile, Trackdir td)
Is a pbs signal present along the trackdir?
Definition rail_map.h:535
bool IsOnewaySignal(Tile t, Track track)
Is the signal at the given track on a tile a one way signal?
Definition rail_map.h:358
SignalType GetSignalType(Tile t, Track track)
Get the signal type for a track on a tile.
Definition rail_map.h:304
void SetDepotReservation(Tile t, bool b)
Set the reservation state of the depot.
Definition rail_map.h:269
bool HasDepotReservation(Tile t)
Get the reservation state of the depot.
Definition rail_map.h:257
bool HasSignals(Tile t)
Checks if a rail tile has signals.
Definition rail_map.h:72
SignalState GetSignalStateByTrackdir(Tile tile, Trackdir trackdir)
Gets the state of the signal along the given trackdir.
Definition rail_map.h:506
static bool IsRailDepotTile(Tile t)
Is this tile rail tile and a rail depot?
Definition rail_map.h:105
bool HasBlockSignalOnTrackdir(Tile tile, Trackdir td)
Check whether a block signal is present along the trackdir.
Definition rail_map.h:560
@ RAILTYPE_RAIL
Standard non-electric rails.
Definition rail_type.h:27
Pseudo random number generator.
bool Chance16(const uint32_t a, const uint32_t b, const std::source_location location=std::source_location::current())
Flips a coin with given probability.
bool Chance16R(const uint32_t a, const uint32_t b, uint32_t &r, const std::source_location location=std::source_location::current())
Flips a coin with a given probability and saves the randomize-number in a variable.
void UpdateLevelCrossing(TileIndex tile, bool sound=true, bool force_bar=false)
Update a level crossing to barred or open (crossing may include multiple adjacent tiles).
bool HasCrossingReservation(Tile t)
Get the reservation state of the rail crossing.
Definition road_map.h:390
bool IsLevelCrossingTile(Tile t)
Return whether a tile is a level crossing tile.
Definition road_map.h:79
Axis GetCrossingRoadAxis(Tile t)
Get the road axis of a level crossing.
Definition road_map.h:335
void SetCrossingBarred(Tile t, bool barred)
Set the bar state of a level crossing.
Definition road_map.h:438
Axis GetCrossingRailAxis(Tile t)
Get the rail axis of a level crossing.
Definition road_map.h:347
bool IsCrossingBarred(Tile t)
Check if the level crossing is barred.
Definition road_map.h:426
void SetCrossingReservation(Tile t, bool b)
Set the reservation state of the rail crossing.
Definition road_map.h:403
@ Invalid
Invalid marker.
Definition road_type.h:41
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
@ EndOfLineOnly
Trains can only flip when the track ends.
@ None
Trains cannot flip anywhere and must back up if the track ends.
SigSegState UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner)
Update signals, starting at one side of a tile Will check tile next to this at opposite side too.
Definition signal.cpp:654
void UpdateSignalsInBuffer()
Update signals in buffer Called from 'outside'.
Definition signal.cpp:580
void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner)
Add side of tile to signal update buffer.
Definition signal.cpp:628
void SetSignalsOnBothDir(TileIndex tile, Track track, Owner owner)
Update signals at segments that are at both ends of given (existent or non-existent) track.
Definition signal.cpp:672
SigSegState
State of the signal segment.
Definition signal_func.h:55
@ SIGSEG_PBS
Segment is a PBS segment.
Definition signal_func.h:58
@ SIGSEG_FULL
Occupied by a train.
Definition signal_func.h:57
@ SIGTYPE_PBS
normal pbs signal
Definition signal_type.h:28
@ SIGNAL_STATE_RED
The signal is red.
Definition signal_type.h:43
@ SIGNAL_STATE_GREEN
The signal is green.
Definition signal_type.h:44
Functions related to sound.
SoundFx
Sound effects from baseset.
Definition sound_type.h:46
@ SND_04_DEPARTURE_STEAM
2 == 0x02 Station departure: steam engine
Definition sound_type.h:50
@ SND_41_DEPARTURE_MAGLEV
65 == 0x41 Station departure: maglev engine
Definition sound_type.h:113
@ SND_13_TRAIN_COLLISION
15 == 0x11 Train+train crash
Definition sound_type.h:65
@ SND_0E_LEVEL_CROSSING
12 == 0x0C Train passes through level crossing
Definition sound_type.h:60
@ SND_0A_DEPARTURE_TRAIN
8 == 0x08 Station departure: diesel and electric engine
Definition sound_type.h:56
@ SND_47_DEPARTURE_MONORAIL
71 == 0x47 Station departure: monorail engine
Definition sound_type.h:119
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1619
void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
Forcibly modify station ratings near a given tile.
bool IsRailWaypointTile(Tile t)
Is this tile a station tile and a rail waypoint?
bool IsCompatibleTrainStationTile(Tile test_tile, Tile station_tile)
Check if a tile is a valid continuation to a railstation tile.
bool IsRailStationTile(Tile t)
Is this tile a station tile and a rail station?
StationID GetStationIndex(Tile t)
Get StationID from a tile.
Definition station_map.h:28
Axis GetRailStationAxis(Tile t)
Get the rail direction of a rail station.
@ HVOT_TRAIN
Station has seen a train.
@ Train
Station with train station.
@ VehicleArrives
Trigger platform when train arrives.
@ VehicleArrives
Trigger platform when train arrives.
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:271
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Data structure for storing engine speed changes of an acceleration type.
uint8_t large_turn
Speed change due to a large turn.
uint8_t z_up
Fraction to remove when moving up.
uint8_t small_turn
Speed change due to a small turn.
uint8_t z_down
Fraction to add when moving down.
std::string name
Name of vehicle.
TimerGameTick::Ticks current_order_time
How many ticks have passed since this order started.
VehicleOrderID cur_real_order_index
The index to the current real (non-implicit) order.
VehicleFlags vehicle_flags
Used for gradual loading and other miscellaneous things (.
void ResetDepotUnbunching()
Resets all the data used for depot unbunching.
TileIndex xy
Base tile of the station.
StationFacilities facilities
The facilities that this station has.
TileArea train_station
Tile area the train 'station' part covers.
VehicleType type
Type of vehicle.
uint16_t speed
maximum travel speed (1 unit = 1/1.6 mph = 1 km-ish/h)
Definition bridge.h:39
bool Follow(TileIndex old_tile, Trackdir old_td)
Main follower routine.
bool is_tunnel
last turn passed tunnel
bool is_bridge
last turn passed bridge ramp
int tiles_skipped
number of skipped tunnel or station tiles
DiagDirection exitdir
exit direction (leaving the old tile)
TrackdirBits new_td_bits
the new set of available trackdirs
TileIndex new_tile
the new tile (the vehicle has entered)
Trackdir old_td
the trackdir (the vehicle was on) before move
bool is_station
last turn passed station
TileIndex old_tile
the origin (vehicle moved from) before move
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:139
Structure to return information about the closest depot location, and whether it could be found.
T y
Y coordinate.
T x
X coordinate.
uint16_t cargo_age_period
Number of ticks before carried cargo is aged.
EngineMiscFlags misc_flags
Miscellaneous flags.
VehicleCallbackMasks callback_mask
Bitmask of vehicle callbacks that have to be called.
Helper container to find a depot.
uint best_length
The distance towards the depot in penalty, or UINT_MAX if not found.
bool reverse
True if reversing is necessary for the train to get to this depot.
TileIndex tile
The tile of the depot.
Dynamic data of a loaded NewGRF.
Definition newgrf.h:119
uint traininfo_vehicle_width
Width (in pixels) of a 8/8 train vehicle in depot GUI and vehicle details.
Definition newgrf.h:160
int traininfo_vehicle_pitch
Vertical offset for drawing train images in depot GUI and vehicle details.
Definition newgrf.h:159
Position information of a vehicle after it moved.
TileIndex new_tile
Tile of the vehicle after moving.
int y
x and y position of the vehicle after moving
TileIndex old_tile
Current tile of the vehicle.
EngineID first_engine
Cached EngineID of the front vehicle. EngineID::Invalid() for the front vehicle itself.
uint32_t cached_power
Total power of the consist (valid only for the first engine).
uint16_t cached_total_length
Length of the whole vehicle (valid only for the first engine).
uint8_t cached_veh_length
Length of this vehicle in units of 1/VEHICLE_LENGTH of normal length. It is cached because this can b...
bool IsChainInDepot() const override
Check whether the whole vehicle chain is in the depot.
void SetFreeWagon()
Set a vehicle as a free wagon.
int UpdateInclination(bool new_tile, bool update_delta)
Checks if the vehicle is in a slope and sets the required flags in that case.
bool CanLeadTrain() const
Check if this vehicle can lead a train.
void SetMultiheaded()
Set a vehicle as a multiheaded engine.
bool IsEngine() const
Check if a vehicle is an engine (can be first in a consist).
bool IsRearDualheaded() const
Tell if we are dealing with the rear end of a multiheaded engine.
bool IsMultiheaded() const
Check if the vehicle is a multiheaded engine.
void SetEngine()
Set engine status.
void SetWagon()
Set a vehicle to be a wagon.
void SetFrontEngine()
Set front engine state.
void ClearFreeWagon()
Clear a vehicle from being a free wagon.
bool IsWagon() const
Check if a vehicle is a wagon.
uint Crash(bool flooded) override
Common code executed for crashed ground vehicles.
uint DoUpdateSpeed(uint accel, int min_speed, int max_speed)
void ClearFrontEngine()
Remove the front engine state.
void SetLastSpeed()
Update the GUI variant of the current speed of the vehicle.
static void CountVehicle(const Vehicle *v, int delta)
Update num_vehicle when adding or removing a vehicle.
static uint Size()
Get the size of the map.
Definition map_func.h:280
VehicleSpriteSeq sprite_seq
Vehicle appearance.
static void Backup(const Vehicle *v, uint32_t user)
Create an order backup for the given vehicle.
If you change this, keep in mind that it is also saved in 2 other places:
Definition order_base.h:34
OrderDepotTypeFlags GetDepotOrderType() const
What caused us going to the depot?
Definition order_base.h:170
DestinationID GetDestination() const
Gets the destination of this order.
Definition order_base.h:100
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:67
OrderStopLocation GetStopLocation() const
Where must we stop at the platform?
Definition order_base.h:164
OrderType GetType() const
Get the type of order of this order.
Definition order_base.h:73
void MakeDummy()
Makes this order a Dummy order.
void SetDestination(DestinationID destination)
Sets the destination of this order.
Definition order_base.h:107
OrderDepotActionFlags GetDepotActionType() const
What are we going to do when in the depot.
Definition order_base.h:176
void Free()
'Free' the order
Definition order_cmd.cpp:47
bool ShouldStopAtStation(const Vehicle *v, StationID station) const
Check whether the given vehicle should stop at the given station based on this order and the non-stop...
void MakeGoToDepot(DestinationID destination, OrderDepotTypeFlags order, OrderNonStopFlags non_stop_type=OrderNonStopFlag::NonStop, OrderDepotActionFlags action={}, CargoType cargo=CARGO_NO_REFIT)
Makes this order a Go To Depot order.
Definition order_cmd.cpp:73
OrderNonStopFlags GetNonStopType() const
At which stations must we stop?
Definition order_base.h:158
TileIndex tile
The base tile of the area.
This struct contains information about the end of a reserved path.
Definition pbs.h:26
Trackdir trackdir
The reserved trackdir on the tile.
Definition pbs.h:28
TileIndex tile
Tile the path ends, INVALID_TILE if no valid path was found.
Definition pbs.h:27
bool okay
True if tile is a safe waiting position, false otherwise.
Definition pbs.h:29
static Pool::IterateWrapper< Vehicle > Iterate(size_t from=0)
static Engine * Get(auto index)
static T * Create(Targs &&... args)
static Vehicle * GetIfValid(auto index)
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 shorten_factor
length on main map for this type is 8 - shorten_factor
Definition engine_type.h:91
RailTypes railtypes
Railtypes, mangled if elrail is disabled.
Definition engine_type.h:78
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
RailVehicleType railveh_type
Type of rail vehicle.
Definition engine_type.h:76
uint8_t capacity
Cargo capacity of vehicle; For multiheaded engines the capacity of each single engine.
Definition engine_type.h:87
int Width() const
Get width of Rect.
int Height() const
Get height of Rect.
static Station * Get(auto index)
T * GetMovingFront() const
Get the moving front of the vehicle chain.
T * GetMovingPrev() const
Get the previous vehicle in the vehicle chain, relative to its current movement.
T * Next() const
Get next vehicle in the chain.
T * Previous() const
Get previous vehicle in the chain.
static Train * From(Vehicle *v)
T * First() const
Get the first vehicle in the chain.
T * GetNextVehicle() const
Get the next real (non-articulated part) vehicle in the consist.
void UpdateViewport(bool force_update, bool update_delta)
Update vehicle sprite- and position caches.
T * GetMovingNext() const
Get the next vehicle in the vehicle chain, relative to its current movement.
T * GetLastEnginePart()
Get the last part of an articulated engine.
T * GetFirstEnginePart()
Get the first part of an articulated engine.
T * Last()
Get the last vehicle in the chain.
T * GetMovingBack() const
Get the moving back of the vehicle chain.
Station data structure.
uint GetPlatformLength(TileIndex tile, DiagDirection dir) const override
Determines the REMAINING length of a platform, starting at (and including) the given tile.
Definition station.cpp:292
Track status of a tile.
Definition track_type.h:132
TrackdirBits signals
Red signals on the tile.
Definition track_type.h:134
TrackdirBits trackdirs
Trackdirs present on the tile.
Definition track_type.h:133
uint16_t cached_max_curve_speed
max consist speed limited by curves
Definition train.h:84
'Train' is either a loco or a wagon.
Definition train.h:97
void PlayLeaveStationSound(bool force=false) const override
Play the sound associated with leaving the station.
void UpdateAcceleration()
Update acceleration of the train from the cached power and weight.
void OnNewCalendarDay() override
Calendar day handler.
Train * GetNextUnit() const
Get the next real (non-articulated part and non rear part of dualheaded engine) vehicle in the consis...
Definition train.h:156
Trackdir GetVehicleTrackdir() const override
Get the tracks of the train vehicle.
void GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const override
Get the sprite to display the train.
Train * other_multiheaded_part
Link between the two ends of a multiheaded engine.
Definition train.h:105
RailTypes railtypes
On which rail types the train can run.
Definition train.h:108
uint16_t crash_anim_pos
Crash animation counter.
Definition train.h:99
bool Tick() override
Update train vehicle data for a tick.
void ReserveTrackUnderConsist() const
Tries to reserve track under whole train consist.
TileIndex GetOrderStationLocation(StationID station) override
Get the location of the next station to visit.
TrainForceProceeding force_proceed
How the train should behave when it encounters next obstacle.
Definition train.h:111
int GetDisplayImageWidth(Point *offset=nullptr) const
Get the width of a train vehicle image in the GUI.
ClosestDepot FindClosestDepot() override
Find the closest depot for this vehicle and tell us the location, DestinationID and whether we should...
TrackBits track
On which track the train currently is.
Definition train.h:110
void UpdateDeltaXY() override
Updates the x and y offsets and the size of the sprite used for this vehicle.
VehicleRailFlags flags
Which flags has this train currently set.
Definition train.h:98
int CalcNextVehicleOffset() const
Calculate the offset from this vehicle's center to the following center taking the vehicle lengths in...
Definition train.h:180
uint16_t GetMaxWeight() const override
Calculates the weight value that this vehicle will have when fully loaded with its current cargo.
uint16_t GetCurveSpeedLimit() const
Computes train speed limit caused by curves.
bool IsPrimaryVehicle() const override
Whether this is the primary vehicle in the chain.
Definition train.h:124
void MarkDirty() override
Goods at the consist have changed, update the graphics, cargo, and acceleration.
int UpdateSpeed()
This function looks at the vehicle and updates its speed (cur_speed and subspeed) variables.
uint Crash(bool flooded=false) override
The train vehicle crashed!
VehicleAccelerationModel GetAccelerationType() const
Allows to know the acceleration type of a vehicle.
Definition train.h:194
Train(VehicleID index)
Create new Train object.
Definition train.h:114
AccelStatus GetAccelerationStatus() const
Checks the current acceleration status of this vehicle.
Definition train.h:291
TrainCache tcache
Set of cached variables, recalculated on load and each time a vehicle is added to/removed from the co...
Definition train.h:102
void OnNewEconomyDay() override
Economy day handler.
int GetCursorImageOffset() const
Get the offset for train image when it is used as cursor.
void ConsistChanged(ConsistChangeFlags allowed_changes)
Recalculates the cached stuff of a train.
Money GetRunningCost() const override
Get running cost for the train consist.
RailTypes compatible_railtypes
With which rail types the train is compatible.
Definition train.h:107
uint16_t wait_counter
Ticks waiting in front of a signal, ticks being stuck or a counter for forced proceeding through sign...
Definition train.h:100
int GetCurrentMaxSpeed() const override
Calculates the maximum speed of the vehicle under its current conditions.
Sprite sequence for a vehicle part.
bool IsValid() const
Check whether the sequence contains any sprites.
void GetBounds(Rect *bounds) const
Determine shared bounds of all sprites.
Definition vehicle.cpp:123
void Set(SpriteID sprite)
Assign a single sprite to the sequence.
void Draw(int x, int y, PaletteID default_pal, bool force_pal) const
Draw the sprite sequence.
Definition vehicle.cpp:151
Vehicle data structure.
EngineID engine_type
The type of engine used for this vehicle.
int32_t z_pos
z coordinate.
Direction direction
facing
const Engine * GetEngine() const
Retrieves the engine of the vehicle.
Definition vehicle.cpp:748
Direction GetMovingDirection() const
Get the moving direction of this vehicle chain.
void IncrementRealOrderIndex()
Advanced cur_real_order_index to the next real order, keeps care of the wrap-around and invalidates t...
bool IsStoppedInDepot() const
Check whether the vehicle is in the depot and stopped.
bool IsMovingFront() const
Is this vehicle the moving front of the vehicle chain?
Order * GetOrder(int index) const
Returns order 'index' of a vehicle or nullptr when it doesn't exists.
void LeaveStation()
Perform all actions when leaving a station.
Definition vehicle.cpp:2372
void AddToShared(Vehicle *shared_chain)
Adds this vehicle to a shared vehicle chain.
Definition vehicle.cpp:3023
VehicleCargoList cargo
The cargo this vehicle is carrying.
TimerGameEconomy::Date date_of_last_service
Last economy date the vehicle had a service at a depot.
uint16_t cargo_cap
total capacity
StationID last_loading_station
Last station the vehicle has stopped at and could possibly leave from with any cargo loaded.
VehicleOrderID GetNumOrders() const
Get the number of orders this vehicle has.
void SetMovingDirection(Direction d)
Set the movement direction of this vehicle chain.
uint16_t random_bits
Bits used for randomized variational spritegroups.
void ReleaseUnitNumber()
Release the vehicle's unit number.
Definition vehicle.cpp:2442
uint8_t day_counter
Increased by one for each day.
void HandleLoading(bool mode=false)
Handle the loading of the vehicle; when not it skips through dummy orders and does nothing in all oth...
Definition vehicle.cpp:2453
Money profit_this_year
Profit this year << 8, low 8 bits are fract.
SpriteID colourmap
NOSAVE: cached colour mapping.
uint8_t breakdown_ctr
Counter for managing breakdown events.
uint GetAdvanceDistance()
Determines the vehicle "progress" needed for moving a step.
GroupID group_id
Index of group Pool array.
VehStates vehstatus
Status.
TimerGameCalendar::Date date_of_last_service_newgrf
Last calendar date the vehicle had a service at a depot, unchanged by the date cheat to protect again...
uint8_t subspeed
fractional speed
bool IsArticulatedPart() const
Check if the vehicle is an articulated part of an engine.
void LeaveUnbunchingDepot()
Leave an unbunching depot and calculate the next departure time for shared order vehicles.
Definition vehicle.cpp:2533
CargoType cargo_type
type of cargo this vehicle is carrying
uint8_t acceleration
used by train & aircraft
Vehicle * First() const
Get the first vehicle of this vehicle chain.
Order current_order
The current order (+ status, like: loading).
void HandlePathfindingResult(bool path_found)
Handle the pathfinding result, especially the lost status.
Definition vehicle.cpp:792
Vehicle * Next() const
Get the next vehicle of this vehicle.
int32_t y_pos
y coordinate.
int32_t x_pos
x coordinate.
const GRFFile * GetGRF() const
Retrieve the NewGRF the vehicle is tied to.
Definition vehicle.cpp:758
OrderList * orders
Pointer to the order list for this vehicle.
Money value
Value of the vehicle.
uint16_t refit_cap
Capacity left over from before last refit.
void InvalidateNewGRFCache()
Invalidates cached NewGRF variables.
VehicleCache vcache
Cache of often used vehicle values.
uint32_t GetGRFID() const
Retrieve the GRF ID of the NewGRF the vehicle is tied to.
Definition vehicle.cpp:768
SpriteBounds bounds
Bounding box of vehicle.
void BeginLoading()
Prepare everything to begin the loading when arriving at a station.
Definition vehicle.cpp:2227
uint8_t spritenum
currently displayed sprite index 0xfd == custom sprite, 0xfe == custom second head sprite 0xff == res...
uint16_t cur_speed
current speed
uint8_t cargo_subtype
Used for livery refits (NewGRF variations).
bool IsFrontEngine() const
Check if the vehicle is a front engine.
bool IsWaitingForUnbunching() const
Check whether a vehicle inside a depot is waiting for unbunching.
Definition vehicle.cpp:2580
TextEffectID fill_percent_te_id
a text-effect id to a loading indicator object
void SetNext(Vehicle *next)
Set the next vehicle of this vehicle.
Definition vehicle.cpp:2987
TimerGameCalendar::Date max_age
Maximum age.
MutableSpriteCache sprite_cache
Cache of sprites and values related to recalculating them, see MutableSpriteCache.
uint16_t reliability
Reliability.
bool HandleBreakdown()
Handle all of the aspects of a vehicle breakdown This includes adding smoke and sounds,...
Definition vehicle.cpp:1374
uint8_t progress
The percentage (if divided by 256) this vehicle already crossed the tile unit.
uint16_t reliability_spd_dec
Reliability decrease speed.
uint8_t tick_counter
Increased by one for each tick.
TileIndex tile
Current tile index.
TileIndex dest_tile
Heading for this tile.
void CopyVehicleConfigAndStatistics(Vehicle *src)
Copy certain configurations and statistics of a vehicle after successful autoreplace/renew The functi...
void UpdatePosition()
Update the position of the vehicle.
Definition vehicle.cpp:1699
StationID last_station_visited
The last station we stopped at.
bool IsDrivingBackwards() const
Is this vehicle moving backwards?
void ShowVisualEffect() const
Draw visual effects (smoke and/or sparks) for a vehicle chain.
Definition vehicle.cpp:2836
TimerGameCalendar::Year build_year
Year the vehicle has been built.
Owner owner
Which company owns the vehicle?
UnitID unitnumber
unit number, for display purposes only
bool NeedsAutomaticServicing() const
Checks if the current order should be interrupted for a service-in-depot order.
Definition vehicle.cpp:292
uint8_t running_ticks
Number of ticks this vehicle was not stopped this day.
@ EnteredStation
The vehicle entered a station.
Definition tile_cmd.h:25
@ CannotEnter
The vehicle cannot enter the tile.
Definition tile_cmd.h:27
@ EnteredWormhole
The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/...
Definition tile_cmd.h:26
VehicleEnterTileStates VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
Call the tile callback function for a vehicle entering a tile.
Definition vehicle.cpp:1863
static bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
bool IsTileOwner(Tile tile, Owner owner)
Checks if a tile belongs to the given owner.
Definition tile_map.h:214
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition tile_map.h:178
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:100
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
@ TunnelBridge
Tunnel entry/exit and bridge heads.
Definition tile_type.h:58
@ Station
A tile of a station or airport.
Definition tile_type.h:54
@ Railway
A tile with railway.
Definition tile_type.h:50
Definition of the game-calendar-timer.
Definition of the game-economy-timer.
Trackdir RemoveFirstTrackdir(TrackdirBits *trackdirs)
Removes first Trackdir from TrackdirBits and returns it.
Definition track_func.h:156
Track TrackdirToTrack(Trackdir trackdir)
Returns the Track that a given Trackdir represents.
Definition track_func.h:262
TrackBits TrackToTrackBits(Track track)
Maps a Track to the corresponding TrackBits value.
Definition track_func.h:77
TrackdirBits TrackBitsToTrackdirBits(TrackBits bits)
Converts TrackBits to TrackdirBits while allowing both directions.
Definition track_func.h:319
DiagDirection VehicleExitDir(Direction direction, TrackBits track)
Determine the side in which the vehicle will leave the tile.
Definition track_func.h:672
Track TrackBitsToTrack(TrackBits tracks)
Converts TrackBits to Track.
Definition track_func.h:193
TrackBits TrackCrossesTracks(Track track)
Maps a track to all tracks that make 90 deg turns with it.
Definition track_func.h:375
Trackdir ReverseTrackdir(Trackdir trackdir)
Maps a trackdir to the reverse trackdir.
Definition track_func.h:247
bool TracksOverlap(TrackBits bits)
Checks if the given tracks overlap, ie form a crossing.
Definition track_func.h:603
Trackdir TrackDirectionToTrackdir(Track track, Direction dir)
Maps a track and a full (8-way) direction to the trackdir that represents the track running in the gi...
Definition track_func.h:456
TrackBits AxisToTrackBits(Axis a)
Maps an Axis to the corresponding TrackBits value.
Definition track_func.h:88
bool IsValidTrackdir(Trackdir trackdir)
Checks if a Trackdir is valid for non-road vehicles.
Definition track_func.h:52
Trackdir FindFirstTrackdir(TrackdirBits trackdirs)
Returns first Trackdir from TrackdirBits or INVALID_TRACKDIR.
Definition track_func.h:211
TrackdirBits TrackdirCrossesTrackdirs(Trackdir trackdir)
Maps a trackdir to all trackdirs that make 90 deg turns with it.
Definition track_func.h:564
TrackdirBits TrackdirReachesTrackdirs(Trackdir trackdir)
Maps a trackdir to the trackdirs that can be reached from it (ie, when entering the next tile.
Definition track_func.h:542
TrackdirBits DiagdirReachesTrackdirs(DiagDirection diagdir)
Returns all trackdirs that can be reached when entering a tile from a given (diagonal) direction.
Definition track_func.h:513
Track FindFirstTrack(TrackBits tracks)
Returns first Track from TrackBits or INVALID_TRACK.
Definition track_func.h:177
Trackdir TrackEnterdirToTrackdir(Track track, DiagDirection diagdir)
Maps a track and an (4-way) dir to the trackdir that represents the track with the entry in the given...
Definition track_func.h:441
TrackBits DiagdirReachesTracks(DiagDirection diagdir)
Returns all tracks that can be reached when entering a tile from a given (diagonal) direction.
Definition track_func.h:531
TrackBits DiagDirToDiagTrackBits(DiagDirection diagdir)
Maps a DiagDirection to the associated diagonal TrackBits.
Definition track_func.h:482
Trackdir DiagDirToDiagTrackdir(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal trackdir that runs in that direction.
Definition track_func.h:495
DiagDirection TrackdirToExitdir(Trackdir trackdir)
Maps a trackdir to the (4-way) direction the tile is exited when following that trackdir.
Definition track_func.h:394
Track DiagDirToDiagTrack(DiagDirection diagdir)
Maps a DiagDirection to the associated diagonal Track.
Definition track_func.h:470
TrackBits TrackdirBitsToTrackBits(TrackdirBits bits)
Discards all directional information from a TrackdirBits value.
Definition track_func.h:308
TrackBits
Bitfield corresponding to Track.
Definition track_type.h:42
@ TRACK_BIT_WORMHOLE
Bitflag for a wormhole (used for tunnels).
Definition track_type.h:59
@ TRACK_BIT_UPPER
Upper track.
Definition track_type.h:46
@ TRACK_BIT_DEPOT
Bitflag for a depot.
Definition track_type.h:60
@ TRACK_BIT_LEFT
Left track.
Definition track_type.h:48
@ TRACK_BIT_Y
Y-axis track.
Definition track_type.h:45
@ TRACK_BIT_NONE
No track.
Definition track_type.h:43
@ TRACK_BIT_X
X-axis track.
Definition track_type.h:44
@ TRACK_BIT_MASK
Bitmask for the first 6 bits.
Definition track_type.h:58
@ TRACK_BIT_LOWER
Lower track.
Definition track_type.h:47
@ TRACK_BIT_RIGHT
Right track.
Definition track_type.h:49
Trackdir
Enumeration for tracks and directions.
Definition track_type.h:73
@ INVALID_TRACKDIR
Flag for an invalid trackdir.
Definition track_type.h:92
TrackdirBits
Enumeration of bitmasks for the TrackDirs.
Definition track_type.h:111
@ TRACKDIR_BIT_NONE
No track build.
Definition track_type.h:112
Track
These are used to specify a single track.
Definition track_type.h:19
@ INVALID_TRACK
Flag for an invalid track.
Definition track_type.h:28
@ TRACK_Y
Track along the y-axis (north-west to south-east).
Definition track_type.h:22
@ TRACK_BEGIN
Used for iterations.
Definition track_type.h:20
@ TRACK_X
Track along the x-axis (north-east to south-west).
Definition track_type.h:21
@ Capacity
Allow vehicles to change capacity.
Definition train.h:48
@ Length
Allow vehicles to change length.
Definition train.h:47
EnumBitSet< ConsistChangeFlag, uint8_t > ConsistChangeFlags
Bitset of the ConsistChangeFlag elements.
Definition train.h:51
static constexpr ConsistChangeFlags CCF_TRACK
Valid changes while vehicle is driving, and possibly changing tracks.
Definition train.h:53
bool TryPathReserve(Train *v, bool mark_as_stuck=false, bool first_tile_okay=false)
Try to reserve a path to a safe position.
int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *moving_front, int *station_ahead, int *station_length)
Get the stop location of (the center) of the front vehicle of a train at a platform of a station.
void FreeTrainTrackReservation(const Train *v)
Free the reserved path in front of a vehicle.
@ Reversed
Used for vehicle var 0xFE bit 8 (toggled each time the train is reversed, accurate for first vehicle ...
Definition train.h:31
@ LeavingStation
Train is just leaving a station.
Definition train.h:33
@ PoweredWagon
Wagon is powered.
Definition train.h:27
@ Reversing
Train is slowing down to reverse.
Definition train.h:26
@ Stuck
Train can't get a path reservation.
Definition train.h:32
@ AllowedOnNormalRail
Electric train engine is allowed to run on normal rail. *‍/.
Definition train.h:30
@ Flipped
Reverse the visible direction of the vehicle.
Definition train.h:28
TrainForceProceeding
Modes for ignoring signals.
Definition train.h:39
@ TFP_SIGNAL
Ignore next signal, after the signal ignore being stuck.
Definition train.h:42
@ TFP_NONE
Normal operation.
Definition train.h:40
@ TFP_STUCK
Proceed till next signal, but ignore being stuck till then. This includes force leaving depots.
Definition train.h:41
static constexpr ConsistChangeFlags CCF_ARRANGE
Valid changes for arranging the consist in a depot.
Definition train.h:57
static CommandCost CmdBuildRailWagon(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
Build a railroad wagon.
void FreeTrainTrackReservation(const Train *consist)
Free the reserved path in front of a vehicle.
static void NormaliseTrainHead(Train *head)
Normalise the head of the train again, i.e.
static CommandCost ValidateTrains(Train *original_dst, Train *dst, Train *original_src, Train *src, bool check_limit)
Validate whether we are going to create valid trains.
static bool CheckTrainStayInDepot(Train *v)
Will the train stay in the depot the next tick?
static FindDepotData FindClosestTrainDepot(Train *v, int max_distance)
Try to find a depot nearby.
CommandCost CmdForceTrainProceed(DoCommandFlags flags, VehicleID veh_id)
Force a train through a red signal.
void UpdateLevelCrossing(TileIndex tile, bool sound, bool force_bar)
Update a level crossing to barred or open (crossing may include multiple adjacent tiles).
int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *moving_front, int *station_ahead, int *station_length)
Get the stop location of (the center) of the front vehicle of a train at a platform of a station.
static bool CheckCompatibleRail(const Train *v, TileIndex tile, bool check_railtype)
Check if the vehicle is compatible with the specified tile.
static void AdvanceWagonsBeforeSwap(Train *moving_front)
Advances wagons for train reversing, needed for variable length wagons.
static void MakeTrainBackup(TrainList &list, Train *t)
Make a backup of a train into a train list.
static void ArrangeTrains(Train **dst_head, Train *dst, Train **src_head, Train *src, bool move_chain)
Arrange the trains in the wanted way.
static bool TryReserveSafeTrack(const Train *v, TileIndex tile, Trackdir td, bool override_railtype)
Try to reserve any path to a safe tile, ignoring the vehicle's destination.
static const uint16_t _breakdown_speeds[16]
Maximum speeds for train that is broken down or approaching line end.
static void InsertInConsist(Train *dst, Train *chain)
Inserts a chain into the train at dst.
void UpdateAdjacentLevelCrossingTilesOnLevelCrossingRemoval(TileIndex tile, Axis road_axis)
Update adjacent level crossing tiles in this multi-track crossing, due to removal of a level crossing...
static void SwapTrainFlags(uint16_t *swap_flag1, uint16_t *swap_flag2)
Swap the two up/down flags in two ways:
static constexpr DiagDirectionIndexArray< uint8_t > _vehicle_initial_y_fract
Initial y subtile coordinate of rail vehicles for each direction.
Definition train_cmd.cpp:56
static void MarkTrainAsStuck(Train *consist)
Mark a train as stuck and stop it if it isn't stopped right now.
static void UpdateLevelCrossingTile(TileIndex tile, bool sound, bool force_barred)
Sets a level crossing tile to the correct state.
static constexpr DiagDirectionIndexArray< uint8_t > _vehicle_initial_x_fract
Initial x subtile coordinate of rail vehicles for each direction.
Definition train_cmd.cpp:54
static void CheckNextTrainTile(Train *v)
Check if the train is on the last reserved tile and try to extend the path then.
static bool HandleCrashedTrain(Train *v)
Handle a crashed train.
void NormalizeTrainVehInDepot(const Train *u)
Move all free vehicles in the depot to the train.
CommandCost CmdReverseTrainDirection(DoCommandFlags flags, VehicleID veh_id, bool reverse_single_veh)
Reverse train.
static bool CheckLevelCrossing(TileIndex tile)
Check if a level crossing should be barred.
static CommandCost CheckNewTrain(Train *original_dst, Train *dst, Train *original_src, Train *src)
Check/validate whether we may actually build a new train.
CommandCost CmdBuildRailVehicle(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
Build a railroad vehicle.
static void UpdateStatusAfterSwap(Train *v, bool reverse=true)
Updates some variables after swapping the vehicle.
uint8_t FreightWagonMult(CargoType cargo)
Return the cargo weight multiplier to use for a rail vehicle.
Definition train_cmd.cpp:71
static uint CheckTrainCollision(Vehicle *v, Train *moving_front)
Collision test function.
bool TryPathReserve(Train *consist, bool mark_as_stuck, bool first_tile_okay)
Try to reserve a path to a safe position.
static uint TrainCrashed(Train *v)
Marks train as crashed and creates an AI event.
static void NormaliseDualHeads(Train *t)
Normalise the dual heads in the train, i.e.
static CommandCost CheckTrainAttachment(Train *t)
Check whether the train parts can be attached.
static const AccelerationSlowdownParams _accel_slowdown[]
Speed update fractions for each acceleration type.
CommandCost CmdMoveRailVehicle(DoCommandFlags flags, VehicleID src_veh, VehicleID dest_veh, bool move_chain)
Move a rail vehicle around inside the depot.
static Track DoTrainPathfind(const Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found, bool do_track_reservation, PBSTileInfo *dest, TileIndex *final_dest)
Perform pathfinding for a train.
static void TrainEnterStation(Train *consist, StationID station)
Trains enters a station, send out a news item if it is the first train, and start loading.
static bool TrainLocoHandler(Train *consist, bool mode)
Per-tick handler of each front engine.
static bool TrainCanLeaveTile(const Train *moving_front)
Determines whether train would like to leave the tile.
static void AffectSpeedByZChange(Train *consist, int z_diff)
Modify the speed of the vehicle due to a change in altitude.
static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_dir)
Clear the reservation of tile that was just left by a wagon on track_dir.
static void ReverseTrainDirection(Train *consist)
Turn a train around.
static bool IsTrain(const Vehicle *v)
Check if the vehicle is a train.
void CheckTrainsLengths()
Checks if lengths of all rail vehicles are valid.
Definition train_cmd.cpp:78
static bool CheckReverseTrain(const Train *consist)
Can the train reverse?
static void MaybeBarCrossingWithSound(TileIndex tile)
Bars crossing and plays ding-ding sound if not barred already.
bool TrainOnCrossing(TileIndex tile)
Check if a level crossing tile has a train on it.
static void ChangeTrainDirRandomly(Train *v)
Rotate all vehicles of a (crashed) train chain randomly to animate the crash.
void MarkDirtyAdjacentLevelCrossingTiles(TileIndex tile, Axis road_axis)
Find adjacent level crossing tiles in this multi-track crossing and mark them dirty.
static void NormaliseSubtypes(Train *chain)
Normalise the sub types of the parts in this chain.
bool TrainController(Train *v, Vehicle *nomove, bool reverse=true)
Move a vehicle chain one movement stop forwards.
CommandCost CmdSellRailWagon(DoCommandFlags flags, Vehicle *t, bool sell_chain, bool backup_order, ClientID user)
Sell a (single) train wagon/engine.
bool IsValidImageIndex< VehicleType::Train >(uint8_t image_index)
Helper to check whether an image index is valid for a particular vehicle.
Definition train_cmd.cpp:60
static TrainForceProceeding DetermineNextTrainForceProceeding(const Train *t)
Determine to what force_proceed should be changed.
static void RemoveFromConsist(Train *part, bool chain=false)
Remove the given wagon from its consist.
static bool TrainCheckIfLineEnds(Train *v, bool reverse=true)
Checks for line end.
static void RestoreTrainBackup(TrainList &list)
Restore the train from the backup list.
static void DeleteLastWagon(Train *v)
Deletes/Clears the last wagon of a crashed train.
static bool TrainApproachingLineEnd(Train *moving_front, bool signal, bool reverse)
Train is approaching line end, slow down and possibly reverse.
static bool TrainApproachingCrossing(TileIndex tile)
Finds a vehicle approaching rail-road crossing.
void ReverseTrainSwapVeh(Train *v, int l, int r)
Swap vehicles l and r in consist v, and reverse their direction.
static PBSTileInfo ExtendTrainReservation(const Train *v, TrackBits *new_tracks, DiagDirection *enterdir)
Extend a train path as far as possible.
static TileIndex TrainApproachingCrossingTile(const Train *v)
Determines whether train is approaching a rail-road crossing (thus making it barred).
static void AdvanceWagonsAfterSwap(Train *moving_front)
Advances wagons for train reversing, needed for variable length wagons.
void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type)
Get the size of the sprite of a train sprite heading west, or both heads (used for lists).
static void CheckIfTrainNeedsService(Train *v)
Check whether a train needs service, and if so, find a depot or service it.
static bool TrainApproachingCrossingEnum(const Vehicle *v, TileIndex tile)
Checks if a train is approaching a rail-road crossing.
static std::vector< VehicleID > GetFreeWagonsInDepot(TileIndex tile)
Get a list of free wagons in a depot.
Command definitions related to trains.
Sprites to use for trains.
static const uint8_t _engine_sprite_and[]
For how many directions do we have sprites?
static const uint8_t _engine_sprite_add[]
Non-zero for multihead trains.
@ TRANSPORT_RAIL
Transport by train.
bool IsTunnel(Tile t)
Is this a tunnel (entrance)?
Definition tunnel_map.h:23
void MarkBridgeDirty(TileIndex begin, TileIndex end, DiagDirection direction, uint bridge_height)
Mark bridge tiles dirty.
DiagDirection GetTunnelBridgeDirection(Tile t)
Get the direction pointing to the other end.
TileIndex GetOtherTunnelBridgeEnd(Tile t)
Determines type of the wormhole and returns its other end.
void SetTunnelBridgeReservation(Tile t, bool b)
Set the reservation state of the rail tunnel/bridge.
void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBug bug_type, bool critical)
Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
Definition vehicle.cpp:337
void VehicleEnterDepot(Vehicle *v)
Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it,...
Definition vehicle.cpp:1562
UnitID GetFreeUnitNumber(VehicleType type)
Get an unused unit number for a vehicle (if allowed).
Definition vehicle.cpp:1920
void VehicleLengthChanged(const Vehicle *u)
Logs a bug in GRF and shows a warning message if this is for the first time this happened.
Definition vehicle.cpp:363
void VehicleServiceInDepot(Vehicle *v)
Service a vehicle and all subsequent vehicles in the consist.
Definition vehicle.cpp:187
void CheckVehicleBreakdown(Vehicle *v)
Periodic check for a vehicle to maybe break down.
Definition vehicle.cpp:1318
GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
Get position information of a vehicle when moving one pixel in the direction it is facing.
Definition vehicle.cpp:1802
void DecreaseVehicleValue(Vehicle *v)
Decrease the value of a vehicle.
Definition vehicle.cpp:1297
void EconomyAgeVehicle(Vehicle *v)
Update economy age of a vehicle.
Definition vehicle.cpp:1440
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
Finds vehicle in tunnel / bridge.
Definition vehicle.cpp:581
void AgeVehicle(Vehicle *v)
Update age of a vehicle.
Definition vehicle.cpp:1452
Direction VehicleEnterTileCoordinates(GetNewVehiclePosResult &gp, DiagDirection enterdir, Track track)
Lookup new subposition coordinates and direction to use when entering a new tile, applying the subcoo...
Definition vehicle.cpp:3401
@ Crashed
Vehicle is crashed.
@ TrainSlowing
Train is slowing down.
@ Hidden
Vehicle is not visible.
@ DefaultPalette
Use default vehicle palette.
@ Stopped
Vehicle is stopped by the player.
Functions related to vehicles.
bool IsValidImageIndex(uint8_t image_index)
Helper to check whether an image index is valid for a particular vehicle.
@ CUSTOM_VEHICLE_SPRITENUM_REVERSED
Vehicle sprite from NewGRF with reverse driving direction (from articulation callback).
bool HasVehicleOnTile(TileIndex tile, UnaryPred &&predicate)
Loop over vehicles on a tile, and check whether a predicate is true for any of them.
@ VIWD_CONSIST_CHANGED
Vehicle composition was changed.
Definition vehicle_gui.h:37
EngineImageType
Visualisation contexts of vehicles and engines.
PoolID< uint32_t, struct VehicleIDTag, 0xFF000, 0xFFFFF > VehicleID
The type all our vehicle IDs have.
@ Train
Train vehicle type.
static const uint VEHICLE_LENGTH
The length of a vehicle in tile units.
@ WID_VV_REFIT
Open the refit window.
@ WID_VV_START_STOP
Start or stop this vehicle, and show information about the current state.
Functions related to (drawing on) viewports.
void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
Close a window by its class and window number (if it is open).
Definition window.cpp:1209
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting).
Definition window.cpp:3230
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3322
void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, WidgetID widget_index)
Mark a particular widget in a particular window as dirty (in need of repainting).
Definition window.cpp:3216
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3200
void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
Mark window data of all windows of a given class as invalid (in need of re-computing) Note that by de...
Definition window.cpp:3340
FindDepotData YapfTrainFindNearestDepot(const Train *v, int max_distance)
Used when user sends train to the nearest depot or if train needs servicing using YAPF.
bool YapfTrainFindNearestSafeTile(const Train *v, TileIndex tile, Trackdir td, bool override_railtype)
Try to extend the reserved path of a train to the nearest safe tile using YAPF.
bool YapfTrainCheckReverse(const Train *v)
Returns true if it is better to reverse the train before leaving station using YAPF.
Track YapfTrainChooseTrack(const Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found, bool reserve_track, struct PBSTileInfo *target, TileIndex *dest)
Finds the best path for given train using YAPF.
Base includes/functions for YAPF.
Functions related to zooming.
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
Definition zoom_func.h:107
int UnScaleGUI(int value)
Short-hand to apply GUI zoom level.
Definition zoom_func.h:77