OpenTTD Source 20260421-master-gc2fbc6fdeb
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
53static const uint8_t _vehicle_initial_x_fract[4] = {10, 8, 4, 8};
54static const uint8_t _vehicle_initial_y_fract[4] = { 8, 4, 8, 10};
55
57template <>
58bool IsValidImageIndex<VEH_TRAIN>(uint8_t image_index)
59{
60 return image_index < lengthof(_engine_sprite_base);
61}
62
63
70{
71 if (!CargoSpec::Get(cargo)->is_freight) return 1;
72 return _settings_game.vehicle.freight_trains;
73}
74
77{
78 bool first = true;
79
80 for (const Train *v : Train::Iterate()) {
81 if (v->First() == v && !v->vehstatus.Test(VehState::Crashed)) {
82 for (const Train *u = v->GetMovingFront(), *w = v->GetMovingNext(); w != nullptr; u = w, w = w->GetMovingNext()) {
83 if (u->track != TRACK_BIT_DEPOT) {
84 if ((w->track != TRACK_BIT_DEPOT &&
85 std::max(abs(u->x_pos - w->x_pos), abs(u->y_pos - w->y_pos)) != u->CalcNextVehicleOffset()) ||
86 (w->track == TRACK_BIT_DEPOT && TicksToLeaveDepot(u) <= 0)) {
87 ShowErrorMessage(GetEncodedString(STR_BROKEN_VEHICLE_LENGTH, v->index, v->owner), {}, WL_CRITICAL);
88
89 if (!_networking && first) {
90 first = false;
91 Command<Commands::Pause>::Post(PauseMode::Error, true);
92 }
93 /* Break so we warn only once for each train. */
94 break;
95 }
96 }
97 }
98 }
99 }
100}
101
109{
110 uint16_t max_speed = UINT16_MAX;
111
112 assert(this->IsFrontEngine() || this->IsFreeWagon());
113
114 const RailVehicleInfo *rvi_v = RailVehInfo(this->engine_type);
115 EngineID first_engine = this->IsFrontEngine() ? this->engine_type : EngineID::Invalid();
116 this->gcache.cached_total_length = 0;
117 this->compatible_railtypes = {};
118
119 bool train_can_tilt = true;
120 int16_t min_curve_speed_mod = INT16_MAX;
121
122 for (Train *u = this; u != nullptr; u = u->Next()) {
123 const RailVehicleInfo *rvi_u = RailVehInfo(u->engine_type);
124
125 /* Check the this->first cache. */
126 assert(u->First() == this);
127
128 /* update the 'first engine' */
129 u->gcache.first_engine = this == u ? EngineID::Invalid() : first_engine;
130 u->railtypes = rvi_u->railtypes;
131
132 if (u->IsEngine()) first_engine = u->engine_type;
133
134 /* Set user defined data to its default value */
135 u->tcache.user_def_data = rvi_u->user_def_data;
136 this->InvalidateNewGRFCache();
137 u->InvalidateNewGRFCache();
138 }
139
140 for (Train *u = this; u != nullptr; u = u->Next()) {
141 /* Update user defined data (must be done before other properties) */
142 u->tcache.user_def_data = GetVehicleProperty(u, PROP_TRAIN_USER_DATA, u->tcache.user_def_data);
143 this->InvalidateNewGRFCache();
144 u->InvalidateNewGRFCache();
145 }
146
147 for (Train *u = this; u != nullptr; u = u->Next()) {
148 const Engine *e_u = u->GetEngine();
149 const RailVehicleInfo *rvi_u = &e_u->VehInfo<RailVehicleInfo>();
150
151 if (!e_u->info.misc_flags.Test(EngineMiscFlag::RailTilts)) train_can_tilt = false;
152 min_curve_speed_mod = std::min(min_curve_speed_mod, u->GetCurveSpeedModifier());
153
154 /* Cache wagon override sprite group. nullptr is returned if there is none */
155 u->tcache.cached_override = GetWagonOverrideSpriteSet(u->engine_type, u->cargo_type, u->gcache.first_engine);
156
157 /* Reset colour map */
158 u->colourmap = PAL_NONE;
159
160 /* Update powered-wagon-status and visual effect */
161 u->UpdateVisualEffect(true);
162
163 if (rvi_v->pow_wag_power != 0 && rvi_u->railveh_type == RAILVEH_WAGON &&
164 UsesWagonOverride(u) && !HasBit(u->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
165 /* wagon is powered */
166 u->flags.Set(VehicleRailFlag::PoweredWagon); // cache 'powered' status
167 } else {
168 u->flags.Reset(VehicleRailFlag::PoweredWagon);
169 }
170
171 if (!u->IsArticulatedPart()) {
172 /* Do not count powered wagons for the compatible railtypes, as wagons always
173 have railtype normal */
174 if (rvi_u->power > 0) {
175 this->compatible_railtypes.Set(GetAllPoweredRailTypes(u->railtypes));
176 }
177
178 /* Some electric engines can be allowed to run on normal rail. It happens to all
179 * existing electric engines when elrails are disabled and then re-enabled */
180 if (u->flags.Test(VehicleRailFlag::AllowedOnNormalRail)) {
181 u->railtypes.Set(RAILTYPE_RAIL);
182 u->compatible_railtypes.Set(RAILTYPE_RAIL);
183 }
184
185 /* max speed is the minimum of the speed limits of all vehicles in the consist */
186 if ((rvi_u->railveh_type != RAILVEH_WAGON || _settings_game.vehicle.wagon_speed_limits) && !UsesWagonOverride(u)) {
187 uint16_t speed = GetVehicleProperty(u, PROP_TRAIN_SPEED, rvi_u->max_speed);
188 if (speed != 0) max_speed = std::min(speed, max_speed);
189 }
190 }
191
192 uint16_t new_cap = e_u->DetermineCapacity(u);
193 if (allowed_changes.Test(ConsistChangeFlag::Capacity)) {
194 /* Update vehicle capacity. */
195 if (u->cargo_cap > new_cap) u->cargo.Truncate(new_cap);
196 u->refit_cap = std::min(new_cap, u->refit_cap);
197 u->cargo_cap = new_cap;
198 } else {
199 /* Verify capacity hasn't changed. */
200 if (new_cap != u->cargo_cap) ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_CAPACITY, GRFBug::VehCapacity, true);
201 }
202 u->vcache.cached_cargo_age_period = GetVehicleProperty(u, PROP_TRAIN_CARGO_AGE_PERIOD, e_u->info.cargo_age_period);
203
204 /* check the vehicle length (callback) */
205 uint16_t veh_len = CALLBACK_FAILED;
206 if (e_u->GetGRF() != nullptr && e_u->GetGRF()->grf_version >= 8) {
207 /* Use callback 36 */
208 veh_len = GetVehicleProperty(u, PROP_TRAIN_SHORTEN_FACTOR, CALLBACK_FAILED);
209
210 if (veh_len != CALLBACK_FAILED && veh_len >= VEHICLE_LENGTH) {
212 }
213 } else if (e_u->info.callback_mask.Test(VehicleCallbackMask::Length)) {
214 /* Use callback 11 */
215 veh_len = GetVehicleCallback(CBID_VEHICLE_LENGTH, 0, 0, u->engine_type, u);
216 }
217 if (veh_len == CALLBACK_FAILED) veh_len = rvi_u->shorten_factor;
218 veh_len = VEHICLE_LENGTH - Clamp(veh_len, 0, VEHICLE_LENGTH - 1);
219
220 if (allowed_changes.Test(ConsistChangeFlag::Length)) {
221 /* Update vehicle length. */
222 u->gcache.cached_veh_length = veh_len;
223 } else {
224 /* Verify length hasn't changed. */
225 if (veh_len != u->gcache.cached_veh_length) VehicleLengthChanged(u);
226 }
227
228 this->gcache.cached_total_length += u->gcache.cached_veh_length;
229 this->InvalidateNewGRFCache();
230 u->InvalidateNewGRFCache();
231 }
232
233 /* store consist weight/max speed in cache */
234 this->vcache.cached_max_speed = max_speed;
235 this->tcache.cached_tilt = train_can_tilt;
236 this->tcache.cached_curve_speed_mod = min_curve_speed_mod;
237 this->tcache.cached_max_curve_speed = this->GetCurveSpeedLimit();
238
239 /* 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) */
240 this->CargoChanged();
241
242 if (this->IsFrontEngine()) {
243 this->UpdateAcceleration();
248
249 /* If the consist is changed while in a depot, the vehicle view window must be invalidated to update the availability of refitting. */
251 }
252}
253
264int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *moving_front, int *station_ahead, int *station_length)
265{
266 const Train *consist = moving_front->First();
267 const Station *st = Station::Get(station_id);
268 *station_ahead = st->GetPlatformLength(tile, DirToDiagDir(moving_front->GetMovingDirection())) * TILE_SIZE;
269 *station_length = st->GetPlatformLength(tile) * TILE_SIZE;
270
271 /* Default to the middle of the station for stations stops that are not in
272 * the order list like intermediate stations when non-stop is disabled */
274 if (consist->gcache.cached_total_length >= *station_length) {
275 /* The train is longer than the station, make it stop at the far end of the platform */
277 } else if (consist->current_order.IsType(OT_GOTO_STATION) && consist->current_order.GetDestination() == station_id) {
278 osl = consist->current_order.GetStopLocation();
279 }
280
281 /* The stop location of the FRONT! of the train */
282 int stop;
283 switch (osl) {
284 default: NOT_REACHED();
285
287 stop = consist->gcache.cached_total_length;
288 break;
289
291 stop = *station_length - (*station_length - consist->gcache.cached_total_length) / 2;
292 break;
293
295 stop = *station_length;
296 break;
297 }
298
299 /* Subtract half the front vehicle length of the train so we get the real
300 * stop location of the train. */
301 uint8_t rounding = consist->IsDrivingBackwards() ? 2 : 1;
302 return stop - (consist->gcache.cached_veh_length + rounding) / 2;
303}
304
305
311{
312 assert(this->First() == this);
313
314 static const int absolute_max_speed = UINT16_MAX;
315 int max_speed = absolute_max_speed;
316
317 if (_settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) return max_speed;
318
319 int curvecount[2] = {0, 0};
320
321 /* first find the curve speed limit */
322 int numcurve = 0;
323 int sum = 0;
324 int pos = 0;
325 int lastpos = -1;
326 for (const Train *u = this; u->Next() != nullptr; u = u->Next(), pos += u->gcache.cached_veh_length) {
327 Direction this_dir = u->direction;
328 Direction next_dir = u->Next()->direction;
329
330 DirDiff dirdiff = DirDifference(this_dir, next_dir);
331 if (dirdiff == DIRDIFF_SAME) continue;
332
333 if (dirdiff == DIRDIFF_45LEFT) curvecount[0]++;
334 if (dirdiff == DIRDIFF_45RIGHT) curvecount[1]++;
335 if (dirdiff == DIRDIFF_45LEFT || dirdiff == DIRDIFF_45RIGHT) {
336 if (lastpos != -1) {
337 numcurve++;
338 sum += pos - lastpos;
339 if (pos - lastpos <= static_cast<int>(VEHICLE_LENGTH) && max_speed > 88) {
340 max_speed = 88;
341 }
342 }
343 lastpos = pos;
344 }
345
346 /* if we have a 90 degree turn, fix the speed limit to 60 */
347 if (dirdiff == DIRDIFF_90LEFT || dirdiff == DIRDIFF_90RIGHT) {
348 max_speed = 61;
349 }
350 }
351
352 if (numcurve > 0 && max_speed > 88) {
353 if (curvecount[0] == 1 && curvecount[1] == 1) {
354 max_speed = absolute_max_speed;
355 } else {
356 sum = CeilDiv(sum, VEHICLE_LENGTH);
357 sum /= numcurve;
358 max_speed = 232 - (13 - Clamp(sum, 1, 12)) * (13 - Clamp(sum, 1, 12));
359 }
360 }
361
362 if (max_speed != absolute_max_speed) {
363 /* Apply the current railtype's curve speed advantage */
364 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(this->tile));
365 max_speed += (max_speed / 2) * rti->curve_speed;
366
367 if (this->tcache.cached_tilt) {
368 /* Apply max_speed bonus of 20% for a tilting train */
369 max_speed += max_speed / 5;
370 }
371
372 /* Apply max_speed modifier (cached value is fixed-point binary with 8 fractional bits)
373 * and clamp the result to an acceptable range. */
374 max_speed += (max_speed * this->tcache.cached_curve_speed_mod) / 256;
375 max_speed = Clamp(max_speed, 2, absolute_max_speed);
376 }
377
378 return static_cast<uint16_t>(max_speed);
379}
380
386{
387 const Train *moving_front = this->GetMovingFront();
388 int max_speed = _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL ?
389 this->gcache.cached_max_track_speed :
390 this->tcache.cached_max_curve_speed;
391
392 if (_settings_game.vehicle.train_acceleration_model == AM_REALISTIC && IsRailStationTile(moving_front->tile)) {
393 StationID sid = GetStationIndex(moving_front->tile);
394 if (this->current_order.ShouldStopAtStation(this, sid)) {
395 int station_ahead;
396 int station_length;
397 int stop_at = GetTrainStopLocation(sid, moving_front->tile, moving_front, &station_ahead, &station_length);
398
399 /* The distance to go is whatever is still ahead of the train minus the
400 * distance from the train's stop location to the end of the platform */
401 int distance_to_go = station_ahead / TILE_SIZE - (station_length - stop_at) / TILE_SIZE;
402
403 if (distance_to_go > 0) {
404 int st_max_speed = 120;
405
406 int delta_v = this->cur_speed / (distance_to_go + 1);
407 if (max_speed > (this->cur_speed - delta_v)) {
408 st_max_speed = this->cur_speed - (delta_v / 10);
409 }
410
411 st_max_speed = std::max(st_max_speed, 25 * distance_to_go);
412 max_speed = std::min(max_speed, st_max_speed);
413 }
414 }
415 }
416
417 for (const Train *u = this; u != nullptr; u = u->Next()) {
418 if (_settings_game.vehicle.train_acceleration_model == AM_REALISTIC && u->track == TRACK_BIT_DEPOT) {
419 constexpr int DEPOT_SPEED_LIMIT = 61;
420 max_speed = std::min(max_speed, DEPOT_SPEED_LIMIT);
421 break;
422 }
423
424 /* Vehicle is on the middle part of a bridge. */
425 if (u->track == TRACK_BIT_WORMHOLE && !u->vehstatus.Test(VehState::Hidden)) {
426 max_speed = std::min<int>(max_speed, GetBridgeSpec(GetBridgeType(u->tile))->speed);
427 }
428 }
429
430 max_speed = std::min<int>(max_speed, this->current_order.GetMaxSpeed());
431
432 /* If the train is going backwards, without a leading cab, restrict its speed. */
433 if (!moving_front->CanLeadTrain()) {
434 constexpr int BACKWARDS_NO_CAB_SPEED_LIMIT = 32;
435 max_speed = std::min<int>(max_speed, BACKWARDS_NO_CAB_SPEED_LIMIT);
436 }
437
438 return std::min<int>(max_speed, this->gcache.cached_max_track_speed);
439}
440
443{
444 assert(this->IsFrontEngine() || this->IsFreeWagon());
445
446 uint power = this->gcache.cached_power;
447 uint weight = this->gcache.cached_weight;
448 assert(weight != 0);
449 this->acceleration = Clamp(power / weight * 4, 1, 255);
450}
451
457{
458 if (this->gcache.cached_veh_length != 8 && this->flags.Test(VehicleRailFlag::Flipped) && !EngInfo(this->engine_type)->misc_flags.Test(EngineMiscFlag::RailFlips)) {
459 int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
460
461 const Engine *e = this->GetEngine();
462 if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->VehInfo<RailVehicleInfo>().image_index)) {
463 reference_width = e->GetGRF()->traininfo_vehicle_width;
464 }
465
466 return ScaleSpriteTrad((this->gcache.cached_veh_length - (int)VEHICLE_LENGTH) * reference_width / (int)VEHICLE_LENGTH);
467 }
468 return 0;
469}
470
477{
478 int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
479 int vehicle_pitch = 0;
480
481 const Engine *e = this->GetEngine();
482 if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->VehInfo<RailVehicleInfo>().image_index)) {
483 reference_width = e->GetGRF()->traininfo_vehicle_width;
484 vehicle_pitch = e->GetGRF()->traininfo_vehicle_pitch;
485 }
486
487 if (offset != nullptr) {
488 if (this->flags.Test(VehicleRailFlag::Flipped) && !EngInfo(this->engine_type)->misc_flags.Test(EngineMiscFlag::RailFlips)) {
489 offset->x = ScaleSpriteTrad(((int)this->gcache.cached_veh_length - (int)VEHICLE_LENGTH / 2) * reference_width / (int)VEHICLE_LENGTH);
490 } else {
491 offset->x = ScaleSpriteTrad(reference_width) / 2;
492 }
493 offset->y = ScaleSpriteTrad(vehicle_pitch);
494 }
495 return ScaleSpriteTrad(this->gcache.cached_veh_length * reference_width / VEHICLE_LENGTH);
496}
497
498static SpriteID GetDefaultTrainSprite(uint8_t spritenum, Direction direction)
499{
500 assert(IsValidImageIndex<VEH_TRAIN>(spritenum));
501 return ((direction + _engine_sprite_add[spritenum]) & _engine_sprite_and[spritenum]) + _engine_sprite_base[spritenum];
502}
503
511{
512 uint8_t spritenum = this->spritenum;
513
515
516 if (IsCustomVehicleSpriteNum(spritenum)) {
518 GetCustomVehicleSprite(this, direction, image_type, result);
519 if (result->IsValid()) return;
520
522 }
523
525 SpriteID sprite = GetDefaultTrainSprite(spritenum, direction);
526
527 if (this->cargo.StoredCount() >= this->cargo_cap / 2U) sprite += _wagon_full_adder[spritenum];
528
529 result->Set(sprite);
530}
531
532static void GetRailIcon(EngineID engine, bool rear_head, int &y, EngineImageType image_type, VehicleSpriteSeq *result)
533{
534 const Engine *e = Engine::Get(engine);
535 Direction dir = rear_head ? DIR_E : DIR_W;
536 uint8_t spritenum = e->VehInfo<RailVehicleInfo>().image_index;
537
538 if (IsCustomVehicleSpriteNum(spritenum)) {
539 GetCustomVehicleIcon(engine, dir, image_type, result);
540 if (result->IsValid()) {
541 if (e->GetGRF() != nullptr) {
543 }
544 return;
545 }
546
547 spritenum = Engine::Get(engine)->original_image_index;
548 }
549
550 if (rear_head) spritenum++;
551
552 result->Set(GetDefaultTrainSprite(spritenum, DIR_W));
553}
554
555void DrawTrainEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
556{
557 if (RailVehInfo(engine)->railveh_type == RAILVEH_MULTIHEAD) {
558 int yf = y;
559 int yr = y;
560
561 VehicleSpriteSeq seqf, seqr;
562 GetRailIcon(engine, false, yf, image_type, &seqf);
563 GetRailIcon(engine, true, yr, image_type, &seqr);
564
565 Rect rectf, rectr;
566 seqf.GetBounds(&rectf);
567 seqr.GetBounds(&rectr);
568
569 preferred_x = Clamp(preferred_x,
570 left - UnScaleGUI(rectf.left) + ScaleSpriteTrad(14),
571 right - UnScaleGUI(rectr.right) - ScaleSpriteTrad(15));
572
573 seqf.Draw(preferred_x - ScaleSpriteTrad(14), yf, pal, pal == PALETTE_CRASH);
574 seqr.Draw(preferred_x + ScaleSpriteTrad(15), yr, pal, pal == PALETTE_CRASH);
575 } else {
577 GetRailIcon(engine, false, y, image_type, &seq);
578
579 Rect rect;
580 seq.GetBounds(&rect);
581 preferred_x = Clamp(preferred_x,
582 left - UnScaleGUI(rect.left),
583 right - UnScaleGUI(rect.right));
584
585 seq.Draw(preferred_x, y, pal, pal == PALETTE_CRASH);
586 }
587}
588
598void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type)
599{
600 int y = 0;
601
603 GetRailIcon(engine, false, y, image_type, &seq);
604
605 Rect rect;
606 seq.GetBounds(&rect);
607
608 width = UnScaleGUI(rect.Width());
609 height = UnScaleGUI(rect.Height());
610 xoffs = UnScaleGUI(rect.left);
611 yoffs = UnScaleGUI(rect.top);
612
613 if (RailVehInfo(engine)->railveh_type == RAILVEH_MULTIHEAD) {
614 GetRailIcon(engine, true, y, image_type, &seq);
615 seq.GetBounds(&rect);
616
617 /* Calculate values relative to an imaginary center between the two sprites. */
618 width = ScaleSpriteTrad(TRAININFO_DEFAULT_VEHICLE_WIDTH) + UnScaleGUI(rect.right) - xoffs;
619 height = std::max<uint>(height, UnScaleGUI(rect.Height()));
620 xoffs = xoffs - ScaleSpriteTrad(TRAININFO_DEFAULT_VEHICLE_WIDTH) / 2;
621 yoffs = std::min(yoffs, UnScaleGUI(rect.top));
622 }
623}
624
630static std::vector<VehicleID> GetFreeWagonsInDepot(TileIndex tile)
631{
632 std::vector<VehicleID> free_wagons;
633
634 for (Vehicle *v : VehiclesOnTile(tile)) {
635 if (v->type != VEH_TRAIN) continue;
636 if (v->vehstatus.Test(VehState::Crashed)) continue;
637 if (!Train::From(v)->IsFreeWagon()) continue;
638
639 free_wagons.push_back(v->index);
640 }
641
642 /* Sort by vehicle index for consistency across clients. */
643 std::ranges::sort(free_wagons);
644 return free_wagons;
645}
646
655static CommandCost CmdBuildRailWagon(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
656{
657 const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
658
659 /* Check that the wagon can drive on the track in question */
660 if (!IsCompatibleRail(rvi->railtypes, GetRailType(tile))) return CMD_ERROR;
661
662 if (flags.Test(DoCommandFlag::Execute)) {
663 Train *v = Train::Create();
664 *ret = v;
665 v->spritenum = rvi->image_index;
666
667 v->engine_type = e->index;
668 v->gcache.first_engine = EngineID::Invalid(); // needs to be set before first callback
669
671
672 v->direction = DiagDirToDir(dir);
673 v->tile = tile;
674
675 int x = TileX(tile) * TILE_SIZE | _vehicle_initial_x_fract[dir];
676 int y = TileY(tile) * TILE_SIZE | _vehicle_initial_y_fract[dir];
677
678 v->x_pos = x;
679 v->y_pos = y;
680 v->z_pos = GetSlopePixelZ(x, y, true);
684
685 v->SetWagon();
686
687 v->SetFreeWagon();
689
691 assert(IsValidCargoType(v->cargo_type));
692 v->cargo_cap = rvi->capacity;
693 v->refit_cap = 0;
694
695 v->railtypes = rvi->railtypes;
696
700 v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
701 v->random_bits = Random();
702
704
706 if (prob.has_value()) v->flags.Set(VehicleRailFlag::Flipped, prob.value());
708
709 v->UpdatePosition();
712
714
715 /* Try to connect the vehicle to one of free chains of wagons. */
716 for (VehicleID vehicle : GetFreeWagonsInDepot(tile)) {
717 if (vehicle == v->index) continue;
718
719 const Train *w = Train::Get(vehicle);
720 if (w->engine_type != v->engine_type) continue;
721 if (w->First() == v) continue;
722
723 if (Command<Commands::MoveRailVehicle>::Do(DoCommandFlag::Execute, v->index, w->Last()->index, true).Succeeded()) {
724 break;
725 }
726 }
727 }
728
729 return CommandCost();
730}
731
737{
738 assert(u->IsEngine());
739 for (VehicleID vehicle : GetFreeWagonsInDepot(u->tile)) {
740 if (Command<Commands::MoveRailVehicle>::Do(DoCommandFlag::Execute, vehicle, u->index, true).Failed()) {
741 break;
742 }
743 }
744}
745
746static void AddRearEngineToMultiheadedTrain(Train *v)
747{
748 Train *u = Train::Create();
749 v->value >>= 1;
750 u->value = v->value;
751 u->direction = v->direction;
752 u->owner = v->owner;
753 u->tile = v->tile;
754 u->x_pos = v->x_pos;
755 u->y_pos = v->y_pos;
756 u->z_pos = v->z_pos;
758 u->vehstatus = v->vehstatus;
760 u->spritenum = v->spritenum + 1;
761 u->cargo_type = v->cargo_type;
763 u->cargo_cap = v->cargo_cap;
764 u->refit_cap = v->refit_cap;
765 u->railtypes = v->railtypes;
766 u->engine_type = v->engine_type;
769 u->build_year = v->build_year;
770 u->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
771 u->random_bits = Random();
772 v->SetMultiheaded();
773 u->SetMultiheaded();
774 v->SetNext(u);
776 if (prob.has_value()) u->flags.Set(VehicleRailFlag::Flipped, prob.value());
777 u->UpdatePosition();
778
779 /* Now we need to link the front and rear engines together */
782}
783
792CommandCost CmdBuildRailVehicle(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
793{
794 const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
795
796 if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildRailWagon(flags, tile, e, ret);
797
798 /* Check if depot and new engine uses the same kind of tracks *
799 * We need to see if the engine got power on the tile to avoid electric engines in non-electric depots */
800 if (!HasPowerOnRail(rvi->railtypes, GetRailType(tile))) return CMD_ERROR;
801
802 if (flags.Test(DoCommandFlag::Execute)) {
804 int x = TileX(tile) * TILE_SIZE + _vehicle_initial_x_fract[dir];
805 int y = TileY(tile) * TILE_SIZE + _vehicle_initial_y_fract[dir];
806
807 Train *v = Train::Create();
808 *ret = v;
809 v->direction = DiagDirToDir(dir);
810 v->tile = tile;
812 v->x_pos = x;
813 v->y_pos = y;
814 v->z_pos = GetSlopePixelZ(x, y, true);
817 v->spritenum = rvi->image_index;
819 assert(IsValidCargoType(v->cargo_type));
820 v->cargo_cap = rvi->capacity;
821 v->refit_cap = 0;
822 v->last_station_visited = StationID::Invalid();
823 v->last_loading_station = StationID::Invalid();
824
825 v->engine_type = e->index;
826 v->gcache.first_engine = EngineID::Invalid(); // needs to be set before first callback
827
828 v->reliability = e->reliability;
831
832 v->railtypes = rvi->railtypes;
833
834 v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_trains);
838 v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
839 v->random_bits = Random();
840
842 v->SetServiceIntervalIsPercent(Company::Get(_current_company)->settings.vehicle.servint_ispercent);
843
845
846 v->SetFrontEngine();
847 v->SetEngine();
848
850 if (prob.has_value()) v->flags.Set(VehicleRailFlag::Flipped, prob.value());
851 v->UpdatePosition();
852
853 if (rvi->railveh_type == RAILVEH_MULTIHEAD) {
854 AddRearEngineToMultiheadedTrain(v);
855 } else {
857 }
858
861
863 }
864
865 return CommandCost();
866}
867
868static Train *FindGoodVehiclePos(const Train *src)
869{
870 EngineID eng = src->engine_type;
871
872 for (VehicleID vehicle : GetFreeWagonsInDepot(src->tile)) {
873 Train *dst = Train::Get(vehicle);
874
875 /* check so all vehicles in the line have the same engine. */
876 Train *t = dst;
877 while (t->engine_type == eng) {
878 t = t->Next();
879 if (t == nullptr) return dst;
880 }
881 }
882
883 return nullptr;
884}
885
887typedef std::vector<Train *> TrainList;
888
894static void MakeTrainBackup(TrainList &list, Train *t)
895{
896 for (; t != nullptr; t = t->Next()) list.push_back(t);
897}
898
904{
905 /* No train, nothing to do. */
906 if (list.empty()) return;
907
908 Train *prev = nullptr;
909 /* Iterate over the list and rebuild it. */
910 for (Train *t : list) {
911 if (prev != nullptr) {
912 prev->SetNext(t);
913 } else if (t->Previous() != nullptr) {
914 /* Make sure the head of the train is always the first in the chain. */
915 t->Previous()->SetNext(nullptr);
916 }
917 prev = t;
918 }
919}
920
926static void RemoveFromConsist(Train *part, bool chain = false)
927{
928 Train *tail;
929
930 if (chain) {
931 /* We're moving several vehicles, find the last one in the chain. */
932 tail = part;
933 while (tail->Next() != nullptr) tail = tail->Next();
934 } else {
935 /* We're just moving one vehicle, but make sure we get all the articulated parts. */
936 tail = part->GetLastEnginePart();
937 }
938
939 /* Unlink at the front, but make it point to the next
940 * vehicle after the to be remove part. */
941 if (part->Previous() != nullptr) part->Previous()->SetNext(tail->Next());
942
943 /* Unlink at the back */
944 tail->SetNext(nullptr);
945}
946
952static void InsertInConsist(Train *dst, Train *chain)
953{
954 /* We do not want to add something in the middle of an articulated part. */
955 assert(dst != nullptr && (dst->Next() == nullptr || !dst->Next()->IsArticulatedPart()));
956
957 chain->Last()->SetNext(dst->Next());
958 dst->SetNext(chain);
959}
960
967{
968 for (; t != nullptr; t = t->GetNextVehicle()) {
969 if (!t->IsMultiheaded() || !t->IsEngine()) continue;
970
971 /* Make sure that there are no free cars before next engine */
972 Train *u;
973 for (u = t; u->Next() != nullptr && !u->Next()->IsEngine(); u = u->Next()) {}
974
975 if (u == t->other_multiheaded_part) continue;
976
977 /* Remove the part from the 'wrong' train */
979 /* And add it to the 'right' train */
981 }
982}
983
988static void NormaliseSubtypes(Train *chain)
989{
990 /* Nothing to do */
991 if (chain == nullptr) return;
992
993 /* We must be the first in the chain. */
994 assert(chain->Previous() == nullptr);
995
996 /* Set the appropriate bits for the first in the chain. */
997 if (chain->IsWagon()) {
998 chain->SetFreeWagon();
999 } else {
1000 assert(chain->IsEngine());
1001 chain->SetFrontEngine();
1002 }
1003
1004 /* Now clear the bits for the rest of the chain */
1005 for (Train *t = chain->Next(); t != nullptr; t = t->Next()) {
1006 t->ClearFreeWagon();
1007 t->ClearFrontEngine();
1008 }
1009}
1010
1020static CommandCost CheckNewTrain(Train *original_dst, Train *dst, Train *original_src, Train *src)
1021{
1022 /* Just add 'new' engines and subtract the original ones.
1023 * If that's less than or equal to 0 we can be sure we did
1024 * not add any engines (read: trains) along the way. */
1025 if ((src != nullptr && src->IsEngine() ? 1 : 0) +
1026 (dst != nullptr && dst->IsEngine() ? 1 : 0) -
1027 (original_src != nullptr && original_src->IsEngine() ? 1 : 0) -
1028 (original_dst != nullptr && original_dst->IsEngine() ? 1 : 0) <= 0) {
1029 return CommandCost();
1030 }
1031
1032 /* Get a free unit number and check whether it's within the bounds.
1033 * There will always be a maximum of one new train. */
1034 if (GetFreeUnitNumber(VEH_TRAIN) <= _settings_game.vehicle.max_trains) return CommandCost();
1035
1036 return CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);
1037}
1038
1045{
1046 /* No multi-part train, no need to check. */
1047 if (t == nullptr || t->Next() == nullptr) return CommandCost();
1048
1049 /* The maximum length for a train. For each part we decrease this by one
1050 * and if the result is negative the train is simply too long. */
1051 int allowed_len = _settings_game.vehicle.max_train_length * TILE_SIZE - t->gcache.cached_veh_length;
1052
1053 /* For free-wagon chains, check if they are within the max_train_length limit. */
1054 if (!t->IsEngine()) {
1055 t = t->Next();
1056 while (t != nullptr) {
1057 allowed_len -= t->gcache.cached_veh_length;
1058
1059 t = t->Next();
1060 }
1061
1062 if (allowed_len < 0) return CommandCost(STR_ERROR_TRAIN_TOO_LONG);
1063 return CommandCost();
1064 }
1065
1066 Train *head = t;
1067 Train *prev = t;
1068
1069 /* Break the prev -> t link so it always holds within the loop. */
1070 t = t->Next();
1071 prev->SetNext(nullptr);
1072
1073 /* Make sure the cache is cleared. */
1074 head->InvalidateNewGRFCache();
1075
1076 while (t != nullptr) {
1077 allowed_len -= t->gcache.cached_veh_length;
1078
1079 Train *next = t->Next();
1080
1081 /* Unlink the to-be-added piece; it is already unlinked from the previous
1082 * part due to the fact that the prev -> t link is broken. */
1083 t->SetNext(nullptr);
1084
1085 /* Don't check callback for articulated or rear dual headed parts */
1086 if (!t->IsArticulatedPart() && !t->IsRearDualheaded()) {
1087 /* Back up and clear the first_engine data to avoid using wagon override group */
1088 EngineID first_engine = t->gcache.first_engine;
1089 t->gcache.first_engine = EngineID::Invalid();
1090
1091 /* We don't want the cache to interfere. head's cache is cleared before
1092 * the loop and after each callback does not need to be cleared here. */
1094
1095 std::array<int32_t, 1> regs100;
1096 uint16_t callback = GetVehicleCallbackParent(CBID_TRAIN_ALLOW_WAGON_ATTACH, 0, 0, head->engine_type, t, head, regs100);
1097
1098 /* Restore original first_engine data */
1099 t->gcache.first_engine = first_engine;
1100
1101 /* We do not want to remember any cached variables from the test run */
1103 head->InvalidateNewGRFCache();
1104
1105 if (callback != CALLBACK_FAILED) {
1106 /* A failing callback means everything is okay */
1107 StringID error = STR_NULL;
1108
1109 if (head->GetGRF()->grf_version < 8) {
1110 if (callback == 0xFD) error = STR_ERROR_INCOMPATIBLE_RAIL_TYPES;
1111 if (callback < 0xFD) error = GetGRFStringID(head->GetGRFID(), GRFSTR_MISC_GRF_TEXT + callback);
1112 if (callback >= 0x100) ErrorUnknownCallbackResult(head->GetGRFID(), CBID_TRAIN_ALLOW_WAGON_ATTACH, callback);
1113 } else {
1114 if (callback < 0x400) {
1115 error = GetGRFStringID(head->GetGRFID(), GRFSTR_MISC_GRF_TEXT + callback);
1116 } else {
1117 switch (callback) {
1118 case 0x400: // allow if railtypes match (always the case for OpenTTD)
1119 case 0x401: // allow
1120 break;
1121
1122 case 0x40F:
1123 error = GetGRFStringID(head->GetGRFID(), static_cast<GRFStringID>(regs100[0]));
1124 break;
1125
1126 default: // unknown reason -> disallow
1127 case 0x402: // disallow attaching
1128 error = STR_ERROR_INCOMPATIBLE_RAIL_TYPES;
1129 break;
1130 }
1131 }
1132 }
1133
1134 if (error != STR_NULL) return CommandCost(error);
1135 }
1136 }
1137
1138 /* And link it to the new part. */
1139 prev->SetNext(t);
1140 prev = t;
1141 t = next;
1142 }
1143
1144 if (allowed_len < 0) return CommandCost(STR_ERROR_TRAIN_TOO_LONG);
1145 return CommandCost();
1146}
1147
1158static CommandCost ValidateTrains(Train *original_dst, Train *dst, Train *original_src, Train *src, bool check_limit)
1159{
1160 /* Check whether we may actually construct the trains. */
1162 if (ret.Failed()) return ret;
1163 ret = CheckTrainAttachment(dst);
1164 if (ret.Failed()) return ret;
1165
1166 /* Check whether we need to build a new train. */
1167 return check_limit ? CheckNewTrain(original_dst, dst, original_src, src) : CommandCost();
1168}
1169
1178static void ArrangeTrains(Train **dst_head, Train *dst, Train **src_head, Train *src, bool move_chain)
1179{
1180 /* First determine the front of the two resulting trains */
1181 if (*src_head == *dst_head) {
1182 /* If we aren't moving part(s) to a new train, we are just moving the
1183 * front back and there is not destination head. */
1184 *dst_head = nullptr;
1185 } else if (*dst_head == nullptr) {
1186 /* If we are moving to a new train the head of the move train would become
1187 * the head of the new vehicle. */
1188 *dst_head = src;
1189 }
1190
1191 if (src == *src_head) {
1192 /* If we are moving the front of a train then we are, in effect, creating
1193 * a new head for the train. Point to that. Unless we are moving the whole
1194 * train in which case there is not 'source' train anymore.
1195 * In case we are a multiheaded part we want the complete thing to come
1196 * with us, so src->GetNextUnit(), however... when we are e.g. a wagon
1197 * that is followed by a rear multihead we do not want to include that. */
1198 *src_head = move_chain ? nullptr :
1199 (src->IsMultiheaded() ? src->GetNextUnit() : src->GetNextVehicle());
1200 }
1201
1202 /* Now it's just simply removing the part that we are going to move from the
1203 * source train and *if* the destination is a not a new train add the chain
1204 * at the destination location. */
1205 RemoveFromConsist(src, move_chain);
1206 if (*dst_head != src) InsertInConsist(dst, src);
1207
1208 /* Now normalise the dual heads, that is move the dual heads around in such
1209 * a way that the head and rear of a dual head are in the same train */
1210 NormaliseDualHeads(*src_head);
1211 NormaliseDualHeads(*dst_head);
1212}
1213
1219static void NormaliseTrainHead(Train *head)
1220{
1221 /* Not much to do! */
1222 if (head == nullptr) return;
1223
1224 /* Tell the 'world' the train changed. */
1226 UpdateTrainGroupID(head);
1227
1228 /* Not a front engine, i.e. a free wagon chain. No need to do more. */
1229 if (!head->IsFrontEngine()) return;
1230
1231 /* Update the refit button and window */
1234
1235 /* If we don't have a unit number yet, set one. */
1236 if (head->unitnumber != 0) return;
1237 head->unitnumber = Company::Get(head->owner)->freeunits[head->type].UseID(GetFreeUnitNumber(VEH_TRAIN));
1238}
1239
1249CommandCost CmdMoveRailVehicle(DoCommandFlags flags, VehicleID src_veh, VehicleID dest_veh, bool move_chain)
1250{
1251 Train *src = Train::GetIfValid(src_veh);
1252 if (src == nullptr) return CMD_ERROR;
1253
1254 CommandCost ret = CheckOwnership(src->owner);
1255 if (ret.Failed()) return ret;
1256
1257 /* Do not allow moving crashed vehicles inside the depot, it is likely to cause asserts later */
1258 if (src->vehstatus.Test(VehState::Crashed)) return CMD_ERROR;
1259
1260 /* if nothing is selected as destination, try and find a matching vehicle to drag to. */
1261 Train *dst;
1262 if (dest_veh == VehicleID::Invalid()) {
1263 dst = (src->IsEngine() || flags.Test(DoCommandFlag::AutoReplace)) ? nullptr : FindGoodVehiclePos(src);
1264 } else {
1265 dst = Train::GetIfValid(dest_veh);
1266 if (dst == nullptr) return CMD_ERROR;
1267
1268 ret = CheckOwnership(dst->owner);
1269 if (ret.Failed()) return ret;
1270
1271 /* Do not allow appending to crashed vehicles, too */
1272 if (dst->vehstatus.Test(VehState::Crashed)) return CMD_ERROR;
1273 }
1274
1275 /* if an articulated part is being handled, deal with its parent vehicle */
1276 src = src->GetFirstEnginePart();
1277 if (dst != nullptr) {
1278 dst = dst->GetFirstEnginePart();
1279 }
1280
1281 /* don't move the same vehicle.. */
1282 if (src == dst) return CommandCost();
1283
1284 /* locate the head of the two chains */
1285 Train *src_head = src->First();
1286 Train *dst_head;
1287 if (dst != nullptr) {
1288 dst_head = dst->First();
1289 if (dst_head->tile != src_head->tile) return CMD_ERROR;
1290 /* Now deal with articulated part of destination wagon */
1291 dst = dst->GetLastEnginePart();
1292 } else {
1293 dst_head = nullptr;
1294 }
1295
1296 if (src->IsRearDualheaded()) return CommandCost(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT);
1297
1298 /* When moving all wagons, we can't have the same src_head and dst_head */
1299 if (move_chain && src_head == dst_head) return CommandCost();
1300
1301 /* When moving a multiheaded part to be place after itself, bail out. */
1302 if (!move_chain && dst != nullptr && dst->IsRearDualheaded() && src == dst->other_multiheaded_part) return CommandCost();
1303
1304 /* Check if all vehicles in the source train are stopped inside a depot. */
1305 if (!src_head->IsStoppedInDepot()) return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
1306
1307 /* Check if all vehicles in the destination train are stopped inside a depot. */
1308 if (dst_head != nullptr && !dst_head->IsStoppedInDepot()) return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
1309
1310 /* First make a backup of the order of the trains. That way we can do
1311 * whatever we want with the order and later on easily revert. */
1312 TrainList original_src;
1313 TrainList original_dst;
1314
1315 MakeTrainBackup(original_src, src_head);
1316 MakeTrainBackup(original_dst, dst_head);
1317
1318 /* Also make backup of the original heads as ArrangeTrains can change them.
1319 * For the destination head we do not care if it is the same as the source
1320 * head because in that case it's just a copy. */
1321 Train *original_src_head = src_head;
1322 Train *original_dst_head = (dst_head == src_head ? nullptr : dst_head);
1323
1324 /* We want this information from before the rearrangement, but execute this after the validation.
1325 * original_src_head can't be nullptr; src is by definition != nullptr, so src_head can't be nullptr as
1326 * src->GetFirst() always yields non-nullptr, so eventually original_src_head != nullptr as well. */
1327 bool original_src_head_front_engine = original_src_head->IsFrontEngine();
1328 bool original_dst_head_front_engine = original_dst_head != nullptr && original_dst_head->IsFrontEngine();
1329
1330 /* (Re)arrange the trains in the wanted arrangement. */
1331 ArrangeTrains(&dst_head, dst, &src_head, src, move_chain);
1332
1333 if (!flags.Test(DoCommandFlag::AutoReplace)) {
1334 /* If the autoreplace flag is set we do not need to test for the validity
1335 * because we are going to revert the train to its original state. As we
1336 * assume the original state was correct autoreplace can skip this. */
1337 ret = ValidateTrains(original_dst_head, dst_head, original_src_head, src_head, true);
1338 if (ret.Failed()) {
1339 /* Restore the train we had. */
1340 RestoreTrainBackup(original_src);
1341 RestoreTrainBackup(original_dst);
1342 return ret;
1343 }
1344 }
1345
1346 /* do it? */
1347 if (flags.Test(DoCommandFlag::Execute)) {
1348 /* Remove old heads from the statistics */
1349 if (original_src_head_front_engine) GroupStatistics::CountVehicle(original_src_head, -1);
1350 if (original_dst_head_front_engine) GroupStatistics::CountVehicle(original_dst_head, -1);
1351
1352 /* First normalise the sub types of the chains. */
1353 NormaliseSubtypes(src_head);
1354 NormaliseSubtypes(dst_head);
1355
1356 /* There are 14 different cases:
1357 * 1) front engine gets moved to a new train, it stays a front engine.
1358 * a) the 'next' part is a wagon that becomes a free wagon chain.
1359 * b) the 'next' part is an engine that becomes a front engine.
1360 * c) there is no 'next' part, nothing else happens
1361 * 2) front engine gets moved to another train, it is not a front engine anymore
1362 * a) the 'next' part is a wagon that becomes a free wagon chain.
1363 * b) the 'next' part is an engine that becomes a front engine.
1364 * c) there is no 'next' part, nothing else happens
1365 * 3) front engine gets moved to later in the current train, it is not a front engine anymore.
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 * 4) free wagon gets moved
1369 * a) the 'next' part is a wagon that becomes a free wagon chain.
1370 * b) the 'next' part is an engine that becomes a front engine.
1371 * c) there is no 'next' part, nothing else happens
1372 * 5) non front engine gets moved and becomes a new train, nothing else happens
1373 * 6) non front engine gets moved within a train / to another train, nothing happens
1374 * 7) wagon gets moved, nothing happens
1375 */
1376 if (src == original_src_head && src->IsEngine() && !src->IsFrontEngine()) {
1377 /* Cases #2 and #3: the front engine gets trashed. */
1378 CloseWindowById(WC_VEHICLE_VIEW, src->index);
1380 CloseWindowById(WC_VEHICLE_REFIT, src->index);
1385
1386 if (src_head != nullptr && src_head->IsFrontEngine()) {
1387 /* Cases #?b: Transfer order, unit number and other stuff
1388 * to the new front engine. */
1389 src_head->orders = src->orders;
1390 if (src_head->orders != nullptr) src_head->AddToShared(src);
1391 src_head->CopyVehicleConfigAndStatistics(src);
1392 }
1393 /* Remove stuff not valid anymore for non-front engines. */
1395 src->ReleaseUnitNumber();
1396 src->name.clear();
1397 }
1398
1399 /* We weren't a front engine but are becoming one. So
1400 * we should be put in the default group. */
1401 if (original_src_head != src && dst_head == src) {
1404 }
1405
1406 /* Handle 'new engine' part of cases #1b, #2b, #3b, #4b and #5 in NormaliseTrainHead. */
1407 NormaliseTrainHead(src_head);
1408 NormaliseTrainHead(dst_head);
1409
1410 /* Add new heads to statistics.
1411 * This should be done after NormaliseTrainHead due to engine total limit checks in GetFreeUnitNumber. */
1412 if (src_head != nullptr && src_head->IsFrontEngine()) GroupStatistics::CountVehicle(src_head, 1);
1413 if (dst_head != nullptr && dst_head->IsFrontEngine()) GroupStatistics::CountVehicle(dst_head, 1);
1414
1416 CheckCargoCapacity(src_head);
1417 CheckCargoCapacity(dst_head);
1418 }
1419
1420 if (src_head != nullptr) src_head->First()->MarkDirty();
1421 if (dst_head != nullptr) dst_head->First()->MarkDirty();
1422
1423 /* We are undoubtedly changing something in the depot and train list. */
1426 } else {
1427 /* We don't want to execute what we're just tried. */
1428 RestoreTrainBackup(original_src);
1429 RestoreTrainBackup(original_dst);
1430 }
1431
1432 return CommandCost();
1433}
1434
1447CommandCost CmdSellRailWagon(DoCommandFlags flags, Vehicle *t, bool sell_chain, bool backup_order, ClientID user)
1448{
1450 Train *first = v->First();
1451
1452 if (v->IsRearDualheaded()) return CommandCost(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT);
1453
1454 /* First make a backup of the order of the train. That way we can do
1455 * whatever we want with the order and later on easily revert. */
1456 TrainList original;
1457 MakeTrainBackup(original, first);
1458
1459 /* We need to keep track of the new head and the head of what we're going to sell. */
1460 Train *new_head = first;
1461 Train *sell_head = nullptr;
1462
1463 /* Split the train in the wanted way. */
1464 ArrangeTrains(&sell_head, nullptr, &new_head, v, sell_chain);
1465
1466 /* We don't need to validate the second train; it's going to be sold. */
1467 CommandCost ret = ValidateTrains(nullptr, nullptr, first, new_head, !flags.Test(DoCommandFlag::AutoReplace));
1468 if (ret.Failed()) {
1469 /* Restore the train we had. */
1470 RestoreTrainBackup(original);
1471 return ret;
1472 }
1473
1474 if (first->orders == nullptr && !OrderList::CanAllocateItem()) {
1475 /* Restore the train we had. */
1476 RestoreTrainBackup(original);
1477 return CommandCost(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS);
1478 }
1479
1481 for (Train *part = sell_head; part != nullptr; part = part->Next()) cost.AddCost(-part->value);
1482
1483 /* do it? */
1484 if (flags.Test(DoCommandFlag::Execute)) {
1485 /* First normalise the sub types of the chain. */
1486 NormaliseSubtypes(new_head);
1487
1488 if (v == first && !sell_chain && new_head != nullptr && new_head->IsFrontEngine()) {
1489 if (v->IsEngine()) {
1490 /* We are selling the front engine. In this case we want to
1491 * 'give' the order, unit number and such to the new head. */
1492 new_head->orders = first->orders;
1493 new_head->AddToShared(first);
1494 DeleteVehicleOrders(first);
1495
1496 /* Copy other important data from the front engine */
1497 new_head->CopyVehicleConfigAndStatistics(first);
1498 }
1499 GroupStatistics::CountVehicle(new_head, 1); // after copying over the profit, if required
1500 } else if (v->IsPrimaryVehicle() && backup_order) {
1501 OrderBackup::Backup(v, user);
1502 }
1503
1504 /* We need to update the information about the train. */
1505 NormaliseTrainHead(new_head);
1506
1507 /* We are undoubtedly changing something in the depot and train list. */
1510
1511 /* Actually delete the sold 'goods' */
1512 delete sell_head;
1513 } else {
1514 /* We don't want to execute what we're just tried. */
1515 RestoreTrainBackup(original);
1516 }
1517
1518 return cost;
1519}
1520
1522{
1523 /* Set common defaults. */
1524 this->bounds = {{-1, -1, 0}, {3, 3, 6}, {}};
1525
1526 /* Set if flipped and engine is NOT flagged with custom flip handling. */
1527 int flipped = this->flags.Test(VehicleRailFlag::Flipped) && !EngInfo(this->engine_type)->misc_flags.Test(EngineMiscFlag::RailFlips);
1528 /* If flipped and vehicle length is odd, we need to adjust the bounding box offset slightly. */
1529 int flip_offs = flipped && (this->gcache.cached_veh_length & 1);
1530
1531 Direction dir = this->direction;
1532 if (flipped) dir = ReverseDir(dir);
1533
1534 if (!IsDiagonalDirection(dir)) {
1535 static const Point _sign_table[] = {
1536 /* x, y */
1537 {-1, -1}, // DIR_N
1538 {-1, 1}, // DIR_E
1539 { 1, 1}, // DIR_S
1540 { 1, -1}, // DIR_W
1541 };
1542
1543 int half_shorten = (VEHICLE_LENGTH - this->gcache.cached_veh_length + flipped) / 2;
1544
1545 /* For all straight directions, move the bound box to the centre of the vehicle, but keep the size. */
1546 this->bounds.offset.x -= half_shorten * _sign_table[DirToDiagDir(dir)].x;
1547 this->bounds.offset.y -= half_shorten * _sign_table[DirToDiagDir(dir)].y;
1548 } else {
1549 switch (dir) {
1550 /* Shorten southern corner of the bounding box according the vehicle length
1551 * and center the bounding box on the vehicle. */
1552 case DIR_NE:
1553 this->bounds.origin.x = -(this->gcache.cached_veh_length + 1) / 2 + flip_offs;
1554 this->bounds.extent.x = this->gcache.cached_veh_length;
1555 this->bounds.offset.x = 1;
1556 break;
1557
1558 case DIR_NW:
1559 this->bounds.origin.y = -(this->gcache.cached_veh_length + 1) / 2 + flip_offs;
1560 this->bounds.extent.y = this->gcache.cached_veh_length;
1561 this->bounds.offset.y = 1;
1562 break;
1563
1564 /* Move northern corner of the bounding box down according to vehicle length
1565 * and center the bounding box on the vehicle. */
1566 case DIR_SW:
1567 this->bounds.origin.x = -(this->gcache.cached_veh_length) / 2 - flip_offs;
1568 this->bounds.extent.x = this->gcache.cached_veh_length;
1569 this->bounds.offset.x = 1 - (VEHICLE_LENGTH - this->gcache.cached_veh_length);
1570 break;
1571
1572 case DIR_SE:
1573 this->bounds.origin.y = -(this->gcache.cached_veh_length) / 2 - flip_offs;
1574 this->bounds.extent.y = this->gcache.cached_veh_length;
1575 this->bounds.offset.y = 1 - (VEHICLE_LENGTH - this->gcache.cached_veh_length);
1576 break;
1577
1578 default:
1579 NOT_REACHED();
1580 }
1581 }
1582}
1583
1588static void MarkTrainAsStuck(Train *consist)
1589{
1590 if (!consist->flags.Test(VehicleRailFlag::Stuck)) {
1591 /* It is the first time the problem occurred, set the "train stuck" flag. */
1593
1594 consist->wait_counter = 0;
1595
1596 /* Stop train */
1597 consist->cur_speed = 0;
1598 consist->subspeed = 0;
1599 consist->SetLastSpeed();
1600
1602 }
1603}
1604
1612static void SwapTrainFlags(uint16_t *swap_flag1, uint16_t *swap_flag2)
1613{
1614 uint16_t flag1 = *swap_flag1;
1615 uint16_t flag2 = *swap_flag2;
1616
1617 /* Clear the flags */
1618 ClrBit(*swap_flag1, GVF_GOINGUP_BIT);
1619 ClrBit(*swap_flag1, GVF_GOINGDOWN_BIT);
1620 ClrBit(*swap_flag2, GVF_GOINGUP_BIT);
1621 ClrBit(*swap_flag2, GVF_GOINGDOWN_BIT);
1622
1623 /* Reverse the rail-flags (if needed) */
1624 if (HasBit(flag1, GVF_GOINGUP_BIT)) {
1625 SetBit(*swap_flag2, GVF_GOINGDOWN_BIT);
1626 } else if (HasBit(flag1, GVF_GOINGDOWN_BIT)) {
1627 SetBit(*swap_flag2, GVF_GOINGUP_BIT);
1628 }
1629 if (HasBit(flag2, GVF_GOINGUP_BIT)) {
1630 SetBit(*swap_flag1, GVF_GOINGDOWN_BIT);
1631 } else if (HasBit(flag2, GVF_GOINGDOWN_BIT)) {
1632 SetBit(*swap_flag1, GVF_GOINGUP_BIT);
1633 }
1634}
1635
1641static void UpdateStatusAfterSwap(Train *v, bool reverse = true)
1642{
1643 /* Maybe reverse the direction. */
1644 if (reverse) v->direction = ReverseDir(v->direction);
1645
1646 /* Call the proper EnterTile function unless we are in a wormhole. */
1647 if (v->track != TRACK_BIT_WORMHOLE) {
1648 VehicleEnterTile(v, v->tile, v->x_pos, v->y_pos);
1649 } else {
1650 /* VehicleEnterTile_TunnelBridge() sets TRACK_BIT_WORMHOLE when the vehicle
1651 * is on the last bit of the bridge head (frame == TILE_SIZE - 1).
1652 * If we were swapped with such a vehicle, we have set TRACK_BIT_WORMHOLE,
1653 * when we shouldn't have. Check if this is the case. */
1654 TileIndex vt = TileVirtXY(v->x_pos, v->y_pos);
1656 VehicleEnterTile(v, vt, v->x_pos, v->y_pos);
1657 if (v->track != TRACK_BIT_WORMHOLE && IsBridgeTile(v->tile)) {
1658 /* We have just left the wormhole, possibly set the
1659 * "goingdown" bit. UpdateInclination() can be used
1660 * because we are at the border of the tile. */
1661 v->UpdatePosition();
1662 v->UpdateInclination(true, true);
1663 return;
1664 }
1665 }
1666 }
1667
1668 v->UpdatePosition();
1669 v->UpdateViewport(true, true);
1670}
1671
1678void ReverseTrainSwapVeh(Train *v, int l, int r)
1679{
1680 Train *a, *b;
1681
1682 /* locate vehicles to swap */
1683 for (a = v; l != 0; l--) a = a->Next();
1684 for (b = v; r != 0; r--) b = b->Next();
1685
1686 if (a != b) {
1687 /* swap the hidden bits */
1688 {
1689 bool a_hidden = a->vehstatus.Test(VehState::Hidden);
1690 bool b_hidden = b->vehstatus.Test(VehState::Hidden);
1691 b->vehstatus.Set(VehState::Hidden, a_hidden);
1692 a->vehstatus.Set(VehState::Hidden, b_hidden);
1693 }
1694
1695 std::swap(a->track, b->track);
1696 std::swap(a->direction, b->direction);
1697 std::swap(a->x_pos, b->x_pos);
1698 std::swap(a->y_pos, b->y_pos);
1699 std::swap(a->tile, b->tile);
1700 std::swap(a->z_pos, b->z_pos);
1701
1703
1706 } else {
1707 /* Swap GVF_GOINGUP_BIT/GVF_GOINGDOWN_BIT.
1708 * This is a little bit redundant way, a->gv_flags will
1709 * be (re)set twice, but it reduces code duplication */
1712 }
1713}
1714
1720static bool IsTrain(const Vehicle *v)
1721{
1722 return v->type == VEH_TRAIN;
1723}
1724
1732{
1733 assert(IsLevelCrossingTile(tile));
1734
1735 return HasVehicleOnTile(tile, IsTrain);
1736}
1737
1745{
1746 if (v->type != VEH_TRAIN || v->vehstatus.Test(VehState::Crashed)) return false;
1747
1748 const Train *t = Train::From(v);
1749 if (!t->IsMovingFront()) return false;
1750
1751 return TrainApproachingCrossingTile(t) == tile;
1752}
1753
1754
1762{
1763 assert(IsLevelCrossingTile(tile));
1764
1766 TileIndex tile_from = tile + TileOffsByDiagDir(dir);
1767
1768 if (HasVehicleOnTile(tile_from, [&](const Vehicle *v) {
1769 return TrainApproachingCrossingEnum(v, tile);
1770 })) return true;
1771
1772 dir = ReverseDiagDir(dir);
1773 tile_from = tile + TileOffsByDiagDir(dir);
1774
1775 return HasVehicleOnTile(tile_from, [&](const Vehicle *v) {
1776 return TrainApproachingCrossingEnum(v, tile);
1777 });
1778}
1779
1785static inline bool CheckLevelCrossing(TileIndex tile)
1786{
1787 /* reserved || train on crossing || train approaching crossing */
1789}
1790
1798static void UpdateLevelCrossingTile(TileIndex tile, bool sound, bool force_barred)
1799{
1800 assert(IsLevelCrossingTile(tile));
1801 bool set_barred;
1802
1803 /* We force the crossing to be barred when an adjacent crossing is barred, otherwise let it decide for itself. */
1804 set_barred = force_barred || CheckLevelCrossing(tile);
1805
1806 /* The state has changed */
1807 if (set_barred != IsCrossingBarred(tile)) {
1808 if (set_barred && sound && _settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, tile);
1809 SetCrossingBarred(tile, set_barred);
1810 MarkTileDirtyByTile(tile);
1811 }
1812}
1813
1820void UpdateLevelCrossing(TileIndex tile, bool sound, bool force_bar)
1821{
1822 if (!IsLevelCrossingTile(tile)) return;
1823
1824 bool forced_state = force_bar;
1825
1826 const Axis axis = GetCrossingRoadAxis(tile);
1827 const DiagDirection dir1 = AxisToDiagDir(axis);
1828 const DiagDirection dir2 = ReverseDiagDir(dir1);
1829
1830 /* Check if an adjacent crossing is barred. */
1831 for (DiagDirection dir : { dir1, dir2 }) {
1832 for (TileIndex t = tile; !forced_state && t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, dir)) {
1833 forced_state |= CheckLevelCrossing(t);
1834 }
1835 }
1836
1837 /* Now that we know whether all tiles in this crossing should be barred or open,
1838 * we need to update those tiles. We start with the tile itself, then look along the road axis. */
1839 UpdateLevelCrossingTile(tile, sound, forced_state);
1840 for (DiagDirection dir : { dir1, dir2 }) {
1841 for (TileIndex t = TileAddByDiagDir(tile, dir); t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, dir)) {
1842 UpdateLevelCrossingTile(t, sound, forced_state);
1843 }
1844 }
1845}
1846
1853{
1854 const DiagDirection dir1 = AxisToDiagDir(road_axis);
1855 const DiagDirection dir2 = ReverseDiagDir(dir1);
1856 for (DiagDirection dir : { dir1, dir2 }) {
1857 const TileIndex t = TileAddByDiagDir(tile, dir);
1858 if (t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis) {
1860 }
1861 }
1862}
1863
1870{
1871 const DiagDirection dir1 = AxisToDiagDir(road_axis);
1872 const DiagDirection dir2 = ReverseDiagDir(dir1);
1873 for (DiagDirection dir : { dir1, dir2 }) {
1874 const TileIndexDiff diff = TileOffsByDiagDir(dir);
1875 bool occupied = false;
1876 for (TileIndex t = tile + diff; t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis; t += diff) {
1877 occupied |= CheckLevelCrossing(t);
1878 }
1879 if (occupied) {
1880 /* Mark the immediately adjacent tile dirty */
1881 const TileIndex t = tile + diff;
1882 if (t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis) {
1884 }
1885 } else {
1886 /* Unbar the crossing tiles in this direction as necessary */
1887 for (TileIndex t = tile + diff; t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis; t += diff) {
1888 if (IsCrossingBarred(t)) {
1889 /* The crossing tile is barred, unbar it and continue to check the next tile */
1890 SetCrossingBarred(t, false);
1892 } else {
1893 /* The crossing tile is already unbarred, mark the tile dirty and stop checking */
1895 break;
1896 }
1897 }
1898 }
1899 }
1900}
1901
1907static inline void MaybeBarCrossingWithSound(TileIndex tile)
1908{
1909 if (!IsCrossingBarred(tile)) {
1910 SetCrossingReservation(tile, true);
1911 UpdateLevelCrossing(tile, true);
1912 }
1913}
1914
1915
1921static void AdvanceWagonsBeforeSwap(Train *moving_front)
1922{
1923 Train *base = moving_front;
1924 Train *first = base; // first vehicle to move
1925 Train *last = moving_front->GetMovingBack(); // last vehicle to move
1926 uint length = CountVehiclesInChain(moving_front->First());
1927
1928 while (length > 2) {
1929 last = last->GetMovingPrev();
1930 first = first->GetMovingNext();
1931
1932 int differential = base->CalcNextVehicleOffset() - last->CalcNextVehicleOffset();
1933
1934 /* do not update images now
1935 * negative differential will be handled in AdvanceWagonsAfterSwap() */
1936 for (int i = 0; i < differential; i++) TrainController(first, last->GetMovingNext());
1937
1938 base = first; // == base->GetMovingNext()
1939 length -= 2;
1940 }
1941}
1942
1943
1949static void AdvanceWagonsAfterSwap(Train *moving_front)
1950{
1951 /* first of all, fix the situation when the train was entering a depot */
1952 Train *dep = moving_front; // last vehicle in front of just left depot
1953 while (dep->GetMovingNext() != nullptr && (dep->track == TRACK_BIT_DEPOT || dep->GetMovingNext()->track != TRACK_BIT_DEPOT)) {
1954 dep = dep->GetMovingNext(); // find first vehicle outside of a depot, with next vehicle inside a depot
1955 }
1956
1957 Train *leave = dep->GetMovingNext(); // first vehicle in a depot we are leaving now
1958
1959 if (leave != nullptr) {
1960 /* 'pull' next wagon out of the depot, so we won't miss it (it could stay in depot forever) */
1961 int d = TicksToLeaveDepot(dep);
1962
1963 if (d <= 0) {
1964 leave->vehstatus.Reset(VehState::Hidden); // move it out of the depot
1965 leave->track = TrackToTrackBits(GetRailDepotTrack(leave->tile));
1966 for (int i = 0; i >= d; i--) TrainController(leave, nullptr); // maybe move it, and maybe let another wagon leave
1967 }
1968 } else {
1969 dep = nullptr; // no vehicle in a depot, so no vehicle leaving a depot
1970 }
1971
1972 Train *base = moving_front;
1973 Train *first = base; // first vehicle to move
1974 Train *last = moving_front->GetMovingBack(); // last vehicle to move
1975 uint length = CountVehiclesInChain(moving_front->First());
1976
1977 /* We have to make sure all wagons that leave a depot because of train reversing are moved correctly
1978 * they have already correct spacing, so we have to make sure they are moved how they should */
1979 bool nomove = (dep == nullptr); // If there is no vehicle leaving a depot, limit the number of wagons moved immediately.
1980
1981 while (length > 2) {
1982 /* we reached vehicle (originally) in front of a depot, stop now
1983 * (we would move wagons that are already moved with new wagon length). */
1984 if (base == dep) break;
1985
1986 /* the last wagon was that one leaving a depot, so do not move it anymore */
1987 if (last == dep) nomove = true;
1988
1989 last = last->GetMovingPrev();
1990 first = first->GetMovingNext();
1991
1992 int differential = last->CalcNextVehicleOffset() - base->CalcNextVehicleOffset();
1993
1994 /* do not update images now */
1995 for (int i = 0; i < differential; i++) TrainController(first, (nomove ? last->GetMovingNext() : nullptr));
1996
1997 base = first; // == base->GetMovingNext()
1998 length -= 2;
1999 }
2000}
2001
2002static bool IsWholeTrainInsideDepot(const Train *v)
2003{
2004 for (const Train *u = v; u != nullptr; u = u->Next()) {
2005 if (u->track != TRACK_BIT_DEPOT || u->tile != v->tile) return false;
2006 }
2007 return true;
2008}
2009
2014static void ReverseTrainDirection(Train *consist)
2015{
2016 Train *moving_front = consist->GetMovingFront();
2017 if (IsRailDepotTile(moving_front->tile)) {
2018 if (IsWholeTrainInsideDepot(consist)) return;
2020 }
2021
2022 /* Clear path reservation in front if train is not stuck. */
2024
2025 /* Check if we were approaching a rail/road-crossing */
2026 TileIndex crossing = TrainApproachingCrossingTile(moving_front);
2027
2028 /* Check if we should back up or flip the train. */
2029 if (consist->vehicle_flags.Test(VehicleFlag::DrivingBackwards) || _settings_game.difficulty.train_flip_reverse_allowed == TrainFlipReversingAllowed::None || consist->Last()->CanLeadTrain()) {
2030 /* The train will back up. */
2032
2033 for (Train *u = consist; u != nullptr; u = u->Next()) {
2034 /* Invert going up/down */
2038 }
2039 UpdateStatusAfterSwap(u, false);
2040 }
2041 /* We may have entered a depot and stopped driving backwards. */
2042 moving_front = consist->GetMovingFront();
2043 } else {
2044 /* The train will flip. */
2045 int r = CountVehiclesInChain(consist) - 1; // number of vehicles - 1
2046
2047 AdvanceWagonsBeforeSwap(moving_front);
2048
2049 /* swap start<>end, start+1<>end-1, ... */
2050 int l = 0;
2051 do {
2052 ReverseTrainSwapVeh(consist, l++, r--);
2053 } while (l <= r);
2054
2055 AdvanceWagonsAfterSwap(moving_front);
2056 }
2057
2058 if (IsRailDepotTile(moving_front->tile)) {
2060 }
2061
2064
2065 /* recalculate cached data */
2066 consist->ConsistChanged(CCF_TRACK);
2067
2068 /* update all images */
2069 for (Train *u = consist; u != nullptr; u = u->Next()) u->UpdateViewport(false, false);
2070
2071 /* update crossing we were approaching */
2072 if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing);
2073
2074 /* maybe we are approaching crossing now, after reversal */
2075 crossing = TrainApproachingCrossingTile(moving_front);
2076 if (crossing != INVALID_TILE) MaybeBarCrossingWithSound(crossing);
2077
2078 /* If we are inside a depot after reversing, don't bother with path reserving. */
2079 if (moving_front->track == TRACK_BIT_DEPOT) {
2080 /* Can't be stuck here as inside a depot is always a safe tile. */
2083 return;
2084 }
2085
2086 /* VehicleExitDir does not always produce the desired dir for depots and
2087 * tunnels/bridges that is needed for UpdateSignalsOnSegment. */
2088 DiagDirection dir = VehicleExitDir(moving_front->GetMovingDirection(), moving_front->track);
2089 if (IsRailDepotTile(moving_front->tile) || IsTileType(moving_front->tile, TileType::TunnelBridge)) dir = INVALID_DIAGDIR;
2090
2091 if (UpdateSignalsOnSegment(moving_front->tile, dir, consist->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
2092 /* If we are currently on a tile with conventional signals, we can't treat the
2093 * current tile as a safe tile or we would enter a PBS block without a reservation. */
2094 bool first_tile_okay = !(IsTileType(moving_front->tile, TileType::Railway) &&
2095 HasSignalOnTrackdir(moving_front->tile, moving_front->GetVehicleTrackdir()) &&
2096 !IsPbsSignal(GetSignalType(moving_front->tile, FindFirstTrack(moving_front->track))));
2097
2098 /* If we are on a depot tile facing outwards, do not treat the current tile as safe. */
2099 if (IsRailDepotTile(moving_front->tile) && TrackdirToExitdir(moving_front->GetVehicleTrackdir()) == GetRailDepotDirection(moving_front->tile)) first_tile_okay = false;
2100
2101 if (IsRailStationTile(moving_front->tile)) SetRailStationPlatformReservation(moving_front->tile, TrackdirToExitdir(moving_front->GetVehicleTrackdir()), true);
2102 if (TryPathReserve(consist, false, first_tile_okay)) {
2103 /* Do a look-ahead now in case our current tile was already a safe tile. */
2104 CheckNextTrainTile(consist);
2105 } else if (consist->current_order.GetType() != OT_LOADING) {
2106 /* Do not wait for a way out when we're still loading */
2107 MarkTrainAsStuck(consist);
2108 }
2109 } else if (consist->flags.Test(VehicleRailFlag::Stuck)) {
2110 /* A train not inside a PBS block can't be stuck. */
2112 consist->wait_counter = 0;
2113 }
2114}
2115
2123CommandCost CmdReverseTrainDirection(DoCommandFlags flags, VehicleID veh_id, bool reverse_single_veh)
2124{
2125 Train *v = Train::GetIfValid(veh_id);
2126 if (v == nullptr) return CMD_ERROR;
2127
2129 if (ret.Failed()) return ret;
2130
2131 if (reverse_single_veh) {
2132 /* turn a single unit around */
2133
2134 if (v->IsMultiheaded() || EngInfo(v->engine_type)->callback_mask.Test(VehicleCallbackMask::ArticEngine)) {
2135 return CommandCost(STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE_MULTIPLE_UNITS);
2136 }
2137
2138 Train *front = v->First();
2139 /* make sure the vehicle is stopped in the depot */
2140 if (!front->IsStoppedInDepot()) {
2141 return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
2142 }
2143
2144 if (flags.Test(DoCommandFlag::Execute)) {
2146
2149 SetWindowDirty(WC_VEHICLE_DETAILS, front->index);
2152 }
2153 } else {
2154 /* turn the whole train around */
2155 if (!v->IsPrimaryVehicle()) return CMD_ERROR;
2156 if (v->vehstatus.Test(VehState::Crashed) || v->breakdown_ctr != 0) return CMD_ERROR;
2157
2158 if (flags.Test(DoCommandFlag::Execute)) {
2159 /* Properly leave the station if we are loading and won't be loading anymore */
2160 if (v->current_order.IsType(OT_LOADING)) {
2161 const Vehicle *moving_back = v->GetMovingBack();
2162
2163 /* not a station || different station --> leave the station */
2164 if (!IsTileType(moving_back->tile, TileType::Station) || GetStationIndex(moving_back->tile) != GetStationIndex(v->GetMovingFront()->tile)) {
2165 v->LeaveStation();
2166 }
2167 }
2168
2169 /* We cancel any 'skip signal at dangers' here */
2172
2173 if (_settings_game.vehicle.train_acceleration_model != AM_ORIGINAL && v->cur_speed != 0) {
2175 } else {
2176 v->cur_speed = 0;
2177 v->SetLastSpeed();
2180 }
2181
2182 /* Unbunching data is no longer valid. */
2184 }
2185 }
2186 return CommandCost();
2187}
2188
2200{
2203
2204 const Train *moving_front = t->GetMovingFront();
2205 TileIndex next_tile = TileAddByDiagDir(moving_front->tile, TrackdirToExitdir(moving_front->GetVehicleTrackdir()));
2206 if (next_tile == INVALID_TILE || !IsTileType(next_tile, TileType::Railway) || !HasSignals(next_tile)) return TFP_STUCK;
2207 TrackBits new_tracks = DiagdirReachesTracks(TrackdirToExitdir(moving_front->GetVehicleTrackdir())) & GetTrackBits(next_tile);
2208 return new_tracks != TRACK_BIT_NONE && HasSignalOnTrack(next_tile, FindFirstTrack(new_tracks)) ? TFP_SIGNAL : TFP_STUCK;
2209}
2210
2217CommandCost CmdForceTrainProceed(DoCommandFlags flags, VehicleID veh_id)
2218{
2219 Train *t = Train::GetIfValid(veh_id);
2220 if (t == nullptr) return CMD_ERROR;
2221
2222 if (!t->IsPrimaryVehicle()) return CMD_ERROR;
2223
2225 if (ret.Failed()) return ret;
2226
2227
2228 if (flags.Test(DoCommandFlag::Execute)) {
2231
2232 /* Unbunching data is no longer valid. */
2234 }
2235
2236 return CommandCost();
2237}
2238
2246static FindDepotData FindClosestTrainDepot(Train *v, int max_distance)
2247{
2248 assert(!v->vehstatus.Test(VehState::Crashed));
2249
2250 return YapfTrainFindNearestDepot(v->GetMovingFront(), max_distance);
2251}
2252
2254{
2255 FindDepotData tfdd = FindClosestTrainDepot(this, 0);
2256 if (tfdd.best_length == UINT_MAX) return ClosestDepot();
2257
2258 return ClosestDepot(tfdd.tile, GetDepotIndex(tfdd.tile), tfdd.reverse);
2259}
2260
2261void Train::PlayLeaveStationSound(bool force) const
2262{
2263 static const SoundFx sfx[] = {
2269 };
2270
2271 if (PlayVehicleSound(this, VSE_START, force)) return;
2272
2273 SndPlayVehicleFx(sfx[RailVehInfo(this->engine_type)->engclass], this);
2274}
2275
2280static void CheckNextTrainTile(Train *consist)
2281{
2282 /* Don't do any look-ahead if path_backoff_interval is 255. */
2283 if (_settings_game.pf.path_backoff_interval == 255) return;
2284
2285 const Train *moving_front = consist->GetMovingFront();
2286
2287 /* Exit if we are inside a depot. */
2288 if (moving_front->track == TRACK_BIT_DEPOT) return;
2289
2290 switch (consist->current_order.GetType()) {
2291 /* Exit if we reached our destination depot. */
2292 case OT_GOTO_DEPOT:
2293 if (moving_front->tile == consist->dest_tile) return;
2294 break;
2295
2296 case OT_GOTO_WAYPOINT:
2297 /* If we reached our waypoint, make sure we see that. */
2298 if (IsRailWaypointTile(moving_front->tile) && GetStationIndex(moving_front->tile) == consist->current_order.GetDestination()) ProcessOrders(consist);
2299 break;
2300
2301 case OT_NOTHING:
2302 case OT_LEAVESTATION:
2303 case OT_LOADING:
2304 /* Exit if the current order doesn't have a destination, but the train has orders. */
2305 if (consist->GetNumOrders() > 0) return;
2306 break;
2307
2308 default:
2309 break;
2310 }
2311 /* Exit if we are on a station tile and are going to stop. */
2312 if (IsRailStationTile(moving_front->tile) && consist->current_order.ShouldStopAtStation(consist, GetStationIndex(moving_front->tile))) return;
2313
2314 Trackdir td = moving_front->GetVehicleTrackdir();
2315
2316 /* On a tile with a red non-pbs signal, don't look ahead. */
2317 if (IsTileType(moving_front->tile, TileType::Railway) && HasSignalOnTrackdir(moving_front->tile, td) &&
2318 !IsPbsSignal(GetSignalType(moving_front->tile, TrackdirToTrack(td))) &&
2319 GetSignalStateByTrackdir(moving_front->tile, td) == SIGNAL_STATE_RED) return;
2320
2321 CFollowTrackRail ft(consist);
2322 if (!ft.Follow(moving_front->tile, td)) return;
2323
2325 /* Next tile is not reserved. */
2328 /* If the next tile is a PBS signal, try to make a reservation. */
2332 }
2333 ChooseTrainTrack(consist, ft.new_tile, ft.exitdir, tracks, false, nullptr, false);
2334 }
2335 }
2336 }
2337}
2338
2345{
2346 /* bail out if not all wagons are in the same depot or not in a depot at all */
2347 for (const Train *u = v; u != nullptr; u = u->Next()) {
2348 if (u->track != TRACK_BIT_DEPOT || u->tile != v->tile) return false;
2349 }
2350
2351 /* if the train got no power, then keep it in the depot */
2352 if (v->gcache.cached_power == 0) {
2355 return true;
2356 }
2357
2358 /* Check if we should wait here for unbunching. */
2359 if (v->IsWaitingForUnbunching()) return true;
2360
2361 SigSegState seg_state;
2362
2363 if (v->force_proceed == TFP_NONE) {
2364 /* force proceed was not pressed */
2365 if (++v->wait_counter < 37) {
2367 return true;
2368 }
2369
2370 v->wait_counter = 0;
2371
2372 seg_state = _settings_game.pf.reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
2373 if (seg_state == SIGSEG_FULL || HasDepotReservation(v->tile)) {
2374 /* Full and no PBS signal in block or depot reserved, can't exit. */
2376 return true;
2377 }
2378 } else {
2379 seg_state = _settings_game.pf.reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
2380 }
2381
2382 /* We are leaving a depot, but have to go to the exact same one; re-enter. */
2383 if (v->current_order.IsType(OT_GOTO_DEPOT) && v->tile == v->dest_tile) {
2384 /* Service when depot has no reservation. */
2386 return true;
2387 }
2388
2389 /* Only leave when we can reserve a path to our destination. */
2390 if (seg_state == SIGSEG_PBS && !TryPathReserve(v) && v->force_proceed == TFP_NONE) {
2391 /* No path and no force proceed. */
2394 return true;
2395 }
2396
2397 SetDepotReservation(v->tile, true);
2398 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
2399
2404
2405 v->track = TRACK_BIT_X;
2406 if (v->direction & 2) v->track = TRACK_BIT_Y;
2407
2409 v->cur_speed = 0;
2410
2411 v->UpdateViewport(true, true);
2412 v->UpdatePosition();
2414 v->UpdateAcceleration();
2416
2417 return false;
2418}
2419
2426static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_dir)
2427{
2428 DiagDirection dir = TrackdirToExitdir(track_dir);
2429
2431 /* Are we just leaving a tunnel/bridge? */
2432 if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) {
2434
2435 if (TunnelBridgeIsFree(tile, end, v).Succeeded()) {
2436 /* Free the reservation only if no other train is on the tiles. */
2437 SetTunnelBridgeReservation(tile, false);
2438 SetTunnelBridgeReservation(end, false);
2439
2440 if (_settings_client.gui.show_track_reservation) {
2441 if (IsBridge(tile)) {
2442 MarkBridgeDirty(tile);
2443 } else {
2444 MarkTileDirtyByTile(tile);
2446 }
2447 }
2448 }
2449 }
2450 } else if (IsRailStationTile(tile)) {
2451 TileIndex new_tile = TileAddByDiagDir(tile, dir);
2452 /* If the new tile is not a further tile of the same station, we
2453 * clear the reservation for the whole platform. */
2454 if (!IsCompatibleTrainStationTile(new_tile, tile)) {
2456 }
2457 } else {
2458 /* Any other tile */
2459 UnreserveRailTrack(tile, TrackdirToTrack(track_dir));
2460 }
2461}
2462
2468{
2469 assert(consist->IsFrontEngine());
2470
2471 const Train *moving_front = consist->GetMovingFront();
2472 TileIndex tile = moving_front->tile;
2473 Trackdir td = moving_front->GetVehicleTrackdir();
2474 bool free_tile = tile != moving_front->tile || !(IsRailStationTile(moving_front->tile) || IsTileType(moving_front->tile, TileType::TunnelBridge));
2475 StationID station_id = IsRailStationTile(moving_front->tile) ? GetStationIndex(moving_front->tile) : StationID::Invalid();
2476
2477 /* Can't be holding a reservation if we enter a depot. */
2478 if (IsRailDepotTile(tile) && TrackdirToExitdir(td) != GetRailDepotDirection(tile)) return;
2479 if (moving_front->track == TRACK_BIT_DEPOT) {
2480 /* Front engine is in a depot. We enter if some part is not in the depot. */
2481 for (const Train *u = consist; u != nullptr; u = u->Next()) {
2482 if (u->track != TRACK_BIT_DEPOT || u->tile != consist->tile) return;
2483 }
2484 }
2485 /* Don't free reservation if it's not ours. */
2487
2488 CFollowTrackRail ft(consist, GetAllCompatibleRailTypes(consist->railtypes));
2489 while (ft.Follow(tile, td)) {
2490 tile = ft.new_tile;
2492 td = RemoveFirstTrackdir(&bits);
2493 assert(bits == TRACKDIR_BIT_NONE);
2494
2495 if (!IsValidTrackdir(td)) break;
2496
2497 if (IsTileType(tile, TileType::Railway)) {
2498 if (HasSignalOnTrackdir(tile, td) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(td)))) {
2499 /* Conventional signal along trackdir: remove reservation and stop. */
2501 break;
2502 }
2503 if (HasPbsSignalOnTrackdir(tile, td)) {
2504 if (GetSignalStateByTrackdir(tile, td) == SIGNAL_STATE_RED) {
2505 /* Red PBS signal? Can't be our reservation, would be green then. */
2506 break;
2507 } else {
2508 /* Turn the signal back to red. */
2510 MarkTileDirtyByTile(tile);
2511 }
2512 } else if (HasPbsSignalOnTrackdir(tile, ReverseTrackdir(td))) {
2513 /* Reservation passes an opposing path signal. Mark signal for update to re-establish the proper default state. */
2515 } else if (HasSignalOnTrackdir(tile, ReverseTrackdir(td)) && IsOnewaySignal(tile, TrackdirToTrack(td))) {
2516 break;
2517 }
2518 }
2519
2520 /* Don't free first station/bridge/tunnel if we are on it. */
2521 if (free_tile || (!(ft.is_station && GetStationIndex(ft.new_tile) == station_id) && !ft.is_tunnel && !ft.is_bridge)) ClearPathReservation(consist, tile, td);
2522
2523 free_tile = true;
2524 }
2525
2527}
2528
2529static const uint8_t _initial_tile_subcoord[6][4][3] = {
2530{{ 15, 8, 1 }, { 0, 0, 0 }, { 0, 8, 5 }, { 0, 0, 0 }},
2531{{ 0, 0, 0 }, { 8, 0, 3 }, { 0, 0, 0 }, { 8, 15, 7 }},
2532{{ 0, 0, 0 }, { 7, 0, 2 }, { 0, 7, 6 }, { 0, 0, 0 }},
2533{{ 15, 8, 2 }, { 0, 0, 0 }, { 0, 0, 0 }, { 8, 15, 6 }},
2534{{ 15, 7, 0 }, { 8, 0, 4 }, { 0, 0, 0 }, { 0, 0, 0 }},
2535{{ 0, 0, 0 }, { 0, 0, 0 }, { 0, 8, 4 }, { 7, 15, 0 }},
2536};
2537
2551static Track DoTrainPathfind(const Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found, bool do_track_reservation, PBSTileInfo *dest, TileIndex *final_dest)
2552{
2553 if (final_dest != nullptr) *final_dest = INVALID_TILE;
2554 return YapfTrainChooseTrack(v, tile, enterdir, tracks, path_found, do_track_reservation, dest, final_dest);
2555}
2556
2565static PBSTileInfo ExtendTrainReservation(const Train *v, TrackBits *new_tracks, DiagDirection *enterdir)
2566{
2568
2569 CFollowTrackRail ft(v);
2570
2571 std::vector<std::pair<TileIndex, Trackdir>> signals_set_to_red;
2572
2573 TileIndex tile = origin.tile;
2574 Trackdir cur_td = origin.trackdir;
2575 while (ft.Follow(tile, cur_td)) {
2577 /* Possible signal tile. */
2579 }
2580
2583 if (ft.new_td_bits == TRACKDIR_BIT_NONE) break;
2584 }
2585
2586 /* Station, depot or waypoint are a possible target. */
2587 bool target_seen = ft.is_station || (IsTileType(ft.new_tile, TileType::Railway) && !IsPlainRail(ft.new_tile));
2588 if (target_seen || KillFirstBit(ft.new_td_bits) != TRACKDIR_BIT_NONE) {
2589 /* Choice found or possible target encountered.
2590 * On finding a possible target, we need to stop and let the pathfinder handle the
2591 * remaining path. This is because we don't know if this target is in one of our
2592 * orders, so we might cause pathfinding to fail later on if we find a choice.
2593 * This failure would cause a bogous call to TryReserveSafePath which might reserve
2594 * a wrong path not leading to our next destination. */
2596
2597 /* If we did skip some tiles, backtrack to the first skipped tile so the pathfinder
2598 * actually starts its search at the first unreserved tile. */
2599 if (ft.tiles_skipped != 0) ft.new_tile -= TileOffsByDiagDir(ft.exitdir) * ft.tiles_skipped;
2600
2601 /* Choice found, path valid but not okay. Save info about the choice tile as well. */
2602 if (new_tracks != nullptr) *new_tracks = TrackdirBitsToTrackBits(ft.new_td_bits);
2603 if (enterdir != nullptr) *enterdir = ft.exitdir;
2604 return PBSTileInfo(ft.new_tile, ft.old_td, false);
2605 }
2606
2607 tile = ft.new_tile;
2608 cur_td = FindFirstTrackdir(ft.new_td_bits);
2609
2610 Trackdir rev_td = ReverseTrackdir(cur_td);
2611 if (IsSafeWaitingPosition(v, tile, cur_td, true, _settings_game.pf.forbid_90_deg)) {
2612 bool wp_free = IsWaitingPositionFree(v, tile, cur_td, _settings_game.pf.forbid_90_deg);
2613 if (!(wp_free && TryReserveRailTrack(tile, TrackdirToTrack(cur_td)))) break;
2614 /* Green path signal opposing the path? Turn to red. */
2615 if (HasPbsSignalOnTrackdir(tile, rev_td) && GetSignalStateByTrackdir(tile, rev_td) == SIGNAL_STATE_GREEN) {
2616 signals_set_to_red.emplace_back(tile, rev_td);
2618 MarkTileDirtyByTile(tile);
2619 }
2620 /* Safe position is all good, path valid and okay. */
2621 return PBSTileInfo(tile, cur_td, true);
2622 }
2623
2624 if (!TryReserveRailTrack(tile, TrackdirToTrack(cur_td))) break;
2625
2626 /* Green path signal opposing the path? Turn to red. */
2627 if (HasPbsSignalOnTrackdir(tile, rev_td) && GetSignalStateByTrackdir(tile, rev_td) == SIGNAL_STATE_GREEN) {
2628 signals_set_to_red.emplace_back(tile, rev_td);
2630 MarkTileDirtyByTile(tile);
2631 }
2632 }
2633
2634 if (ft.err == CFollowTrackRail::EC_OWNER || ft.err == CFollowTrackRail::EC_NO_WAY) {
2635 /* End of line, path valid and okay. */
2636 return PBSTileInfo(ft.old_tile, ft.old_td, true);
2637 }
2638
2639 /* Sorry, can't reserve path, back out. */
2640 tile = origin.tile;
2641 cur_td = origin.trackdir;
2642 TileIndex stopped = ft.old_tile;
2643 Trackdir stopped_td = ft.old_td;
2644 while (tile != stopped || cur_td != stopped_td) {
2645 if (!ft.Follow(tile, cur_td)) break;
2646
2649 assert(ft.new_td_bits != TRACKDIR_BIT_NONE);
2650 }
2652
2653 tile = ft.new_tile;
2654 cur_td = FindFirstTrackdir(ft.new_td_bits);
2655
2656 UnreserveRailTrack(tile, TrackdirToTrack(cur_td));
2657 }
2658
2659 /* Re-instate green signals we turned to red. */
2660 for (auto [sig_tile, td] : signals_set_to_red) {
2662 }
2663
2664 /* Path invalid. */
2665 return PBSTileInfo();
2666}
2667
2678static bool TryReserveSafeTrack(const Train *v, TileIndex tile, Trackdir td, bool override_railtype)
2679{
2680 return YapfTrainFindNearestSafeTile(v, tile, td, override_railtype);
2681}
2682
2684class VehicleOrderSaver {
2685private:
2686 Train *v;
2687 Order old_order;
2688 TileIndex old_dest_tile;
2689 StationID old_last_station_visited;
2690 VehicleOrderID index;
2691 bool suppress_implicit_orders;
2692 bool restored;
2693
2694public:
2695 VehicleOrderSaver(Train *_v) :
2696 v(_v),
2697 old_order(_v->current_order),
2698 old_dest_tile(_v->dest_tile),
2699 old_last_station_visited(_v->last_station_visited),
2700 index(_v->cur_real_order_index),
2701 suppress_implicit_orders(HasBit(_v->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)),
2702 restored(false)
2703 {
2704 }
2705
2709 void Restore()
2710 {
2711 this->v->current_order = this->old_order;
2712 this->v->dest_tile = this->old_dest_tile;
2713 this->v->last_station_visited = this->old_last_station_visited;
2714 AssignBit(this->v->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS, suppress_implicit_orders);
2715 this->restored = true;
2716 }
2717
2722 {
2723 if (!this->restored) this->Restore();
2724 }
2725
2731 bool SwitchToNextOrder(bool skip_first)
2732 {
2733 if (this->v->GetNumOrders() == 0) return false;
2734
2735 if (skip_first) ++this->index;
2736
2737 int depth = 0;
2738
2739 do {
2740 /* Wrap around. */
2741 if (this->index >= this->v->GetNumOrders()) this->index = 0;
2742
2743 Order *order = this->v->GetOrder(this->index);
2744 assert(order != nullptr);
2745
2746 switch (order->GetType()) {
2747 case OT_GOTO_DEPOT:
2748 /* Skip service in depot orders when the train doesn't need service. */
2749 if (order->GetDepotOrderType().Test(OrderDepotTypeFlag::Service) && !this->v->NeedsServicing()) break;
2750 [[fallthrough]];
2751 case OT_GOTO_STATION:
2752 case OT_GOTO_WAYPOINT:
2753 this->v->current_order = *order;
2754 return UpdateOrderDest(this->v, order, 0, true);
2755 case OT_CONDITIONAL: {
2756 VehicleOrderID next = ProcessConditionalOrder(order, this->v);
2757 if (next != INVALID_VEH_ORDER_ID) {
2758 depth++;
2759 this->index = next;
2760 /* Don't increment next, so no break here. */
2761 continue;
2762 }
2763 break;
2764 }
2765 default:
2766 break;
2767 }
2768 /* Don't increment inside the while because otherwise conditional
2769 * orders can lead to an infinite loop. */
2770 ++this->index;
2771 depth++;
2772 } while (this->index != this->v->cur_real_order_index && depth < this->v->GetNumOrders());
2773
2774 return false;
2775 }
2776};
2777
2778/* choose a track */
2779static Track ChooseTrainTrack(Train *consist, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck)
2780{
2781 Track best_track = INVALID_TRACK;
2782 bool do_track_reservation = _settings_game.pf.reserve_paths || force_res;
2783 bool changed_signal = false;
2784 TileIndex final_dest = INVALID_TILE;
2785
2786 assert((tracks & ~TRACK_BIT_MASK) == 0);
2787
2788 if (got_reservation != nullptr) *got_reservation = false;
2789
2790 /* Don't use tracks here as the setting to forbid 90 deg turns might have been switched between reservation and now. */
2791 TrackBits res_tracks = (TrackBits)(GetReservedTrackbits(tile) & DiagdirReachesTracks(enterdir));
2792 /* Do we have a suitable reserved track? */
2793 if (res_tracks != TRACK_BIT_NONE) return FindFirstTrack(res_tracks);
2794
2795 /* Quick return in case only one possible track is available */
2796 if (KillFirstBit(tracks) == TRACK_BIT_NONE) {
2797 Track track = FindFirstTrack(tracks);
2798 /* We need to check for signals only here, as a junction tile can't have signals. */
2799 if (track != INVALID_TRACK && HasPbsSignalOnTrackdir(tile, TrackEnterdirToTrackdir(track, enterdir))) {
2800 do_track_reservation = true;
2801 changed_signal = true;
2803 } else if (!do_track_reservation) {
2804 return track;
2805 }
2806 best_track = track;
2807 }
2808
2809 const Train *moving_front = consist->GetMovingFront();
2810
2811 PBSTileInfo res_dest(tile, INVALID_TRACKDIR, false);
2812 DiagDirection dest_enterdir = enterdir;
2813 if (do_track_reservation) {
2814 res_dest = ExtendTrainReservation(consist, &tracks, &dest_enterdir);
2815 if (res_dest.tile == INVALID_TILE) {
2816 /* Reservation failed? */
2817 if (mark_stuck) MarkTrainAsStuck(consist);
2818 if (changed_signal) SetSignalStateByTrackdir(tile, TrackEnterdirToTrackdir(best_track, enterdir), SIGNAL_STATE_RED);
2819 return FindFirstTrack(tracks);
2820 }
2821 if (res_dest.okay) {
2822 /* Got a valid reservation that ends at a safe target, quick exit. */
2823 if (got_reservation != nullptr) *got_reservation = true;
2824 if (changed_signal) MarkTileDirtyByTile(tile);
2825 TryReserveRailTrack(moving_front->tile, TrackdirToTrack(moving_front->GetVehicleTrackdir()));
2826 return best_track;
2827 }
2828
2829 /* Check if the train needs service here, so it has a chance to always find a depot.
2830 * Also check if the current order is a service order so we don't reserve a path to
2831 * the destination but instead to the next one if service isn't needed. */
2832 CheckIfTrainNeedsService(consist);
2833 if (consist->current_order.IsType(OT_DUMMY) || consist->current_order.IsType(OT_CONDITIONAL) || consist->current_order.IsType(OT_GOTO_DEPOT)) ProcessOrders(consist);
2834 }
2835
2836 /* Save the current train order. The destructor will restore the old order on function exit. */
2837 VehicleOrderSaver orders(consist);
2838
2839 /* If the current tile is the destination of the current order and
2840 * a reservation was requested, advance to the next order.
2841 * Don't advance on a depot order as depots are always safe end points
2842 * for a path and no look-ahead is necessary. This also avoids a
2843 * problem with depot orders not part of the order list when the
2844 * order list itself is empty. */
2845 if (consist->current_order.IsType(OT_LEAVESTATION)) {
2846 orders.SwitchToNextOrder(false);
2847 } else if (consist->current_order.IsType(OT_LOADING) || (!consist->current_order.IsType(OT_GOTO_DEPOT) && (
2848 consist->current_order.IsType(OT_GOTO_STATION) ?
2849 IsRailStationTile(moving_front->tile) && consist->current_order.GetDestination() == GetStationIndex(moving_front->tile) :
2850 moving_front->tile == consist->dest_tile))) {
2851 orders.SwitchToNextOrder(true);
2852 }
2853
2854 if (res_dest.tile != INVALID_TILE && !res_dest.okay) {
2855 /* Pathfinders are able to tell that route was only 'guessed'. */
2856 bool path_found = true;
2857 TileIndex new_tile = res_dest.tile;
2858
2859 Track next_track = DoTrainPathfind(consist, new_tile, dest_enterdir, tracks, path_found, do_track_reservation, &res_dest, &final_dest);
2860 if (new_tile == tile) best_track = next_track;
2861 consist->HandlePathfindingResult(path_found);
2862 }
2863
2864 /* No track reservation requested -> finished. */
2865 if (!do_track_reservation) return best_track;
2866
2867 /* A path was found, but could not be reserved. */
2868 if (res_dest.tile != INVALID_TILE && !res_dest.okay) {
2869 if (mark_stuck) MarkTrainAsStuck(consist);
2871 return best_track;
2872 }
2873
2874 /* No possible reservation target found, we are probably lost. */
2875 if (res_dest.tile == INVALID_TILE) {
2876 /* Try to find any safe destination. */
2877 PBSTileInfo origin = FollowTrainReservation(consist);
2878 if (TryReserveSafeTrack(consist, origin.tile, origin.trackdir, false)) {
2879 TrackBits res = GetReservedTrackbits(tile) & DiagdirReachesTracks(enterdir);
2880 best_track = FindFirstTrack(res);
2881 TryReserveRailTrack(moving_front->tile, TrackdirToTrack(moving_front->GetVehicleTrackdir()));
2882 if (got_reservation != nullptr) *got_reservation = true;
2883 if (changed_signal) MarkTileDirtyByTile(tile);
2884 } else {
2886 if (mark_stuck) MarkTrainAsStuck(consist);
2887 }
2888 return best_track;
2889 }
2890
2891 if (got_reservation != nullptr) *got_reservation = true;
2892
2893 /* Reservation target found and free, check if it is safe. */
2894 while (!IsSafeWaitingPosition(consist, res_dest.tile, res_dest.trackdir, true, _settings_game.pf.forbid_90_deg)) {
2895 /* Extend reservation until we have found a safe position. */
2896 DiagDirection exitdir = TrackdirToExitdir(res_dest.trackdir);
2897 TileIndex next_tile = TileAddByDiagDir(res_dest.tile, exitdir);
2899 if (Rail90DegTurnDisallowed(GetTileRailType(res_dest.tile), GetTileRailType(next_tile))) {
2900 reachable &= ~TrackCrossesTracks(TrackdirToTrack(res_dest.trackdir));
2901 }
2902
2903 /* Get next order with destination. */
2904 if (orders.SwitchToNextOrder(true)) {
2905 PBSTileInfo cur_dest;
2906 bool path_found;
2907 DoTrainPathfind(consist, next_tile, exitdir, reachable, path_found, true, &cur_dest, nullptr);
2908 if (cur_dest.tile != INVALID_TILE) {
2909 res_dest = cur_dest;
2910 if (res_dest.okay) continue;
2911 /* Path found, but could not be reserved. */
2913 if (mark_stuck) MarkTrainAsStuck(consist);
2914 if (got_reservation != nullptr) *got_reservation = false;
2915 changed_signal = false;
2916 break;
2917 }
2918 }
2919 /* No order or no safe position found, try any position. */
2920 if (!TryReserveSafeTrack(consist, res_dest.tile, res_dest.trackdir, true)) {
2922 if (mark_stuck) MarkTrainAsStuck(consist);
2923 if (got_reservation != nullptr) *got_reservation = false;
2924 changed_signal = false;
2925 }
2926 break;
2927 }
2928
2929 TryReserveRailTrack(moving_front->tile, TrackdirToTrack(moving_front->GetVehicleTrackdir()));
2930
2931 if (changed_signal) MarkTileDirtyByTile(tile);
2932
2933 orders.Restore();
2934 if (consist->current_order.IsType(OT_GOTO_DEPOT) &&
2936 final_dest != INVALID_TILE && IsRailDepotTile(final_dest)) {
2937 consist->current_order.SetDestination(GetDepotIndex(final_dest));
2938 consist->dest_tile = final_dest;
2940 }
2941
2942 return best_track;
2943}
2944
2953bool TryPathReserve(Train *consist, bool mark_as_stuck, bool first_tile_okay)
2954{
2955 assert(consist->IsFrontEngine());
2956
2957 const Train *moving_front = consist->GetMovingFront();
2958
2959 /* We have to handle depots specially as the track follower won't look
2960 * at the depot tile itself but starts from the next tile. If we are still
2961 * inside the depot, a depot reservation can never be ours. */
2962 if (moving_front->track == TRACK_BIT_DEPOT) {
2963 if (HasDepotReservation(moving_front->tile)) {
2964 if (mark_as_stuck) MarkTrainAsStuck(consist);
2965 return false;
2966 } else {
2967 /* Depot not reserved, but the next tile might be. */
2968 TileIndex next_tile = TileAddByDiagDir(moving_front->tile, GetRailDepotDirection(moving_front->tile));
2969 if (HasReservedTracks(next_tile, DiagdirReachesTracks(GetRailDepotDirection(moving_front->tile)))) return false;
2970 }
2971 }
2972
2973 Vehicle *other_train = nullptr;
2974 PBSTileInfo origin = FollowTrainReservation(consist, &other_train);
2975 /* The path we are driving on is already blocked by some other train.
2976 * This can only happen in certain situations when mixing path and
2977 * block signals or when changing tracks and/or signals.
2978 * Exit here as doing any further reservations will probably just
2979 * make matters worse. */
2980 if (other_train != nullptr && other_train->index != consist->index) {
2981 if (mark_as_stuck) MarkTrainAsStuck(consist);
2982 return false;
2983 }
2984 /* If we have a reserved path and the path ends at a safe tile, we are finished already. */
2985 if (origin.okay && (moving_front->tile != origin.tile || first_tile_okay)) {
2986 /* Can't be stuck then. */
2989 return true;
2990 }
2991
2992 /* If we are in a depot, tentatively reserve the depot. */
2993 if (moving_front->track == TRACK_BIT_DEPOT) {
2994 SetDepotReservation(moving_front->tile, true);
2995 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(moving_front->tile);
2996 }
2997
2998 DiagDirection exitdir = TrackdirToExitdir(origin.trackdir);
2999 TileIndex new_tile = TileAddByDiagDir(origin.tile, exitdir);
3001
3003
3004 bool res_made = false;
3005 ChooseTrainTrack(consist, new_tile, exitdir, reachable, true, &res_made, mark_as_stuck);
3006
3007 if (!res_made) {
3008 /* Free the depot reservation as well. */
3009 if (moving_front->track == TRACK_BIT_DEPOT) SetDepotReservation(moving_front->tile, false);
3010 return false;
3011 }
3012
3013 if (consist->flags.Test(VehicleRailFlag::Stuck)) {
3014 consist->wait_counter = 0;
3016 }
3018 return true;
3019}
3020
3026static bool CheckReverseTrain(const Train *consist)
3027{
3028 const Train *moving_front = consist->GetMovingFront();
3029 if (_settings_game.difficulty.train_flip_reverse_allowed == TrainFlipReversingAllowed::EndOfLineOnly ||
3030 moving_front->track == TRACK_BIT_DEPOT || moving_front->track == TRACK_BIT_WORMHOLE ||
3031 !(moving_front->GetMovingDirection() & 1)) {
3032 return false;
3033 }
3034
3035 assert(moving_front->track != TRACK_BIT_NONE);
3036
3037 return YapfTrainCheckReverse(consist);
3038}
3039
3046{
3047 if (station == this->last_station_visited) this->last_station_visited = StationID::Invalid();
3048
3049 const Station *st = Station::Get(station);
3051 /* The destination station has no trainstation tiles. */
3053 return TileIndex{};
3054 }
3055
3056 return st->xy;
3057}
3058
3061{
3062 Train *v = this;
3063 do {
3064 v->colourmap = PAL_NONE;
3065 v->UpdateViewport(true, false);
3066 } while ((v = v->Next()) != nullptr);
3067
3068 /* need to update acceleration and cached values since the goods on the train changed. */
3069 this->CargoChanged();
3070 this->UpdateAcceleration();
3071}
3072
3081{
3082 switch (_settings_game.vehicle.train_acceleration_model) {
3083 default: NOT_REACHED();
3084 case AM_ORIGINAL:
3085 return this->DoUpdateSpeed(this->acceleration * (this->GetAccelerationStatus() == AS_BRAKE ? -4 : 2), 0, this->GetCurrentMaxSpeed());
3086
3087 case AM_REALISTIC:
3088 return this->DoUpdateSpeed(this->GetAcceleration(), this->GetAccelerationStatus() == AS_BRAKE ? 0 : 2, this->GetCurrentMaxSpeed());
3089 }
3090}
3091
3097static void TrainEnterStation(Train *consist, StationID station)
3098{
3099 consist->last_station_visited = station;
3100
3101 /* check if a train ever visited this station before */
3102 Station *st = Station::Get(station);
3103 if (!(st->had_vehicle_of_type & HVOT_TRAIN)) {
3104 st->had_vehicle_of_type |= HVOT_TRAIN;
3106 GetEncodedString(STR_NEWS_FIRST_TRAIN_ARRIVAL, st->index),
3108 consist->index,
3109 st->index
3110 );
3111 AI::NewEvent(consist->owner, new ScriptEventStationFirstVehicle(st->index, consist->index));
3112 Game::NewEvent(new ScriptEventStationFirstVehicle(st->index, consist->index));
3113 }
3114
3115 consist->force_proceed = TFP_NONE;
3116 InvalidateWindowData(WC_VEHICLE_VIEW, consist->index);
3117
3118 consist->BeginLoading();
3119
3120 TileIndex tile = consist->GetMovingFront()->tile;
3122 TriggerStationAnimation(st, tile, StationAnimationTrigger::VehicleArrives);
3123}
3124
3132static inline bool CheckCompatibleRail(const Train *v, TileIndex tile, bool check_railtype)
3133{
3134 return IsTileOwner(tile, v->owner) &&
3135 (!check_railtype || !v->IsFrontEngine() || v->compatible_railtypes.Test(GetRailType(tile)));
3136}
3137
3140 uint8_t small_turn;
3141 uint8_t large_turn;
3142 uint8_t z_up;
3143 uint8_t z_down;
3144};
3145
3148 /* normal accel */
3149 {256 / 4, 256 / 2, 256 / 4, 2},
3150 {256 / 4, 256 / 2, 256 / 4, 2},
3151 {0, 256 / 2, 256 / 4, 2},
3152};
3153
3159static inline void AffectSpeedByZChange(Train *consist, int z_diff)
3160{
3161 if (z_diff == 0 || _settings_game.vehicle.train_acceleration_model != AM_ORIGINAL) return;
3162
3163 const AccelerationSlowdownParams *asp = &_accel_slowdown[static_cast<int>(consist->GetAccelerationType())];
3164
3165 if (z_diff > 0) {
3166 consist->cur_speed -= (consist->cur_speed * asp->z_up >> 8);
3167 } else {
3168 uint16_t spd = consist->cur_speed + asp->z_down;
3169 if (spd <= consist->gcache.cached_max_track_speed) consist->cur_speed = spd;
3170 }
3171}
3172
3173static bool TrainMovedChangeSignals(TileIndex tile, DiagDirection dir)
3174{
3175 if (IsTileType(tile, TileType::Railway) &&
3178 Trackdir trackdir = FindFirstTrackdir(tracks);
3179 if (UpdateSignalsOnSegment(tile, TrackdirToExitdir(trackdir), GetTileOwner(tile)) == SIGSEG_PBS && HasSignalOnTrackdir(tile, trackdir)) {
3180 /* A PBS block with a non-PBS signal facing us? */
3181 if (!IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) return true;
3182 }
3183 }
3184 return false;
3185}
3186
3189{
3190 for (const Train *u = this; u != nullptr; u = u->Next()) {
3191 switch (u->track) {
3192 case TRACK_BIT_WORMHOLE:
3194 break;
3195 case TRACK_BIT_DEPOT:
3196 break;
3197 default:
3199 break;
3200 }
3201 }
3202}
3203
3210uint Train::Crash(bool flooded)
3211{
3212 uint victims = 0;
3213 if (this->IsFrontEngine()) {
3214 victims += 2; // driver
3215
3216 /* Remove the reserved path in front of the train if it is not stuck.
3217 * Also clear all reserved tracks the train is currently on. */
3219 for (const Train *v = this; v != nullptr; v = v->Next()) {
3222 /* ClearPathReservation will not free the wormhole exit
3223 * if the train has just entered the wormhole. */
3225 }
3226 }
3227
3228 /* we may need to update crossing we were approaching,
3229 * but must be updated after the train has been marked crashed */
3231 if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing);
3232
3233 /* Remove the loading indicators (if any) */
3235 }
3236
3237 victims += this->GroundVehicleBase::Crash(flooded);
3238
3239 this->crash_anim_pos = flooded ? 4000 : 1; // max 4440, disappear pretty fast when flooded
3240 return victims;
3241}
3242
3249static uint TrainCrashed(Train *v)
3250{
3251 uint victims = 0;
3252
3253 /* do not crash train twice */
3254 if (!v->vehstatus.Test(VehState::Crashed)) {
3255 victims = v->Crash();
3256 TileIndex tile = v->GetMovingFront()->tile;
3257 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, tile, ScriptEventVehicleCrashed::CRASH_TRAIN, victims, v->owner));
3258 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, tile, ScriptEventVehicleCrashed::CRASH_TRAIN, victims, v->owner));
3259 }
3260
3261 /* Try to re-reserve track under already crashed train too.
3262 * Crash() clears the reservation! */
3264
3265 return victims;
3266}
3267
3274static uint CheckTrainCollision(Vehicle *v, Train *moving_front)
3275{
3276 /* Make sure we are a train, and are not in a depot. */
3277 if (v->type != VEH_TRAIN) return 0;
3278
3279 /* We can't crash into trains in a depot. */
3280 if (Train::From(v)->track == TRACK_BIT_DEPOT) return 0;
3281
3282 /* Do not crash into trains of another company. */
3283 if (v->owner != moving_front->First()->owner) return 0;
3284
3285 /* Do not collide with our own wagons */
3286 if (v->First() == moving_front->First()) return 0;
3287
3288 int x_diff = v->x_pos - moving_front->x_pos;
3289 int y_diff = v->y_pos - moving_front->y_pos;
3290
3291 /* Do fast calculation to check whether trains are not in close vicinity
3292 * and quickly reject trains distant enough for any collision.
3293 * Differences are shifted by 7, mapping range [-7 .. 8] into [0 .. 15]
3294 * Differences are then ORed and then we check for any higher bits */
3295 uint hash = (y_diff + 7) | (x_diff + 7);
3296 if (hash & ~15) return 0;
3297
3298 /* Slower check using multiplication */
3299 int min_diff = (Train::From(v)->gcache.cached_veh_length + 1) / 2 + (moving_front->gcache.cached_veh_length + 1) / 2 - 1;
3300 if (x_diff * x_diff + y_diff * y_diff > min_diff * min_diff) return 0;
3301
3302 /* Happens when there is a train under bridge next to bridge head */
3303 if (abs(v->z_pos - moving_front->z_pos) > 5) return 0;
3304
3305 /* Crash both trains. Two statements required to guarantee execution
3306 * order because RandomRange() is involved. */
3307 uint num_victims = TrainCrashed(moving_front->First());
3308 return num_victims + TrainCrashed(Train::From(v)->First());
3309}
3310
3319static bool CheckTrainCollision(Train *moving_front)
3320{
3321 /* can't collide in depot */
3322 if (moving_front->track == TRACK_BIT_DEPOT) return false;
3323
3324 assert(moving_front->track == TRACK_BIT_WORMHOLE || TileVirtXY(moving_front->x_pos, moving_front->y_pos) == moving_front->tile);
3325
3326 uint num_victims = 0;
3327
3328 /* find colliding vehicles */
3329 if (moving_front->track == TRACK_BIT_WORMHOLE) {
3330 for (Vehicle *u : VehiclesOnTile(moving_front->tile)) {
3331 num_victims += CheckTrainCollision(u, moving_front);
3332 }
3333 for (Vehicle *u : VehiclesOnTile(GetOtherTunnelBridgeEnd(moving_front->tile))) {
3334 num_victims += CheckTrainCollision(u, moving_front);
3335 }
3336 } else {
3337 for (Vehicle *u : VehiclesNearTileXY(moving_front->x_pos, moving_front->y_pos, 7)) {
3338 num_victims += CheckTrainCollision(u, moving_front);
3339 }
3340 }
3341
3342 /* any dead -> no crash */
3343 if (num_victims == 0) return false;
3344
3345 AddTileNewsItem(GetEncodedString(STR_NEWS_TRAIN_CRASH, num_victims), NewsType::Accident, moving_front->tile);
3346
3347 ModifyStationRatingAround(moving_front->tile, moving_front->First()->owner, -160, 30);
3348 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_13_TRAIN_COLLISION, moving_front);
3349 return true;
3350}
3351
3359bool TrainController(Train *v, Vehicle *nomove, bool reverse)
3360{
3361 Train *first = v->First();
3362 Train *prev;
3363 bool direction_changed = false; // has direction of any part changed?
3364
3365 /* For every vehicle after and including the given vehicle */
3366 for (prev = v->GetMovingPrev(); v != nomove; prev = v, v = v->GetMovingNext()) {
3367 DiagDirection enterdir = DIAGDIR_BEGIN;
3368 bool update_signals_crossing = false; // will we update signals or crossing state?
3369
3371 if (v->track != TRACK_BIT_WORMHOLE) {
3372 /* Not inside tunnel */
3373 if (gp.old_tile == gp.new_tile) {
3374 /* Staying in the old tile */
3375 if (v->track == TRACK_BIT_DEPOT) {
3376 /* Inside depot */
3377 gp.x = v->x_pos;
3378 gp.y = v->y_pos;
3379 } else {
3380 /* Not inside depot */
3381
3382 /* Reverse when we are at the end of the track already, do not move to the new position */
3383 if (v->IsMovingFront() && !TrainCheckIfLineEnds(v, reverse)) return false;
3384
3385 auto vets = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
3386 if (vets.Test(VehicleEnterTileState::CannotEnter)) {
3387 goto invalid_rail;
3388 }
3390 /* The new position is the end of the platform */
3392 }
3393 }
3394 } else {
3395 /* A new tile is about to be entered. */
3396
3397 /* Determine what direction we're entering the new tile from */
3398 enterdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile);
3399 assert(IsValidDiagDirection(enterdir));
3400
3401 /* Get the status of the tracks in the new tile and mask
3402 * away the bits that aren't reachable. */
3404 TrackdirBits reachable_trackdirs = DiagdirReachesTrackdirs(enterdir);
3405
3406 TrackdirBits trackdirbits = TrackStatusToTrackdirBits(ts) & reachable_trackdirs;
3407 TrackBits red_signals = TrackdirBitsToTrackBits(TrackStatusToRedSignals(ts) & reachable_trackdirs);
3408
3409 TrackBits bits = TrackdirBitsToTrackBits(trackdirbits);
3410 if (Rail90DegTurnDisallowed(GetTileRailType(gp.old_tile), GetTileRailType(gp.new_tile)) && prev == nullptr) {
3411 /* We allow wagons to make 90 deg turns, because forbid_90_deg
3412 * can be switched on halfway a turn */
3414 }
3415
3416 if (bits == TRACK_BIT_NONE) goto invalid_rail;
3417
3418 /* Check if the new tile constrains tracks that are compatible
3419 * with the current train, if not, bail out. */
3420 if (!CheckCompatibleRail(v->First(), gp.new_tile, v->IsMovingFront())) goto invalid_rail;
3421
3422 TrackBits chosen_track;
3423 if (v->IsMovingFront()) {
3424 /* Currently the locomotive is active. Determine which one of the
3425 * available tracks to choose */
3426 chosen_track = TrackToTrackBits(ChooseTrainTrack(first, gp.new_tile, enterdir, bits, false, nullptr, true));
3427 assert(chosen_track & (bits | GetReservedTrackbits(gp.new_tile)));
3428
3429 if (first->force_proceed != TFP_NONE && IsPlainRailTile(gp.new_tile) && HasSignals(gp.new_tile)) {
3430 /* For each signal we find decrease the counter by one.
3431 * We start at two, so the first signal we pass decreases
3432 * this to one, then if we reach the next signal it is
3433 * decreased to zero and we won't pass that new signal. */
3434 Trackdir dir = FindFirstTrackdir(trackdirbits);
3435 if (HasSignalOnTrackdir(gp.new_tile, dir) ||
3438 /* However, we do not want to be stopped by PBS signals
3439 * entered via the back. */
3440 first->force_proceed = (first->force_proceed == TFP_SIGNAL) ? TFP_STUCK : TFP_NONE;
3442 }
3443 }
3444
3445 /* Check if it's a red signal and that force proceed is not clicked. */
3446 if ((red_signals & chosen_track) && first->force_proceed == TFP_NONE) {
3447 /* In front of a red signal */
3448 Trackdir i = FindFirstTrackdir(trackdirbits);
3449
3450 /* Don't handle stuck trains here. */
3451 if (first->flags.Test(VehicleRailFlag::Stuck)) return false;
3452
3454 first->cur_speed = 0;
3455 first->subspeed = 0;
3456 first->progress = 255; // make sure that every bit of acceleration will hit the signal again, so speed stays 0.
3457 if (!_settings_game.pf.reverse_at_signals || ++first->wait_counter < _settings_game.pf.wait_oneway_signal * Ticks::DAY_TICKS * 2) return false;
3458 } else if (HasSignalOnTrackdir(gp.new_tile, i)) {
3459 first->cur_speed = 0;
3460 first->subspeed = 0;
3461 first->progress = 255; // make sure that every bit of acceleration will hit the signal again, so speed stays 0.
3462 if (!_settings_game.pf.reverse_at_signals || ++first->wait_counter < _settings_game.pf.wait_twoway_signal * Ticks::DAY_TICKS * 2) {
3463 DiagDirection exitdir = TrackdirToExitdir(i);
3464 TileIndex o_tile = TileAddByDiagDir(gp.new_tile, exitdir);
3465
3466 exitdir = ReverseDiagDir(exitdir);
3467
3468 /* check if a train is waiting on the other side */
3469 if (!HasVehicleOnTile(o_tile, [&exitdir](const Vehicle *u) {
3470 if (u->type != VEH_TRAIN || u->vehstatus.Test(VehState::Crashed)) return false;
3471 const Train *t = Train::From(u);
3472
3473 /* not front engine of a train, inside wormhole or depot, crashed */
3474 if (!t->IsFrontEngine() || !(t->track & TRACK_BIT_MASK)) return false;
3475
3476 if (t->cur_speed > 5 || VehicleExitDir(t->direction, t->track) != exitdir) return false;
3477
3478 return true;
3479 })) return false;
3480 }
3481 }
3482
3483 /* If we would reverse but are currently in a PBS block and
3484 * reversing of stuck trains is disabled, don't reverse.
3485 * This does not apply if the reason for reversing is a one-way
3486 * signal blocking us, because a train would then be stuck forever. */
3487 if (!_settings_game.pf.reverse_at_signals && !HasOnewaySignalBlockingTrackdir(gp.new_tile, i) &&
3488 UpdateSignalsOnSegment(v->tile, enterdir, v->owner) == SIGSEG_PBS) {
3489 first->wait_counter = 0;
3490 return false;
3491 }
3492 goto reverse_train_direction;
3493 } else {
3494 TryReserveRailTrack(gp.new_tile, TrackBitsToTrack(chosen_track), false);
3495 }
3496 } else {
3497 /* The wagon is active, simply follow the prev vehicle. */
3498 if (prev->tile == gp.new_tile) {
3499 /* Choose the same track as prev */
3500 if (prev->track == TRACK_BIT_WORMHOLE) {
3501 /* Vehicles entering tunnels enter the wormhole earlier than for bridges.
3502 * However, just choose the track into the wormhole. */
3503 assert(IsTunnel(prev->tile));
3504 chosen_track = bits;
3505 } else {
3506 chosen_track = prev->track;
3507 }
3508 } else {
3509 /* Choose the track that leads to the tile where prev is.
3510 * This case is active if 'prev' is already on the second next tile, when 'v' just enters the next tile.
3511 * I.e. when the tile between them has only space for a single vehicle like
3512 * 1) horizontal/vertical track tiles and
3513 * 2) some orientations of tunnel entries, where the vehicle is already inside the wormhole at 8/16 from the tile edge.
3514 * Is also the train just reversing, the wagon inside the tunnel is 'on' the tile of the opposite tunnel entry.
3515 */
3516 static const TrackBits _connecting_track[DIAGDIR_END][DIAGDIR_END] = {
3521 };
3522 DiagDirection exitdir = DiagdirBetweenTiles(gp.new_tile, prev->tile);
3523 assert(IsValidDiagDirection(exitdir));
3524 chosen_track = _connecting_track[enterdir][exitdir];
3525 }
3526 chosen_track &= bits;
3527 }
3528
3529 /* Make sure chosen track is a valid track */
3530 assert(
3531 chosen_track == TRACK_BIT_X || chosen_track == TRACK_BIT_Y ||
3532 chosen_track == TRACK_BIT_UPPER || chosen_track == TRACK_BIT_LOWER ||
3533 chosen_track == TRACK_BIT_LEFT || chosen_track == TRACK_BIT_RIGHT);
3534
3535 /* Update XY to reflect the entrance to the new tile, and select the direction to use */
3536 const uint8_t *b = _initial_tile_subcoord[FindFirstBit(chosen_track)][enterdir];
3537 gp.x = (gp.x & ~0xF) | b[0];
3538 gp.y = (gp.y & ~0xF) | b[1];
3539 Direction chosen_dir = (Direction)b[2];
3540
3541 /* Call the landscape function and tell it that the vehicle entered the tile */
3542 auto vets = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
3543 if (vets.Test(VehicleEnterTileState::CannotEnter)) {
3544 goto invalid_rail;
3545 }
3546
3548 Track track = FindFirstTrack(chosen_track);
3549 Trackdir tdir = TrackDirectionToTrackdir(track, chosen_dir);
3550 if (v->IsMovingFront() && HasPbsSignalOnTrackdir(gp.new_tile, tdir)) {
3553 }
3554
3555 /* Clear any track reservation when the last vehicle leaves the tile */
3556 if (v->GetMovingNext() == nullptr) ClearPathReservation(v, v->tile, v->GetVehicleTrackdir());
3557
3558 v->tile = gp.new_tile;
3559
3561 first->ConsistChanged(CCF_TRACK);
3562 }
3563
3564 v->track = chosen_track;
3565 assert(v->track);
3566 }
3567
3568 /* We need to update signal status, but after the vehicle position hash
3569 * has been updated by UpdateInclination() */
3570 update_signals_crossing = true;
3571
3572 if (chosen_dir != v->GetMovingDirection()) {
3573 if (prev == nullptr && _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) {
3574 const AccelerationSlowdownParams *asp = &_accel_slowdown[static_cast<int>(v->GetAccelerationType())];
3575 DirDiff diff = DirDifference(v->direction, chosen_dir);
3576 v->cur_speed -= (diff == DIRDIFF_45RIGHT || diff == DIRDIFF_45LEFT ? asp->small_turn : asp->large_turn) * v->cur_speed >> 8;
3577 }
3578 direction_changed = true;
3579 v->SetMovingDirection(chosen_dir);
3580 }
3581
3582 if (v->IsMovingFront()) {
3583 first->wait_counter = 0;
3584
3585 /* If we are approaching a crossing that is reserved, play the sound now. */
3586 TileIndex crossing = TrainApproachingCrossingTile(v); // We know we are the moving front, so we can check v.
3587 if (crossing != INVALID_TILE && HasCrossingReservation(crossing) && _settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, crossing);
3588
3589 /* Always try to extend the reservation when entering a tile. */
3590 CheckNextTrainTile(first);
3591 }
3592
3594 /* The new position is the location where we want to stop */
3596 }
3597 }
3598 } else {
3600 /* Perform look-ahead on tunnel exit. */
3601 if (v->IsMovingFront()) {
3603 CheckNextTrainTile(first);
3604 }
3605 /* Prevent v->UpdateInclination() being called with wrong parameters.
3606 * This could happen if the train was reversed inside the tunnel/bridge. */
3607 if (gp.old_tile == gp.new_tile) {
3609 }
3610 } else {
3611 v->x_pos = gp.x;
3612 v->y_pos = gp.y;
3613 v->UpdatePosition();
3614 if (!v->vehstatus.Test(VehState::Hidden)) v->Vehicle::UpdateViewport(true);
3615 continue;
3616 }
3617 }
3618
3619 /* update image of train, as well as delta XY */
3620 v->UpdateDeltaXY();
3621
3622 v->x_pos = gp.x;
3623 v->y_pos = gp.y;
3624 v->UpdatePosition();
3625
3626 /* update the Z position of the vehicle */
3627 int old_z = v->UpdateInclination(gp.new_tile != gp.old_tile, false);
3628
3629 if (prev == nullptr) {
3630 /* This is the first vehicle in the train */
3631 AffectSpeedByZChange(first, v->z_pos - old_z);
3632 }
3633
3634 if (update_signals_crossing) {
3635 if (v->IsMovingFront()) {
3636 if (TrainMovedChangeSignals(gp.new_tile, enterdir)) {
3637 /* We are entering a block with PBS signals right now, but
3638 * not through a PBS signal. This means we don't have a
3639 * reservation right now. As a conventional signal will only
3640 * ever be green if no other train is in the block, getting
3641 * a path should always be possible. If the player built
3642 * such a strange network that it is not possible, the train
3643 * will be marked as stuck and the player has to deal with
3644 * the problem. */
3645 if ((!HasReservedTracks(gp.new_tile, v->track) &&
3647 !TryPathReserve(first)) {
3648 MarkTrainAsStuck(first);
3649 }
3650 }
3651 }
3652
3653 /* Signals can only change when the first
3654 * (above) or the last vehicle moves. */
3655 if (v->GetMovingNext() == nullptr) {
3656 TrainMovedChangeSignals(gp.old_tile, ReverseDiagDir(enterdir));
3658 }
3659 }
3660
3661 /* Do not check on every tick to save some computing time. */
3662 if (v->IsMovingFront() && first->tick_counter % _settings_game.pf.path_backoff_interval == 0) CheckNextTrainTile(first);
3663 }
3664
3665 if (direction_changed) first->tcache.cached_max_curve_speed = first->GetCurveSpeedLimit();
3666
3667 return true;
3668
3669invalid_rail:
3670 /* We've reached end of line?? */
3671 if (prev != nullptr) FatalError("Disconnecting train");
3672
3673reverse_train_direction:
3674 if (reverse) {
3675 first->wait_counter = 0;
3676 first->cur_speed = 0;
3677 first->subspeed = 0;
3678 ReverseTrainDirection(first);
3679 }
3680
3681 return false;
3682}
3683
3684static bool IsRailStationPlatformOccupied(TileIndex tile)
3685{
3687
3688 for (TileIndex t = tile; IsCompatibleTrainStationTile(t, tile); t -= delta) {
3689 if (HasVehicleOnTile(t, IsTrain)) return true;
3690 }
3691 for (TileIndex t = tile + delta; IsCompatibleTrainStationTile(t, tile); t += delta) {
3692 if (HasVehicleOnTile(t, IsTrain)) return true;
3693 }
3694
3695 return false;
3696}
3697
3705static void DeleteLastWagon(Train *v)
3706{
3707 Train *first = v->First();
3708
3709 /* Go to the last wagon and delete the link pointing there
3710 * new_last is then the one-before-last wagon, and v the last
3711 * one which will physically be removed */
3712 Train *new_last = v;
3713 for (; v->Next() != nullptr; v = v->Next()) new_last = v;
3714 new_last->SetNext(nullptr);
3715
3716 if (first != v) {
3717 /* Recalculate cached train properties */
3719 /* Update the depot window in case a part of the consist is in a depot. */
3722 }
3723
3724 /* 'v' shouldn't be accessed after it has been deleted */
3725 TrackBits trackbits = v->track;
3726 TileIndex tile = v->tile;
3727 Owner owner = v->owner;
3728
3729 delete v;
3730 v = nullptr; // make sure nobody will try to read 'v' anymore
3731
3732 if (trackbits == TRACK_BIT_WORMHOLE) {
3733 /* Vehicle is inside a wormhole, v->track contains no useful value then. */
3735 }
3736
3737 Track track = TrackBitsToTrack(trackbits);
3738 if (HasReservedTracks(tile, trackbits)) {
3739 UnreserveRailTrack(tile, track);
3740
3741 /* If there are still crashed vehicles on the tile, give the track reservation to them */
3742 TrackBits remaining_trackbits = TRACK_BIT_NONE;
3743 for (const Vehicle *u : VehiclesOnTile(tile)) {
3744 if (u->type != VEH_TRAIN || !u->vehstatus.Test(VehState::Crashed)) continue;
3745 TrackBits train_tbits = Train::From(u)->track;
3746 if (train_tbits == TRACK_BIT_WORMHOLE) {
3747 /* Vehicle is inside a wormhole, u->track contains no useful value then. */
3748 remaining_trackbits |= DiagDirToDiagTrackBits(GetTunnelBridgeDirection(u->tile));
3749 } else if (train_tbits != TRACK_BIT_DEPOT) {
3750 remaining_trackbits |= train_tbits;
3751 }
3752 }
3753
3754 /* It is important that these two are the first in the loop, as reservation cannot deal with every trackbit combination */
3755 assert(TRACK_BEGIN == TRACK_X && TRACK_Y == TRACK_BEGIN + 1);
3756 for (Track t : SetTrackBitIterator(remaining_trackbits)) TryReserveRailTrack(tile, t);
3757 }
3758
3759 /* check if the wagon was on a road/rail-crossing */
3761
3762 if (IsRailStationTile(tile)) {
3763 bool occupied = IsRailStationPlatformOccupied(tile);
3765 SetRailStationPlatformReservation(tile, dir, occupied);
3767 }
3768
3769 /* Update signals */
3772 } else {
3773 SetSignalsOnBothDir(tile, track, owner);
3774 }
3775}
3776
3782{
3783 static const DirDiff delta[] = {
3785 };
3786
3787 do {
3788 /* We don't need to twist around vehicles if they're not visible */
3789 if (!v->vehstatus.Test(VehState::Hidden)) {
3790 v->direction = ChangeDir(v->direction, delta[GB(Random(), 0, 2)]);
3791 /* Refrain from updating the z position of the vehicle when on
3792 * a bridge, because UpdateInclination() will put the vehicle under
3793 * the bridge in that case */
3794 if (v->track != TRACK_BIT_WORMHOLE) {
3795 v->UpdatePosition();
3796 v->UpdateInclination(false, true);
3797 } else {
3798 v->UpdateViewport(false, true);
3799 }
3800 }
3801 } while ((v = v->Next()) != nullptr);
3802}
3803
3810{
3811 int state = ++v->crash_anim_pos;
3812
3813 if (state == 4 && !v->vehstatus.Test(VehState::Hidden)) {
3815 }
3816
3817 uint32_t r;
3818 if (state <= 200 && Chance16R(1, 7, r)) {
3819 int index = (r * 10 >> 16);
3820
3821 Vehicle *u = v;
3822 do {
3823 if (--index < 0) {
3824 r = Random();
3825
3827 GB(r, 8, 3) + 2,
3828 GB(r, 16, 3) + 2,
3829 GB(r, 0, 3) + 5,
3831 break;
3832 }
3833 } while ((u = u->Next()) != nullptr);
3834 }
3835
3836 if (state <= 240 && !(v->tick_counter & 3)) ChangeTrainDirRandomly(v);
3837
3838 if (state >= 4440 && !(v->tick_counter & 0x1F)) {
3839 bool ret = v->Next() != nullptr;
3840 DeleteLastWagon(v);
3841 return ret;
3842 }
3843
3844 return true;
3845}
3846
3848static const uint16_t _breakdown_speeds[16] = {
3849 225, 210, 195, 180, 165, 150, 135, 120, 105, 90, 75, 60, 45, 30, 15, 15
3850};
3851
3852
3861static bool TrainApproachingLineEnd(Train *moving_front, bool signal, bool reverse)
3862{
3863 /* Calc position within the current tile */
3864 uint x = moving_front->x_pos & 0xF;
3865 uint y = moving_front->y_pos & 0xF;
3866
3867 Direction vdir = moving_front->GetMovingDirection();
3868
3869 /* for diagonal directions, 'x' will be 0..15 -
3870 * for other directions, it will be 1, 3, 5, ..., 15 */
3871 switch (vdir) {
3872 case DIR_N : x = ~x + ~y + 25; break;
3873 case DIR_NW: x = y; [[fallthrough]];
3874 case DIR_NE: x = ~x + 16; break;
3875 case DIR_E : x = ~x + y + 9; break;
3876 case DIR_SE: x = y; break;
3877 case DIR_S : x = x + y - 7; break;
3878 case DIR_W : x = ~y + x + 9; break;
3879 default: break;
3880 }
3881
3882 Train *consist = moving_front->First();
3883
3884 /* Do not reverse when approaching red signal. Make sure the vehicle's front
3885 * does not cross the tile boundary when we do reverse, but as the vehicle's
3886 * location is based on their center, use half a vehicle's length as offset.
3887 * Multiply the half-length by two for straight directions to compensate that
3888 * we only get odd x offsets there. */
3889 uint8_t rounding = moving_front->IsDrivingBackwards() ? 0 : 1;
3890 if (!signal && x + (moving_front->gcache.cached_veh_length + rounding) / 2 * (IsDiagonalDirection(vdir) ? 1 : 2) >= TILE_SIZE) {
3891 /* we are too near the tile end, reverse now */
3892 consist->cur_speed = 0;
3893 if (reverse) ReverseTrainDirection(consist);
3894 return false;
3895 }
3896
3897 /* slow down */
3899 uint16_t break_speed = _breakdown_speeds[x & 0xF];
3900 if (break_speed < consist->cur_speed) consist->cur_speed = break_speed;
3901
3902 return true;
3903}
3904
3905
3911static bool TrainCanLeaveTile(const Train *moving_front)
3912{
3913 /* Exit if inside a tunnel/bridge or a depot */
3914 if (moving_front->track == TRACK_BIT_WORMHOLE || moving_front->track == TRACK_BIT_DEPOT) return false;
3915
3916 TileIndex tile = moving_front->tile;
3917
3918 /* entering a tunnel/bridge? */
3921 if (DiagDirToDir(dir) == moving_front->GetMovingDirection()) return false;
3922 }
3923
3924 /* entering a depot? */
3925 if (IsRailDepotTile(tile)) {
3927 if (DiagDirToDir(dir) == moving_front->GetMovingDirection()) return false;
3928 }
3929
3930 return true;
3931}
3932
3933
3942{
3943 assert(moving_front->IsMovingFront());
3944 assert(!moving_front->First()->vehstatus.Test(VehState::Crashed));
3945
3946 if (!TrainCanLeaveTile(moving_front)) return INVALID_TILE;
3947
3948 DiagDirection dir = VehicleExitDir(moving_front->GetMovingDirection(), moving_front->track);
3949 TileIndex tile = moving_front->tile + TileOffsByDiagDir(dir);
3950
3951 /* not a crossing || wrong axis || unusable rail (wrong type or owner) */
3952 if (!IsLevelCrossingTile(tile) || DiagDirToAxis(dir) == GetCrossingRoadAxis(tile) ||
3953 !CheckCompatibleRail(moving_front->First(), tile, true)) {
3954 return INVALID_TILE;
3955 }
3956
3957 return tile;
3958}
3959
3960
3968static bool TrainCheckIfLineEnds(Train *moving_front, bool reverse)
3969{
3970 /* First, handle broken down train */
3971
3972 Train *consist = moving_front->First();
3973 int t = consist->breakdown_ctr;
3974 if (t > 1) {
3976
3977 uint16_t break_speed = _breakdown_speeds[GB(~t, 4, 4)];
3978 if (break_speed < consist->cur_speed) consist->cur_speed = break_speed;
3979 } else {
3981 }
3982
3983 if (!TrainCanLeaveTile(moving_front)) return true;
3984
3985 /* Determine the non-diagonal direction in which we will exit this tile */
3986 DiagDirection dir = VehicleExitDir(moving_front->GetMovingDirection(), moving_front->track);
3987 /* Calculate next tile */
3988 TileIndex tile = moving_front->tile + TileOffsByDiagDir(dir);
3989
3990 /* Determine the track status on the next tile */
3992 TrackdirBits reachable_trackdirs = DiagdirReachesTrackdirs(dir);
3993
3994 TrackdirBits trackdirbits = TrackStatusToTrackdirBits(ts) & reachable_trackdirs;
3995 TrackdirBits red_signals = TrackStatusToRedSignals(ts) & reachable_trackdirs;
3996
3997 /* We are sure the train is not entering a depot, it is detected above */
3998
3999 /* mask unreachable track bits if we are forbidden to do 90deg turns */
4000 TrackBits bits = TrackdirBitsToTrackBits(trackdirbits);
4001 if (Rail90DegTurnDisallowed(GetTileRailType(moving_front->tile), GetTileRailType(tile))) {
4002 bits &= ~TrackCrossesTracks(FindFirstTrack(moving_front->track));
4003 }
4004
4005 /* no suitable trackbits at all || unusable rail (wrong type or owner) */
4006 if (bits == TRACK_BIT_NONE || !CheckCompatibleRail(consist, tile, true)) {
4007 return TrainApproachingLineEnd(moving_front, false, reverse);
4008 }
4009
4010 /* approaching red signal */
4011 if ((trackdirbits & red_signals) != 0) return TrainApproachingLineEnd(moving_front, true, reverse);
4012
4013 /* approaching a rail/road crossing? then make it red */
4015
4016 return true;
4017}
4018
4025static bool TrainLocoHandler(Train *consist, bool mode)
4026{
4027 /* train has crashed? */
4028 if (consist->vehstatus.Test(VehState::Crashed)) {
4029 return mode ? true : HandleCrashedTrain(consist); // 'this' can be deleted here
4030 }
4031
4032 if (consist->force_proceed != TFP_NONE) {
4035 }
4036
4037 /* train is broken down? */
4038 if (consist->HandleBreakdown()) return true;
4039
4040 if (consist->flags.Test(VehicleRailFlag::Reversing) && consist->cur_speed == 0) {
4041 ReverseTrainDirection(consist);
4042 }
4043
4044 /* exit if train is stopped */
4045 if (consist->vehstatus.Test(VehState::Stopped) && consist->cur_speed == 0) return true;
4046
4047 bool valid_order = !consist->current_order.IsType(OT_NOTHING) && consist->current_order.GetType() != OT_CONDITIONAL;
4048 if (ProcessOrders(consist) && CheckReverseTrain(consist)) {
4049 consist->wait_counter = 0;
4050 consist->cur_speed = 0;
4051 consist->subspeed = 0;
4053 ReverseTrainDirection(consist);
4054 return true;
4055 } else if (consist->flags.Test(VehicleRailFlag::LeavingStation)) {
4056 /* Try to reserve a path when leaving the station as we
4057 * might not be marked as wanting a reservation, e.g.
4058 * when an overlength train gets turned around in a station. */
4059 const Train *moving_front = consist->GetMovingFront();
4060 DiagDirection dir = VehicleExitDir(moving_front->GetMovingDirection(), moving_front->track);
4061 if (IsRailDepotTile(moving_front->tile) || IsTileType(moving_front->tile, TileType::TunnelBridge)) dir = INVALID_DIAGDIR;
4062
4063 if (UpdateSignalsOnSegment(moving_front->tile, dir, consist->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
4064 TryPathReserve(consist, true, true);
4065 }
4067 }
4068
4069 consist->HandleLoading(mode);
4070
4071 if (consist->current_order.IsType(OT_LOADING)) return true;
4072
4073 if (CheckTrainStayInDepot(consist)) return true;
4074
4075 if (!mode) consist->ShowVisualEffect();
4076
4077 /* We had no order but have an order now, do look ahead. */
4078 if (!valid_order && !consist->current_order.IsType(OT_NOTHING)) {
4079 CheckNextTrainTile(consist);
4080 }
4081
4082 /* Handle stuck trains. */
4083 if (!mode && consist->flags.Test(VehicleRailFlag::Stuck)) {
4084 ++consist->wait_counter;
4085
4086 /* Should we try reversing this tick if still stuck? */
4087 bool turn_around = consist->wait_counter % (_settings_game.pf.wait_for_pbs_path * Ticks::DAY_TICKS) == 0 && _settings_game.pf.reverse_at_signals;
4088
4089 if (!turn_around && consist->wait_counter % _settings_game.pf.path_backoff_interval != 0 && consist->force_proceed == TFP_NONE) return true;
4090 if (!TryPathReserve(consist)) {
4091 /* Still stuck. */
4092 if (turn_around) ReverseTrainDirection(consist);
4093
4094 if (consist->flags.Test(VehicleRailFlag::Stuck) && consist->wait_counter > 2 * _settings_game.pf.wait_for_pbs_path * Ticks::DAY_TICKS) {
4095 /* Show message to player. */
4096 if (_settings_client.gui.lost_vehicle_warn && consist->owner == _local_company) {
4097 AddVehicleAdviceNewsItem(AdviceType::TrainStuck, GetEncodedString(STR_NEWS_TRAIN_IS_STUCK, consist->index), consist->index);
4098 }
4099 consist->wait_counter = 0;
4100 }
4101 /* Exit if force proceed not pressed, else reset stuck flag anyway. */
4102 if (consist->force_proceed == TFP_NONE) return true;
4104 consist->wait_counter = 0;
4106 }
4107 }
4108
4109 if (consist->current_order.IsType(OT_LEAVESTATION)) {
4110 consist->current_order.Free();
4112 return true;
4113 }
4114
4115 int j = consist->UpdateSpeed();
4116
4117 /* we need to invalidate the widget if we are stopping from 'Stopping 0 km/h' to 'Stopped' */
4118 if (consist->cur_speed == 0 && consist->vehstatus.Test(VehState::Stopped)) {
4119 /* If we manually stopped, we're not force-proceeding anymore. */
4120 consist->force_proceed = TFP_NONE;
4121 InvalidateWindowData(WC_VEHICLE_VIEW, consist->index);
4122 }
4123
4124 Train* moving_front = consist->GetMovingFront();
4125 int adv_spd = moving_front->GetAdvanceDistance();
4126 if (j < adv_spd) {
4127 /* if the vehicle has speed 0, update the last_speed field. */
4128 if (consist->cur_speed == 0) consist->SetLastSpeed();
4129 } else {
4130 TrainCheckIfLineEnds(moving_front);
4131 moving_front = moving_front->GetMovingFront();
4132 /* Loop until the train has finished moving. */
4133 for (;;) {
4134 j -= adv_spd;
4135 TrainController(moving_front, nullptr);
4136 moving_front = moving_front->GetMovingFront();
4137 /* Don't continue to move if the train crashed. */
4138 if (CheckTrainCollision(moving_front)) break;
4139 /* Determine distance to next map position */
4140 adv_spd = moving_front->GetAdvanceDistance();
4141
4142 /* No more moving this tick */
4143 if (j < adv_spd || consist->cur_speed == 0) break;
4144
4145 OrderType order_type = consist->current_order.GetType();
4146 /* Do not skip waypoints (incl. 'via' stations) when passing through at full speed. */
4147 if ((order_type == OT_GOTO_WAYPOINT || order_type == OT_GOTO_STATION) &&
4149 IsTileType(moving_front->tile, TileType::Station) &&
4150 consist->current_order.GetDestination() == GetStationIndex(moving_front->tile)) {
4151 ProcessOrders(consist);
4152 }
4153 }
4154 consist->SetLastSpeed();
4155 }
4156
4157 for (Train *u = consist; u != nullptr; u = u->Next()) {
4158 if (u->vehstatus.Test(VehState::Hidden)) continue;
4159
4160 u->UpdateViewport(false, false);
4161 }
4162
4163 if (consist->progress == 0) consist->progress = j; // Save unused spd for next time, if TrainController didn't set progress
4164
4165 return true;
4166}
4167
4173{
4174 Money cost = 0;
4175 const Train *v = this;
4176
4177 do {
4178 const Engine *e = v->GetEngine();
4179 if (e->VehInfo<RailVehicleInfo>().running_cost_class == Price::Invalid) continue;
4180
4181 uint cost_factor = GetVehicleProperty(v, PROP_TRAIN_RUNNING_COST_FACTOR, e->VehInfo<RailVehicleInfo>().running_cost);
4182 if (cost_factor == 0) continue;
4183
4184 /* Halve running cost for multiheaded parts */
4185 if (v->IsMultiheaded()) cost_factor /= 2;
4186
4187 cost += GetPrice(e->VehInfo<RailVehicleInfo>().running_cost_class, cost_factor, e->GetGRF());
4188 } while ((v = v->GetNextVehicle()) != nullptr);
4189
4190 return cost;
4191}
4192
4198{
4199 this->tick_counter++;
4200
4201 if (this->IsFrontEngine()) {
4203
4204 if (!this->vehstatus.Test(VehState::Stopped) || this->cur_speed > 0) this->running_ticks++;
4205
4206 this->current_order_time++;
4207
4208 if (!TrainLocoHandler(this, false)) return false;
4209
4210 return TrainLocoHandler(this, true);
4211 } else if (this->IsFreeWagon() && this->vehstatus.Test(VehState::Crashed)) {
4212 /* Delete flooded standalone wagon chain */
4213 if (++this->crash_anim_pos >= 4400) {
4214 delete this;
4215 return false;
4216 }
4217 }
4218
4219 return true;
4220}
4221
4227{
4228 if (Company::Get(v->owner)->settings.vehicle.servint_trains == 0 || !v->NeedsAutomaticServicing()) return;
4229 if (v->IsChainInDepot()) {
4231 return;
4232 }
4233
4234 uint max_penalty = _settings_game.pf.yapf.maximum_go_to_depot_penalty;
4235
4236 FindDepotData tfdd = FindClosestTrainDepot(v, max_penalty);
4237 /* Only go to the depot if it is not too far out of our way. */
4238 if (tfdd.best_length == UINT_MAX || tfdd.best_length > max_penalty) {
4239 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
4240 /* If we were already heading for a depot but it has
4241 * suddenly moved farther away, we continue our normal
4242 * schedule? */
4245 }
4246 return;
4247 }
4248
4249 DepotID depot = GetDepotIndex(tfdd.tile);
4250
4251 if (v->current_order.IsType(OT_GOTO_DEPOT) &&
4252 v->current_order.GetDestination() != depot &&
4253 !Chance16(3, 16)) {
4254 return;
4255 }
4256
4259 v->dest_tile = tfdd.tile;
4261}
4262
4265{
4266 AgeVehicle(this);
4267}
4268
4271{
4272 EconomyAgeVehicle(this);
4273
4274 if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
4275
4276 if (this->IsFrontEngine()) {
4278
4280
4281 CheckOrders(this);
4282
4283 /* update destination */
4284 if (this->current_order.IsType(OT_GOTO_STATION)) {
4285 TileIndex tile = Station::Get(this->current_order.GetDestination().ToStationID())->train_station.tile;
4286 if (tile != INVALID_TILE) this->dest_tile = tile;
4287 }
4288
4289 if (this->running_ticks != 0) {
4290 /* running costs */
4292
4293 this->profit_this_year -= cost.GetCost();
4294 this->running_ticks = 0;
4295
4297
4300 }
4301 }
4302}
4303
4309{
4310 if (this->vehstatus.Test(VehState::Crashed)) return INVALID_TRACKDIR;
4311
4312 if (this->track == TRACK_BIT_DEPOT) {
4313 /* We'll assume the train is facing outwards */
4314 return DiagDirToDiagTrackdir(GetRailDepotDirection(this->tile)); // Train in depot
4315 }
4316
4317 if (this->track == TRACK_BIT_WORMHOLE) {
4318 /* train in tunnel or on bridge, so just use its direction and assume a diagonal track */
4320 }
4321
4322 return TrackDirectionToTrackdir(FindFirstTrack(this->track), this->GetMovingDirection());
4323}
4324
4325uint16_t Train::GetMaxWeight() const
4326{
4327 uint16_t weight = CargoSpec::Get(this->cargo_type)->WeightOfNUnitsInTrain(this->GetEngine()->DetermineCapacity(this));
4328
4329 /* Vehicle weight is not added for articulated parts. */
4330 if (!this->IsArticulatedPart()) {
4331 weight += GetVehicleProperty(this, PROP_TRAIN_WEIGHT, RailVehInfo(this->engine_type)->weight);
4332 }
4333
4334 /* Powered wagons have extra weight added. */
4335 if (this->flags.Test(VehicleRailFlag::PoweredWagon)) {
4336 weight += RailVehInfo(this->gcache.first_engine)->pow_wag_weight;
4337 }
4338
4339 return weight;
4340}
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 uint8_t FindFirstBit(T x)
Search the first set bit in a value.
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
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:21
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:108
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition ai_core.cpp:235
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Flip(Tvalue_type value)
Flip the value-th bit.
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:160
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:204
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:446
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.
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.
@ DIRDIFF_90LEFT
Angle of 90 degrees left.
@ DIRDIFF_45LEFT
Angle of 45 degrees left.
@ DIRDIFF_45RIGHT
Angle of 45 degrees right.
@ DIRDIFF_SAME
Both directions faces to the same direction.
@ DIRDIFF_90RIGHT
Angle of 90 degrees right.
Direction
Defines the 8 directions on the map.
@ DIR_SW
Southwest.
@ DIR_NW
Northwest.
@ DIR_N
North.
@ DIR_SE
Southeast.
@ DIR_S
South.
@ DIR_NE
Northeast.
@ DIR_W
West.
@ DIR_E
East.
Axis
Allow incrementing of DiagDirDiff variables.
DiagDirection
Enumeration for diagonal directions.
@ DIAGDIR_END
Used for iterations.
@ DIAGDIR_BEGIN
Used for iterations.
@ INVALID_DIAGDIR
Flag for an invalid DiagDirection.
Money GetPrice(Price index, uint cost_factor, const GRFFile *grf_file, int shift)
Determine a certain price.
Definition economy.cpp:940
@ EXPENSES_TRAIN_RUN
Running costs trains.
@ EXPENSES_NEW_VEHICLES
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.
@ RAILVEH_WAGON
simple wagon, not motorized
Definition engine_type.h:34
@ RAILVEH_MULTIHEAD
indicates a combination of two locomotives
Definition engine_type.h:33
Functions related to errors.
@ WL_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:580
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:73
@ 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:72
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:441
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
@ 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.
uint traininfo_vehicle_width
Width (in pixels) of a 8/8 train vehicle in depot GUI and vehicle details.
Definition newgrf.h:159
int traininfo_vehicle_pitch
Vertical offset for drawing train images in depot GUI and vehicle details.
Definition newgrf.h:158
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
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
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:2369
void AddToShared(Vehicle *shared_chain)
Adds this vehicle to a shared vehicle chain.
Definition vehicle.cpp:3008
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:2439
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:2450
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:2530
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:2224
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:2577
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:2972
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:1377
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:1703
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:2821
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:1860
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:719
TrackdirBits TrackStatusToTrackdirBits(TrackStatus ts)
Returns the present-trackdir-information of a TrackStatus.
Definition track_func.h:354
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:422
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:650
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:503
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:611
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:589
TrackdirBits DiagdirReachesTrackdirs(DiagDirection diagdir)
Returns all trackdirs that can be reached when entering a tile from a given (diagonal) direction.
Definition track_func.h:560
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:488
TrackBits DiagdirReachesTracks(DiagDirection diagdir)
Returns all tracks that can be reached when entering a tile from a given (diagonal) direction.
Definition track_func.h:578
TrackBits DiagDirToDiagTrackBits(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal track bits incidating with that diagdir.
Definition track_func.h:529
Trackdir DiagDirToDiagTrackdir(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal trackdir that runs in that direction.
Definition track_func.h:542
TrackdirBits TrackStatusToRedSignals(TrackStatus ts)
Returns the red-signal-information of a TrackStatus.
Definition track_func.h:378
DiagDirection TrackdirToExitdir(Trackdir trackdir)
Maps a trackdir to the (4-way) direction the tile is exited when following that trackdir.
Definition track_func.h:441
Track DiagDirToDiagTrack(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal track incidating with that diagdir.
Definition track_func.h:517
TrackBits TrackdirBitsToTrackBits(TrackdirBits bits)
Discards all directional information from a TrackdirBits value.
Definition track_func.h:308
TrackBits
Allow incrementing of Track variables.
Definition track_type.h:35
@ TRACK_BIT_WORMHOLE
Bitflag for a wormhole (used for tunnels).
Definition track_type.h:52
@ TRACK_BIT_UPPER
Upper track.
Definition track_type.h:39
@ TRACK_BIT_DEPOT
Bitflag for a depot.
Definition track_type.h:53
@ TRACK_BIT_LEFT
Left track.
Definition track_type.h:41
@ TRACK_BIT_Y
Y-axis track.
Definition track_type.h:38
@ TRACK_BIT_NONE
No track.
Definition track_type.h:36
@ TRACK_BIT_X
X-axis track.
Definition track_type.h:37
@ TRACK_BIT_MASK
Bitmask for the first 6 bits.
Definition track_type.h:51
@ TRACK_BIT_LOWER
Lower track.
Definition track_type.h:40
@ TRACK_BIT_RIGHT
Right track.
Definition track_type.h:42
Trackdir
Enumeration for tracks and directions.
Definition track_type.h:66
@ INVALID_TRACKDIR
Flag for an invalid trackdir.
Definition track_type.h:85
TrackdirBits
Allow incrementing of Trackdir variables.
Definition track_type.h:97
@ TRACKDIR_BIT_NONE
No track build.
Definition track_type.h:98
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 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 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.
bool IsValidImageIndex< VEH_TRAIN >(uint8_t image_index)
Helper to check whether an image index is valid for a particular vehicle.
Definition train_cmd.cpp:58
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:69
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:76
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.
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:1565
UnitID GetFreeUnitNumber(VehicleType type)
Get an unused unit number for a vehicle (if allowed).
Definition vehicle.cpp:1917
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:1321
GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
Get position information of a vehicle when moving one pixel in the direction it is facing.
Definition vehicle.cpp:1806
void DecreaseVehicleValue(Vehicle *v)
Decrease the value of a vehicle.
Definition vehicle.cpp:1300
void EconomyAgeVehicle(Vehicle *v)
Update economy age of a vehicle.
Definition vehicle.cpp:1443
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:1455
@ 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.
@ VEH_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
@ WC_VEHICLE_ORDERS
Vehicle orders; Window numbers:
@ WC_VEHICLE_DEPOT
Depot view; Window numbers:
@ WC_TRAINS_LIST
Trains list; Window numbers:
@ WC_VEHICLE_REFIT
Vehicle refit; Window numbers:
@ WC_VEHICLE_DETAILS
Vehicle details; Window numbers:
@ WC_COMPANY
Company view; Window numbers:
@ WC_VEHICLE_VIEW
Vehicle view; Window numbers:
@ WC_VEHICLE_TIMETABLE
Vehicle timetable; Window numbers:
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