OpenTTD Source 20260917-master-gec444f7315
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"
38#include "script/api/script_event_types.hpp"
41
43
44#include "table/strings.h"
45#include "table/train_sprites.h"
46
47#include "safeguards.h"
48
49static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck);
50static bool TrainCheckIfLineEnds(Train *v, bool reverse = true);
51bool TrainController(Train *v, Vehicle *nomove, bool reverse = true); // Also used in vehicle_sl.cpp.
53static void CheckIfTrainNeedsService(Train *v);
54static void CheckNextTrainTile(Train *v);
55
60
62template <>
64{
65 return image_index < lengthof(_engine_sprite_base);
66}
67
68
75{
76 if (!CargoSpec::Get(cargo)->is_freight) return 1;
77 return _settings_game.vehicle.freight_trains;
78}
79
82{
83 bool first = true;
84
85 for (const Train *v : Train::Iterate()) {
86 if (v->First() == v && !v->vehstatus.Test(VehState::Crashed)) {
87 for (const Train *u = v->GetMovingFront(), *w = v->GetMovingNext(); w != nullptr; u = w, w = w->GetMovingNext()) {
88 if (u->track != Track::Depot) {
89 if ((w->track != Track::Depot &&
90 std::max(abs(u->x_pos - w->x_pos), abs(u->y_pos - w->y_pos)) != u->CalcNextVehicleOffset()) ||
91 (w->track == Track::Depot && TicksToLeaveDepot(u) <= 0)) {
92 ShowErrorMessage(GetEncodedString(STR_BROKEN_VEHICLE_LENGTH, v->index, v->owner), {}, WarningLevel::Critical);
93
94 if (!_networking && first) {
95 first = false;
96 Command<Commands::Pause>::Post(PauseMode::Error, true);
97 }
98 /* Break so we warn only once for each train. */
99 break;
100 }
101 }
102 }
103 }
104 }
105}
106
114{
115 uint16_t max_speed = UINT16_MAX;
116
117 assert(this->IsFrontEngine() || this->IsFreeWagon());
118
119 const RailVehicleInfo *rvi_v = RailVehInfo(this->engine_type);
120 EngineID first_engine = this->IsFrontEngine() ? this->engine_type : EngineID::Invalid();
121 this->gcache.cached_total_length = 0;
122 this->compatible_railtypes = {};
123
124 bool train_can_tilt = true;
125 int16_t min_curve_speed_mod = INT16_MAX;
126
127 for (Train *u = this; u != nullptr; u = u->Next()) {
128 const RailVehicleInfo *rvi_u = RailVehInfo(u->engine_type);
129
130 /* Check the this->first cache. */
131 assert(u->First() == this);
132
133 /* update the 'first engine' */
134 u->gcache.first_engine = this == u ? EngineID::Invalid() : first_engine;
135 u->railtypes = rvi_u->railtypes;
136
137 if (u->IsEngine()) first_engine = u->engine_type;
138
139 /* Set user defined data to its default value */
140 u->tcache.user_def_data = rvi_u->user_def_data;
141 this->InvalidateNewGRFCache();
142 u->InvalidateNewGRFCache();
143 }
144
145 for (Train *u = this; u != nullptr; u = u->Next()) {
146 /* Update user defined data (must be done before other properties) */
147 u->tcache.user_def_data = GetVehicleProperty(u, PROP_TRAIN_USER_DATA, u->tcache.user_def_data);
148 this->InvalidateNewGRFCache();
149 u->InvalidateNewGRFCache();
150 }
151
152 for (Train *u = this; u != nullptr; u = u->Next()) {
153 const Engine *e_u = u->GetEngine();
154 const RailVehicleInfo *rvi_u = &e_u->VehInfo<RailVehicleInfo>();
155
156 if (!e_u->info.misc_flags.Test(EngineMiscFlag::RailTilts)) train_can_tilt = false;
157 min_curve_speed_mod = std::min(min_curve_speed_mod, u->GetCurveSpeedModifier());
158
159 /* Cache wagon override sprite group. nullptr is returned if there is none */
160 u->tcache.cached_override = GetWagonOverrideSpriteSet(u->engine_type, u->cargo_type, u->gcache.first_engine);
161
162 /* Reset colour map */
163 u->colourmap = PAL_NONE;
164
165 /* Update powered-wagon-status and visual effect */
166 u->UpdateVisualEffect(true);
167
168 if (rvi_v->pow_wag_power != 0 && rvi_u->railveh_type == RailVehicleType::Wagon &&
169 UsesWagonOverride(u) && !HasBit(u->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
170 /* wagon is powered */
171 u->flags.Set(VehicleRailFlag::PoweredWagon); // cache 'powered' status
172 } else {
173 u->flags.Reset(VehicleRailFlag::PoweredWagon);
174 }
175
176 if (!u->IsArticulatedPart()) {
177 /* Do not count powered wagons for the compatible railtypes, as wagons always
178 have railtype normal */
179 if (rvi_u->power > 0) {
180 this->compatible_railtypes.Set(GetAllPoweredRailTypes(u->railtypes));
181 }
182
183 /* Some electric engines can be allowed to run on normal rail. It happens to all
184 * existing electric engines when elrails are disabled and then re-enabled */
185 if (u->flags.Test(VehicleRailFlag::AllowedOnNormalRail)) {
186 u->railtypes.Set(RAILTYPE_RAIL);
187 u->compatible_railtypes.Set(RAILTYPE_RAIL);
188 }
189
190 /* max speed is the minimum of the speed limits of all vehicles in the consist */
191 if ((rvi_u->railveh_type != RailVehicleType::Wagon || _settings_game.vehicle.wagon_speed_limits) && !UsesWagonOverride(u)) {
192 uint16_t speed = GetVehicleProperty(u, PROP_TRAIN_SPEED, rvi_u->max_speed);
193 if (speed != 0) max_speed = std::min(speed, max_speed);
194 }
195 }
196
197 uint16_t new_cap = e_u->DetermineCapacity(u);
198 if (allowed_changes.Test(ConsistChangeFlag::Capacity)) {
199 /* Update vehicle capacity. */
200 if (u->cargo.TotalCount() > new_cap) u->cargo.Truncate(u->cargo.TotalCount() - new_cap);
201 u->refit_cap = std::min(new_cap, u->refit_cap);
202 u->cargo_cap = new_cap;
203 } else {
204 /* Verify capacity hasn't changed. */
205 if (new_cap != u->cargo_cap) ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_CAPACITY, GRFBug::VehCapacity, true);
206 }
207 u->vcache.cached_cargo_age_period = GetVehicleProperty(u, PROP_TRAIN_CARGO_AGE_PERIOD, e_u->info.cargo_age_period);
208
209 /* check the vehicle length (callback) */
210 uint16_t veh_len = CALLBACK_FAILED;
211 if (e_u->GetGRF() != nullptr && e_u->GetGRF()->grf_version >= 8) {
212 /* Use callback 36 */
213 veh_len = GetVehicleProperty(u, PROP_TRAIN_SHORTEN_FACTOR, CALLBACK_FAILED);
214
215 if (veh_len != CALLBACK_FAILED && veh_len >= VEHICLE_LENGTH) {
217 }
218 } else if (e_u->info.callback_mask.Test(VehicleCallbackMask::Length)) {
219 /* Use callback 11 */
220 veh_len = GetVehicleCallback(CBID_VEHICLE_LENGTH, 0, 0, u->engine_type, u);
221 }
222 if (veh_len == CALLBACK_FAILED) veh_len = rvi_u->shorten_factor;
223 veh_len = VEHICLE_LENGTH - Clamp(veh_len, 0, VEHICLE_LENGTH - 1);
224
225 if (allowed_changes.Test(ConsistChangeFlag::Length)) {
226 /* Update vehicle length. */
227 u->gcache.cached_veh_length = veh_len;
228 } else {
229 /* Verify length hasn't changed. */
230 if (veh_len != u->gcache.cached_veh_length) VehicleLengthChanged(u);
231 }
232
233 this->gcache.cached_total_length += u->gcache.cached_veh_length;
234 this->InvalidateNewGRFCache();
235 u->InvalidateNewGRFCache();
236 }
237
238 /* store consist weight/max speed in cache */
239 this->vcache.cached_max_speed = max_speed;
240 this->tcache.cached_tilt = train_can_tilt;
241 this->tcache.cached_curve_speed_mod = min_curve_speed_mod;
242 this->tcache.cached_max_curve_speed = this->GetCurveSpeedLimit();
243
244 /* 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) */
245 this->CargoChanged();
246
247 if (this->IsFrontEngine()) {
248 this->UpdateAcceleration();
249 SetWindowDirty(WindowClass::VehicleDetails, this->index);
250 InvalidateWindowData(WindowClass::VehicleRefit, this->index, VIWD_CONSIST_CHANGED);
251 InvalidateWindowData(WindowClass::VehicleOrders, this->index, VIWD_CONSIST_CHANGED);
253
254 /* If the consist is changed while in a depot, the vehicle view window must be invalidated to update the availability of refitting. */
255 InvalidateWindowData(WindowClass::VehicleView, this->index, VIWD_CONSIST_CHANGED);
256 }
257}
258
269int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *moving_front, int *station_ahead, int *station_length)
270{
271 const Train *consist = moving_front->First();
272 const Station *st = Station::Get(station_id);
273 *station_ahead = st->GetPlatformLength(tile, DirToDiagDir(moving_front->GetMovingDirection())) * TILE_SIZE;
274 *station_length = st->GetPlatformLength(tile) * TILE_SIZE;
275
276 /* Default to the middle of the station for stations stops that are not in
277 * the order list like intermediate stations when non-stop is disabled */
279 if (consist->gcache.cached_total_length >= *station_length) {
280 /* The train is longer than the station, make it stop at the far end of the platform */
282 } else if (consist->current_order.IsType(OT_GOTO_STATION) && consist->current_order.GetDestination() == station_id) {
283 osl = consist->current_order.GetStopLocation();
284 }
285
286 /* The stop location of the FRONT! of the train */
287 int stop;
288 switch (osl) {
289 default: NOT_REACHED();
290
292 stop = consist->gcache.cached_total_length;
293 break;
294
296 stop = *station_length - (*station_length - consist->gcache.cached_total_length) / 2;
297 break;
298
300 stop = *station_length;
301 break;
302 }
303
304 /* Subtract half the front vehicle length of the train so we get the real
305 * stop location of the train. */
306 uint8_t rounding = consist->IsDrivingBackwards() ? 2 : 1;
307 return stop - (consist->gcache.cached_veh_length + rounding) / 2;
308}
309
310
316{
317 assert(this->First() == this);
318
319 static const int absolute_max_speed = UINT16_MAX;
320 int max_speed = absolute_max_speed;
321
322 if (_settings_game.vehicle.train_acceleration_model == AccelerationModel::Original) return max_speed;
323
324 int curvecount[2] = {0, 0};
325
326 /* first find the curve speed limit */
327 int numcurve = 0;
328 int sum = 0;
329 int pos = 0;
330 int lastpos = -1;
331 for (const Train *u = this; u->Next() != nullptr; u = u->Next(), pos += u->gcache.cached_veh_length) {
332 Direction this_dir = u->direction;
333 Direction next_dir = u->Next()->direction;
334
335 DirDiff dirdiff = DirDifference(this_dir, next_dir);
336 if (dirdiff == DirDiff::Same) continue;
337
338 if (dirdiff == DirDiff::Left45) curvecount[0]++;
339 if (dirdiff == DirDiff::Right45) curvecount[1]++;
340 if (dirdiff == DirDiff::Left45 || dirdiff == DirDiff::Right45) {
341 if (lastpos != -1) {
342 numcurve++;
343 sum += pos - lastpos;
344 if (pos - lastpos <= static_cast<int>(VEHICLE_LENGTH) && max_speed > 88) {
345 max_speed = 88;
346 }
347 }
348 lastpos = pos;
349 }
350
351 /* if we have a 90 degree turn, fix the speed limit to 60 */
352 if (dirdiff == DirDiff::Left90 || dirdiff == DirDiff::Right90) {
353 max_speed = 61;
354 }
355 }
356
357 if (numcurve > 0 && max_speed > 88) {
358 if (curvecount[0] == 1 && curvecount[1] == 1) {
359 max_speed = absolute_max_speed;
360 } else {
361 sum = CeilDiv(sum, VEHICLE_LENGTH);
362 sum /= numcurve;
363 max_speed = 232 - (13 - Clamp(sum, 1, 12)) * (13 - Clamp(sum, 1, 12));
364 }
365 }
366
367 if (max_speed != absolute_max_speed) {
368 /* Apply the current railtype's curve speed advantage */
369 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(this->tile));
370 max_speed += (max_speed / 2) * rti->curve_speed;
371
372 if (this->tcache.cached_tilt) {
373 /* Apply max_speed bonus of 20% for a tilting train */
374 max_speed += max_speed / 5;
375 }
376
377 /* Apply max_speed modifier (cached value is fixed-point binary with 8 fractional bits)
378 * and clamp the result to an acceptable range. */
379 max_speed += (max_speed * this->tcache.cached_curve_speed_mod) / 256;
380 max_speed = Clamp(max_speed, 2, absolute_max_speed);
381 }
382
383 return static_cast<uint16_t>(max_speed);
384}
385
391{
392 const Train *moving_front = this->GetMovingFront();
393 int max_speed = _settings_game.vehicle.train_acceleration_model == AccelerationModel::Original ?
394 this->gcache.cached_max_track_speed :
395 this->tcache.cached_max_curve_speed;
396
397 if (_settings_game.vehicle.train_acceleration_model == AccelerationModel::Realistic && IsRailStationTile(moving_front->tile)) {
398 StationID sid = GetStationIndex(moving_front->tile);
399 if (this->current_order.ShouldStopAtStation(this, sid)) {
400 int station_ahead;
401 int station_length;
402 int stop_at = GetTrainStopLocation(sid, moving_front->tile, moving_front, &station_ahead, &station_length);
403
404 /* The distance to go is whatever is still ahead of the train minus the
405 * distance from the train's stop location to the end of the platform */
406 int distance_to_go = station_ahead / TILE_SIZE - (station_length - stop_at) / TILE_SIZE;
407
408 if (distance_to_go > 0) {
409 int st_max_speed = 120;
410
411 int delta_v = this->cur_speed / (distance_to_go + 1);
412 if (max_speed > (this->cur_speed - delta_v)) {
413 st_max_speed = this->cur_speed - (delta_v / 10);
414 }
415
416 st_max_speed = std::max(st_max_speed, 25 * distance_to_go);
417 max_speed = std::min(max_speed, st_max_speed);
418 }
419 }
420 }
421
422 for (const Train *u = this; u != nullptr; u = u->Next()) {
423 if (_settings_game.vehicle.train_acceleration_model == AccelerationModel::Realistic && u->track == Track::Depot) {
424 constexpr int DEPOT_SPEED_LIMIT = 61;
425 max_speed = std::min(max_speed, DEPOT_SPEED_LIMIT);
426 break;
427 }
428
429 /* Vehicle is on the middle part of a bridge. */
430 if (u->track == Track::Wormhole && !u->vehstatus.Test(VehState::Hidden)) {
431 max_speed = std::min<int>(max_speed, GetBridgeSpec(GetBridgeType(u->tile))->speed);
432 }
433 }
434
435 max_speed = std::min<int>(max_speed, this->current_order.GetMaxSpeed());
436
437 /* If the train is going backwards, without a leading cab, restrict its speed. */
438 if (!moving_front->CanLeadTrain()) {
439 constexpr int BACKWARDS_NO_CAB_SPEED_LIMIT = 32;
440 max_speed = std::min<int>(max_speed, BACKWARDS_NO_CAB_SPEED_LIMIT);
441 }
442
443 return std::min<int>(max_speed, this->gcache.cached_max_track_speed);
444}
445
448{
449 assert(this->IsFrontEngine() || this->IsFreeWagon());
450
451 uint power = this->gcache.cached_power;
452 uint weight = this->gcache.cached_weight;
453 assert(weight != 0);
454 this->acceleration = Clamp(power / weight * 4, 1, 255);
455}
456
462{
463 if (this->gcache.cached_veh_length != 8 && this->flags.Test(VehicleRailFlag::Flipped) && !EngInfo(this->engine_type)->misc_flags.Test(EngineMiscFlag::RailFlips)) {
464 int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
465
466 const Engine *e = this->GetEngine();
467 if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->VehInfo<RailVehicleInfo>().image_index)) {
468 reference_width = e->GetGRF()->traininfo_vehicle_width;
469 }
470
471 return ScaleSpriteTrad((this->gcache.cached_veh_length - (int)VEHICLE_LENGTH) * reference_width / (int)VEHICLE_LENGTH);
472 }
473 return 0;
474}
475
482{
483 int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
484 int vehicle_pitch = 0;
485
486 const Engine *e = this->GetEngine();
487 if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->VehInfo<RailVehicleInfo>().image_index)) {
488 reference_width = e->GetGRF()->traininfo_vehicle_width;
489 vehicle_pitch = e->GetGRF()->traininfo_vehicle_pitch;
490 }
491
492 if (offset != nullptr) {
493 if (this->flags.Test(VehicleRailFlag::Flipped) && !EngInfo(this->engine_type)->misc_flags.Test(EngineMiscFlag::RailFlips)) {
494 offset->x = ScaleSpriteTrad(((int)this->gcache.cached_veh_length - (int)VEHICLE_LENGTH / 2) * reference_width / (int)VEHICLE_LENGTH);
495 } else {
496 offset->x = ScaleSpriteTrad(reference_width) / 2;
497 }
498 offset->y = ScaleSpriteTrad(vehicle_pitch);
499 }
500 return ScaleSpriteTrad(this->gcache.cached_veh_length * reference_width / VEHICLE_LENGTH);
501}
502
503static SpriteID GetDefaultTrainSprite(uint8_t spritenum, Direction direction)
504{
505 assert(IsValidImageIndex<VehicleType::Train>(spritenum));
506 return ((to_underlying(direction) + _engine_sprite_add[spritenum]) & _engine_sprite_and[spritenum]) + _engine_sprite_base[spritenum];
507}
508
516{
517 uint8_t spritenum = this->spritenum;
518
520
521 if (IsCustomVehicleSpriteNum(spritenum)) {
523 GetCustomVehicleSprite(this, direction, image_type, result);
524 if (result->IsValid()) return;
525
527 }
528
530 SpriteID sprite = GetDefaultTrainSprite(spritenum, direction);
531
532 if (this->cargo.StoredCount() >= this->cargo_cap / 2U) sprite += _wagon_full_adder[spritenum];
533
534 result->Set(sprite);
535}
536
537static void GetRailIcon(EngineID engine, bool rear_head, int &y, EngineImageType image_type, VehicleSpriteSeq *result)
538{
539 const Engine *e = Engine::Get(engine);
540 Direction dir = rear_head ? Direction::E : Direction::W;
541 uint8_t spritenum = e->VehInfo<RailVehicleInfo>().image_index;
542
543 if (IsCustomVehicleSpriteNum(spritenum)) {
544 GetCustomVehicleIcon(engine, dir, image_type, result);
545 if (result->IsValid()) {
546 if (e->GetGRF() != nullptr) {
548 }
549 return;
550 }
551
552 spritenum = Engine::Get(engine)->original_image_index;
553 }
554
555 if (rear_head) spritenum++;
556
557 result->Set(GetDefaultTrainSprite(spritenum, Direction::W));
558}
559
560void DrawTrainEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
561{
562 const GRFFile *grf = Engine::Get(engine)->GetGRF();
563 int vehicle_width = ScaleSpriteTrad(grf == nullptr ? TRAININFO_DEFAULT_VEHICLE_WIDTH : grf->traininfo_vehicle_width);
564
565 if (RailVehInfo(engine)->railveh_type == RailVehicleType::Multihead) {
566 int yf = y;
567 int yr = y;
568
569 VehicleSpriteSeq seqf, seqr;
570 GetRailIcon(engine, false, yf, image_type, &seqf);
571 GetRailIcon(engine, true, yr, image_type, &seqr);
572
573 Rect rectf, rectr;
574 seqf.GetBounds(&rectf);
575 seqr.GetBounds(&rectr);
576
577 preferred_x = Clamp(preferred_x,
578 left - UnScaleGUI(rectf.left) + vehicle_width / 2,
579 right - UnScaleGUI(rectr.right) - (vehicle_width - vehicle_width / 2));
580
581 seqf.Draw(preferred_x - vehicle_width / 2, yf, pal, pal == PALETTE_CRASH);
582 seqr.Draw(preferred_x + (vehicle_width - vehicle_width / 2), yr, pal, pal == PALETTE_CRASH);
583 } else {
585 GetRailIcon(engine, false, y, image_type, &seq);
586
587 Rect rect;
588 seq.GetBounds(&rect);
589 preferred_x = Clamp(preferred_x,
590 left - UnScaleGUI(rect.left),
591 right - UnScaleGUI(rect.right));
592
593 seq.Draw(preferred_x, y, pal, pal == PALETTE_CRASH);
594 }
595}
596
606void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type)
607{
608 int y = 0;
609
611 GetRailIcon(engine, false, y, image_type, &seq);
612
613 Rect rect;
614 seq.GetBounds(&rect);
615
616 width = UnScaleGUI(rect.Width());
617 height = UnScaleGUI(rect.Height());
618 xoffs = UnScaleGUI(rect.left);
619 yoffs = UnScaleGUI(rect.top);
620
621 if (RailVehInfo(engine)->railveh_type == RailVehicleType::Multihead) {
622 const GRFFile *grf = Engine::Get(engine)->GetGRF();
623 int vehicle_width = ScaleSpriteTrad(grf == nullptr ? TRAININFO_DEFAULT_VEHICLE_WIDTH : grf->traininfo_vehicle_width);
624
625 GetRailIcon(engine, true, y, image_type, &seq);
626 seq.GetBounds(&rect);
627
628 /* Calculate values relative to an imaginary center between the two sprites. */
629 width = vehicle_width + UnScaleGUI(rect.right) - xoffs;
630 height = std::max<uint>(height, UnScaleGUI(rect.Height()));
631 xoffs = xoffs - vehicle_width / 2;
632 yoffs = std::min(yoffs, UnScaleGUI(rect.top));
633 }
634}
635
641static std::vector<VehicleID> GetFreeWagonsInDepot(TileIndex tile)
642{
643 std::vector<VehicleID> free_wagons;
644
645 for (Vehicle *v : VehiclesOnTile(tile)) {
646 if (v->type != VehicleType::Train) continue;
647 if (v->vehstatus.Test(VehState::Crashed)) continue;
648 if (!Train::From(v)->IsFreeWagon()) continue;
649
650 free_wagons.push_back(v->index);
651 }
652
653 /* Sort by vehicle index for consistency across clients. */
654 std::ranges::sort(free_wagons);
655 return free_wagons;
656}
657
667{
668 const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
669
670 /* Check that the wagon can drive on the track in question */
671 if (!IsCompatibleRail(rvi->railtypes, GetRailType(tile))) return CMD_ERROR;
672
673 if (flags.Test(DoCommandFlag::Execute)) {
674 Train *v = Train::Create();
675 *ret = v;
676 v->spritenum = rvi->image_index;
677
678 v->engine_type = e->index;
679 v->gcache.first_engine = EngineID::Invalid(); // needs to be set before first callback
680
682
683 v->direction = DiagDirToDir(dir);
684 v->tile = tile;
685
686 int x = TileX(tile) * TILE_SIZE | _vehicle_initial_x_fract[dir];
687 int y = TileY(tile) * TILE_SIZE | _vehicle_initial_y_fract[dir];
688
689 v->x_pos = x;
690 v->y_pos = y;
691 v->z_pos = GetSlopePixelZ(x, y, true);
693 v->track = Track::Depot;
695
696 v->SetWagon();
697
698 v->SetFreeWagon();
699 InvalidateWindowData(WindowClass::VehicleDepot, v->tile);
700
702 assert(IsValidCargoType(v->cargo_type));
703 v->cargo_cap = rvi->capacity;
704 v->refit_cap = 0;
705
706 v->railtypes = rvi->railtypes;
708
713 v->random_bits = Random();
714
716
718 if (prob.has_value()) v->flags.Set(VehicleRailFlag::Flipped, prob.value());
720
721 v->UpdatePosition();
724
726
727 /* Try to connect the vehicle to one of free chains of wagons. */
728 for (VehicleID vehicle : GetFreeWagonsInDepot(tile)) {
729 if (vehicle == v->index) continue;
730
731 const Train *w = Train::Get(vehicle);
732 if (w->engine_type != v->engine_type) continue;
733 if (w->First() == v) continue;
734
735 if (Command<Commands::MoveRailVehicle>::Do(DoCommandFlag::Execute, v->index, w->Last()->index, true).Succeeded()) {
736 break;
737 }
738 }
739 }
740
741 return CommandCost();
742}
743
749{
750 assert(u->IsEngine());
751 for (VehicleID vehicle : GetFreeWagonsInDepot(u->tile)) {
752 if (Command<Commands::MoveRailVehicle>::Do(DoCommandFlag::Execute, vehicle, u->index, true).Failed()) {
753 break;
754 }
755 }
756}
757
758static void AddRearEngineToMultiheadedTrain(Train *v)
759{
760 Train *u = Train::Create();
761 v->value >>= 1;
762 u->value = v->value;
763 u->direction = v->direction;
764 u->owner = v->owner;
765 u->tile = v->tile;
766 u->x_pos = v->x_pos;
767 u->y_pos = v->y_pos;
768 u->z_pos = v->z_pos;
769 u->track = Track::Depot;
770 u->vehstatus = v->vehstatus;
772 u->spritenum = v->spritenum + 1;
773 u->cargo_type = v->cargo_type;
775 u->cargo_cap = v->cargo_cap;
776 u->refit_cap = v->refit_cap;
777 u->railtypes = v->railtypes;
778 u->flags.Set(VehicleRailFlag::AllowedOnNormalRail, _settings_game.vehicle.disable_elrails && RailVehInfo(v->engine_type)->intended_railtypes.Test(RAILTYPE_ELECTRIC));
779 u->engine_type = v->engine_type;
782 u->build_year = v->build_year;
784 u->random_bits = Random();
785 v->SetMultiheaded();
786 u->SetMultiheaded();
787 v->SetNext(u);
789 if (prob.has_value()) u->flags.Set(VehicleRailFlag::Flipped, prob.value());
790 u->UpdatePosition();
791
792 /* Now we need to link the front and rear engines together */
795}
796
806{
807 const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
808
809 if (rvi->railveh_type == RailVehicleType::Wagon) return CmdBuildRailWagon(flags, tile, e, ret);
810
811 /* Check if depot and new engine uses the same kind of tracks *
812 * We need to see if the engine got power on the tile to avoid electric engines in non-electric depots */
813 if (!HasPowerOnRail(rvi->railtypes, GetRailType(tile))) return CMD_ERROR;
814
815 if (flags.Test(DoCommandFlag::Execute)) {
817 int x = TileX(tile) * TILE_SIZE + _vehicle_initial_x_fract[dir];
818 int y = TileY(tile) * TILE_SIZE + _vehicle_initial_y_fract[dir];
819
820 Train *v = Train::Create();
821 *ret = v;
822 v->direction = DiagDirToDir(dir);
823 v->tile = tile;
825 v->x_pos = x;
826 v->y_pos = y;
827 v->z_pos = GetSlopePixelZ(x, y, true);
828 v->track = Track::Depot;
830 v->spritenum = rvi->image_index;
832 assert(IsValidCargoType(v->cargo_type));
833 v->cargo_cap = rvi->capacity;
834 v->refit_cap = 0;
835 v->last_station_visited = StationID::Invalid();
836 v->last_loading_station = StationID::Invalid();
837
838 v->engine_type = e->index;
839 v->gcache.first_engine = EngineID::Invalid(); // needs to be set before first callback
840
841 v->reliability = e->reliability;
844
845 v->railtypes = rvi->railtypes;
847
848 v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_trains);
853 v->random_bits = Random();
854
856 v->SetServiceIntervalIsPercent(Company::Get(_current_company)->settings.vehicle.servint_ispercent);
857
859
860 v->SetFrontEngine();
861 v->SetEngine();
862
864 if (prob.has_value()) v->flags.Set(VehicleRailFlag::Flipped, prob.value());
865 v->UpdatePosition();
866
868 AddRearEngineToMultiheadedTrain(v);
869 } else {
871 }
872
875
877 }
878
879 return CommandCost();
880}
881
882static Train *FindGoodVehiclePos(const Train *src)
883{
884 EngineID eng = src->engine_type;
885
886 for (VehicleID vehicle : GetFreeWagonsInDepot(src->tile)) {
887 Train *dst = Train::Get(vehicle);
888
889 /* check so all vehicles in the line have the same engine. */
890 Train *t = dst;
891 while (t->engine_type == eng) {
892 t = t->Next();
893 if (t == nullptr) return dst;
894 }
895 }
896
897 return nullptr;
898}
899
901typedef std::vector<Train *> TrainList;
902
908static void MakeTrainBackup(TrainList &list, Train *t)
909{
910 for (; t != nullptr; t = t->Next()) list.push_back(t);
911}
912
918{
919 /* No train, nothing to do. */
920 if (list.empty()) return;
921
922 Train *prev = nullptr;
923 /* Iterate over the list and rebuild it. */
924 for (Train *t : list) {
925 if (prev != nullptr) {
926 prev->SetNext(t);
927 } else if (t->Previous() != nullptr) {
928 /* Make sure the head of the train is always the first in the chain. */
929 t->Previous()->SetNext(nullptr);
930 }
931 prev = t;
932 }
933}
934
940static void RemoveFromConsist(Train *part, bool chain = false)
941{
942 Train *tail;
943
944 if (chain) {
945 /* We're moving several vehicles, find the last one in the chain. */
946 tail = part;
947 while (tail->Next() != nullptr) tail = tail->Next();
948 } else {
949 /* We're just moving one vehicle, but make sure we get all the articulated parts. */
950 tail = part->GetLastEnginePart();
951 }
952
953 /* Unlink at the front, but make it point to the next
954 * vehicle after the to be remove part. */
955 if (part->Previous() != nullptr) part->Previous()->SetNext(tail->Next());
956
957 /* Unlink at the back */
958 tail->SetNext(nullptr);
959}
960
966static void InsertInConsist(Train *dst, Train *chain)
967{
968 /* We do not want to add something in the middle of an articulated part. */
969 assert(dst != nullptr && (dst->Next() == nullptr || !dst->Next()->IsArticulatedPart()));
970
971 chain->Last()->SetNext(dst->Next());
972 dst->SetNext(chain);
973}
974
981{
982 for (; t != nullptr; t = t->GetNextVehicle()) {
983 if (!t->IsMultiheaded() || !t->IsEngine()) continue;
984
985 /* Make sure that there are no free cars before next engine */
986 Train *u;
987 for (u = t; u->Next() != nullptr && !u->Next()->IsEngine(); u = u->Next()) {}
988
989 if (u == t->other_multiheaded_part) continue;
990
991 /* Remove the part from the 'wrong' train */
993 /* And add it to the 'right' train */
995 }
996}
997
1002static void NormaliseSubtypes(Train *chain)
1003{
1004 /* Nothing to do */
1005 if (chain == nullptr) return;
1006
1007 /* We must be the first in the chain. */
1008 assert(chain->Previous() == nullptr);
1009
1010 /* Set the appropriate bits for the first in the chain. */
1011 if (chain->IsWagon()) {
1012 chain->SetFreeWagon();
1013 } else {
1014 assert(chain->IsEngine());
1015 chain->SetFrontEngine();
1016 }
1017
1018 /* Now clear the bits for the rest of the chain */
1019 for (Train *t = chain->Next(); t != nullptr; t = t->Next()) {
1020 t->ClearFreeWagon();
1021 t->ClearFrontEngine();
1022 }
1023}
1024
1034static CommandCost CheckNewTrain(Train *original_dst, Train *dst, Train *original_src, Train *src)
1035{
1036 /* Just add 'new' engines and subtract the original ones.
1037 * If that's less than or equal to 0 we can be sure we did
1038 * not add any engines (read: trains) along the way. */
1039 if ((src != nullptr && src->IsEngine() ? 1 : 0) +
1040 (dst != nullptr && dst->IsEngine() ? 1 : 0) -
1041 (original_src != nullptr && original_src->IsEngine() ? 1 : 0) -
1042 (original_dst != nullptr && original_dst->IsEngine() ? 1 : 0) <= 0) {
1043 return CommandCost();
1044 }
1045
1046 /* Get a free unit number and check whether it's within the bounds.
1047 * There will always be a maximum of one new train. */
1048 if (GetFreeUnitNumber(VehicleType::Train) <= _settings_game.vehicle.max_trains) return CommandCost();
1049
1050 return CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);
1051}
1052
1059{
1060 /* No multi-part train, no need to check. */
1061 if (t == nullptr || t->Next() == nullptr) return CommandCost();
1062
1063 /* The maximum length for a train. For each part we decrease this by one
1064 * and if the result is negative the train is simply too long. */
1065 int allowed_len = _settings_game.vehicle.max_train_length * TILE_SIZE - t->gcache.cached_veh_length;
1066
1067 /* For free-wagon chains, check if they are within the max_train_length limit. */
1068 if (!t->IsEngine()) {
1069 t = t->Next();
1070 while (t != nullptr) {
1071 allowed_len -= t->gcache.cached_veh_length;
1072
1073 t = t->Next();
1074 }
1075
1076 if (allowed_len < 0) return CommandCost(STR_ERROR_TRAIN_TOO_LONG);
1077 return CommandCost();
1078 }
1079
1080 Train *head = t;
1081 Train *prev = t;
1082
1083 /* Break the prev -> t link so it always holds within the loop. */
1084 t = t->Next();
1085 prev->SetNext(nullptr);
1086
1087 /* Make sure the cache is cleared. */
1088 head->InvalidateNewGRFCache();
1089
1090 while (t != nullptr) {
1091 allowed_len -= t->gcache.cached_veh_length;
1092
1093 Train *next = t->Next();
1094
1095 /* Unlink the to-be-added piece; it is already unlinked from the previous
1096 * part due to the fact that the prev -> t link is broken. */
1097 t->SetNext(nullptr);
1098
1099 /* Don't check callback for articulated or rear dual headed parts */
1100 if (!t->IsArticulatedPart() && !t->IsRearDualheaded()) {
1101 /* Back up and clear the first_engine data to avoid using wagon override group */
1102 EngineID first_engine = t->gcache.first_engine;
1103 t->gcache.first_engine = EngineID::Invalid();
1104
1105 /* We don't want the cache to interfere. head's cache is cleared before
1106 * the loop and after each callback does not need to be cleared here. */
1108
1109 std::array<int32_t, 1> regs100;
1110 uint16_t callback = GetVehicleCallbackParent(CBID_TRAIN_ALLOW_WAGON_ATTACH, 0, 0, head->engine_type, t, head, regs100);
1111
1112 /* Restore original first_engine data */
1113 t->gcache.first_engine = first_engine;
1114
1115 /* We do not want to remember any cached variables from the test run */
1117 head->InvalidateNewGRFCache();
1118
1119 if (callback != CALLBACK_FAILED) {
1120 /* A failing callback means everything is okay */
1121 StringID error = STR_NULL;
1122
1123 if (head->GetGRF()->grf_version < 8) {
1124 if (callback == 0xFD) error = STR_ERROR_INCOMPATIBLE_RAIL_TYPES;
1125 if (callback < 0xFD) error = GetGRFStringID(head->GetGRFID(), GRFSTR_MISC_GRF_TEXT + callback);
1126 if (callback >= 0x100) ErrorUnknownCallbackResult(head->GetGRFID(), CBID_TRAIN_ALLOW_WAGON_ATTACH, callback);
1127 } else {
1128 if (callback < 0x400) {
1129 error = GetGRFStringID(head->GetGRFID(), GRFSTR_MISC_GRF_TEXT + callback);
1130 } else {
1131 switch (callback) {
1132 case 0x400: // allow if railtypes match (always the case for OpenTTD)
1133 case 0x401: // allow
1134 break;
1135
1136 case 0x40F:
1137 error = GetGRFStringID(head->GetGRFID(), static_cast<GRFStringID>(regs100[0]));
1138 break;
1139
1140 default: // unknown reason -> disallow
1141 case 0x402: // disallow attaching
1142 error = STR_ERROR_INCOMPATIBLE_RAIL_TYPES;
1143 break;
1144 }
1145 }
1146 }
1147
1148 if (error != STR_NULL) return CommandCost(error);
1149 }
1150 }
1151
1152 /* And link it to the new part. */
1153 prev->SetNext(t);
1154 prev = t;
1155 t = next;
1156 }
1157
1158 if (allowed_len < 0) return CommandCost(STR_ERROR_TRAIN_TOO_LONG);
1159 return CommandCost();
1160}
1161
1172static CommandCost ValidateTrains(Train *original_dst, Train *dst, Train *original_src, Train *src, bool check_limit)
1173{
1174 /* Check whether we may actually construct the trains. */
1176 if (ret.Failed()) return ret;
1177 ret = CheckTrainAttachment(dst);
1178 if (ret.Failed()) return ret;
1179
1180 /* Check whether we need to build a new train. */
1181 return check_limit ? CheckNewTrain(original_dst, dst, original_src, src) : CommandCost();
1182}
1183
1192static void ArrangeTrains(Train **dst_head, Train *dst, Train **src_head, Train *src, bool move_chain)
1193{
1194 /* First determine the front of the two resulting trains */
1195 if (*src_head == *dst_head) {
1196 /* If we aren't moving part(s) to a new train, we are just moving the
1197 * front back and there is not destination head. */
1198 *dst_head = nullptr;
1199 } else if (*dst_head == nullptr) {
1200 /* If we are moving to a new train the head of the move train would become
1201 * the head of the new vehicle. */
1202 *dst_head = src;
1203 }
1204
1205 if (src == *src_head) {
1206 /* If we are moving the front of a train then we are, in effect, creating
1207 * a new head for the train. Point to that. Unless we are moving the whole
1208 * train in which case there is not 'source' train anymore.
1209 * In case we are a multiheaded part we want the complete thing to come
1210 * with us, so src->GetNextUnit(), however... when we are e.g. a wagon
1211 * that is followed by a rear multihead we do not want to include that. */
1212 *src_head = move_chain ? nullptr :
1213 (src->IsMultiheaded() ? src->GetNextUnit() : src->GetNextVehicle());
1214 }
1215
1216 /* Now it's just simply removing the part that we are going to move from the
1217 * source train and *if* the destination is a not a new train add the chain
1218 * at the destination location. */
1219 RemoveFromConsist(src, move_chain);
1220 if (*dst_head != src) InsertInConsist(dst, src);
1221
1222 /* Now normalise the dual heads, that is move the dual heads around in such
1223 * a way that the head and rear of a dual head are in the same train */
1224 NormaliseDualHeads(*src_head);
1225 NormaliseDualHeads(*dst_head);
1226}
1227
1233static void NormaliseTrainHead(Train *head)
1234{
1235 /* Not much to do! */
1236 if (head == nullptr) return;
1237
1238 /* Tell the 'world' the train changed. */
1240 UpdateTrainGroupID(head);
1241
1242 /* Not a front engine, i.e. a free wagon chain. No need to do more. */
1243 if (!head->IsFrontEngine()) return;
1244
1245 /* Update the refit button and window */
1246 InvalidateWindowData(WindowClass::VehicleRefit, head->index, VIWD_CONSIST_CHANGED);
1247 SetWindowWidgetDirty(WindowClass::VehicleView, head->index, WID_VV_REFIT);
1248
1249 /* If we don't have a unit number yet, set one. */
1250 if (head->unitnumber != 0) return;
1251 head->unitnumber = Company::Get(head->owner)->freeunits[head->type].UseID(GetFreeUnitNumber(VehicleType::Train));
1252}
1253
1263CommandCost CmdMoveRailVehicle(DoCommandFlags flags, VehicleID src_veh, VehicleID dest_veh, bool move_chain)
1264{
1265 Train *src = Train::GetIfValid(src_veh);
1266 if (src == nullptr) return CMD_ERROR;
1267
1268 CommandCost ret = CheckOwnership(src->owner);
1269 if (ret.Failed()) return ret;
1270
1271 /* Do not allow moving crashed vehicles inside the depot, it is likely to cause asserts later */
1272 if (src->vehstatus.Test(VehState::Crashed)) return CMD_ERROR;
1273
1274 /* if nothing is selected as destination, try and find a matching vehicle to drag to. */
1275 Train *dst;
1276 if (dest_veh == VehicleID::Invalid()) {
1277 dst = (src->IsEngine() || flags.Test(DoCommandFlag::AutoReplace)) ? nullptr : FindGoodVehiclePos(src);
1278 } else {
1279 dst = Train::GetIfValid(dest_veh);
1280 if (dst == nullptr) return CMD_ERROR;
1281
1282 ret = CheckOwnership(dst->owner);
1283 if (ret.Failed()) return ret;
1284
1285 /* Do not allow appending to crashed vehicles, too */
1286 if (dst->vehstatus.Test(VehState::Crashed)) return CMD_ERROR;
1287 }
1288
1289 /* if an articulated part is being handled, deal with its parent vehicle */
1290 src = src->GetFirstEnginePart();
1291 if (dst != nullptr) {
1292 dst = dst->GetFirstEnginePart();
1293 }
1294
1295 /* don't move the same vehicle.. */
1296 if (src == dst) return CommandCost();
1297
1298 /* locate the head of the two chains */
1299 Train *src_head = src->First();
1300 Train *dst_head;
1301 if (dst != nullptr) {
1302 dst_head = dst->First();
1303 if (dst_head->tile != src_head->tile) return CMD_ERROR;
1304 /* Now deal with articulated part of destination wagon */
1305 dst = dst->GetLastEnginePart();
1306 } else {
1307 dst_head = nullptr;
1308 }
1309
1310 if (src->IsRearDualheaded()) return CommandCost(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT);
1311
1312 /* When moving all wagons, we can't have the same src_head and dst_head */
1313 if (move_chain && src_head == dst_head) return CommandCost();
1314
1315 /* When moving a multiheaded part to be place after itself, bail out. */
1316 if (!move_chain && dst != nullptr && dst->IsRearDualheaded() && src == dst->other_multiheaded_part) return CommandCost();
1317
1318 /* Check if all vehicles in the source train are stopped inside a depot. */
1319 if (!src_head->IsStoppedInDepot()) return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
1320
1321 /* Check if all vehicles in the destination train are stopped inside a depot. */
1322 if (dst_head != nullptr && !dst_head->IsStoppedInDepot()) return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
1323
1324 /* First make a backup of the order of the trains. That way we can do
1325 * whatever we want with the order and later on easily revert. */
1326 TrainList original_src;
1327 TrainList original_dst;
1328
1329 MakeTrainBackup(original_src, src_head);
1330 MakeTrainBackup(original_dst, dst_head);
1331
1332 /* Also make backup of the original heads as ArrangeTrains can change them.
1333 * For the destination head we do not care if it is the same as the source
1334 * head because in that case it's just a copy. */
1335 Train *original_src_head = src_head;
1336 Train *original_dst_head = (dst_head == src_head ? nullptr : dst_head);
1337
1338 /* We want this information from before the rearrangement, but execute this after the validation.
1339 * original_src_head can't be nullptr; src is by definition != nullptr, so src_head can't be nullptr as
1340 * src->GetFirst() always yields non-nullptr, so eventually original_src_head != nullptr as well. */
1341 bool original_src_head_front_engine = original_src_head->IsFrontEngine();
1342 bool original_dst_head_front_engine = original_dst_head != nullptr && original_dst_head->IsFrontEngine();
1343
1344 /* (Re)arrange the trains in the wanted arrangement. */
1345 ArrangeTrains(&dst_head, dst, &src_head, src, move_chain);
1346
1347 if (!flags.Test(DoCommandFlag::AutoReplace)) {
1348 /* If the autoreplace flag is set we do not need to test for the validity
1349 * because we are going to revert the train to its original state. As we
1350 * assume the original state was correct autoreplace can skip this. */
1351 ret = ValidateTrains(original_dst_head, dst_head, original_src_head, src_head, true);
1352 if (ret.Failed()) {
1353 /* Restore the train we had. */
1354 RestoreTrainBackup(original_src);
1355 RestoreTrainBackup(original_dst);
1356 return ret;
1357 }
1358 }
1359
1360 /* do it? */
1361 if (flags.Test(DoCommandFlag::Execute)) {
1362 /* Remove old heads from the statistics */
1363 if (original_src_head_front_engine) GroupStatistics::CountVehicle(original_src_head, -1);
1364 if (original_dst_head_front_engine) GroupStatistics::CountVehicle(original_dst_head, -1);
1365
1366 /* First normalise the sub types of the chains. */
1367 NormaliseSubtypes(src_head);
1368 NormaliseSubtypes(dst_head);
1369
1370 /* There are 14 different cases:
1371 * 1) front engine gets moved to a new train, it stays a front engine.
1372 * a) the 'next' part is a wagon that becomes a free wagon chain.
1373 * b) the 'next' part is an engine that becomes a front engine.
1374 * c) there is no 'next' part, nothing else happens
1375 * 2) front engine gets moved to another train, it is not a front engine anymore
1376 * a) the 'next' part is a wagon that becomes a free wagon chain.
1377 * b) the 'next' part is an engine that becomes a front engine.
1378 * c) there is no 'next' part, nothing else happens
1379 * 3) front engine gets moved to later in the current train, it is not a front engine anymore.
1380 * a) the 'next' part is a wagon that becomes a free wagon chain.
1381 * b) the 'next' part is an engine that becomes a front engine.
1382 * 4) free wagon gets moved
1383 * a) the 'next' part is a wagon that becomes a free wagon chain.
1384 * b) the 'next' part is an engine that becomes a front engine.
1385 * c) there is no 'next' part, nothing else happens
1386 * 5) non front engine gets moved and becomes a new train, nothing else happens
1387 * 6) non front engine gets moved within a train / to another train, nothing happens
1388 * 7) wagon gets moved, nothing happens
1389 */
1390 if (src == original_src_head && src->IsEngine() && !src->IsFrontEngine()) {
1391 /* Cases #2 and #3: the front engine gets trashed. */
1392 CloseWindowById(WindowClass::VehicleView, src->index);
1393 CloseWindowById(WindowClass::VehicleOrders, src->index);
1394 CloseWindowById(WindowClass::VehicleRefit, src->index);
1395 CloseWindowById(WindowClass::VehicleDetails, src->index);
1396 CloseWindowById(WindowClass::VehicleTimetable, src->index);
1398 SetWindowDirty(WindowClass::Company, _current_company);
1399
1400 if (src_head != nullptr && src_head->IsFrontEngine()) {
1401 /* Cases #?b: Transfer order, unit number and other stuff
1402 * to the new front engine. */
1403 src_head->orders = src->orders;
1404 if (src_head->orders != nullptr) src_head->AddToShared(src);
1405 src_head->CopyVehicleConfigAndStatistics(src);
1406 }
1407 /* Remove stuff not valid anymore for non-front engines. */
1409 src->ReleaseUnitNumber();
1410 src->name.clear();
1411 }
1412
1413 /* We weren't a front engine but are becoming one. So
1414 * we should be put in the default group. */
1415 if (original_src_head != src && dst_head == src) {
1417 SetWindowDirty(WindowClass::Company, _current_company);
1418 }
1419
1420 /* Handle 'new engine' part of cases #1b, #2b, #3b, #4b and #5 in NormaliseTrainHead. */
1421 NormaliseTrainHead(src_head);
1422 NormaliseTrainHead(dst_head);
1423
1424 /* Add new heads to statistics.
1425 * This should be done after NormaliseTrainHead due to engine total limit checks in GetFreeUnitNumber. */
1426 if (src_head != nullptr && src_head->IsFrontEngine()) GroupStatistics::CountVehicle(src_head, 1);
1427 if (dst_head != nullptr && dst_head->IsFrontEngine()) GroupStatistics::CountVehicle(dst_head, 1);
1428
1430 CheckCargoCapacity(src_head);
1431 CheckCargoCapacity(dst_head);
1432 }
1433
1434 if (src_head != nullptr) src_head->First()->MarkDirty();
1435 if (dst_head != nullptr) dst_head->First()->MarkDirty();
1436
1437 /* We are undoubtedly changing something in the depot and train list. */
1438 InvalidateWindowData(WindowClass::VehicleDepot, src->tile);
1439 InvalidateWindowClassesData(WindowClass::TrainList, 0);
1440 } else {
1441 /* We don't want to execute what we're just tried. */
1442 RestoreTrainBackup(original_src);
1443 RestoreTrainBackup(original_dst);
1444 }
1445
1446 return CommandCost();
1447}
1448
1461CommandCost CmdSellRailWagon(DoCommandFlags flags, Vehicle *t, bool sell_chain, bool backup_order, ClientID user)
1462{
1464 Train *first = v->First();
1465
1466 if (v->IsRearDualheaded()) return CommandCost(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT);
1467
1468 /* First make a backup of the order of the train. That way we can do
1469 * whatever we want with the order and later on easily revert. */
1470 TrainList original;
1471 MakeTrainBackup(original, first);
1472
1473 /* We need to keep track of the new head and the head of what we're going to sell. */
1474 Train *new_head = first;
1475 Train *sell_head = nullptr;
1476
1477 /* Split the train in the wanted way. */
1478 ArrangeTrains(&sell_head, nullptr, &new_head, v, sell_chain);
1479
1480 /* We don't need to validate the second train; it's going to be sold. */
1481 CommandCost ret = ValidateTrains(nullptr, nullptr, first, new_head, !flags.Test(DoCommandFlag::AutoReplace));
1482 if (ret.Failed()) {
1483 /* Restore the train we had. */
1484 RestoreTrainBackup(original);
1485 return ret;
1486 }
1487
1488 if (first->orders == nullptr && !OrderList::CanAllocateItem()) {
1489 /* Restore the train we had. */
1490 RestoreTrainBackup(original);
1491 return CommandCost(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS);
1492 }
1493
1495 for (Train *part = sell_head; part != nullptr; part = part->Next()) cost.AddCost(-part->value);
1496
1497 /* do it? */
1498 if (flags.Test(DoCommandFlag::Execute)) {
1499 /* First normalise the sub types of the chain. */
1500 NormaliseSubtypes(new_head);
1501
1502 if (v == first && !sell_chain && new_head != nullptr && new_head->IsFrontEngine()) {
1503 if (v->IsEngine()) {
1504 /* We are selling the front engine. In this case we want to
1505 * 'give' the order, unit number and such to the new head. */
1506 new_head->orders = first->orders;
1507 new_head->AddToShared(first);
1508 DeleteVehicleOrders(first);
1509
1510 /* Copy other important data from the front engine */
1511 new_head->CopyVehicleConfigAndStatistics(first);
1512 }
1513 GroupStatistics::CountVehicle(new_head, 1); // after copying over the profit, if required
1514 } else if (v->IsPrimaryVehicle() && backup_order) {
1515 OrderBackup::Backup(v, user);
1516 }
1517
1518 /* We need to update the information about the train. */
1519 NormaliseTrainHead(new_head);
1520
1521 /* We are undoubtedly changing something in the depot and train list. */
1522 InvalidateWindowData(WindowClass::VehicleDepot, v->tile);
1523 InvalidateWindowClassesData(WindowClass::TrainList, 0);
1524
1525 /* Actually delete the sold 'goods' */
1526 delete sell_head;
1527 } else {
1528 /* We don't want to execute what we're just tried. */
1529 RestoreTrainBackup(original);
1530 }
1531
1532 return cost;
1533}
1534
1536{
1537 /* Set common defaults. */
1538 this->bounds = {{-1, -1, 0}, {3, 3, 6}, {}};
1539
1540 /* Set if flipped and engine is NOT flagged with custom flip handling. */
1541 int flipped = this->flags.Test(VehicleRailFlag::Flipped) && !EngInfo(this->engine_type)->misc_flags.Test(EngineMiscFlag::RailFlips);
1542 /* If flipped and vehicle length is odd, we need to adjust the bounding box offset slightly. */
1543 int flip_offs = flipped && (this->gcache.cached_veh_length & 1);
1544
1545 Direction dir = this->direction;
1546 if (flipped) dir = ReverseDir(dir);
1547
1548 if (!IsDiagonalDirection(dir)) {
1549 static constexpr DiagDirectionIndexArray<Point> _sign_table{{{
1550 /* x, y */
1551 {-1, -1}, // DiagDirection::N
1552 {-1, 1}, // DiagDirection::E
1553 { 1, 1}, // DiagDirection::S
1554 { 1, -1}, // DiagDirection::W
1555 }}};
1556
1557 int half_shorten = (VEHICLE_LENGTH - this->gcache.cached_veh_length + flipped) / 2;
1558
1559 /* For all straight directions, move the bound box to the centre of the vehicle, but keep the size. */
1560 this->bounds.offset.x -= half_shorten * _sign_table[DirToDiagDir(dir)].x;
1561 this->bounds.offset.y -= half_shorten * _sign_table[DirToDiagDir(dir)].y;
1562 } else {
1563 switch (dir) {
1564 /* Shorten southern corner of the bounding box according the vehicle length
1565 * and center the bounding box on the vehicle. */
1566 case Direction::NE:
1567 this->bounds.origin.x = -(this->gcache.cached_veh_length + 1) / 2 + flip_offs;
1568 this->bounds.extent.x = this->gcache.cached_veh_length;
1569 this->bounds.offset.x = 1;
1570 break;
1571
1572 case Direction::NW:
1573 this->bounds.origin.y = -(this->gcache.cached_veh_length + 1) / 2 + flip_offs;
1574 this->bounds.extent.y = this->gcache.cached_veh_length;
1575 this->bounds.offset.y = 1;
1576 break;
1577
1578 /* Move northern corner of the bounding box down according to vehicle length
1579 * and center the bounding box on the vehicle. */
1580 case Direction::SW:
1581 this->bounds.origin.x = -(this->gcache.cached_veh_length) / 2 - flip_offs;
1582 this->bounds.extent.x = this->gcache.cached_veh_length;
1583 this->bounds.offset.x = 1 - (VEHICLE_LENGTH - this->gcache.cached_veh_length);
1584 break;
1585
1586 case Direction::SE:
1587 this->bounds.origin.y = -(this->gcache.cached_veh_length) / 2 - flip_offs;
1588 this->bounds.extent.y = this->gcache.cached_veh_length;
1589 this->bounds.offset.y = 1 - (VEHICLE_LENGTH - this->gcache.cached_veh_length);
1590 break;
1591
1592 default:
1593 NOT_REACHED();
1594 }
1595 }
1596}
1597
1602static void MarkTrainAsStuck(Train *consist)
1603{
1604 if (!consist->flags.Test(VehicleRailFlag::Stuck)) {
1605 /* It is the first time the problem occurred, set the "train stuck" flag. */
1607
1608 consist->wait_counter = 0;
1609
1610 /* Stop train */
1611 consist->cur_speed = 0;
1612 consist->subspeed = 0;
1613 consist->SetLastSpeed();
1614
1615 SetWindowWidgetDirty(WindowClass::VehicleView, consist->index, WID_VV_START_STOP);
1616 }
1617}
1618
1626static void SwapTrainFlags(GroundVehicleFlags *swap_flag1, GroundVehicleFlags *swap_flag2)
1627{
1628 GroundVehicleFlags flag1 = *swap_flag1;
1629 GroundVehicleFlags flag2 = *swap_flag2;
1630
1631 /* Reverse the rail-flags (if needed) */
1636}
1637
1643static void UpdateStatusAfterSwap(Train *v, bool reverse = true)
1644{
1645 /* Maybe reverse the direction. */
1646 if (reverse) v->direction = ReverseDir(v->direction);
1647
1648 /* Call the proper EnterTile function unless we are in a wormhole. */
1649 if (v->track != Track::Wormhole) {
1650 VehicleEnterTile(v, v->tile, v->x_pos, v->y_pos);
1651 } else {
1652 /* VehicleEnterTile_TunnelBridge() sets Track::Wormhole when the vehicle
1653 * is on the last bit of the bridge head (frame == TILE_SIZE - 1).
1654 * If we were swapped with such a vehicle, we have set Track::Wormhole,
1655 * when we shouldn't have. Check if this is the case. */
1656 TileIndex vt = TileVirtXY(v->x_pos, v->y_pos);
1658 VehicleEnterTile(v, vt, v->x_pos, v->y_pos);
1659 if (v->track != Track::Wormhole && IsBridgeTile(v->tile)) {
1660 /* We have just left the wormhole, possibly set the
1661 * "goingdown" bit. UpdateInclination() can be used
1662 * because we are at the border of the tile. */
1663 v->UpdatePosition();
1664 v->UpdateInclination(true, true);
1665 return;
1666 }
1667 }
1668 }
1669
1670 v->UpdatePosition();
1671 v->UpdateViewport(true, true);
1672}
1673
1681static void ReverseTrainSwapVeh(Train *v, int l, int r)
1682{
1683 Train *a, *b;
1684
1685 /* locate vehicles to swap */
1686 for (a = v; l != 0; l--) a = a->Next();
1687 for (b = v; r != 0; r--) b = b->Next();
1688
1689 if (a != b) {
1690 /* swap the hidden bits */
1691 {
1692 bool a_hidden = a->vehstatus.Test(VehState::Hidden);
1693 bool b_hidden = b->vehstatus.Test(VehState::Hidden);
1694 b->vehstatus.Set(VehState::Hidden, a_hidden);
1695 a->vehstatus.Set(VehState::Hidden, b_hidden);
1696 }
1697
1698 std::swap(a->track, b->track);
1699 std::swap(a->direction, b->direction);
1700 std::swap(a->x_pos, b->x_pos);
1701 std::swap(a->y_pos, b->y_pos);
1702 std::swap(a->tile, b->tile);
1703 std::swap(a->z_pos, b->z_pos);
1704
1706 } else {
1707 /* Swap GroundVehicleFlag::GoingUp/GroundVehicleFlag::GoingDown.
1708 * This is a little bit redundant way, a->gv_flags will
1709 * be (re)set twice, but it reduces code duplication */
1711 }
1712}
1713
1719{
1720 int r = CountVehiclesInChain(v) - 1; // number of vehicles - 1
1721
1722 /* swap start<>end, start+1<>end-1, ... */
1723 int l = 0;
1724 do {
1725 ReverseTrainSwapVeh(v, l++, r--);
1726 } while (l <= r);
1727
1728 for (Train *u = v; u != nullptr; u = u->Next()) {
1730 }
1731}
1732
1738static bool IsTrain(const Vehicle *v)
1739{
1740 return v->type == VehicleType::Train;
1741}
1742
1750{
1751 assert(IsLevelCrossingTile(tile));
1752
1753 return HasVehicleOnTile(tile, IsTrain);
1754}
1755
1763{
1764 if (v->type != VehicleType::Train || v->vehstatus.Test(VehState::Crashed)) return false;
1765
1766 const Train *t = Train::From(v);
1767 if (!t->IsMovingFront()) return false;
1768
1769 return TrainApproachingCrossingTile(t) == tile;
1770}
1771
1772
1780{
1781 assert(IsLevelCrossingTile(tile));
1782
1784 TileIndex tile_from = tile + TileOffsByDiagDir(dir);
1785
1786 if (HasVehicleOnTile(tile_from, [&](const Vehicle *v) {
1787 return TrainApproachingCrossingEnum(v, tile);
1788 })) return true;
1789
1790 dir = ReverseDiagDir(dir);
1791 tile_from = tile + TileOffsByDiagDir(dir);
1792
1793 return HasVehicleOnTile(tile_from, [&](const Vehicle *v) {
1794 return TrainApproachingCrossingEnum(v, tile);
1795 });
1796}
1797
1803static inline bool CheckLevelCrossing(TileIndex tile)
1804{
1805 /* reserved || train on crossing || train approaching crossing */
1807}
1808
1816static void UpdateLevelCrossingTile(TileIndex tile, bool sound, bool force_barred)
1817{
1818 assert(IsLevelCrossingTile(tile));
1819 bool set_barred;
1820
1821 /* We force the crossing to be barred when an adjacent crossing is barred, otherwise let it decide for itself. */
1822 set_barred = force_barred || CheckLevelCrossing(tile);
1823
1824 /* The state has changed */
1825 if (set_barred != IsCrossingBarred(tile)) {
1826 if (set_barred && sound && _settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, tile);
1827 SetCrossingBarred(tile, set_barred);
1828 MarkTileDirtyByTile(tile);
1829 }
1830}
1831
1838void UpdateLevelCrossing(TileIndex tile, bool sound, bool force_bar)
1839{
1840 if (!IsLevelCrossingTile(tile)) return;
1841
1842 bool forced_state = force_bar;
1843
1844 Axis axis = GetCrossingRoadAxis(tile);
1845 DiagDirections diagdirs = AxisToDiagDirs(axis);
1846
1847 /* Check if an adjacent crossing is barred. */
1848 for (DiagDirection dir : diagdirs) {
1849 for (TileIndex t = tile; !forced_state && t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, dir)) {
1850 forced_state |= CheckLevelCrossing(t);
1851 }
1852 }
1853
1854 /* Now that we know whether all tiles in this crossing should be barred or open,
1855 * we need to update those tiles. We start with the tile itself, then look along the road axis. */
1856 UpdateLevelCrossingTile(tile, sound, forced_state);
1857 for (DiagDirection dir : diagdirs) {
1858 for (TileIndex t = TileAddByDiagDir(tile, dir); t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, dir)) {
1859 UpdateLevelCrossingTile(t, sound, forced_state);
1860 }
1861 }
1862}
1863
1870{
1871 for (DiagDirection dir : AxisToDiagDirs(road_axis)) {
1872 const TileIndex t = TileAddByDiagDir(tile, dir);
1873 if (t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis) {
1875 }
1876 }
1877}
1878
1885{
1886 for (DiagDirection dir : AxisToDiagDirs(road_axis)) {
1887 const TileIndexDiff diff = TileOffsByDiagDir(dir);
1888 bool occupied = false;
1889 for (TileIndex t = tile + diff; t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis; t += diff) {
1890 occupied |= CheckLevelCrossing(t);
1891 }
1892 if (occupied) {
1893 /* Mark the immediately adjacent tile dirty */
1894 const TileIndex t = tile + diff;
1895 if (t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis) {
1897 }
1898 } else {
1899 /* Unbar the crossing tiles in this direction as necessary */
1900 for (TileIndex t = tile + diff; t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis; t += diff) {
1901 if (IsCrossingBarred(t)) {
1902 /* The crossing tile is barred, unbar it and continue to check the next tile */
1903 SetCrossingBarred(t, false);
1905 } else {
1906 /* The crossing tile is already unbarred, mark the tile dirty and stop checking */
1908 break;
1909 }
1910 }
1911 }
1912 }
1913}
1914
1920static inline void MaybeBarCrossingWithSound(TileIndex tile)
1921{
1922 if (!IsCrossingBarred(tile)) {
1923 SetCrossingReservation(tile, true);
1924 UpdateLevelCrossing(tile, true);
1925 }
1926}
1927
1928
1934static void AdvanceWagonsBeforeSwap(Train *moving_front)
1935{
1936 Train *base = moving_front;
1937 Train *first = base; // first vehicle to move
1938 Train *last = moving_front->GetMovingBack(); // last vehicle to move
1939 uint length = CountVehiclesInChain(moving_front->First());
1940
1941 while (length > 2) {
1942 last = last->GetMovingPrev();
1943 first = first->GetMovingNext();
1944
1945 int differential = base->CalcNextVehicleOffset() - last->CalcNextVehicleOffset();
1946
1947 /* do not update images now
1948 * negative differential will be handled in AdvanceWagonsAfterSwap() */
1949 for (int i = 0; i < differential; i++) TrainController(first, last->GetMovingNext());
1950
1951 base = first; // == base->GetMovingNext()
1952 length -= 2;
1953 }
1954}
1955
1956
1962static void AdvanceWagonsAfterSwap(Train *moving_front)
1963{
1964 /* first of all, fix the situation when the train was entering a depot */
1965 Train *dep = moving_front; // last vehicle in front of just left depot
1966 while (dep->GetMovingNext() != nullptr && (dep->track == Track::Depot || dep->GetMovingNext()->track != Track::Depot)) {
1967 dep = dep->GetMovingNext(); // find first vehicle outside of a depot, with next vehicle inside a depot
1968 }
1969
1970 Train *leave = dep->GetMovingNext(); // first vehicle in a depot we are leaving now
1971
1972 if (leave != nullptr) {
1973 /* 'pull' next wagon out of the depot, so we won't miss it (it could stay in depot forever) */
1974 int d = TicksToLeaveDepot(dep);
1975
1976 if (d <= 0) {
1977 leave->vehstatus.Reset(VehState::Hidden); // move it out of the depot
1978 leave->track = GetRailDepotTrack(leave->tile);
1979 for (int i = 0; i >= d; i--) TrainController(leave, nullptr); // maybe move it, and maybe let another wagon leave
1980 }
1981 } else {
1982 dep = nullptr; // no vehicle in a depot, so no vehicle leaving a depot
1983 }
1984
1985 Train *base = moving_front;
1986 Train *first = base; // first vehicle to move
1987 Train *last = moving_front->GetMovingBack(); // last vehicle to move
1988 uint length = CountVehiclesInChain(moving_front->First());
1989
1990 /* We have to make sure all wagons that leave a depot because of train reversing are moved correctly
1991 * they have already correct spacing, so we have to make sure they are moved how they should */
1992 bool nomove = (dep == nullptr); // If there is no vehicle leaving a depot, limit the number of wagons moved immediately.
1993
1994 while (length > 2) {
1995 /* we reached vehicle (originally) in front of a depot, stop now
1996 * (we would move wagons that are already moved with new wagon length). */
1997 if (base == dep) break;
1998
1999 /* the last wagon was that one leaving a depot, so do not move it anymore */
2000 if (last == dep) nomove = true;
2001
2002 last = last->GetMovingPrev();
2003 first = first->GetMovingNext();
2004
2005 int differential = last->CalcNextVehicleOffset() - base->CalcNextVehicleOffset();
2006
2007 /* do not update images now */
2008 for (int i = 0; i < differential; i++) TrainController(first, (nomove ? last->GetMovingNext() : nullptr));
2009
2010 base = first; // == base->GetMovingNext()
2011 length -= 2;
2012 }
2013}
2014
2015static bool IsWholeTrainInsideDepot(const Train *v)
2016{
2017 for (const Train *u = v; u != nullptr; u = u->Next()) {
2018 if (u->track != Track::Depot || u->tile != v->tile) return false;
2019 }
2020 return true;
2021}
2022
2027static void ReverseTrainDirection(Train *consist)
2028{
2029 Train *moving_front = consist->GetMovingFront();
2030 if (IsRailDepotTile(moving_front->tile)) {
2031 if (IsWholeTrainInsideDepot(consist)) return;
2032 InvalidateWindowData(WindowClass::VehicleDepot, moving_front->tile);
2033 }
2034
2035 /* Clear path reservation in front if train is not stuck. */
2037
2038 /* Check if we were approaching a rail/road-crossing */
2039 TileIndex crossing = TrainApproachingCrossingTile(moving_front);
2040
2041 /* Check if we should back up or flip the train. */
2042 if (consist->vehicle_flags.Test(VehicleFlag::DrivingBackwards) || _settings_game.difficulty.train_flip_reverse_allowed == TrainFlipReversingAllowed::None || consist->Last()->CanLeadTrain()) {
2043 /* The train will back up. */
2045
2046 for (Train *u = consist; u != nullptr; u = u->Next()) {
2047 /* Invert going up/down */
2048 if (u->gv_flags.Any({GroundVehicleFlag::GoingUp, GroundVehicleFlag::GoingDown})) {
2050 }
2051 UpdateStatusAfterSwap(u, false);
2052 }
2053 /* We may have entered a depot and stopped driving backwards. */
2054 moving_front = consist->GetMovingFront();
2055 } else {
2056 /* The train will flip. */
2057 AdvanceWagonsBeforeSwap(moving_front);
2058
2059 /* swap start<>end, start+1<>end-1, ... */
2060 ReverseTrainSwapVehicles(consist);
2061
2062 AdvanceWagonsAfterSwap(moving_front);
2063 }
2064
2065 if (IsRailDepotTile(moving_front->tile)) {
2066 InvalidateWindowData(WindowClass::VehicleDepot, moving_front->tile);
2067 }
2068
2071
2072 /* recalculate cached data */
2073 consist->ConsistChanged(CCF_TRACK);
2074
2075 /* update all images */
2076 for (Train *u = consist; u != nullptr; u = u->Next()) u->UpdateViewport(false, false);
2077
2078 /* update crossing we were approaching */
2079 if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing);
2080
2081 /* maybe we are approaching crossing now, after reversal */
2082 crossing = TrainApproachingCrossingTile(moving_front);
2083 if (crossing != INVALID_TILE) MaybeBarCrossingWithSound(crossing);
2084
2085 /* If we are inside a depot after reversing, don't bother with path reserving. */
2086 if (moving_front->track == Track::Depot) {
2087 /* Can't be stuck here as inside a depot is always a safe tile. */
2088 if (consist->flags.Test(VehicleRailFlag::Stuck)) SetWindowWidgetDirty(WindowClass::VehicleView, consist->index, WID_VV_START_STOP);
2090 return;
2091 }
2092
2093 /* VehicleExitDir does not always produce the desired dir for depots and
2094 * tunnels/bridges that is needed for UpdateSignalsOnSegment. */
2095 DiagDirection dir = VehicleExitDir(moving_front->GetMovingDirection(), moving_front->track);
2096 if (IsRailDepotTile(moving_front->tile) || IsTileType(moving_front->tile, TileType::TunnelBridge)) dir = DiagDirection::Invalid;
2097
2098 if (UpdateSignalsOnSegment(moving_front->tile, dir, consist->owner) == SigSegState::Path || _settings_game.pf.reserve_paths) {
2099 /* If we are currently on a tile with conventional signals, we can't treat the
2100 * current tile as a safe tile or we would enter a PBS block without a reservation. */
2101 bool first_tile_okay = !HasBlockSignalOnTrackdir(moving_front->tile, moving_front->GetVehicleTrackdir());
2102
2103 /* If we are on a depot tile facing outwards, do not treat the current tile as safe. */
2104 if (IsRailDepotTile(moving_front->tile) && TrackdirToExitdir(moving_front->GetVehicleTrackdir()) == GetRailDepotDirection(moving_front->tile)) first_tile_okay = false;
2105
2106 if (IsRailStationTile(moving_front->tile)) SetRailStationPlatformReservation(moving_front->tile, TrackdirToExitdir(moving_front->GetVehicleTrackdir()), true);
2107 if (TryPathReserve(consist, false, first_tile_okay)) {
2108 /* Do a look-ahead now in case our current tile was already a safe tile. */
2109 CheckNextTrainTile(consist);
2110 } else if (consist->current_order.GetType() != OT_LOADING) {
2111 /* Do not wait for a way out when we're still loading */
2112 MarkTrainAsStuck(consist);
2113 }
2114 } else if (consist->flags.Test(VehicleRailFlag::Stuck)) {
2115 /* A train not inside a PBS block can't be stuck. */
2117 consist->wait_counter = 0;
2118 }
2119}
2120
2128CommandCost CmdReverseTrainDirection(DoCommandFlags flags, VehicleID veh_id, bool reverse_single_veh)
2129{
2130 Train *v = Train::GetIfValid(veh_id);
2131 if (v == nullptr) return CMD_ERROR;
2132
2134 if (ret.Failed()) return ret;
2135
2136 if (reverse_single_veh) {
2137 /* turn a single unit around */
2138
2139 if (v->IsMultiheaded() || EngInfo(v->engine_type)->callback_mask.Test(VehicleCallbackMask::ArticEngine)) {
2140 return CommandCost(STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE_MULTIPLE_UNITS);
2141 }
2142
2143 Train *front = v->First();
2144 /* make sure the vehicle is stopped in the depot */
2145 if (!front->IsStoppedInDepot()) {
2146 return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
2147 }
2148
2149 if (flags.Test(DoCommandFlag::Execute)) {
2151
2153 SetWindowDirty(WindowClass::VehicleDepot, front->tile);
2154 SetWindowDirty(WindowClass::VehicleDetails, front->index);
2155 InvalidateWindowData(WindowClass::VehicleView, front->index);
2156 SetWindowClassesDirty(WindowClass::TrainList);
2157 }
2158 } else {
2159 /* turn the whole train around */
2160 if (!v->IsPrimaryVehicle()) return CMD_ERROR;
2161 if (v->vehstatus.Test(VehState::Crashed) || v->breakdown_ctr != 0) return CMD_ERROR;
2162
2163 if (flags.Test(DoCommandFlag::Execute)) {
2164 /* Properly leave the station if we are loading and won't be loading anymore */
2165 if (v->current_order.IsType(OT_LOADING)) {
2166 const Vehicle *moving_back = v->GetMovingBack();
2167
2168 /* not a station || different station --> leave the station */
2169 if (!IsTileType(moving_back->tile, TileType::Station) || GetStationIndex(moving_back->tile) != GetStationIndex(v->GetMovingFront()->tile)) {
2170 v->LeaveStation();
2171 }
2172 }
2173
2174 /* We cancel any 'skip signal at dangers' here */
2176 InvalidateWindowData(WindowClass::VehicleView, v->index);
2177
2178 if (_settings_game.vehicle.train_acceleration_model != AccelerationModel::Original && v->cur_speed != 0) {
2180 } else {
2181 v->cur_speed = 0;
2182 v->SetLastSpeed();
2185 }
2186
2187 /* Unbunching data is no longer valid. */
2189 }
2190 }
2191 return CommandCost();
2192}
2193
2205{
2208
2209 const Train *moving_front = t->GetMovingFront();
2210 TileIndex next_tile = TileAddByDiagDir(moving_front->tile, TrackdirToExitdir(moving_front->GetVehicleTrackdir()));
2211 if (next_tile == INVALID_TILE || !IsTileType(next_tile, TileType::Railway) || !HasSignals(next_tile)) return TFP_STUCK;
2212 TrackBits new_tracks = DiagdirReachesTracks(TrackdirToExitdir(moving_front->GetVehicleTrackdir())) & GetTrackBits(next_tile);
2213 return new_tracks.Any() && HasSignalOnTrack(next_tile, FindFirstTrack(new_tracks)) ? TFP_SIGNAL : TFP_STUCK;
2214}
2215
2223{
2224 Train *t = Train::GetIfValid(veh_id);
2225 if (t == nullptr) return CMD_ERROR;
2226
2227 if (!t->IsPrimaryVehicle()) return CMD_ERROR;
2228
2230 if (ret.Failed()) return ret;
2231
2232
2233 if (flags.Test(DoCommandFlag::Execute)) {
2235 InvalidateWindowData(WindowClass::VehicleView, t->index);
2236
2237 /* Unbunching data is no longer valid. */
2239 }
2240
2241 return CommandCost();
2242}
2243
2251static FindDepotData FindClosestTrainDepot(Train *v, int max_distance)
2252{
2253 assert(!v->vehstatus.Test(VehState::Crashed));
2254
2255 return YapfTrainFindNearestDepot(v, max_distance);
2256}
2257
2259{
2260 FindDepotData tfdd = FindClosestTrainDepot(this, 0);
2261 if (tfdd.best_length == UINT_MAX) return ClosestDepot();
2262
2263 return ClosestDepot(tfdd.tile, GetDepotIndex(tfdd.tile), tfdd.reverse);
2264}
2265
2266void Train::PlayLeaveStationSound(bool force) const
2267{
2268 static const SoundFx sfx[] = {
2274 };
2275
2276 if (PlayVehicleSound(this, VSE_START, force)) return;
2277
2278 SndPlayVehicleFx(sfx[to_underlying(RailVehInfo(this->engine_type)->engclass)], this);
2279}
2280
2285static void CheckNextTrainTile(Train *consist)
2286{
2287 /* Don't do any look-ahead if path_backoff_interval is 255. */
2288 if (_settings_game.pf.path_backoff_interval == 255) return;
2289
2290 const Train *moving_front = consist->GetMovingFront();
2291
2292 /* Exit if we are inside a depot. */
2293 if (moving_front->track == Track::Depot) return;
2294
2295 switch (consist->current_order.GetType()) {
2296 /* Exit if we reached our destination depot. */
2297 case OT_GOTO_DEPOT:
2298 if (moving_front->tile == consist->dest_tile) return;
2299 break;
2300
2301 case OT_GOTO_WAYPOINT:
2302 /* If we reached our waypoint, make sure we see that. */
2303 if (IsRailWaypointTile(moving_front->tile) && GetStationIndex(moving_front->tile) == consist->current_order.GetDestination()) ProcessOrders(consist);
2304 break;
2305
2306 case OT_NOTHING:
2307 case OT_LEAVESTATION:
2308 case OT_LOADING:
2309 /* Exit if the current order doesn't have a destination, but the train has orders. */
2310 if (consist->GetNumOrders() > 0) return;
2311 break;
2312
2313 default:
2314 break;
2315 }
2316 /* Exit if we are on a station tile and are going to stop. */
2317 if (IsRailStationTile(moving_front->tile) && consist->current_order.ShouldStopAtStation(consist, GetStationIndex(moving_front->tile))) return;
2318
2319 Trackdir td = moving_front->GetVehicleTrackdir();
2320
2321 /* On a tile with a red non-pbs signal, don't look ahead. */
2322 if (HasBlockSignalOnTrackdir(moving_front->tile, td) && GetSignalStateByTrackdir(moving_front->tile, td) == SignalState::Red) return;
2323
2324 CFollowTrackRail ft(consist);
2325 if (!ft.Follow(moving_front->tile, td)) return;
2326
2328 /* Next tile is not reserved. */
2329 if (ft.new_td_bits.Count() == 1) {
2331 /* If the next tile is a PBS signal, try to make a reservation. */
2335 }
2336 ChooseTrainTrack(consist, ft.new_tile, ft.exitdir, tracks, false, nullptr, false);
2337 }
2338 }
2339 }
2340}
2341
2348{
2349 /* bail out if not all wagons are in the same depot or not in a depot at all */
2350 for (const Train *u = v; u != nullptr; u = u->Next()) {
2351 if (u->track != Track::Depot || u->tile != v->tile) return false;
2352 }
2353
2354 /* if the train got no power, then keep it in the depot */
2355 if (v->gcache.cached_power == 0) {
2357 SetWindowDirty(WindowClass::VehicleDepot, v->tile);
2358 return true;
2359 }
2360
2361 /* Check if we should wait here for unbunching. */
2362 if (v->IsWaitingForUnbunching()) return true;
2363
2364 SigSegState seg_state;
2365
2366 if (v->force_proceed == TFP_NONE) {
2367 /* force proceed was not pressed */
2368 if (++v->wait_counter < 37) {
2369 SetWindowClassesDirty(WindowClass::TrainList);
2370 return true;
2371 }
2372
2373 v->wait_counter = 0;
2374
2376 if (seg_state == SigSegState::Full || HasDepotReservation(v->tile)) {
2377 /* Full and no PBS signal in block or depot reserved, can't exit. */
2378 SetWindowClassesDirty(WindowClass::TrainList);
2379 return true;
2380 }
2381 } else {
2383 }
2384
2385 /* We are leaving a depot, but have to go to the exact same one; re-enter. */
2386 if (v->current_order.IsType(OT_GOTO_DEPOT) && v->tile == v->dest_tile) {
2387 /* Service when depot has no reservation. */
2389 return true;
2390 }
2391
2392 /* Only leave when we can reserve a path to our destination. */
2393 if (seg_state == SigSegState::Path && !TryPathReserve(v) && v->force_proceed == TFP_NONE) {
2394 /* No path and no force proceed. */
2395 SetWindowClassesDirty(WindowClass::TrainList);
2397 return true;
2398 }
2399
2400 SetDepotReservation(v->tile, true);
2401 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
2402
2406 SetWindowClassesDirty(WindowClass::TrainList);
2407
2409
2411 v->cur_speed = 0;
2412
2413 v->UpdateViewport(true, true);
2414 v->UpdatePosition();
2416 v->UpdateAcceleration();
2417 InvalidateWindowData(WindowClass::VehicleDepot, v->tile);
2418
2419 return false;
2420}
2421
2428static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_dir)
2429{
2430 DiagDirection dir = TrackdirToExitdir(track_dir);
2431
2433 /* Are we just leaving a tunnel/bridge? */
2434 if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) {
2436
2437 if (TunnelBridgeIsFree(tile, end, v).Succeeded()) {
2438 /* Free the reservation only if no other train is on the tiles. */
2439 SetTunnelBridgeReservation(tile, false);
2440 SetTunnelBridgeReservation(end, false);
2441
2442 if (_settings_client.gui.show_track_reservation) {
2443 if (IsBridge(tile)) {
2444 MarkBridgeDirty(tile);
2445 } else {
2446 MarkTileDirtyByTile(tile);
2448 }
2449 }
2450 }
2451 }
2452 } else if (IsRailStationTile(tile)) {
2453 TileIndex new_tile = TileAddByDiagDir(tile, dir);
2454 /* If the new tile is not a further tile of the same station, we
2455 * clear the reservation for the whole platform. */
2456 if (!IsCompatibleTrainStationTile(new_tile, tile)) {
2458 }
2459 } else {
2460 /* Any other tile */
2461 UnreserveRailTrack(tile, TrackdirToTrack(track_dir));
2462 }
2463}
2464
2470{
2471 assert(consist->IsFrontEngine());
2472
2473 const Train *moving_front = consist->GetMovingFront();
2474 TileIndex tile = moving_front->tile;
2475 Trackdir td = moving_front->GetVehicleTrackdir();
2476 bool free_tile = tile != moving_front->tile || !(IsRailStationTile(moving_front->tile) || IsTileType(moving_front->tile, TileType::TunnelBridge));
2477 StationID station_id = IsRailStationTile(moving_front->tile) ? GetStationIndex(moving_front->tile) : StationID::Invalid();
2478
2479 /* Can't be holding a reservation if we enter a depot. */
2480 if (IsRailDepotTile(tile) && TrackdirToExitdir(td) != GetRailDepotDirection(tile)) return;
2481 if (moving_front->track == Track::Depot) {
2482 /* Front engine is in a depot. We enter if some part is not in the depot. */
2483 for (const Train *u = consist; u != nullptr; u = u->Next()) {
2484 if (u->track != Track::Depot || u->tile != consist->tile) return;
2485 }
2486 }
2487 /* Don't free reservation if it's not ours. */
2488 if (TracksOverlap(GetReservedTrackbits(tile) | TrackdirToTrack(td))) return;
2489
2490 CFollowTrackRail ft(consist, GetAllCompatibleRailTypes(consist->railtypes));
2491 while (ft.Follow(tile, td)) {
2492 tile = ft.new_tile;
2494 td = RemoveFirstTrackdir(bits);
2495 assert(bits.None());
2496
2497 if (!IsValidTrackdir(td)) break;
2498
2499 if (IsTileType(tile, TileType::Railway)) {
2500 if (HasSignalOnTrackdir(tile, td) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(td)))) {
2501 /* Conventional signal along trackdir: remove reservation and stop. */
2503 break;
2504 }
2505 if (HasPbsSignalOnTrackdir(tile, td)) {
2506 if (GetSignalStateByTrackdir(tile, td) == SignalState::Red) {
2507 /* Red PBS signal? Can't be our reservation, would be green then. */
2508 break;
2509 } else {
2510 /* Turn the signal back to red. */
2512 MarkTileDirtyByTile(tile);
2513 }
2514 } else if (HasPbsSignalOnTrackdir(tile, ReverseTrackdir(td))) {
2515 /* Reservation passes an opposing path signal. Mark signal for update to re-establish the proper default state. */
2517 } else if (HasSignalOnTrackdir(tile, ReverseTrackdir(td)) && IsOnewaySignal(tile, TrackdirToTrack(td))) {
2518 break;
2519 }
2520 }
2521
2522 /* Don't free first station/bridge/tunnel if we are on it. */
2523 if (free_tile || (!(ft.is_station && GetStationIndex(ft.new_tile) == station_id) && !ft.is_tunnel && !ft.is_bridge)) ClearPathReservation(consist, tile, td);
2524
2525 free_tile = true;
2526 }
2527
2529}
2530
2544static Track DoTrainPathfind(const Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found, bool do_track_reservation, PBSTileInfo *dest, TileIndex *final_dest)
2545{
2546 if (final_dest != nullptr) *final_dest = INVALID_TILE;
2547 return YapfTrainChooseTrack(v, tile, enterdir, tracks, path_found, do_track_reservation, dest, final_dest);
2548}
2549
2558static PBSTileInfo ExtendTrainReservation(const Train *v, TrackBits *new_tracks, DiagDirection *enterdir)
2559{
2561
2562 CFollowTrackRail ft(v);
2563
2564 std::vector<std::pair<TileIndex, Trackdir>> signals_set_to_red;
2565
2566 TileIndex tile = origin.tile;
2567 Trackdir cur_td = origin.trackdir;
2568 while (ft.Follow(tile, cur_td)) {
2569 if (ft.new_td_bits.Count() == 1) {
2570 /* Possible signal tile. */
2572 }
2573
2576 if (ft.new_td_bits.None()) break;
2577 }
2578
2579 /* Station, depot or waypoint are a possible target. */
2580 bool target_seen = ft.is_station || (IsTileType(ft.new_tile, TileType::Railway) && !IsPlainRail(ft.new_tile));
2581 if (target_seen || ft.new_td_bits.Count() > 1) {
2582 /* Choice found or possible target encountered.
2583 * On finding a possible target, we need to stop and let the pathfinder handle the
2584 * remaining path. This is because we don't know if this target is in one of our
2585 * orders, so we might cause pathfinding to fail later on if we find a choice.
2586 * This failure would cause a bogus call to TryReserveSafePath which might reserve
2587 * a wrong path not leading to our next destination. */
2589
2590 /* If we did skip some tiles, backtrack to the first skipped tile so the pathfinder
2591 * actually starts its search at the first unreserved tile. */
2592 if (ft.tiles_skipped != 0) ft.new_tile -= TileOffsByDiagDir(ft.exitdir) * ft.tiles_skipped;
2593
2594 /* Choice found, path valid but not okay. Save info about the choice tile as well. */
2595 if (new_tracks != nullptr) *new_tracks = TrackdirBitsToTrackBits(ft.new_td_bits);
2596 if (enterdir != nullptr) *enterdir = ft.exitdir;
2597 return PBSTileInfo(ft.new_tile, ft.old_td, false);
2598 }
2599
2600 tile = ft.new_tile;
2601 cur_td = FindFirstTrackdir(ft.new_td_bits);
2602
2603 Trackdir rev_td = ReverseTrackdir(cur_td);
2604 if (IsSafeWaitingPosition(v, tile, cur_td, true, _settings_game.pf.forbid_90_deg)) {
2605 bool wp_free = IsWaitingPositionFree(v, tile, cur_td, _settings_game.pf.forbid_90_deg);
2606 if (!(wp_free && TryReserveRailTrack(tile, TrackdirToTrack(cur_td)))) break;
2607 /* Green path signal opposing the path? Turn to red. */
2608 if (HasPbsSignalOnTrackdir(tile, rev_td) && GetSignalStateByTrackdir(tile, rev_td) == SignalState::Green) {
2609 signals_set_to_red.emplace_back(tile, rev_td);
2611 MarkTileDirtyByTile(tile);
2612 }
2613 /* Safe position is all good, path valid and okay. */
2614 return PBSTileInfo(tile, cur_td, true);
2615 }
2616
2617 if (!TryReserveRailTrack(tile, TrackdirToTrack(cur_td))) break;
2618
2619 /* Green path signal opposing the path? Turn to red. */
2620 if (HasPbsSignalOnTrackdir(tile, rev_td) && GetSignalStateByTrackdir(tile, rev_td) == SignalState::Green) {
2621 signals_set_to_red.emplace_back(tile, rev_td);
2623 MarkTileDirtyByTile(tile);
2624 }
2625 }
2626
2627 if (ft.err == CFollowTrackRail::ErrorCode::Owner || ft.err == CFollowTrackRail::ErrorCode::NoWay) {
2628 /* End of line, path valid and okay. */
2629 return PBSTileInfo(ft.old_tile, ft.old_td, true);
2630 }
2631
2632 /* Sorry, can't reserve path, back out. */
2633 tile = origin.tile;
2634 cur_td = origin.trackdir;
2635 TileIndex stopped = ft.old_tile;
2636 Trackdir stopped_td = ft.old_td;
2637 while (tile != stopped || cur_td != stopped_td) {
2638 if (!ft.Follow(tile, cur_td)) break;
2639
2642 assert(ft.new_td_bits.Any());
2643 }
2644 assert(ft.new_td_bits.Count() == 1);
2645
2646 tile = ft.new_tile;
2647 cur_td = FindFirstTrackdir(ft.new_td_bits);
2648
2649 UnreserveRailTrack(tile, TrackdirToTrack(cur_td));
2650 }
2651
2652 /* Re-instate green signals we turned to red. */
2653 for (auto [sig_tile, td] : signals_set_to_red) {
2655 }
2656
2657 /* Path invalid. */
2658 return PBSTileInfo();
2659}
2660
2671static bool TryReserveSafeTrack(const Train *v, TileIndex tile, Trackdir td, bool override_railtype)
2672{
2673 return YapfTrainFindNearestSafeTile(v, tile, td, override_railtype);
2674}
2675
2677class VehicleOrderSaver {
2678private:
2679 Train *v;
2680 Order old_order;
2681 TileIndex old_dest_tile;
2682 StationID old_last_station_visited;
2683 VehicleOrderID index;
2684 bool suppress_implicit_orders;
2685 bool restored;
2686
2687public:
2688 VehicleOrderSaver(Train *_v) :
2689 v(_v),
2690 old_order(_v->current_order),
2691 old_dest_tile(_v->dest_tile),
2692 old_last_station_visited(_v->last_station_visited),
2693 index(_v->cur_real_order_index),
2694 suppress_implicit_orders(_v->gv_flags.Test(GroundVehicleFlag::SuppressImplicitOrders)),
2695 restored(false)
2696 {
2697 }
2698
2702 void Restore()
2703 {
2704 this->v->current_order = this->old_order;
2705 this->v->dest_tile = this->old_dest_tile;
2706 this->v->last_station_visited = this->old_last_station_visited;
2707 this->v->gv_flags.Set(GroundVehicleFlag::SuppressImplicitOrders, suppress_implicit_orders);
2708 this->restored = true;
2709 }
2710
2715 {
2716 if (!this->restored) this->Restore();
2717 }
2718
2724 bool SwitchToNextOrder(bool skip_first)
2725 {
2726 if (this->v->GetNumOrders() == 0) return false;
2727
2728 if (skip_first) ++this->index;
2729
2730 int depth = 0;
2731
2732 do {
2733 /* Wrap around. */
2734 if (this->index >= this->v->GetNumOrders()) this->index = 0;
2735
2736 Order *order = this->v->GetOrder(this->index);
2737 assert(order != nullptr);
2738
2739 switch (order->GetType()) {
2740 case OT_GOTO_DEPOT:
2741 /* Skip service in depot orders when the train doesn't need service. */
2742 if (order->GetDepotOrderType().Test(OrderDepotTypeFlag::Service) && !this->v->NeedsServicing()) break;
2743 [[fallthrough]];
2744 case OT_GOTO_STATION:
2745 case OT_GOTO_WAYPOINT:
2746 this->v->current_order = *order;
2747 return UpdateOrderDest(this->v, order, 0, true);
2748 case OT_CONDITIONAL: {
2749 VehicleOrderID next = ProcessConditionalOrder(order, this->v);
2750 if (next != INVALID_VEH_ORDER_ID) {
2751 depth++;
2752 this->index = next;
2753 /* Don't increment next, so no break here. */
2754 continue;
2755 }
2756 break;
2757 }
2758 default:
2759 break;
2760 }
2761 /* Don't increment inside the while because otherwise conditional
2762 * orders can lead to an infinite loop. */
2763 ++this->index;
2764 depth++;
2765 } while (this->index != this->v->cur_real_order_index && depth < this->v->GetNumOrders());
2766
2767 return false;
2768 }
2769};
2770
2771/* choose a track */
2772static Track ChooseTrainTrack(Train *consist, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck)
2773{
2774 Track best_track = Track::Invalid;
2775 bool do_track_reservation = _settings_game.pf.reserve_paths || force_res;
2776 bool changed_signal = false;
2777 TileIndex final_dest = INVALID_TILE;
2778
2779 assert(tracks == (tracks & TRACK_BIT_ALL));
2780
2781 if (got_reservation != nullptr) *got_reservation = false;
2782
2783 /* Don't use tracks here as the setting to forbid 90 deg turns might have been switched between reservation and now. */
2784 TrackBits res_tracks = GetReservedTrackbits(tile) & DiagdirReachesTracks(enterdir);
2785 /* Do we have a suitable reserved track? */
2786 if (res_tracks.Any()) return FindFirstTrack(res_tracks);
2787
2788 /* Quick return in case only one possible track is available */
2789 if (tracks.Count() == 1) {
2790 Track track = FindFirstTrack(tracks);
2791 /* We need to check for signals only here, as a junction tile can't have signals. */
2792 if (IsValidTrack(track) && HasPbsSignalOnTrackdir(tile, TrackEnterdirToTrackdir(track, enterdir))) {
2793 do_track_reservation = true;
2794 changed_signal = true;
2796 } else if (!do_track_reservation) {
2797 return track;
2798 }
2799 best_track = track;
2800 }
2801
2802 const Train *moving_front = consist->GetMovingFront();
2803
2804 PBSTileInfo res_dest(tile, Trackdir::Invalid, false);
2805 DiagDirection dest_enterdir = enterdir;
2806 if (do_track_reservation) {
2807 res_dest = ExtendTrainReservation(consist, &tracks, &dest_enterdir);
2808 if (res_dest.tile == INVALID_TILE) {
2809 /* Reservation failed? */
2810 if (mark_stuck) MarkTrainAsStuck(consist);
2811 if (changed_signal) SetSignalStateByTrackdir(tile, TrackEnterdirToTrackdir(best_track, enterdir), SignalState::Red);
2812 return FindFirstTrack(tracks);
2813 }
2814 if (res_dest.okay) {
2815 /* Got a valid reservation that ends at a safe target, quick exit. */
2816 if (got_reservation != nullptr) *got_reservation = true;
2817 if (changed_signal) MarkTileDirtyByTile(tile);
2818 TryReserveRailTrack(moving_front->tile, TrackdirToTrack(moving_front->GetVehicleTrackdir()));
2819 return best_track;
2820 }
2821
2822 /* Check if the train needs service here, so it has a chance to always find a depot.
2823 * Also check if the current order is a service order so we don't reserve a path to
2824 * the destination but instead to the next one if service isn't needed. */
2825 CheckIfTrainNeedsService(consist);
2826 if (consist->current_order.IsType(OT_DUMMY) || consist->current_order.IsType(OT_CONDITIONAL) || consist->current_order.IsType(OT_GOTO_DEPOT)) ProcessOrders(consist);
2827 }
2828
2829 /* Save the current train order. The destructor will restore the old order on function exit. */
2830 VehicleOrderSaver orders(consist);
2831
2832 /* If the current tile is the destination of the current order and
2833 * a reservation was requested, advance to the next order.
2834 * Don't advance on a depot order as depots are always safe end points
2835 * for a path and no look-ahead is necessary. This also avoids a
2836 * problem with depot orders not part of the order list when the
2837 * order list itself is empty. */
2838 if (consist->current_order.IsType(OT_LEAVESTATION)) {
2839 orders.SwitchToNextOrder(false);
2840 } else if (consist->current_order.IsType(OT_LOADING) || (!consist->current_order.IsType(OT_GOTO_DEPOT) && (
2841 consist->current_order.IsType(OT_GOTO_STATION) ?
2842 IsRailStationTile(moving_front->tile) && consist->current_order.GetDestination() == GetStationIndex(moving_front->tile) :
2843 moving_front->tile == consist->dest_tile))) {
2844 orders.SwitchToNextOrder(true);
2845 }
2846
2847 if (res_dest.tile != INVALID_TILE && !res_dest.okay) {
2848 /* Pathfinders are able to tell that route was only 'guessed'. */
2849 bool path_found = true;
2850 TileIndex new_tile = res_dest.tile;
2851
2852 Track next_track = DoTrainPathfind(consist, new_tile, dest_enterdir, tracks, path_found, do_track_reservation, &res_dest, &final_dest);
2853 if (new_tile == tile) best_track = next_track;
2854 consist->HandlePathfindingResult(path_found);
2855 }
2856
2857 /* No track reservation requested -> finished. */
2858 if (!do_track_reservation) return best_track;
2859
2860 /* A path was found, but could not be reserved. */
2861 if (res_dest.tile != INVALID_TILE && !res_dest.okay) {
2862 if (mark_stuck) MarkTrainAsStuck(consist);
2864 return best_track;
2865 }
2866
2867 /* No possible reservation target found, we are probably lost. */
2868 if (res_dest.tile == INVALID_TILE) {
2869 /* Try to find any safe destination. */
2870 PBSTileInfo origin = FollowTrainReservation(consist);
2871 if (TryReserveSafeTrack(consist, origin.tile, origin.trackdir, false)) {
2872 TrackBits res = GetReservedTrackbits(tile) & DiagdirReachesTracks(enterdir);
2873 best_track = FindFirstTrack(res);
2874 TryReserveRailTrack(moving_front->tile, TrackdirToTrack(moving_front->GetVehicleTrackdir()));
2875 if (got_reservation != nullptr) *got_reservation = true;
2876 if (changed_signal) MarkTileDirtyByTile(tile);
2877 } else {
2879 if (mark_stuck) MarkTrainAsStuck(consist);
2880 }
2881 return best_track;
2882 }
2883
2884 if (got_reservation != nullptr) *got_reservation = true;
2885
2886 /* Reservation target found and free, check if it is safe. */
2887 while (!IsSafeWaitingPosition(consist, res_dest.tile, res_dest.trackdir, true, _settings_game.pf.forbid_90_deg)) {
2888 /* Extend reservation until we have found a safe position. */
2889 DiagDirection exitdir = TrackdirToExitdir(res_dest.trackdir);
2890 TileIndex next_tile = TileAddByDiagDir(res_dest.tile, exitdir);
2892 if (Rail90DegTurnDisallowed(GetTileRailType(res_dest.tile), GetTileRailType(next_tile))) {
2893 reachable.Reset(TrackCrossesTracks(TrackdirToTrack(res_dest.trackdir)));
2894 }
2895
2896 /* Get next order with destination. */
2897 if (orders.SwitchToNextOrder(true)) {
2898 PBSTileInfo cur_dest;
2899 bool path_found;
2900 DoTrainPathfind(consist, next_tile, exitdir, reachable, path_found, true, &cur_dest, nullptr);
2901 if (cur_dest.tile != INVALID_TILE) {
2902 res_dest = cur_dest;
2903 if (res_dest.okay) continue;
2904 /* Path found, but could not be reserved. */
2906 if (mark_stuck) MarkTrainAsStuck(consist);
2907 if (got_reservation != nullptr) *got_reservation = false;
2908 changed_signal = false;
2909 break;
2910 }
2911 }
2912 /* No order or no safe position found, try any position. */
2913 if (!TryReserveSafeTrack(consist, res_dest.tile, res_dest.trackdir, true)) {
2915 if (mark_stuck) MarkTrainAsStuck(consist);
2916 if (got_reservation != nullptr) *got_reservation = false;
2917 changed_signal = false;
2918 }
2919 break;
2920 }
2921
2922 TryReserveRailTrack(moving_front->tile, TrackdirToTrack(moving_front->GetVehicleTrackdir()));
2923
2924 if (changed_signal) MarkTileDirtyByTile(tile);
2925
2926 orders.Restore();
2927 if (consist->current_order.IsType(OT_GOTO_DEPOT) &&
2929 final_dest != INVALID_TILE && IsRailDepotTile(final_dest)) {
2930 consist->current_order.SetDestination(GetDepotIndex(final_dest));
2931 consist->dest_tile = final_dest;
2932 SetWindowWidgetDirty(WindowClass::VehicleView, consist->index, WID_VV_START_STOP);
2933 }
2934
2935 return best_track;
2936}
2937
2946bool TryPathReserve(Train *consist, bool mark_as_stuck, bool first_tile_okay)
2947{
2948 assert(consist->IsFrontEngine());
2949
2950 const Train *moving_front = consist->GetMovingFront();
2951
2952 /* We have to handle depots specially as the track follower won't look
2953 * at the depot tile itself but starts from the next tile. If we are still
2954 * inside the depot, a depot reservation can never be ours. */
2955 if (moving_front->track == Track::Depot) {
2956 if (HasDepotReservation(moving_front->tile)) {
2957 if (mark_as_stuck) MarkTrainAsStuck(consist);
2958 return false;
2959 } else {
2960 /* Depot not reserved, but the next tile might be. */
2961 TileIndex next_tile = TileAddByDiagDir(moving_front->tile, GetRailDepotDirection(moving_front->tile));
2962 if (HasReservedTracks(next_tile, DiagdirReachesTracks(GetRailDepotDirection(moving_front->tile)))) return false;
2963 }
2964 }
2965
2966 Vehicle *other_train = nullptr;
2967 PBSTileInfo origin = FollowTrainReservation(consist, &other_train);
2968 /* The path we are driving on is already blocked by some other train.
2969 * This can only happen in certain situations when mixing path and
2970 * block signals or when changing tracks and/or signals.
2971 * Exit here as doing any further reservations will probably just
2972 * make matters worse. */
2973 if (other_train != nullptr && other_train->index != consist->index) {
2974 if (mark_as_stuck) MarkTrainAsStuck(consist);
2975 return false;
2976 }
2977 /* If we have a reserved path and the path ends at a safe tile, we are finished already. */
2978 if (origin.okay && (moving_front->tile != origin.tile || first_tile_okay)) {
2979 /* Can't be stuck then. */
2980 if (consist->flags.Test(VehicleRailFlag::Stuck)) SetWindowWidgetDirty(WindowClass::VehicleView, consist->index, WID_VV_START_STOP);
2982 return true;
2983 }
2984
2985 /* If we are in a depot, tentatively reserve the depot. */
2986 if (moving_front->track == Track::Depot) {
2987 SetDepotReservation(moving_front->tile, true);
2988 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(moving_front->tile);
2989 }
2990
2991 DiagDirection exitdir = TrackdirToExitdir(origin.trackdir);
2992 TileIndex new_tile = TileAddByDiagDir(origin.tile, exitdir);
2994
2996
2997 bool res_made = false;
2998 ChooseTrainTrack(consist, new_tile, exitdir, reachable, true, &res_made, mark_as_stuck);
2999
3000 if (!res_made) {
3001 /* Free the depot reservation as well. */
3002 if (moving_front->track == Track::Depot) SetDepotReservation(moving_front->tile, false);
3003 return false;
3004 }
3005
3006 if (consist->flags.Test(VehicleRailFlag::Stuck)) {
3007 consist->wait_counter = 0;
3008 SetWindowWidgetDirty(WindowClass::VehicleView, consist->index, WID_VV_START_STOP);
3009 }
3011 return true;
3012}
3013
3019static bool CheckReverseTrain(const Train *consist)
3020{
3021 const Train *moving_front = consist->GetMovingFront();
3022 if (_settings_game.difficulty.train_flip_reverse_allowed == TrainFlipReversingAllowed::EndOfLineOnly ||
3023 moving_front->track == Track::Depot || moving_front->track == Track::Wormhole ||
3024 !IsDiagonalDirection(moving_front->GetMovingDirection())) {
3025 return false;
3026 }
3027
3028 assert(moving_front->track.Any());
3029
3030 return YapfTrainCheckReverse(consist);
3031}
3032
3039{
3040 if (station == this->last_station_visited) this->last_station_visited = StationID::Invalid();
3041
3042 const Station *st = Station::Get(station);
3044 /* The destination station has no trainstation tiles. */
3046 return TileIndex{};
3047 }
3048
3049 return st->xy;
3050}
3051
3054{
3055 Train *v = this;
3056 do {
3057 v->colourmap = PAL_NONE;
3058 v->UpdateViewport(true, false);
3059 } while ((v = v->Next()) != nullptr);
3060
3061 /* need to update acceleration and cached values since the goods on the train changed. */
3062 this->ConsistChanged({});
3063 this->UpdateAcceleration();
3064}
3065
3074{
3075 switch (_settings_game.vehicle.train_acceleration_model) {
3076 default: NOT_REACHED();
3078 return this->DoUpdateSpeed(this->acceleration * (this->GetAccelerationStatus() == AS_BRAKE ? -4 : 2), 0, this->GetCurrentMaxSpeed());
3079
3081 return this->DoUpdateSpeed(this->GetAcceleration(), this->GetAccelerationStatus() == AS_BRAKE ? 0 : 2, this->GetCurrentMaxSpeed());
3082 }
3083}
3084
3090static void TrainEnterStation(Train *consist, StationID station)
3091{
3092 consist->last_station_visited = station;
3093
3094 /* check if a train ever visited this station before */
3095 Station *st = Station::Get(station);
3096 if (!st->had_vehicle_of_type.Test(StationVehicleType::Train)) {
3097 st->had_vehicle_of_type.Set(StationVehicleType::Train);
3099 GetEncodedString(STR_NEWS_FIRST_TRAIN_ARRIVAL, st->index),
3101 consist->index,
3102 st->index
3103 );
3104 AI::NewEvent(consist->owner, new ScriptEventStationFirstVehicle(st->index, consist->index));
3105 Game::NewEvent(new ScriptEventStationFirstVehicle(st->index, consist->index));
3106 }
3107
3108 consist->force_proceed = TFP_NONE;
3109 InvalidateWindowData(WindowClass::VehicleView, consist->index);
3110
3111 consist->BeginLoading();
3112
3113 TileIndex tile = consist->GetMovingFront()->tile;
3115 TriggerStationAnimation(st, tile, StationAnimationTrigger::VehicleArrives);
3116}
3117
3125static inline bool CheckCompatibleRail(const Train *v, TileIndex tile, bool check_railtype)
3126{
3127 return IsTileOwner(tile, v->owner) &&
3128 (!check_railtype || !v->IsFrontEngine() || v->compatible_railtypes.Test(GetRailType(tile)));
3129}
3130
3133 uint8_t small_turn;
3134 uint8_t large_turn;
3135 uint8_t z_up;
3136 uint8_t z_down;
3137};
3138
3141 /* normal accel */
3142 {256 / 4, 256 / 2, 256 / 4, 2},
3143 {256 / 4, 256 / 2, 256 / 4, 2},
3144 {0, 256 / 2, 256 / 4, 2},
3145};
3146
3152static inline void AffectSpeedByZChange(Train *consist, int z_diff)
3153{
3154 if (z_diff == 0 || _settings_game.vehicle.train_acceleration_model != AccelerationModel::Original) return;
3155
3156 const AccelerationSlowdownParams *asp = &_accel_slowdown[static_cast<int>(consist->GetAccelerationType())];
3157
3158 if (z_diff > 0) {
3159 consist->cur_speed -= (consist->cur_speed * asp->z_up >> 8);
3160 } else {
3161 uint16_t spd = consist->cur_speed + asp->z_down;
3162 if (spd <= consist->gcache.cached_max_track_speed) consist->cur_speed = spd;
3163 }
3164}
3165
3166static bool TrainMovedChangeSignals(TileIndex tile, DiagDirection dir)
3167{
3168 if (IsTileType(tile, TileType::Railway) &&
3171 Trackdir trackdir = FindFirstTrackdir(tracks);
3172 if (UpdateSignalsOnSegment(tile, TrackdirToExitdir(trackdir), GetTileOwner(tile)) == SigSegState::Path && HasSignalOnTrackdir(tile, trackdir)) {
3173 /* A PBS block with a non-PBS signal facing us? */
3174 if (!IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) return true;
3175 }
3176 }
3177 return false;
3178}
3179
3182{
3183 for (const Train *u = this; u != nullptr; u = u->Next()) {
3184 switch (u->track.base()) {
3185 case TrackBits{Track::Wormhole}.base():
3187 break;
3188 case TrackBits{Track::Depot}.base():
3189 break;
3190 default:
3192 break;
3193 }
3194 }
3195}
3196
3203uint Train::Crash(bool flooded)
3204{
3205 uint victims = 0;
3206 if (this->IsFrontEngine()) {
3207 victims += 2; // driver
3208
3209 /* Remove the reserved path in front of the train if it is not stuck.
3210 * Also clear all reserved tracks the train is currently on. */
3212 for (const Train *v = this; v != nullptr; v = v->Next()) {
3215 /* ClearPathReservation will not free the wormhole exit
3216 * if the train has just entered the wormhole. */
3218 }
3219 }
3220
3221 /* we may need to update crossing we were approaching,
3222 * but must be updated after the train has been marked crashed */
3224 if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing);
3225
3226 /* Remove the loading indicators (if any) */
3228 }
3229
3230 victims += this->GroundVehicleBase::Crash(flooded);
3231
3232 this->crash_anim_pos = flooded ? 4000 : 1; // max 4440, disappear pretty fast when flooded
3233 return victims;
3234}
3235
3242static uint TrainCrashed(Train *v)
3243{
3244 uint victims = 0;
3245
3246 /* do not crash train twice */
3247 if (!v->vehstatus.Test(VehState::Crashed)) {
3248 victims = v->Crash();
3249 TileIndex tile = v->GetMovingFront()->tile;
3250 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, tile, ScriptEventVehicleCrashed::CRASH_TRAIN, victims, v->owner));
3251 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, tile, ScriptEventVehicleCrashed::CRASH_TRAIN, victims, v->owner));
3252 }
3253
3254 return victims;
3255}
3256
3263static uint TrainsCrashed(Train *v, Train *u)
3264{
3265 /* Crash both trains. Two statements required to guarantee execution
3266 * order because RandomRange() is involved. */
3267 uint victims = TrainCrashed(v);
3268 victims += TrainCrashed(u);
3269
3270 /* Try to re-reserve track under already crashed train too.
3271 * Crash() in TrainCrashed() clears the reservation! */
3274
3275 return victims;
3276}
3277
3284static uint CheckTrainCollision(Vehicle *v, Train *moving_front)
3285{
3286 /* Make sure we are a train, and are not in a depot. */
3287 if (v->type != VehicleType::Train) return 0;
3288
3289 /* We can't crash into trains in a depot. */
3290 if (Train::From(v)->track == Track::Depot) return 0;
3291
3292 /* Do not crash into trains of another company. */
3293 if (v->owner != moving_front->First()->owner) return 0;
3294
3295 /* Do not collide with our own wagons */
3296 if (v->First() == moving_front->First()) return 0;
3297
3298 int x_diff = v->x_pos - moving_front->x_pos;
3299 int y_diff = v->y_pos - moving_front->y_pos;
3300
3301 /* Do fast calculation to check whether trains are not in close vicinity
3302 * and quickly reject trains distant enough for any collision.
3303 * Differences are shifted by 7, mapping range [-7 .. 8] into [0 .. 15]
3304 * Differences are then ORed and then we check for any higher bits */
3305 uint hash = (y_diff + 7) | (x_diff + 7);
3306 if (hash & ~15) return 0;
3307
3308 /* Slower check using multiplication */
3309 int min_diff = (Train::From(v)->gcache.cached_veh_length + 1) / 2 + (moving_front->gcache.cached_veh_length + 1) / 2 - 1;
3310 if (x_diff * x_diff + y_diff * y_diff > min_diff * min_diff) return 0;
3311
3312 /* Happens when there is a train under bridge next to bridge head */
3313 if (abs(v->z_pos - moving_front->z_pos) > 5) return 0;
3314
3315 return TrainsCrashed(moving_front->First(), Train::From(v)->First());
3316}
3317
3326static bool CheckTrainCollision(Train *moving_front)
3327{
3328 /* can't collide in depot */
3329 if (moving_front->track == Track::Depot) return false;
3330
3331 assert(moving_front->track == Track::Wormhole || TileVirtXY(moving_front->x_pos, moving_front->y_pos) == moving_front->tile);
3332
3333 uint num_victims = 0;
3334
3335 /* find colliding vehicles */
3336 if (moving_front->track == Track::Wormhole) {
3337 for (Vehicle *u : VehiclesOnTile(moving_front->tile)) {
3338 num_victims += CheckTrainCollision(u, moving_front);
3339 }
3340 for (Vehicle *u : VehiclesOnTile(GetOtherTunnelBridgeEnd(moving_front->tile))) {
3341 num_victims += CheckTrainCollision(u, moving_front);
3342 }
3343 } else {
3344 for (Vehicle *u : VehiclesNearTileXY(moving_front->x_pos, moving_front->y_pos, 7)) {
3345 num_victims += CheckTrainCollision(u, moving_front);
3346 }
3347 }
3348
3349 /* any dead -> no crash */
3350 if (num_victims == 0) return false;
3351
3352 AddTileNewsItem(GetEncodedString(STR_NEWS_TRAIN_CRASH, num_victims), NewsType::Accident, moving_front->tile);
3353
3354 ModifyStationRatingAround(moving_front->tile, moving_front->First()->owner, -160, 30);
3355 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_13_TRAIN_COLLISION, moving_front);
3356 return true;
3357}
3358
3366bool TrainController(Train *v, Vehicle *nomove, bool reverse)
3367{
3368 Train *first = v->First();
3369 Train *prev;
3370 bool direction_changed = false; // has direction of any part changed?
3371
3372 /* For every vehicle after and including the given vehicle */
3373 for (prev = v->GetMovingPrev(); v != nomove; prev = v, v = v->GetMovingNext()) {
3375 bool update_signals_crossing = false; // will we update signals or crossing state?
3376
3378 if (v->track != Track::Wormhole) {
3379 /* Not inside tunnel */
3380 if (gp.old_tile == gp.new_tile) {
3381 /* Staying in the old tile */
3382 if (v->track == Track::Depot) {
3383 /* Inside depot */
3384 gp.x = v->x_pos;
3385 gp.y = v->y_pos;
3386 } else {
3387 /* Not inside depot */
3388
3389 /* Reverse when we are at the end of the track already, do not move to the new position */
3390 if (v->IsMovingFront() && !TrainCheckIfLineEnds(v, reverse)) return false;
3391
3392 auto vets = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
3393 if (vets.Test(VehicleEnterTileState::CannotEnter)) {
3394 goto invalid_rail;
3395 }
3397 /* The new position is the end of the platform */
3399 }
3400 }
3401 } else {
3402 /* A new tile is about to be entered. */
3403
3404 /* Determine what direction we're entering the new tile from */
3405 enterdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile);
3406 assert(IsValidDiagDirection(enterdir));
3407
3408 /* Get the status of the tracks in the new tile and mask
3409 * away the bits that aren't reachable. */
3411 TrackdirBits reachable_trackdirs = DiagdirReachesTrackdirs(enterdir);
3412
3413 TrackdirBits trackdirbits = ts.trackdirs & reachable_trackdirs;
3414 TrackBits red_signals = TrackdirBitsToTrackBits(ts.signals & reachable_trackdirs);
3415
3416 TrackBits bits = TrackdirBitsToTrackBits(trackdirbits);
3417 if (Rail90DegTurnDisallowed(GetTileRailType(gp.old_tile), GetTileRailType(gp.new_tile)) && prev == nullptr) {
3418 /* We allow wagons to make 90 deg turns, because forbid_90_deg
3419 * can be switched on halfway a turn */
3421 }
3422
3423 if (bits.None()) goto invalid_rail;
3424
3425 /* Check if the new tile constrains tracks that are compatible
3426 * with the current train, if not, bail out. */
3427 if (!CheckCompatibleRail(v->First(), gp.new_tile, v->IsMovingFront())) goto invalid_rail;
3428
3429 TrackBits chosen_track;
3430 if (v->IsMovingFront()) {
3431 /* Currently the locomotive is active. Determine which one of the
3432 * available tracks to choose */
3433 chosen_track = ChooseTrainTrack(first, gp.new_tile, enterdir, bits, false, nullptr, true);
3434 assert(chosen_track.Any(bits | GetReservedTrackbits(gp.new_tile)));
3435
3436 if (first->force_proceed != TFP_NONE && IsPlainRailTile(gp.new_tile) && HasSignals(gp.new_tile)) {
3437 /* For each signal we find decrease the counter by one.
3438 * We start at two, so the first signal we pass decreases
3439 * this to one, then if we reach the next signal it is
3440 * decreased to zero and we won't pass that new signal. */
3441 Trackdir dir = FindFirstTrackdir(trackdirbits);
3442 if (HasSignalOnTrackdir(gp.new_tile, dir) ||
3445 /* However, we do not want to be stopped by PBS signals
3446 * entered via the back. */
3447 first->force_proceed = (first->force_proceed == TFP_SIGNAL) ? TFP_STUCK : TFP_NONE;
3448 InvalidateWindowData(WindowClass::VehicleView, first->index);
3449 }
3450 }
3451
3452 /* Check if it's a red signal and that force proceed is not clicked. */
3453 if (red_signals.Any(chosen_track) && first->force_proceed == TFP_NONE) {
3454 /* In front of a red signal */
3455 Trackdir i = FindFirstTrackdir(trackdirbits);
3456
3457 /* Don't handle stuck trains here. */
3458 if (first->flags.Test(VehicleRailFlag::Stuck)) return false;
3459
3461 first->cur_speed = 0;
3462 first->subspeed = 0;
3463 first->progress = 255; // make sure that every bit of acceleration will hit the signal again, so speed stays 0.
3464 if (!_settings_game.pf.reverse_at_signals || ++first->wait_counter < _settings_game.pf.wait_oneway_signal * Ticks::DAY_TICKS * 2) return false;
3465 } else if (HasSignalOnTrackdir(gp.new_tile, i)) {
3466 first->cur_speed = 0;
3467 first->subspeed = 0;
3468 first->progress = 255; // make sure that every bit of acceleration will hit the signal again, so speed stays 0.
3469 if (!_settings_game.pf.reverse_at_signals || ++first->wait_counter < _settings_game.pf.wait_twoway_signal * Ticks::DAY_TICKS * 2) {
3470 DiagDirection exitdir = TrackdirToExitdir(i);
3471 TileIndex o_tile = TileAddByDiagDir(gp.new_tile, exitdir);
3472
3473 exitdir = ReverseDiagDir(exitdir);
3474
3475 /* check if a train is waiting on the other side */
3476 if (!HasVehicleOnTile(o_tile, [&exitdir](const Vehicle *u) {
3477 if (u->type != VehicleType::Train || u->vehstatus.Test(VehState::Crashed)) return false;
3478 const Train *t = Train::From(u);
3479
3480 /* not front engine of a train, inside wormhole or depot, crashed */
3481 if (!t->IsFrontEngine() || t->track.Any({Track::Wormhole, Track::Depot})) return false;
3482
3483 if (t->cur_speed > 5 || VehicleExitDir(t->direction, t->track) != exitdir) return false;
3484
3485 return true;
3486 })) return false;
3487 }
3488 }
3489
3490 /* If we would reverse but are currently in a PBS block and
3491 * reversing of stuck trains is disabled, don't reverse.
3492 * This does not apply if the reason for reversing is a one-way
3493 * signal blocking us, because a train would then be stuck forever. */
3494 if (!_settings_game.pf.reverse_at_signals && !HasOnewaySignalBlockingTrackdir(gp.new_tile, i) &&
3495 UpdateSignalsOnSegment(v->tile, enterdir, v->owner) == SigSegState::Path) {
3496 first->wait_counter = 0;
3497 return false;
3498 }
3499 goto reverse_train_direction;
3500 } else {
3501 TryReserveRailTrack(gp.new_tile, TrackBitsToTrack(chosen_track), false);
3502 }
3503 } else {
3504 /* The wagon is active, simply follow the prev vehicle. */
3505 if (prev->tile == gp.new_tile) {
3506 /* Choose the same track as prev */
3507 if (prev->track == Track::Wormhole) {
3508 /* Vehicles entering tunnels enter the wormhole earlier than for bridges.
3509 * However, just choose the track into the wormhole. */
3510 assert(IsTunnel(prev->tile));
3511 chosen_track = bits;
3512 } else {
3513 chosen_track = prev->track;
3514 }
3515 } else {
3516 /* Choose the track that leads to the tile where prev is.
3517 * This case is active if 'prev' is already on the second next tile, when 'v' just enters the next tile.
3518 * I.e. when the tile between them has only space for a single vehicle like
3519 * 1) horizontal/vertical track tiles and
3520 * 2) some orientations of tunnel entries, where the vehicle is already inside the wormhole at 8/16 from the tile edge.
3521 * Is also the train just reversing, the wagon inside the tunnel is 'on' the tile of the opposite tunnel entry.
3522 */
3523 static const DiagDirectionIndexArray<DiagDirectionIndexArray<TrackBits>> _connecting_track{{{
3524 {{{Track::X, Track::Lower, {}, Track::Left }}},
3525 {{{Track::Upper, Track::Y, Track::Left, {} }}},
3528 }}};
3529 DiagDirection exitdir = DiagdirBetweenTiles(gp.new_tile, prev->tile);
3530 assert(IsValidDiagDirection(exitdir));
3531 chosen_track = _connecting_track[enterdir][exitdir];
3532 }
3533 chosen_track &= bits;
3534 }
3535
3536 /* Update XY to reflect the entrance to the new tile, and select the direction to use */
3537 Direction chosen_dir = VehicleEnterTileCoordinates(gp, enterdir, TrackBitsToTrack(chosen_track));
3538
3539 /* Call the landscape function and tell it that the vehicle entered the tile */
3540 auto vets = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
3541 if (vets.Test(VehicleEnterTileState::CannotEnter)) {
3542 goto invalid_rail;
3543 }
3544
3546 Track track = FindFirstTrack(chosen_track);
3547 Trackdir tdir = TrackDirectionToTrackdir(track, chosen_dir);
3548 if (v->IsMovingFront() && HasPbsSignalOnTrackdir(gp.new_tile, tdir)) {
3551 }
3552
3553 /* Clear any track reservation when the last vehicle leaves the tile */
3554 if (v->GetMovingNext() == nullptr) ClearPathReservation(v, v->tile, v->GetVehicleTrackdir());
3555
3556 v->tile = gp.new_tile;
3557
3559 first->ConsistChanged(CCF_TRACK);
3560 }
3561
3562 v->track = chosen_track;
3563 assert(v->track.Any());
3564 }
3565
3566 /* We need to update signal status, but after the vehicle position hash
3567 * has been updated by UpdateInclination() */
3568 update_signals_crossing = true;
3569
3570 if (chosen_dir != v->GetMovingDirection()) {
3571 if (prev == nullptr && _settings_game.vehicle.train_acceleration_model == AccelerationModel::Original) {
3572 const AccelerationSlowdownParams *asp = &_accel_slowdown[static_cast<int>(v->GetAccelerationType())];
3573 DirDiff diff = DirDifference(v->direction, chosen_dir);
3574 v->cur_speed -= (diff == DirDiff::Right45 || diff == DirDiff::Left45 ? asp->small_turn : asp->large_turn) * v->cur_speed >> 8;
3575 }
3576 direction_changed = true;
3577 v->SetMovingDirection(chosen_dir);
3578 }
3579
3580 if (v->IsMovingFront()) {
3581 first->wait_counter = 0;
3582
3583 /* If we are approaching a crossing that is reserved, play the sound now. */
3584 TileIndex crossing = TrainApproachingCrossingTile(v); // We know we are the moving front, so we can check v.
3585 if (crossing != INVALID_TILE && HasCrossingReservation(crossing) && _settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, crossing);
3586
3587 /* Always try to extend the reservation when entering a tile. */
3588 CheckNextTrainTile(first);
3589 }
3590
3592 /* The new position is the location where we want to stop */
3594 }
3595 }
3596 } else {
3598 /* Perform look-ahead on tunnel exit. */
3599 if (v->IsMovingFront()) {
3601 CheckNextTrainTile(first);
3602 }
3603 /* Prevent v->UpdateInclination() being called with wrong parameters.
3604 * This could happen if the train was reversed inside the tunnel/bridge. */
3605 if (gp.old_tile == gp.new_tile) {
3607 }
3608 } else {
3609 v->x_pos = gp.x;
3610 v->y_pos = gp.y;
3611 v->UpdatePosition();
3612 if (!v->vehstatus.Test(VehState::Hidden)) v->Vehicle::UpdateViewport(true);
3613 continue;
3614 }
3615 }
3616
3617 /* update image of train, as well as delta XY */
3618 v->UpdateDeltaXY();
3619
3620 v->x_pos = gp.x;
3621 v->y_pos = gp.y;
3622 v->UpdatePosition();
3623
3624 /* update the Z position of the vehicle */
3625 int old_z = v->UpdateInclination(gp.new_tile != gp.old_tile, false);
3626
3627 if (prev == nullptr) {
3628 /* This is the first vehicle in the train */
3629 AffectSpeedByZChange(first, v->z_pos - old_z);
3630 }
3631
3632 if (update_signals_crossing) {
3633 if (v->IsMovingFront()) {
3634 if (TrainMovedChangeSignals(gp.new_tile, enterdir)) {
3635 /* We are entering a block with PBS signals right now, but
3636 * not through a PBS signal. This means we don't have a
3637 * reservation right now. As a conventional signal will only
3638 * ever be green if no other train is in the block, getting
3639 * a path should always be possible. If the player built
3640 * such a strange network that it is not possible, the train
3641 * will be marked as stuck and the player has to deal with
3642 * the problem. */
3643 if ((!HasReservedTracks(gp.new_tile, v->track) &&
3645 !TryPathReserve(first)) {
3646 MarkTrainAsStuck(first);
3647 }
3648 }
3649 }
3650
3651 /* Signals can only change when the first
3652 * (above) or the last vehicle moves. */
3653 if (v->GetMovingNext() == nullptr) {
3654 TrainMovedChangeSignals(gp.old_tile, ReverseDiagDir(enterdir));
3656 }
3657 }
3658
3659 /* Do not check on every tick to save some computing time. */
3660 if (v->IsMovingFront() && first->tick_counter % _settings_game.pf.path_backoff_interval == 0) CheckNextTrainTile(first);
3661 }
3662
3663 if (direction_changed) first->tcache.cached_max_curve_speed = first->GetCurveSpeedLimit();
3664
3665 return true;
3666
3667invalid_rail:
3668 /* We've reached end of line?? */
3669 if (prev != nullptr) FatalError("Disconnecting train");
3670
3671reverse_train_direction:
3672 if (reverse) {
3673 first->wait_counter = 0;
3674 first->cur_speed = 0;
3675 first->subspeed = 0;
3676 ReverseTrainDirection(first);
3677 }
3678
3679 return false;
3680}
3681
3682static bool IsRailStationPlatformOccupied(TileIndex tile)
3683{
3685
3686 for (TileIndex t = tile; IsCompatibleTrainStationTile(t, tile); t -= delta) {
3687 if (HasVehicleOnTile(t, IsTrain)) return true;
3688 }
3689 for (TileIndex t = tile + delta; IsCompatibleTrainStationTile(t, tile); t += delta) {
3690 if (HasVehicleOnTile(t, IsTrain)) return true;
3691 }
3692
3693 return false;
3694}
3695
3703static void DeleteLastWagon(Train *v)
3704{
3705 Train *first = v->First();
3706
3707 /* Go to the last wagon and delete the link pointing there
3708 * new_last is then the one-before-last wagon, and v the last
3709 * one which will physically be removed */
3710 Train *new_last = v;
3711 for (; v->Next() != nullptr; v = v->Next()) new_last = v;
3712 new_last->SetNext(nullptr);
3713
3714 if (first != v) {
3715 /* Recalculate cached train properties */
3717 /* Update the depot window in case a part of the consist is in a depot. */
3718 SetWindowDirty(WindowClass::VehicleDepot, first->tile);
3719 SetWindowDirty(WindowClass::VehicleDepot, v->tile);
3720 }
3721
3722 /* 'v' shouldn't be accessed after it has been deleted */
3723 TrackBits trackbits = v->track;
3724 TileIndex tile = v->tile;
3725 Owner owner = v->owner;
3726
3727 delete v;
3728 v = nullptr; // make sure nobody will try to read 'v' anymore
3729
3730 if (trackbits == Track::Wormhole) {
3731 /* Vehicle is inside a wormhole, v->track contains no useful value then. */
3733 }
3734
3735 if (trackbits != Track::Depot && HasReservedTracks(tile, trackbits)) {
3736 Track track = TrackBitsToTrack(trackbits);
3737 UnreserveRailTrack(tile, track);
3738
3739 /* If there are still crashed vehicles on the tile, give the track reservation to them */
3740 TrackBits remaining_trackbits{};
3741 for (const Vehicle *u : VehiclesOnTile(tile)) {
3742 if (u->type != VehicleType::Train || !u->vehstatus.Test(VehState::Crashed)) continue;
3743 TrackBits train_tbits = Train::From(u)->track;
3744 if (train_tbits == Track::Wormhole) {
3745 /* Vehicle is inside a wormhole, u->track contains no useful value then. */
3746 remaining_trackbits.Set(DiagDirToDiagTrack(GetTunnelBridgeDirection(u->tile)));
3747 } else if (train_tbits != Track::Depot) {
3748 remaining_trackbits.Set(train_tbits);
3749 }
3750 }
3751
3752 /* It is important that these two are the first in the loop, as reservation cannot deal with every trackbit combination */
3754 for (Track t : remaining_trackbits) TryReserveRailTrack(tile, t);
3755 }
3756
3757 /* check if the wagon was on a road/rail-crossing */
3759
3760 if (IsRailStationTile(tile)) {
3761 bool occupied = IsRailStationPlatformOccupied(tile);
3763 SetRailStationPlatformReservation(tile, dir, occupied);
3765 }
3766
3767 /* Update signals */
3770 } else {
3771 SetSignalsOnBothDir(tile, TrackBitsToTrack(trackbits), owner);
3772 }
3773}
3774
3780{
3781 static const DirDiff delta[] = {
3783 };
3784
3785 do {
3786 /* We don't need to twist around vehicles if they're not visible */
3787 if (!v->vehstatus.Test(VehState::Hidden)) {
3788 v->direction = ChangeDir(v->direction, delta[GB(Random(), 0, 2)]);
3789 /* Refrain from updating the z position of the vehicle when on
3790 * a bridge, because UpdateInclination() will put the vehicle under
3791 * the bridge in that case */
3792 if (v->track != Track::Wormhole) {
3793 v->UpdatePosition();
3794 v->UpdateInclination(false, true);
3795 } else {
3796 v->UpdateViewport(false, true);
3797 }
3798 }
3799 } while ((v = v->Next()) != nullptr);
3800}
3801
3808{
3809 int state = ++v->crash_anim_pos;
3810
3811 if (state == 4 && !v->vehstatus.Test(VehState::Hidden)) {
3813 }
3814
3815 uint32_t r;
3816 if (state <= 200 && Chance16R(1, 7, r)) {
3817 int index = (r * 10 >> 16);
3818
3819 Vehicle *u = v;
3820 do {
3821 if (--index < 0) {
3822 r = Random();
3823
3825 GB(r, 8, 3) + 2,
3826 GB(r, 16, 3) + 2,
3827 GB(r, 0, 3) + 5,
3829 break;
3830 }
3831 } while ((u = u->Next()) != nullptr);
3832 }
3833
3834 if (state <= 240 && !(v->tick_counter & 3)) ChangeTrainDirRandomly(v);
3835
3836 if (state >= 4440 && !(v->tick_counter & 0x1F)) {
3837 bool ret = v->Next() != nullptr;
3838 DeleteLastWagon(v);
3839 return ret;
3840 }
3841
3842 return true;
3843}
3844
3846static const uint16_t _breakdown_speeds[16] = {
3847 225, 210, 195, 180, 165, 150, 135, 120, 105, 90, 75, 60, 45, 30, 15, 15
3848};
3849
3850
3859static bool TrainApproachingLineEnd(Train *moving_front, bool signal, bool reverse)
3860{
3861 /* Calc position within the current tile */
3862 uint x = moving_front->x_pos & 0xF;
3863 uint y = moving_front->y_pos & 0xF;
3864
3865 Direction vdir = moving_front->GetMovingDirection();
3866
3867 /* for diagonal directions, 'x' will be 0..15 -
3868 * for other directions, it will be 1, 3, 5, ..., 15 */
3869 switch (vdir) {
3870 case Direction::N : x = ~x + ~y + 25; break;
3871 case Direction::NW: x = y; [[fallthrough]];
3872 case Direction::NE: x = ~x + 16; break;
3873 case Direction::E : x = ~x + y + 9; break;
3874 case Direction::SE: x = y; break;
3875 case Direction::S : x = x + y - 7; break;
3876 case Direction::W : x = ~y + x + 9; break;
3877 default: break;
3878 }
3879
3880 Train *consist = moving_front->First();
3881
3882 /* Do not reverse when approaching red signal. Make sure the vehicle's front
3883 * does not cross the tile boundary when we do reverse, but as the vehicle's
3884 * location is based on their center, use half a vehicle's length as offset.
3885 * Multiply the half-length by two for straight directions to compensate that
3886 * we only get odd x offsets there. */
3887 uint8_t rounding = moving_front->IsDrivingBackwards() ? 0 : 1;
3888 if (!signal && x + (moving_front->gcache.cached_veh_length + rounding) / 2 * (IsDiagonalDirection(vdir) ? 1 : 2) >= TILE_SIZE) {
3889 /* we are too near the tile end, reverse now */
3890 consist->cur_speed = 0;
3891 if (reverse) ReverseTrainDirection(consist);
3892 return false;
3893 }
3894
3895 /* slow down */
3897 uint16_t break_speed = _breakdown_speeds[x & 0xF];
3898 if (break_speed < consist->cur_speed) consist->cur_speed = break_speed;
3899
3900 return true;
3901}
3902
3903
3909static bool TrainCanLeaveTile(const Train *moving_front)
3910{
3911 /* Exit if inside a tunnel/bridge or a depot */
3912 if (moving_front->track == Track::Wormhole || moving_front->track == Track::Depot) return false;
3913
3914 TileIndex tile = moving_front->tile;
3915
3916 /* entering a tunnel/bridge? */
3919 if (DiagDirToDir(dir) == moving_front->GetMovingDirection()) return false;
3920 }
3921
3922 /* entering a depot? */
3923 if (IsRailDepotTile(tile)) {
3925 if (DiagDirToDir(dir) == moving_front->GetMovingDirection()) return false;
3926 }
3927
3928 return true;
3929}
3930
3931
3940{
3941 assert(moving_front->IsMovingFront());
3942 assert(!moving_front->First()->vehstatus.Test(VehState::Crashed));
3943
3944 if (!TrainCanLeaveTile(moving_front)) return INVALID_TILE;
3945
3946 DiagDirection dir = VehicleExitDir(moving_front->GetMovingDirection(), moving_front->track);
3947 TileIndex tile = moving_front->tile + TileOffsByDiagDir(dir);
3948
3949 /* not a crossing || wrong axis || unusable rail (wrong type or owner) */
3950 if (!IsLevelCrossingTile(tile) || DiagDirToAxis(dir) == GetCrossingRoadAxis(tile) ||
3951 !CheckCompatibleRail(moving_front->First(), tile, true)) {
3952 return INVALID_TILE;
3953 }
3954
3955 return tile;
3956}
3957
3958
3966static bool TrainCheckIfLineEnds(Train *moving_front, bool reverse)
3967{
3968 /* First, handle broken down train */
3969
3970 Train *consist = moving_front->First();
3971 int t = consist->breakdown_ctr;
3972 if (t > 1) {
3974
3975 uint16_t break_speed = _breakdown_speeds[GB(~t, 4, 4)];
3976 if (break_speed < consist->cur_speed) consist->cur_speed = break_speed;
3977 } else {
3979 }
3980
3981 if (!TrainCanLeaveTile(moving_front)) return true;
3982
3983 /* Determine the non-diagonal direction in which we will exit this tile */
3984 DiagDirection dir = VehicleExitDir(moving_front->GetMovingDirection(), moving_front->track);
3985 /* Calculate next tile */
3986 TileIndex tile = moving_front->tile + TileOffsByDiagDir(dir);
3987
3988 /* Determine the track status on the next tile */
3990 TrackdirBits reachable_trackdirs = DiagdirReachesTrackdirs(dir);
3991
3992 TrackdirBits trackdirbits = ts.trackdirs & reachable_trackdirs;
3993 TrackdirBits red_signals = ts.signals & reachable_trackdirs;
3994
3995 /* We are sure the train is not entering a depot, it is detected above */
3996
3997 /* mask unreachable track bits if we are forbidden to do 90deg turns */
3998 TrackBits bits = TrackdirBitsToTrackBits(trackdirbits);
3999 if (Rail90DegTurnDisallowed(GetTileRailType(moving_front->tile), GetTileRailType(tile))) {
4000 bits.Reset(TrackCrossesTracks(FindFirstTrack(moving_front->track)));
4001 }
4002
4003 /* no suitable trackbits at all || unusable rail (wrong type or owner) */
4004 if (bits.None() || !CheckCompatibleRail(consist, tile, true)) {
4005 return TrainApproachingLineEnd(moving_front, false, reverse);
4006 }
4007
4008 /* approaching red signal */
4009 if (trackdirbits.Any(red_signals)) return TrainApproachingLineEnd(moving_front, true, reverse);
4010
4011 /* approaching a rail/road crossing? then make it red */
4013
4014 return true;
4015}
4016
4023static bool TrainLocoHandler(Train *consist, bool mode)
4024{
4025 /* train has crashed? */
4026 if (consist->vehstatus.Test(VehState::Crashed)) {
4027 return mode ? true : HandleCrashedTrain(consist); // 'this' can be deleted here
4028 }
4029
4030 if (consist->force_proceed != TFP_NONE) {
4032 SetWindowWidgetDirty(WindowClass::VehicleView, consist->index, WID_VV_START_STOP);
4033 }
4034
4035 /* train is broken down? */
4036 if (consist->HandleBreakdown()) return true;
4037
4038 if (consist->flags.Test(VehicleRailFlag::Reversing) && consist->cur_speed == 0) {
4039 ReverseTrainDirection(consist);
4040 }
4041
4042 /* exit if train is stopped */
4043 if (consist->vehstatus.Test(VehState::Stopped) && consist->cur_speed == 0) return true;
4044
4045 bool valid_order = !consist->current_order.IsType(OT_NOTHING) && consist->current_order.GetType() != OT_CONDITIONAL;
4046 if (ProcessOrders(consist) && CheckReverseTrain(consist)) {
4047 consist->wait_counter = 0;
4048 consist->cur_speed = 0;
4049 consist->subspeed = 0;
4051 ReverseTrainDirection(consist);
4052 return true;
4053 } else if (consist->flags.Test(VehicleRailFlag::LeavingStation)) {
4054 /* Try to reserve a path when leaving the station as we
4055 * might not be marked as wanting a reservation, e.g.
4056 * when an overlength train gets turned around in a station. */
4057 const Train *moving_front = consist->GetMovingFront();
4058 DiagDirection dir = VehicleExitDir(moving_front->GetMovingDirection(), moving_front->track);
4059 if (IsRailDepotTile(moving_front->tile) || IsTileType(moving_front->tile, TileType::TunnelBridge)) dir = DiagDirection::Invalid;
4060
4061 if (UpdateSignalsOnSegment(moving_front->tile, dir, consist->owner) == SigSegState::Path || _settings_game.pf.reserve_paths) {
4062 TryPathReserve(consist, true, true);
4063 }
4065 }
4066
4067 consist->HandleLoading(mode);
4068
4069 if (consist->current_order.IsType(OT_LOADING)) return true;
4070
4071 if (CheckTrainStayInDepot(consist)) return true;
4072
4073 if (!mode) consist->ShowVisualEffect();
4074
4075 /* We had no order but have an order now, do look ahead. */
4076 if (!valid_order && !consist->current_order.IsType(OT_NOTHING)) {
4077 CheckNextTrainTile(consist);
4078 }
4079
4080 /* Handle stuck trains. */
4081 if (!mode && consist->flags.Test(VehicleRailFlag::Stuck)) {
4082 ++consist->wait_counter;
4083
4084 /* Should we try reversing this tick if still stuck? */
4085 bool turn_around = consist->wait_counter % (_settings_game.pf.wait_for_pbs_path * Ticks::DAY_TICKS) == 0 && _settings_game.pf.reverse_at_signals;
4086
4087 if (!turn_around && consist->wait_counter % _settings_game.pf.path_backoff_interval != 0 && consist->force_proceed == TFP_NONE) return true;
4088 if (!TryPathReserve(consist)) {
4089 /* Still stuck. */
4090 if (turn_around) ReverseTrainDirection(consist);
4091
4092 if (consist->flags.Test(VehicleRailFlag::Stuck) && consist->wait_counter > 2 * _settings_game.pf.wait_for_pbs_path * Ticks::DAY_TICKS) {
4093 /* Show message to player. */
4094 if (_settings_client.gui.lost_vehicle_warn && consist->owner == _local_company) {
4095 AddVehicleAdviceNewsItem(AdviceType::TrainStuck, GetEncodedString(STR_NEWS_TRAIN_IS_STUCK, consist->index), consist->index);
4096 }
4097 consist->wait_counter = 0;
4098 }
4099 /* Exit if force proceed not pressed, else reset stuck flag anyway. */
4100 if (consist->force_proceed == TFP_NONE) return true;
4102 consist->wait_counter = 0;
4103 SetWindowWidgetDirty(WindowClass::VehicleView, consist->index, WID_VV_START_STOP);
4104 }
4105 }
4106
4107 if (consist->current_order.IsType(OT_LEAVESTATION)) {
4108 consist->current_order.Free();
4109 SetWindowWidgetDirty(WindowClass::VehicleView, consist->index, WID_VV_START_STOP);
4110 return true;
4111 }
4112
4113 int j = consist->UpdateSpeed();
4114
4115 /* we need to invalidate the widget if we are stopping from 'Stopping 0 km/h' to 'Stopped' */
4116 if (consist->cur_speed == 0 && consist->vehstatus.Test(VehState::Stopped)) {
4117 /* If we manually stopped, we're not force-proceeding anymore. */
4118 consist->force_proceed = TFP_NONE;
4119 InvalidateWindowData(WindowClass::VehicleView, consist->index);
4120 }
4121
4122 Train* moving_front = consist->GetMovingFront();
4123 int adv_spd = moving_front->GetAdvanceDistance();
4124 if (j < adv_spd) {
4125 /* if the vehicle has speed 0, update the last_speed field. */
4126 if (consist->cur_speed == 0) consist->SetLastSpeed();
4127 } else {
4128 TrainCheckIfLineEnds(moving_front);
4129 moving_front = moving_front->GetMovingFront();
4130 /* Loop until the train has finished moving. */
4131 for (;;) {
4132 j -= adv_spd;
4133 TrainController(moving_front, nullptr);
4134 moving_front = moving_front->GetMovingFront();
4135 /* Don't continue to move if the train crashed. */
4136 if (CheckTrainCollision(moving_front)) break;
4137 /* Determine distance to next map position */
4138 adv_spd = moving_front->GetAdvanceDistance();
4139
4140 /* No more moving this tick */
4141 if (j < adv_spd || consist->cur_speed == 0) break;
4142
4143 OrderType order_type = consist->current_order.GetType();
4144 /* Do not skip waypoints (incl. 'via' stations) when passing through at full speed. */
4145 if ((order_type == OT_GOTO_WAYPOINT || order_type == OT_GOTO_STATION) &&
4147 IsTileType(moving_front->tile, TileType::Station) &&
4148 consist->current_order.GetDestination() == GetStationIndex(moving_front->tile)) {
4149 ProcessOrders(consist);
4150 }
4151 }
4152 consist->SetLastSpeed();
4153 }
4154
4155 for (Train *u = consist; u != nullptr; u = u->Next()) {
4156 if (u->vehstatus.Test(VehState::Hidden)) continue;
4157
4158 u->UpdateViewport(false, false);
4159 }
4160
4161 if (consist->progress == 0) consist->progress = j; // Save unused spd for next time, if TrainController didn't set progress
4162
4163 return true;
4164}
4165
4171{
4172 Money cost = 0;
4173 const Train *v = this;
4174
4175 do {
4176 const Engine *e = v->GetEngine();
4177 if (e->VehInfo<RailVehicleInfo>().running_cost_class == Price::Invalid) continue;
4178
4179 uint cost_factor = GetVehicleProperty(v, PROP_TRAIN_RUNNING_COST_FACTOR, e->VehInfo<RailVehicleInfo>().running_cost);
4180 if (cost_factor == 0) continue;
4181
4182 /* Halve running cost for multiheaded parts */
4183 if (v->IsMultiheaded()) cost_factor /= 2;
4184
4185 cost += GetPrice(e->VehInfo<RailVehicleInfo>().running_cost_class, cost_factor, e->GetGRF());
4186 } while ((v = v->GetNextVehicle()) != nullptr);
4187
4188 return cost;
4189}
4190
4196{
4197 this->tick_counter++;
4198
4199 if (this->IsFrontEngine()) {
4201
4202 if (!this->vehstatus.Test(VehState::Stopped) || this->cur_speed > 0) this->running_ticks++;
4203
4204 this->current_order_time++;
4205
4206 if (!TrainLocoHandler(this, false)) return false;
4207
4208 return TrainLocoHandler(this, true);
4209 } else if (this->IsFreeWagon() && this->vehstatus.Test(VehState::Crashed)) {
4210 /* Delete flooded standalone wagon chain */
4211 if (++this->crash_anim_pos >= 4400) {
4212 delete this;
4213 return false;
4214 }
4215 }
4216
4217 return true;
4218}
4219
4225{
4226 if (Company::Get(v->owner)->settings.vehicle.servint_trains == 0 || !v->NeedsAutomaticServicing()) return;
4227 if (v->IsChainInDepot()) {
4229 return;
4230 }
4231
4232 uint max_penalty = _settings_game.pf.yapf.maximum_go_to_depot_penalty;
4233
4234 FindDepotData tfdd = FindClosestTrainDepot(v, max_penalty);
4235 /* Only go to the depot if it is not too far out of our way. */
4236 if (tfdd.best_length == UINT_MAX || tfdd.best_length > max_penalty) {
4237 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
4238 /* If we were already heading for a depot but it has
4239 * suddenly moved farther away, we continue our normal
4240 * schedule? */
4242 SetWindowWidgetDirty(WindowClass::VehicleView, v->index, WID_VV_START_STOP);
4243 }
4244 return;
4245 }
4246
4247 DepotID depot = GetDepotIndex(tfdd.tile);
4248
4249 if (v->current_order.IsType(OT_GOTO_DEPOT) &&
4250 v->current_order.GetDestination() != depot &&
4251 !Chance16(3, 16)) {
4252 return;
4253 }
4254
4257 v->dest_tile = tfdd.tile;
4258 SetWindowWidgetDirty(WindowClass::VehicleView, v->index, WID_VV_START_STOP);
4259}
4260
4263{
4264 AgeVehicle(this);
4265}
4266
4269{
4270 EconomyAgeVehicle(this);
4271
4272 if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
4273
4274 if (this->IsFrontEngine()) {
4276
4278
4279 CheckOrders(this);
4280
4281 /* update destination */
4282 if (this->current_order.IsType(OT_GOTO_STATION)) {
4283 TileIndex tile = Station::Get(this->current_order.GetDestination().ToStationID())->train_station.tile;
4284 if (tile != INVALID_TILE) this->dest_tile = tile;
4285 }
4286
4287 if (this->running_ticks != 0) {
4288 /* running costs */
4290
4291 this->profit_this_year -= cost.GetCost();
4292 this->running_ticks = 0;
4293
4295
4296 SetWindowDirty(WindowClass::VehicleDetails, this->index);
4297 SetWindowClassesDirty(WindowClass::TrainList);
4298 }
4299 }
4300}
4301
4307{
4308 if (this->vehstatus.Test(VehState::Crashed)) return Trackdir::Invalid;
4309
4310 if (this->track == Track::Depot) {
4311 /* We'll assume the train is facing outwards */
4312 return DiagDirToDiagTrackdir(GetRailDepotDirection(this->tile)); // Train in depot
4313 }
4314
4315 if (this->track == Track::Wormhole) {
4316 /* train in tunnel or on bridge, so just use its direction and assume a diagonal track */
4318 }
4319
4320 return TrackDirectionToTrackdir(FindFirstTrack(this->track), this->GetMovingDirection());
4321}
4322
4323uint16_t Train::GetMaxWeight() const
4324{
4325 uint16_t weight = CargoSpec::Get(this->cargo_type)->WeightOfNUnitsInTrain(this->GetEngine()->DetermineCapacity(this));
4326
4327 /* Vehicle weight is not added for articulated parts. */
4328 if (!this->IsArticulatedPart()) {
4329 weight += GetVehicleProperty(this, PROP_TRAIN_WEIGHT, RailVehInfo(this->engine_type)->weight);
4330 }
4331
4332 /* Powered wagons have extra weight added. */
4333 if (this->flags.Test(VehicleRailFlag::PoweredWagon)) {
4334 weight += RailVehInfo(this->gcache.first_engine)->pow_wag_weight;
4335 }
4336
4337 return weight;
4338}
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.
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 bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
const BridgeSpec * GetBridgeSpec(BridgeType i)
Get the specification of a bridge type.
Definition bridge.h:63
bool IsBridgeTile(Tile t)
checks if there is a bridge on this tile
Definition bridge_map.h:35
BridgeType GetBridgeType(Tile t)
Determines the type of bridge on a tile.
Definition bridge_map.h:56
bool IsBridge(Tile t)
Checks if this is a bridge, instead of a tunnel.
Definition bridge_map.h:24
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:110
CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:22
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition ai_core.cpp:231
uint Count() const
Count the number of set bits.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Tstorage base() const noexcept
Retrieve the raw value behind this bit set.
constexpr bool None() const
Test if none of the values are set.
constexpr Timpl & Flip()
Flip all bits.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
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?
uint16_t reliability_spd_dec
Speed of reliability decay between services (per day).
Definition engine_base.h:52
const GRFFile * GetGRF() const
Retrieve the NewGRF the engine is tied to.
GrfID GetGRFID() const
Retrieve the GRF ID of the NewGRF the engine is tied to.
Definition engine.cpp:183
uint DetermineCapacity(const Vehicle *v, uint16_t *mail_capacity=nullptr) const
Determines capacity of a given vehicle from scratch.
Definition engine.cpp:227
EngineFlags flags
Flags of the engine.
Definition engine_base.h:59
uint8_t original_image_index
Original vehicle image index, thus the image index of the overridden vehicle.
Definition engine_base.h:63
TimerGameCalendar::Date GetLifeLengthInDays() const
Returns the vehicle's (not model's!) life length in days.
Definition engine.cpp:469
CargoType GetDefaultCargoType() const
Determines the default cargo type of an engine.
Definition engine_base.h:96
uint16_t reliability
Current reliability of the engine.
Definition engine_base.h:51
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:117
uint8_t curve_speed
Multiplier for curve maximum speed advantage.
Definition rail.h:197
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,...
EnumBitSet< DoCommandFlag, uint16_t > DoCommandFlags
Bitset of DoCommandFlag elements.
Definition of stuff that is very close to a company, like the company struct itself.
CommandCost CheckOwnership(Owner owner, TileIndex tile)
Check whether the current owner owns something.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
void SubtractMoneyFromCompanyFract(CompanyID company, const CommandCost &cst)
Subtract money from a company, including the money fraction.
Functions related to companies.
DepotID GetDepotIndex(Tile t)
Get the index of which depot is attached to the tile.
Definition depot_map.h:56
PoolID< uint16_t, struct DepotIDTag, 64000, 0xFFFF > DepotID
Type for the unique identifier of depots.
Definition depot_type.h:15
DirDiff DirDifference(Direction d0, Direction d1)
Calculate the difference between two directions.
Direction DiagDirToDir(DiagDirection dir)
Convert a DiagDirection to a Direction.
DiagDirection ReverseDiagDir(DiagDirection d)
Returns the reverse direction of the given DiagDirection.
DiagDirections AxisToDiagDirs(Axis a)
Converts an Axis to DiagDirections.
Direction ReverseDir(Direction d)
Return the reverse of a direction.
bool IsValidDiagDirection(DiagDirection d)
Checks if an integer value is a valid DiagDirection.
Direction ChangeDir(Direction d, DirDiff delta)
Change a direction by a given difference.
DiagDirection AxisToDiagDir(Axis a)
Converts an Axis to a DiagDirection.
bool IsDiagonalDirection(Direction dir)
Checks if a given Direction is diagonal.
Axis DiagDirToAxis(DiagDirection d)
Convert a DiagDirection to the axis.
DiagDirection DirToDiagDir(Direction dir)
Convert a Direction to a DiagDirection.
DirDiff
Enumeration for the difference between two directions.
@ Left45
Angle of 45 degrees left.
@ Left90
Angle of 90 degrees left.
@ Same
Both directions faces to the same direction.
@ Right45
Angle of 45 degrees right.
@ Right90
Angle of 90 degrees right.
EnumIndexArray< T, DiagDirection, DiagDirection::End > DiagDirectionIndexArray
Array with DiagDirection as index.
Direction
Defines the 8 directions on the map.
@ SW
Southwest.
@ NW
Northwest.
@ NE
Northeast.
@ SE
Southeast.
Axis
Enumeration for the two axis X and Y.
DiagDirection
Enumeration for diagonal directions.
@ Begin
Used for iterations.
@ Invalid
Flag for an invalid DiagDirection.
EnumBitSet< DiagDirection, uint8_t > DiagDirections
Bitset of DiagDirection elements.
Money GetPrice(Price index, uint cost_factor, const GRFFile *grf_file, int shift)
Determine a certain price.
Definition economy.cpp:937
@ TrainRun
Running costs trains.
@ NewVehicles
New vehicles.
@ Invalid
Invalid base price.
EffectVehicle * CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicleType type)
Create an effect vehicle above a particular vehicle.
Functions related to effect vehicles.
@ EV_EXPLOSION_SMALL
Various explosions.
@ EV_EXPLOSION_LARGE
Various explosions.
@ RailFlips
Rail vehicle has old depot-flip handling.
@ RailTilts
Rail vehicle tilts in curves.
@ VE_DISABLE_WAGON_POWER
Flag to disable wagon power.
Definition engine_type.h:68
PoolID< uint16_t, struct EngineIDTag, 64000, 0xFFFF > EngineID
Unique identification number of an engine.
Definition engine_type.h:26
@ ExclusivePreview
This vehicle is in the exclusive preview stage, either being used or being offered to a company.
@ Multihead
indicates a combination of two locomotives
Definition engine_type.h:33
@ Wagon
simple wagon, not motorized
Definition engine_type.h:34
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
Functions related to errors.
@ Critical
Critical errors, the MessageBox is shown in all cases.
Definition error.h:25
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.
@ GameLoopTrains
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.
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:581
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:80
@ 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(GrfID 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(GrfID grfid, GRFStringID stringid)
Returns the index for this stringid associated with its grfID.
Header of Action 04 "universal holder" structure and functions.
StrongType::Typedef< uint32_t, struct GRFStringIDTag, StrongType::Compare, StrongType::Integer > GRFStringID
Type for GRF-internal string IDs.
static constexpr GRFStringID GRFSTR_MISC_GRF_TEXT
Miscellaneous GRF text range.
Functions related to news.
void AddVehicleNewsItem(EncodedString &&headline, NewsType type, VehicleID vehicle, StationID station=StationID::Invalid())
Adds a newsitem referencing a vehicle.
Definition news_func.h:32
void AddVehicleAdviceNewsItem(AdviceType advice_type, EncodedString &&headline, VehicleID vehicle)
Adds a vehicle-advice news item.
Definition news_func.h:43
@ ArrivalCompany
First vehicle arrived for company.
Definition news_type.h:30
@ ArrivalOther
First vehicle arrived for competitor.
Definition news_type.h:31
@ Accident
An accident or disaster has occurred.
Definition news_type.h:32
@ TrainStuck
The train got stuck and needs to be unstuck manually.
Definition news_type.h:56
@ Error
A game paused because a (critical) error.
Definition openttd.h:75
Functions related to order backups.
bool ProcessOrders(Vehicle *v)
Handle the orders of a vehicle and determine the next place to go to if needed.
bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool pbs_look_ahead)
Update the vehicle's destination tile from an order.
void CheckOrders(const Vehicle *v)
Check the orders of a vehicle, to see if there are invalid orders and stuff.
void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist, bool reset_order_indices)
Delete all orders from a vehicle.
VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v)
Process a conditional order and determine the next order.
OrderStopLocation
Where to stop the trains.
Definition order_type.h:98
@ NearEnd
Stop at the near end of the platform.
Definition order_type.h:99
@ FarEnd
Stop at the far end of the platform.
Definition order_type.h:101
@ Middle
Stop at the middle of the platform.
Definition order_type.h:100
@ 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:121
@ Service
This depot order is because of the servicing limit.
Definition order_type.h:109
static const VehicleOrderID INVALID_VEH_ORDER_ID
Invalid vehicle order index (sentinel).
Definition order_type.h:39
OrderType
Order types.
Definition order_type.h:50
void SetRailStationPlatformReservation(TileIndex start, DiagDirection dir, bool b)
Set the reservation for a complete station platform.
Definition pbs.cpp:57
TrackBits GetReservedTrackbits(TileIndex t)
Get the reserved trackbits for any tile, regardless of type.
Definition pbs.cpp:24
bool TryReserveRailTrack(TileIndex tile, Track t, bool trigger_stations)
Try to reserve a specific track on a tile.
Definition pbs.cpp:80
bool IsWaitingPositionFree(const Train *v, TileIndex tile, Trackdir trackdir, bool forbid_90deg)
Check if a safe position is free.
Definition pbs.cpp:439
void UnreserveRailTrack(TileIndex tile, Track t)
Lift the reservation of a specific track on a tile.
Definition pbs.cpp:144
PBSTileInfo FollowTrainReservation(const Train *consist, Vehicle **train_on_res)
Follow a train reservation to the last tile.
Definition pbs.cpp:301
bool IsSafeWaitingPosition(const Train *v, TileIndex tile, Trackdir trackdir, bool include_line_end, bool forbid_90deg)
Determine whether a certain track on a tile is a safe position to end a path.
Definition pbs.cpp:395
bool HasReservedTracks(TileIndex tile, TrackBits tracks)
Check whether some of tracks is reserved on a tile.
Definition pbs.h:58
RailType GetTileRailType(Tile tile)
Return the rail type of tile, or INVALID_RAILTYPE if this is no rail tile.
Definition rail.cpp:39
int TicksToLeaveDepot(const Train *v)
Compute number of ticks when next wagon will leave a depot.
RailTypes GetAllPoweredRailTypes(RailTypes railtypes)
Returns all powered railtypes for a set of railtypes.
Definition rail.h:327
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:354
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:379
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:413
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:303
RailTypes GetAllCompatibleRailTypes(RailTypes railtypes)
Returns all compatible railtypes for a set of railtypes.
Definition rail.h:315
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:547
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:490
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:291
@ 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:519
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:474
bool HasPbsSignalOnTrackdir(Tile tile, Trackdir td)
Is a pbs signal present along the trackdir?
Definition rail_map.h:534
bool IsOnewaySignal(Tile t, Track track)
Is the signal at the given track on a tile a one way signal?
Definition rail_map.h:357
SignalType GetSignalType(Tile t, Track track)
Get the signal type for a track on a tile.
Definition rail_map.h:303
void SetDepotReservation(Tile t, bool b)
Set the reservation state of the depot.
Definition rail_map.h:268
bool HasDepotReservation(Tile t)
Get the reservation state of the depot.
Definition rail_map.h:256
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:505
static bool IsRailDepotTile(Tile t)
Is this tile rail tile and a rail depot?
Definition rail_map.h:105
bool HasBlockSignalOnTrackdir(Tile tile, Trackdir td)
Check whether a block signal is present along the trackdir.
Definition rail_map.h:559
@ RAILTYPE_ELECTRIC
Electric rails.
Definition rail_type.h:29
@ RAILTYPE_RAIL
Standard non-electric rails.
Definition rail_type.h:28
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:379
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:427
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:415
void SetCrossingReservation(Tile t, bool b)
Set the reservation state of the rail crossing.
Definition road_map.h:392
@ Invalid
Invalid marker.
Definition road_type.h:42
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:656
void UpdateSignalsInBuffer()
Update signals in buffer Called from 'outside'.
Definition signal.cpp:582
void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner)
Add side of tile to signal update buffer.
Definition signal.cpp:630
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:674
SigSegState
State of the signal segment.
Definition signal_func.h:55
@ Path
Segment is a path segment.
Definition signal_func.h:58
@ Full
Occupied by a train.
Definition signal_func.h:57
@ Path
normal path signal.
Definition signal_type.h:29
@ Green
The signal is green.
Definition signal_type.h:42
@ Red
The signal is red.
Definition signal_type.h:41
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:1794
static const SpriteID SPR_IMG_QUERY
Definition sprites.h:1275
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.
@ Train
Station with train station.
@ VehicleArrives
Trigger platform when train arrives.
@ VehicleArrives
Trigger platform when train arrives.
@ Train
Station has seen a train.
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:261
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
Functions related to OTTD's strings.
StrongType::Typedef< uint32_t, struct StringIDTag, StrongType::Compare, StrongType::Integer > 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:41
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:141
Structure to return information about the closest depot location, and whether it could be found.
T y
Y coordinate.
T x
X coordinate.
uint16_t cargo_age_period
Number of ticks before carried cargo is aged.
EngineMiscFlags misc_flags
Miscellaneous flags.
VehicleCallbackMasks callback_mask
Bitmask of vehicle callbacks that have to be called.
Helper container to find a depot.
uint best_length
The distance towards the depot in penalty, or UINT_MAX if not found.
bool reverse
True if reversing is necessary for the train to get to this depot.
TileIndex tile
The tile of the depot.
Dynamic data of a loaded NewGRF.
Definition newgrf.h:125
uint traininfo_vehicle_width
Width (in pixels) of a 8/8 train vehicle in depot GUI and vehicle details.
Definition newgrf.h:166
int traininfo_vehicle_pitch
Vertical offset for drawing train images in depot GUI and vehicle details.
Definition newgrf.h:165
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.
GroundVehicleFlags gv_flags
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, ClientID 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:48
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:74
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 T * Create(Targs &&... args)
static Engine * Get(auto index)
static Vehicle * GetIfValid(auto index)
Information about a rail vehicle.
Definition engine_type.h:74
uint16_t power
Power of engine (hp); For multiheaded engines the sum of both engine powers.
Definition engine_type.h:82
uint8_t user_def_data
Property 0x25: "User-defined bit mask" Used only for (very few) NewGRF vehicles.
Definition engine_type.h:94
uint8_t running_cost
Running cost of engine; For multiheaded engines the sum of both running costs.
Definition engine_type.h:84
uint8_t shorten_factor
length on main map for this type is 8 - shorten_factor
Definition engine_type.h:91
RailTypes railtypes
Railtypes, mangled if elrail is disabled.
Definition engine_type.h:78
uint16_t pow_wag_power
Extra power applied to consist if wagon should be powered.
Definition engine_type.h:88
uint16_t max_speed
Maximum speed (1 unit = 1/1.6 mph = 1 km-ish/h).
Definition engine_type.h:81
RailVehicleType railveh_type
Type of rail vehicle.
Definition engine_type.h:76
uint8_t capacity
Cargo capacity of vehicle; For multiheaded engines the capacity of each single engine.
Definition engine_type.h:87
RailTypes intended_railtypes
Intended railtypes, regardless of elrail being enabled or disabled.
Definition engine_type.h:79
int Width() const
Get width of Rect.
int Height() const
Get height of Rect.
static Station * Get(auto index)
T * GetMovingFront() const
Get the moving front of the vehicle chain.
T * GetMovingPrev() const
Get the previous vehicle in the vehicle chain, relative to its current movement.
T * Next() const
Get next vehicle in the chain.
T * Previous() const
Get previous vehicle in the chain.
static Train * From(Vehicle *v)
T * First() const
Get the first vehicle in the chain.
T * GetNextVehicle() const
Get the next real (non-articulated part) vehicle in the consist.
void UpdateViewport(bool force_update, bool update_delta)
Update vehicle sprite- and position caches.
T * GetMovingNext() const
Get the next vehicle in the vehicle chain, relative to its current movement.
T * GetLastEnginePart()
Get the last part of an articulated engine.
T * GetFirstEnginePart()
Get the first part of an articulated engine.
T * Last()
Get the last vehicle in the chain.
T * GetMovingBack() const
Get the moving back of the vehicle chain.
Station data structure.
uint GetPlatformLength(TileIndex tile, DiagDirection dir) const override
Determines the REMAINING length of a platform, starting at (and including) the given tile.
Definition station.cpp:292
Track status of a tile.
Definition track_type.h:105
TrackdirBits signals
Red signals on the tile.
Definition track_type.h:107
TrackdirBits trackdirs
Trackdirs present on the tile.
Definition track_type.h:106
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:124
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:152
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:749
Direction GetMovingDirection() const
Get the moving direction of this vehicle chain.
void IncrementRealOrderIndex()
Advanced cur_real_order_index to the next real order, keeps care of the wrap-around and invalidates t...
bool IsStoppedInDepot() const
Check whether the vehicle is in the depot and stopped.
bool IsMovingFront() const
Is this vehicle the moving front of the vehicle chain?
Order * GetOrder(int index) const
Returns order 'index' of a vehicle or nullptr when it doesn't exists.
void LeaveStation()
Perform all actions when leaving a station.
Definition vehicle.cpp:2372
void AddToShared(Vehicle *shared_chain)
Adds this vehicle to a shared vehicle chain.
Definition vehicle.cpp:3023
VehicleCargoList cargo
The cargo this vehicle is carrying.
TimerGameEconomy::Date date_of_last_service
Last economy date the vehicle had a service at a depot.
uint16_t cargo_cap
total capacity
StationID last_loading_station
Last station the vehicle has stopped at and could possibly leave from with any cargo loaded.
VehicleOrderID GetNumOrders() const
Get the number of orders this vehicle has.
void SetMovingDirection(Direction d)
Set the movement direction of this vehicle chain.
uint16_t random_bits
Bits used for randomized variational spritegroups.
void ReleaseUnitNumber()
Release the vehicle's unit number.
Definition vehicle.cpp:2442
uint8_t day_counter
Increased by one for each day.
void HandleLoading(bool mode=false)
Handle the loading of the vehicle; when not it skips through dummy orders and does nothing in all oth...
Definition vehicle.cpp:2453
Money profit_this_year
Profit this year << 8, low 8 bits are fract.
SpriteID colourmap
NOSAVE: cached colour mapping.
uint8_t breakdown_ctr
Counter for managing breakdown events.
uint GetAdvanceDistance()
Determines the vehicle "progress" needed for moving a step.
GroupID group_id
Index of group Pool array.
VehStates vehstatus
Status.
TimerGameCalendar::Date date_of_last_service_newgrf
Last calendar date the vehicle had a service at a depot, unchanged by the date cheat to protect again...
uint8_t subspeed
fractional speed
bool IsArticulatedPart() const
Check if the vehicle is an articulated part of an engine.
void LeaveUnbunchingDepot()
Leave an unbunching depot and calculate the next departure time for shared order vehicles.
Definition vehicle.cpp:2533
CargoType cargo_type
type of cargo this vehicle is carrying
uint8_t acceleration
used by train & aircraft
Vehicle * First() const
Get the first vehicle of this vehicle chain.
Order current_order
The current order (+ status, like: loading).
void HandlePathfindingResult(bool path_found)
Handle the pathfinding result, especially the lost status.
Definition vehicle.cpp:793
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:759
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.
SpriteBounds bounds
Bounding box of vehicle.
void BeginLoading()
Prepare everything to begin the loading when arriving at a station.
Definition vehicle.cpp:2228
uint8_t spritenum
currently displayed sprite index 0xfd == custom sprite, 0xfe == custom second head sprite 0xff == res...
uint16_t cur_speed
current speed
uint8_t cargo_subtype
Used for livery refits (NewGRF variations).
bool IsFrontEngine() const
Check if the vehicle is a front engine.
bool IsWaitingForUnbunching() const
Check whether a vehicle inside a depot is waiting for unbunching.
Definition vehicle.cpp:2580
TextEffectID fill_percent_te_id
a text-effect id to a loading indicator object
void SetNext(Vehicle *next)
Set the next vehicle of this vehicle.
Definition vehicle.cpp:2987
TimerGameCalendar::Date max_age
Maximum age.
MutableSpriteCache sprite_cache
Cache of sprites and values related to recalculating them, see MutableSpriteCache.
uint16_t reliability
Reliability.
bool HandleBreakdown()
Handle all of the aspects of a vehicle breakdown This includes adding smoke and sounds,...
Definition vehicle.cpp:1375
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:1700
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:2835
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:293
uint8_t running_ticks
Number of ticks this vehicle was not stopped this day.
GrfID GetGRFID() const
Retrieve the GRF ID of the NewGRF the vehicle is tied to.
Definition vehicle.cpp:769
@ 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:1864
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.
Track TrackdirToTrack(Trackdir trackdir)
Returns the Track that a given Trackdir represents.
Definition track_func.h:235
TrackdirBits TrackBitsToTrackdirBits(TrackBits bits)
Converts TrackBits to TrackdirBits while allowing both directions.
Definition track_func.h:292
DiagDirection VehicleExitDir(Direction direction, TrackBits track)
Determine the side in which the vehicle will leave the tile.
Definition track_func.h:609
Track TrackBitsToTrack(TrackBits tracks)
Converts TrackBits to Track.
Definition track_func.h:166
TrackBits TrackCrossesTracks(Track track)
Maps a track to all tracks that make 90 deg turns with it.
Definition track_func.h:324
Trackdir ReverseTrackdir(Trackdir trackdir)
Maps a trackdir to the reverse trackdir.
Definition track_func.h:220
bool TracksOverlap(TrackBits bits)
Checks if the given tracks overlap, ie form a crossing.
Definition track_func.h:540
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:405
bool IsValidTrackdir(Trackdir trackdir)
Checks if a Trackdir is valid for non-road vehicles.
Definition track_func.h:48
Trackdir FindFirstTrackdir(TrackdirBits trackdirs)
Returns first Trackdir from TrackdirBits or Trackdir::Invalid.
Definition track_func.h:184
TrackdirBits TrackdirCrossesTrackdirs(Trackdir trackdir)
Maps a trackdir to all trackdirs that make 90 deg turns with it.
Definition track_func.h:501
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:479
TrackdirBits DiagdirReachesTrackdirs(DiagDirection diagdir)
Returns all trackdirs that can be reached when entering a tile from a given (diagonal) direction.
Definition track_func.h:450
bool IsValidTrack(Track track)
Checks if a Track is valid.
Definition track_func.h:24
Track FindFirstTrack(TrackBits tracks)
Returns first Track from TrackBits or Track::Invalid.
Definition track_func.h:150
Trackdir RemoveFirstTrackdir(TrackdirBits &trackdirs)
Removes first Trackdir from TrackdirBits and returns it.
Definition track_func.h:130
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:390
TrackBits DiagdirReachesTracks(DiagDirection diagdir)
Returns all tracks that can be reached when entering a tile from a given (diagonal) direction.
Definition track_func.h:468
Trackdir DiagDirToDiagTrackdir(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal trackdir that runs in that direction.
Definition track_func.h:432
Track AxisToTrack(Axis a)
Convert an Axis to the corresponding Track Axis::X -> Track::X Axis::Y -> Track::Y Uses the fact that...
Definition track_func.h:62
DiagDirection TrackdirToExitdir(Trackdir trackdir)
Maps a trackdir to the (4-way) direction the tile is exited when following that trackdir.
Definition track_func.h:343
Track DiagDirToDiagTrack(DiagDirection diagdir)
Maps a DiagDirection to the associated diagonal Track.
Definition track_func.h:419
TrackBits TrackdirBitsToTrackBits(TrackdirBits bits)
Discards all directional information from a TrackdirBits value.
Definition track_func.h:281
EnumBitSet< Trackdir, uint16_t > TrackdirBits
Bitset of Trackdir elements.
Definition track_type.h:93
EnumBitSet< Track, uint8_t > TrackBits
Bitset of Track elements.
Definition track_type.h:43
static constexpr TrackBits TRACK_BIT_ALL
All possible tracks.
Definition track_type.h:52
Trackdir
Enumeration for tracks and directions.
Definition track_type.h:63
@ Invalid
Flag for an invalid trackdir.
Definition track_type.h:82
Track
These are used to specify a single track.
Definition track_type.h:19
@ X
Track along the x-axis (north-east to south-west).
Definition track_type.h:21
@ Upper
Track in the upper corner of the tile (north).
Definition track_type.h:23
@ Begin
Used for iterations.
Definition track_type.h:20
@ Invalid
Flag for an invalid track.
Definition track_type.h:32
@ Y
Track along the y-axis (north-west to south-east).
Definition track_type.h:22
@ Right
Track in the right corner of the tile (east).
Definition track_type.h:26
@ Left
Track in the left corner of the tile (west).
Definition track_type.h:25
@ Depot
Special flag indicating a vehicle is inside a depot.
Definition track_type.h:30
@ Lower
Track in the lower corner of the tile (south).
Definition track_type.h:24
@ Wormhole
Special flag indicating vehicle is inside a bridge or tunnel.
Definition track_type.h:29
@ 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 uint TrainsCrashed(Train *v, Train *u)
Marks trains as crashed and creates an script events.
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 constexpr DiagDirectionIndexArray< uint8_t > _vehicle_initial_y_fract
Initial y subtile coordinate of rail vehicles for each direction.
Definition train_cmd.cpp:59
static void MarkTrainAsStuck(Train *consist)
Mark a train as stuck and stop it if it isn't stopped right now.
static void UpdateLevelCrossingTile(TileIndex tile, bool sound, bool force_barred)
Sets a level crossing tile to the correct state.
static constexpr DiagDirectionIndexArray< uint8_t > _vehicle_initial_x_fract
Initial x subtile coordinate of rail vehicles for each direction.
Definition train_cmd.cpp:57
static void CheckNextTrainTile(Train *v)
Check if the train is on the last reserved tile and try to extend the path then.
static bool HandleCrashedTrain(Train *v)
Handle a crashed train.
void NormalizeTrainVehInDepot(const Train *u)
Move all free vehicles in the depot to the train.
CommandCost CmdReverseTrainDirection(DoCommandFlags flags, VehicleID veh_id, bool reverse_single_veh)
Reverse train.
static bool CheckLevelCrossing(TileIndex tile)
Check if a level crossing should be barred.
static CommandCost CheckNewTrain(Train *original_dst, Train *dst, Train *original_src, Train *src)
Check/validate whether we may actually build a new train.
CommandCost CmdBuildRailVehicle(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
Build a railroad vehicle.
static void UpdateStatusAfterSwap(Train *v, bool reverse=true)
Updates some variables after swapping the vehicle.
uint8_t FreightWagonMult(CargoType cargo)
Return the cargo weight multiplier to use for a rail vehicle.
Definition train_cmd.cpp:74
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.
void ReverseTrainSwapVehicles(Train *v)
Swap vehicles in chain starting from v, and reverse their direction.
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 ReverseTrainSwapVeh(Train *v, int l, int r)
Swap vehicles l and r in consist v, and reverse their direction.
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:81
static bool CheckReverseTrain(const Train *consist)
Can the train reverse?
static void MaybeBarCrossingWithSound(TileIndex tile)
Bars crossing and plays ding-ding sound if not barred already.
bool TrainOnCrossing(TileIndex tile)
Check if a level crossing tile has a train on it.
static void ChangeTrainDirRandomly(Train *v)
Rotate all vehicles of a (crashed) train chain randomly to animate the crash.
void MarkDirtyAdjacentLevelCrossingTiles(TileIndex tile, Axis road_axis)
Find adjacent level crossing tiles in this multi-track crossing and mark them dirty.
static void NormaliseSubtypes(Train *chain)
Normalise the sub types of the parts in this chain.
bool TrainController(Train *v, Vehicle *nomove, bool reverse=true)
Move a vehicle chain one movement stop forwards.
CommandCost CmdSellRailWagon(DoCommandFlags flags, Vehicle *t, bool sell_chain, bool backup_order, ClientID user)
Sell a (single) train wagon/engine.
bool IsValidImageIndex< VehicleType::Train >(uint8_t image_index)
Helper to check whether an image index is valid for a particular vehicle.
Definition train_cmd.cpp:63
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.
static void SwapTrainFlags(GroundVehicleFlags *swap_flag1, GroundVehicleFlags *swap_flag2)
Swap the two up/down flags in two ways:
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.
@ 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:338
void VehicleEnterDepot(Vehicle *v)
Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it,...
Definition vehicle.cpp:1563
UnitID GetFreeUnitNumber(VehicleType type)
Get an unused unit number for a vehicle (if allowed).
Definition vehicle.cpp:1921
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:364
void VehicleServiceInDepot(Vehicle *v)
Service a vehicle and all subsequent vehicles in the consist.
Definition vehicle.cpp:188
void CheckVehicleBreakdown(Vehicle *v)
Periodic check for a vehicle to maybe break down.
Definition vehicle.cpp:1319
GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
Get position information of a vehicle when moving one pixel in the direction it is facing.
Definition vehicle.cpp:1803
void DecreaseVehicleValue(Vehicle *v)
Decrease the value of a vehicle.
Definition vehicle.cpp:1298
void EconomyAgeVehicle(Vehicle *v)
Update economy age of a vehicle.
Definition vehicle.cpp:1441
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
Finds vehicle in tunnel / bridge.
Definition vehicle.cpp:582
void AgeVehicle(Vehicle *v)
Update age of a vehicle.
Definition vehicle.cpp:1453
Direction VehicleEnterTileCoordinates(GetNewVehiclePosResult &gp, DiagDirection enterdir, Track track)
Lookup new subposition coordinates and direction to use when entering a new tile, applying the subcoo...
Definition vehicle.cpp:3401
@ Crashed
Vehicle is crashed.
@ TrainSlowing
Train is slowing down.
@ Hidden
Vehicle is not visible.
@ DefaultPalette
Use default vehicle palette.
@ Stopped
Vehicle is stopped by the player.
Functions related to vehicles.
bool IsValidImageIndex(uint8_t image_index)
Helper to check whether an image index is valid for a particular vehicle.
@ CUSTOM_VEHICLE_SPRITENUM_REVERSED
Vehicle sprite from NewGRF with reverse driving direction (from articulation callback).
bool HasVehicleOnTile(TileIndex tile, UnaryPred &&predicate)
Loop over vehicles on a tile, and check whether a predicate is true for any of them.
@ VIWD_CONSIST_CHANGED
Vehicle composition was changed.
Definition vehicle_gui.h:37
EngineImageType
Visualisation contexts of vehicles and engines.
PoolID< uint32_t, struct VehicleIDTag, 0xFF000, 0xFFFFF > VehicleID
The type all our vehicle IDs have.
@ Train
Train vehicle type.
@ Original
Original acceleration model.
@ Realistic
"Realistic" acceleration model.
EnumBitSet< GroundVehicleFlag, uint16_t > GroundVehicleFlags
Bitset of GroundVehicleFlag elements.
@ GoingUp
Vehicle is currently going uphill. (Cached track information for acceleration).
@ SuppressImplicitOrders
Disable insertion and removal of automatic orders until the vehicle completes the real order.
@ GoingDown
Vehicle is currently going downhill. (Cached track information for acceleration).
static const uint VEHICLE_LENGTH
The length of a vehicle in tile units.
Types related to the vehicle widgets.
@ 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:1204
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting).
Definition window.cpp:3242
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:3334
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:3228
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3212
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:3352
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