OpenTTD Source 20260206-master-g4d4e37dbf1
train_cmd.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include "error.h"
13#include "command_func.h"
14#include "error_func.h"
16#include "news_func.h"
17#include "company_func.h"
18#include "newgrf_sound.h"
19#include "newgrf_text.h"
20#include "strings_func.h"
21#include "viewport_func.h"
22#include "vehicle_func.h"
23#include "sound_func.h"
24#include "ai/ai.hpp"
25#include "game/game.hpp"
26#include "newgrf_station.h"
27#include "effectvehicle_func.h"
28#include "network/network.h"
29#include "core/random_func.hpp"
30#include "company_base.h"
31#include "newgrf.h"
32#include "order_backup.h"
33#include "zoom_func.h"
34#include "newgrf_debug.h"
35#include "framerate_type.h"
36#include "train_cmd.h"
37#include "misc_cmd.h"
40
41#include "table/strings.h"
42#include "table/train_sprites.h"
43
44#include "safeguards.h"
45
46static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck);
47static bool TrainCheckIfLineEnds(Train *v, bool reverse = true);
48bool TrainController(Train *v, Vehicle *nomove, bool reverse = true); // Also used in vehicle_sl.cpp.
50static void CheckIfTrainNeedsService(Train *v);
51static void CheckNextTrainTile(Train *v);
52
53static const uint8_t _vehicle_initial_x_fract[4] = {10, 8, 4, 8};
54static const uint8_t _vehicle_initial_y_fract[4] = { 8, 4, 8, 10};
55
56template <>
57bool IsValidImageIndex<VEH_TRAIN>(uint8_t image_index)
58{
59 return image_index < lengthof(_engine_sprite_base);
60}
61
62
69{
70 if (!CargoSpec::Get(cargo)->is_freight) return 1;
71 return _settings_game.vehicle.freight_trains;
72}
73
76{
77 bool first = true;
78
79 for (const Train *v : Train::Iterate()) {
80 if (v->First() == v && !v->vehstatus.Test(VehState::Crashed)) {
81 for (const Train *u = v, *w = v->Next(); w != nullptr; u = w, w = w->Next()) {
82 if (u->track != TRACK_BIT_DEPOT) {
83 if ((w->track != TRACK_BIT_DEPOT &&
84 std::max(abs(u->x_pos - w->x_pos), abs(u->y_pos - w->y_pos)) != u->CalcNextVehicleOffset()) ||
85 (w->track == TRACK_BIT_DEPOT && TicksToLeaveDepot(u) <= 0)) {
86 ShowErrorMessage(GetEncodedString(STR_BROKEN_VEHICLE_LENGTH, v->index, v->owner), {}, WL_CRITICAL);
87
88 if (!_networking && first) {
89 first = false;
90 Command<Commands::Pause>::Post(PauseMode::Error, true);
91 }
92 /* Break so we warn only once for each train. */
93 break;
94 }
95 }
96 }
97 }
98 }
99}
100
107void Train::ConsistChanged(ConsistChangeFlags allowed_changes)
108{
109 uint16_t max_speed = UINT16_MAX;
110
111 assert(this->IsFrontEngine() || this->IsFreeWagon());
112
113 const RailVehicleInfo *rvi_v = RailVehInfo(this->engine_type);
114 EngineID first_engine = this->IsFrontEngine() ? this->engine_type : EngineID::Invalid();
115 this->gcache.cached_total_length = 0;
116 this->compatible_railtypes = {};
117
118 bool train_can_tilt = true;
119 int16_t min_curve_speed_mod = INT16_MAX;
120
121 for (Train *u = this; u != nullptr; u = u->Next()) {
122 const RailVehicleInfo *rvi_u = RailVehInfo(u->engine_type);
123
124 /* Check the this->first cache. */
125 assert(u->First() == this);
126
127 /* update the 'first engine' */
128 u->gcache.first_engine = this == u ? EngineID::Invalid() : first_engine;
129 u->railtypes = rvi_u->railtypes;
130
131 if (u->IsEngine()) first_engine = u->engine_type;
132
133 /* Set user defined data to its default value */
134 u->tcache.user_def_data = rvi_u->user_def_data;
135 this->InvalidateNewGRFCache();
136 u->InvalidateNewGRFCache();
137 }
138
139 for (Train *u = this; u != nullptr; u = u->Next()) {
140 /* Update user defined data (must be done before other properties) */
141 u->tcache.user_def_data = GetVehicleProperty(u, PROP_TRAIN_USER_DATA, u->tcache.user_def_data);
142 this->InvalidateNewGRFCache();
143 u->InvalidateNewGRFCache();
144 }
145
146 for (Train *u = this; u != nullptr; u = u->Next()) {
147 const Engine *e_u = u->GetEngine();
148 const RailVehicleInfo *rvi_u = &e_u->VehInfo<RailVehicleInfo>();
149
150 if (!e_u->info.misc_flags.Test(EngineMiscFlag::RailTilts)) train_can_tilt = false;
151 min_curve_speed_mod = std::min(min_curve_speed_mod, u->GetCurveSpeedModifier());
152
153 /* Cache wagon override sprite group. nullptr is returned if there is none */
154 u->tcache.cached_override = GetWagonOverrideSpriteSet(u->engine_type, u->cargo_type, u->gcache.first_engine);
155
156 /* Reset colour map */
157 u->colourmap = PAL_NONE;
158
159 /* Update powered-wagon-status and visual effect */
160 u->UpdateVisualEffect(true);
161
162 if (rvi_v->pow_wag_power != 0 && rvi_u->railveh_type == RAILVEH_WAGON &&
163 UsesWagonOverride(u) && !HasBit(u->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
164 /* wagon is powered */
165 u->flags.Set(VehicleRailFlag::PoweredWagon); // cache 'powered' status
166 } else {
167 u->flags.Reset(VehicleRailFlag::PoweredWagon);
168 }
169
170 if (!u->IsArticulatedPart()) {
171 /* Do not count powered wagons for the compatible railtypes, as wagons always
172 have railtype normal */
173 if (rvi_u->power > 0) {
174 this->compatible_railtypes.Set(GetAllPoweredRailTypes(u->railtypes));
175 }
176
177 /* Some electric engines can be allowed to run on normal rail. It happens to all
178 * existing electric engines when elrails are disabled and then re-enabled */
179 if (u->flags.Test(VehicleRailFlag::AllowedOnNormalRail)) {
180 u->railtypes.Set(RAILTYPE_RAIL);
181 u->compatible_railtypes.Set(RAILTYPE_RAIL);
182 }
183
184 /* max speed is the minimum of the speed limits of all vehicles in the consist */
185 if ((rvi_u->railveh_type != RAILVEH_WAGON || _settings_game.vehicle.wagon_speed_limits) && !UsesWagonOverride(u)) {
186 uint16_t speed = GetVehicleProperty(u, PROP_TRAIN_SPEED, rvi_u->max_speed);
187 if (speed != 0) max_speed = std::min(speed, max_speed);
188 }
189 }
190
191 uint16_t new_cap = e_u->DetermineCapacity(u);
192 if (allowed_changes.Test(ConsistChangeFlag::Capacity)) {
193 /* Update vehicle capacity. */
194 if (u->cargo_cap > new_cap) u->cargo.Truncate(new_cap);
195 u->refit_cap = std::min(new_cap, u->refit_cap);
196 u->cargo_cap = new_cap;
197 } else {
198 /* Verify capacity hasn't changed. */
199 if (new_cap != u->cargo_cap) ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_CAPACITY, GRFBug::VehCapacity, true);
200 }
201 u->vcache.cached_cargo_age_period = GetVehicleProperty(u, PROP_TRAIN_CARGO_AGE_PERIOD, e_u->info.cargo_age_period);
202
203 /* check the vehicle length (callback) */
204 uint16_t veh_len = CALLBACK_FAILED;
205 if (e_u->GetGRF() != nullptr && e_u->GetGRF()->grf_version >= 8) {
206 /* Use callback 36 */
207 veh_len = GetVehicleProperty(u, PROP_TRAIN_SHORTEN_FACTOR, CALLBACK_FAILED);
208
209 if (veh_len != CALLBACK_FAILED && veh_len >= VEHICLE_LENGTH) {
211 }
212 } else if (e_u->info.callback_mask.Test(VehicleCallbackMask::Length)) {
213 /* Use callback 11 */
214 veh_len = GetVehicleCallback(CBID_VEHICLE_LENGTH, 0, 0, u->engine_type, u);
215 }
216 if (veh_len == CALLBACK_FAILED) veh_len = rvi_u->shorten_factor;
217 veh_len = VEHICLE_LENGTH - Clamp(veh_len, 0, VEHICLE_LENGTH - 1);
218
219 if (allowed_changes.Test(ConsistChangeFlag::Length)) {
220 /* Update vehicle length. */
221 u->gcache.cached_veh_length = veh_len;
222 } else {
223 /* Verify length hasn't changed. */
224 if (veh_len != u->gcache.cached_veh_length) VehicleLengthChanged(u);
225 }
226
227 this->gcache.cached_total_length += u->gcache.cached_veh_length;
228 this->InvalidateNewGRFCache();
229 u->InvalidateNewGRFCache();
230 }
231
232 /* store consist weight/max speed in cache */
233 this->vcache.cached_max_speed = max_speed;
234 this->tcache.cached_tilt = train_can_tilt;
235 this->tcache.cached_curve_speed_mod = min_curve_speed_mod;
236 this->tcache.cached_max_curve_speed = this->GetCurveSpeedLimit();
237
238 /* 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) */
239 this->CargoChanged();
240
241 if (this->IsFrontEngine()) {
242 this->UpdateAcceleration();
246 InvalidateNewGRFInspectWindow(GSF_TRAINS, this->index);
247
248 /* If the consist is changed while in a depot, the vehicle view window must be invalidated to update the availability of refitting. */
250 }
251}
252
263int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, int *station_ahead, int *station_length)
264{
265 const Station *st = Station::Get(station_id);
266 *station_ahead = st->GetPlatformLength(tile, DirToDiagDir(v->direction)) * TILE_SIZE;
267 *station_length = st->GetPlatformLength(tile) * TILE_SIZE;
268
269 /* Default to the middle of the station for stations stops that are not in
270 * the order list like intermediate stations when non-stop is disabled */
272 if (v->gcache.cached_total_length >= *station_length) {
273 /* The train is longer than the station, make it stop at the far end of the platform */
275 } else if (v->current_order.IsType(OT_GOTO_STATION) && v->current_order.GetDestination() == station_id) {
277 }
278
279 /* The stop location of the FRONT! of the train */
280 int stop;
281 switch (osl) {
282 default: NOT_REACHED();
283
285 stop = v->gcache.cached_total_length;
286 break;
287
289 stop = *station_length - (*station_length - v->gcache.cached_total_length) / 2;
290 break;
291
293 stop = *station_length;
294 break;
295 }
296
297 /* Subtract half the front vehicle length of the train so we get the real
298 * stop location of the train. */
299 return stop - (v->gcache.cached_veh_length + 1) / 2;
300}
301
302
308{
309 assert(this->First() == this);
310
311 static const int absolute_max_speed = UINT16_MAX;
312 int max_speed = absolute_max_speed;
313
314 if (_settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) return max_speed;
315
316 int curvecount[2] = {0, 0};
317
318 /* first find the curve speed limit */
319 int numcurve = 0;
320 int sum = 0;
321 int pos = 0;
322 int lastpos = -1;
323 for (const Train *u = this; u->Next() != nullptr; u = u->Next(), pos += u->gcache.cached_veh_length) {
324 Direction this_dir = u->direction;
325 Direction next_dir = u->Next()->direction;
326
327 DirDiff dirdiff = DirDifference(this_dir, next_dir);
328 if (dirdiff == DIRDIFF_SAME) continue;
329
330 if (dirdiff == DIRDIFF_45LEFT) curvecount[0]++;
331 if (dirdiff == DIRDIFF_45RIGHT) curvecount[1]++;
332 if (dirdiff == DIRDIFF_45LEFT || dirdiff == DIRDIFF_45RIGHT) {
333 if (lastpos != -1) {
334 numcurve++;
335 sum += pos - lastpos;
336 if (pos - lastpos <= static_cast<int>(VEHICLE_LENGTH) && max_speed > 88) {
337 max_speed = 88;
338 }
339 }
340 lastpos = pos;
341 }
342
343 /* if we have a 90 degree turn, fix the speed limit to 60 */
344 if (dirdiff == DIRDIFF_90LEFT || dirdiff == DIRDIFF_90RIGHT) {
345 max_speed = 61;
346 }
347 }
348
349 if (numcurve > 0 && max_speed > 88) {
350 if (curvecount[0] == 1 && curvecount[1] == 1) {
351 max_speed = absolute_max_speed;
352 } else {
353 sum = CeilDiv(sum, VEHICLE_LENGTH);
354 sum /= numcurve;
355 max_speed = 232 - (13 - Clamp(sum, 1, 12)) * (13 - Clamp(sum, 1, 12));
356 }
357 }
358
359 if (max_speed != absolute_max_speed) {
360 /* Apply the current railtype's curve speed advantage */
361 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(this->tile));
362 max_speed += (max_speed / 2) * rti->curve_speed;
363
364 if (this->tcache.cached_tilt) {
365 /* Apply max_speed bonus of 20% for a tilting train */
366 max_speed += max_speed / 5;
367 }
368
369 /* Apply max_speed modifier (cached value is fixed-point binary with 8 fractional bits)
370 * and clamp the result to an acceptable range. */
371 max_speed += (max_speed * this->tcache.cached_curve_speed_mod) / 256;
372 max_speed = Clamp(max_speed, 2, absolute_max_speed);
373 }
374
375 return static_cast<uint16_t>(max_speed);
376}
377
383{
384 int max_speed = _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL ?
385 this->gcache.cached_max_track_speed :
386 this->tcache.cached_max_curve_speed;
387
388 if (_settings_game.vehicle.train_acceleration_model == AM_REALISTIC && IsRailStationTile(this->tile)) {
389 StationID sid = GetStationIndex(this->tile);
390 if (this->current_order.ShouldStopAtStation(this, sid)) {
391 int station_ahead;
392 int station_length;
393 int stop_at = GetTrainStopLocation(sid, this->tile, this, &station_ahead, &station_length);
394
395 /* The distance to go is whatever is still ahead of the train minus the
396 * distance from the train's stop location to the end of the platform */
397 int distance_to_go = station_ahead / TILE_SIZE - (station_length - stop_at) / TILE_SIZE;
398
399 if (distance_to_go > 0) {
400 int st_max_speed = 120;
401
402 int delta_v = this->cur_speed / (distance_to_go + 1);
403 if (max_speed > (this->cur_speed - delta_v)) {
404 st_max_speed = this->cur_speed - (delta_v / 10);
405 }
406
407 st_max_speed = std::max(st_max_speed, 25 * distance_to_go);
408 max_speed = std::min(max_speed, st_max_speed);
409 }
410 }
411 }
412
413 for (const Train *u = this; u != nullptr; u = u->Next()) {
414 if (_settings_game.vehicle.train_acceleration_model == AM_REALISTIC && u->track == TRACK_BIT_DEPOT) {
415 max_speed = std::min(max_speed, 61);
416 break;
417 }
418
419 /* Vehicle is on the middle part of a bridge. */
420 if (u->track == TRACK_BIT_WORMHOLE && !u->vehstatus.Test(VehState::Hidden)) {
421 max_speed = std::min<int>(max_speed, GetBridgeSpec(GetBridgeType(u->tile))->speed);
422 }
423 }
424
425 max_speed = std::min<int>(max_speed, this->current_order.GetMaxSpeed());
426 return std::min<int>(max_speed, this->gcache.cached_max_track_speed);
427}
428
431{
432 assert(this->IsFrontEngine() || this->IsFreeWagon());
433
434 uint power = this->gcache.cached_power;
435 uint weight = this->gcache.cached_weight;
436 assert(weight != 0);
437 this->acceleration = Clamp(power / weight * 4, 1, 255);
438}
439
440int Train::GetCursorImageOffset() const
441{
442 if (this->gcache.cached_veh_length != 8 && this->flags.Test(VehicleRailFlag::Flipped) && !EngInfo(this->engine_type)->misc_flags.Test(EngineMiscFlag::RailFlips)) {
443 int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
444
445 const Engine *e = this->GetEngine();
446 if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->VehInfo<RailVehicleInfo>().image_index)) {
447 reference_width = e->GetGRF()->traininfo_vehicle_width;
448 }
449
450 return ScaleSpriteTrad((this->gcache.cached_veh_length - (int)VEHICLE_LENGTH) * reference_width / (int)VEHICLE_LENGTH);
451 }
452 return 0;
453}
454
461{
462 int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
463 int vehicle_pitch = 0;
464
465 const Engine *e = this->GetEngine();
466 if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->VehInfo<RailVehicleInfo>().image_index)) {
467 reference_width = e->GetGRF()->traininfo_vehicle_width;
468 vehicle_pitch = e->GetGRF()->traininfo_vehicle_pitch;
469 }
470
471 if (offset != nullptr) {
472 if (this->flags.Test(VehicleRailFlag::Flipped) && !EngInfo(this->engine_type)->misc_flags.Test(EngineMiscFlag::RailFlips)) {
473 offset->x = ScaleSpriteTrad(((int)this->gcache.cached_veh_length - (int)VEHICLE_LENGTH / 2) * reference_width / (int)VEHICLE_LENGTH);
474 } else {
475 offset->x = ScaleSpriteTrad(reference_width) / 2;
476 }
477 offset->y = ScaleSpriteTrad(vehicle_pitch);
478 }
479 return ScaleSpriteTrad(this->gcache.cached_veh_length * reference_width / VEHICLE_LENGTH);
480}
481
482static SpriteID GetDefaultTrainSprite(uint8_t spritenum, Direction direction)
483{
484 assert(IsValidImageIndex<VEH_TRAIN>(spritenum));
485 return ((direction + _engine_sprite_add[spritenum]) & _engine_sprite_and[spritenum]) + _engine_sprite_base[spritenum];
486}
487
496{
497 uint8_t spritenum = this->spritenum;
498
499 if (this->flags.Test(VehicleRailFlag::Flipped)) direction = ReverseDir(direction);
500
501 if (IsCustomVehicleSpriteNum(spritenum)) {
503 GetCustomVehicleSprite(this, direction, image_type, result);
504 if (result->IsValid()) return;
505
507 }
508
510 SpriteID sprite = GetDefaultTrainSprite(spritenum, direction);
511
512 if (this->cargo.StoredCount() >= this->cargo_cap / 2U) sprite += _wagon_full_adder[spritenum];
513
514 result->Set(sprite);
515}
516
517static void GetRailIcon(EngineID engine, bool rear_head, int &y, EngineImageType image_type, VehicleSpriteSeq *result)
518{
519 const Engine *e = Engine::Get(engine);
520 Direction dir = rear_head ? DIR_E : DIR_W;
521 uint8_t spritenum = e->VehInfo<RailVehicleInfo>().image_index;
522
523 if (IsCustomVehicleSpriteNum(spritenum)) {
524 GetCustomVehicleIcon(engine, dir, image_type, result);
525 if (result->IsValid()) {
526 if (e->GetGRF() != nullptr) {
528 }
529 return;
530 }
531
532 spritenum = Engine::Get(engine)->original_image_index;
533 }
534
535 if (rear_head) spritenum++;
536
537 result->Set(GetDefaultTrainSprite(spritenum, DIR_W));
538}
539
540void DrawTrainEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
541{
542 if (RailVehInfo(engine)->railveh_type == RAILVEH_MULTIHEAD) {
543 int yf = y;
544 int yr = y;
545
546 VehicleSpriteSeq seqf, seqr;
547 GetRailIcon(engine, false, yf, image_type, &seqf);
548 GetRailIcon(engine, true, yr, image_type, &seqr);
549
550 Rect rectf, rectr;
551 seqf.GetBounds(&rectf);
552 seqr.GetBounds(&rectr);
553
554 preferred_x = Clamp(preferred_x,
555 left - UnScaleGUI(rectf.left) + ScaleSpriteTrad(14),
556 right - UnScaleGUI(rectr.right) - ScaleSpriteTrad(15));
557
558 seqf.Draw(preferred_x - ScaleSpriteTrad(14), yf, pal, pal == PALETTE_CRASH);
559 seqr.Draw(preferred_x + ScaleSpriteTrad(15), yr, pal, pal == PALETTE_CRASH);
560 } else {
562 GetRailIcon(engine, false, y, image_type, &seq);
563
564 Rect rect;
565 seq.GetBounds(&rect);
566 preferred_x = Clamp(preferred_x,
567 left - UnScaleGUI(rect.left),
568 right - UnScaleGUI(rect.right));
569
570 seq.Draw(preferred_x, y, pal, pal == PALETTE_CRASH);
571 }
572}
573
583void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type)
584{
585 int y = 0;
586
588 GetRailIcon(engine, false, y, image_type, &seq);
589
590 Rect rect;
591 seq.GetBounds(&rect);
592
593 width = UnScaleGUI(rect.Width());
594 height = UnScaleGUI(rect.Height());
595 xoffs = UnScaleGUI(rect.left);
596 yoffs = UnScaleGUI(rect.top);
597
598 if (RailVehInfo(engine)->railveh_type == RAILVEH_MULTIHEAD) {
599 GetRailIcon(engine, true, y, image_type, &seq);
600 seq.GetBounds(&rect);
601
602 /* Calculate values relative to an imaginary center between the two sprites. */
603 width = ScaleSpriteTrad(TRAININFO_DEFAULT_VEHICLE_WIDTH) + UnScaleGUI(rect.right) - xoffs;
604 height = std::max<uint>(height, UnScaleGUI(rect.Height()));
605 xoffs = xoffs - ScaleSpriteTrad(TRAININFO_DEFAULT_VEHICLE_WIDTH) / 2;
606 yoffs = std::min(yoffs, UnScaleGUI(rect.top));
607 }
608}
609
615static std::vector<VehicleID> GetFreeWagonsInDepot(TileIndex tile)
616{
617 std::vector<VehicleID> free_wagons;
618
619 for (Vehicle *v : VehiclesOnTile(tile)) {
620 if (v->type != VEH_TRAIN) continue;
621 if (v->vehstatus.Test(VehState::Crashed)) continue;
622 if (!Train::From(v)->IsFreeWagon()) continue;
623
624 free_wagons.push_back(v->index);
625 }
626
627 /* Sort by vehicle index for consistency across clients. */
628 std::ranges::sort(free_wagons);
629 return free_wagons;
630}
631
640static CommandCost CmdBuildRailWagon(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
641{
642 const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
643
644 /* Check that the wagon can drive on the track in question */
645 if (!IsCompatibleRail(rvi->railtypes, GetRailType(tile))) return CMD_ERROR;
646
647 if (flags.Test(DoCommandFlag::Execute)) {
648 Train *v = Train::Create();
649 *ret = v;
650 v->spritenum = rvi->image_index;
651
652 v->engine_type = e->index;
653 v->gcache.first_engine = EngineID::Invalid(); // needs to be set before first callback
654
656
657 v->direction = DiagDirToDir(dir);
658 v->tile = tile;
659
660 int x = TileX(tile) * TILE_SIZE | _vehicle_initial_x_fract[dir];
661 int y = TileY(tile) * TILE_SIZE | _vehicle_initial_y_fract[dir];
662
663 v->x_pos = x;
664 v->y_pos = y;
665 v->z_pos = GetSlopePixelZ(x, y, true);
667 v->track = TRACK_BIT_DEPOT;
669
670 v->SetWagon();
671
672 v->SetFreeWagon();
674
676 assert(IsValidCargoType(v->cargo_type));
677 v->cargo_cap = rvi->capacity;
678 v->refit_cap = 0;
679
680 v->railtypes = rvi->railtypes;
681
685 v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
686 v->random_bits = Random();
687
689
691 if (prob.has_value()) v->flags.Set(VehicleRailFlag::Flipped, prob.value());
693
694 v->UpdatePosition();
697
699
700 /* Try to connect the vehicle to one of free chains of wagons. */
701 for (VehicleID vehicle : GetFreeWagonsInDepot(tile)) {
702 if (vehicle == v->index) continue;
703
704 const Train *w = Train::Get(vehicle);
705 if (w->engine_type != v->engine_type) continue;
706 if (w->First() == v) continue;
707
708 if (Command<Commands::MoveRailVehicle>::Do(DoCommandFlag::Execute, v->index, w->Last()->index, true).Succeeded()) {
709 break;
710 }
711 }
712 }
713
714 return CommandCost();
715}
716
719{
720 assert(u->IsEngine());
721 for (VehicleID vehicle : GetFreeWagonsInDepot(u->tile)) {
722 if (Command<Commands::MoveRailVehicle>::Do(DoCommandFlag::Execute, vehicle, u->index, true).Failed()) {
723 break;
724 }
725 }
726}
727
728static void AddRearEngineToMultiheadedTrain(Train *v)
729{
730 Train *u = Train::Create();
731 v->value >>= 1;
732 u->value = v->value;
733 u->direction = v->direction;
734 u->owner = v->owner;
735 u->tile = v->tile;
736 u->x_pos = v->x_pos;
737 u->y_pos = v->y_pos;
738 u->z_pos = v->z_pos;
739 u->track = TRACK_BIT_DEPOT;
740 u->vehstatus = v->vehstatus;
742 u->spritenum = v->spritenum + 1;
743 u->cargo_type = v->cargo_type;
745 u->cargo_cap = v->cargo_cap;
746 u->refit_cap = v->refit_cap;
747 u->railtypes = v->railtypes;
748 u->engine_type = v->engine_type;
751 u->build_year = v->build_year;
752 u->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
753 u->random_bits = Random();
754 v->SetMultiheaded();
755 u->SetMultiheaded();
756 v->SetNext(u);
758 if (prob.has_value()) u->flags.Set(VehicleRailFlag::Flipped, prob.value());
759 u->UpdatePosition();
760
761 /* Now we need to link the front and rear engines together */
762 v->other_multiheaded_part = u;
763 u->other_multiheaded_part = v;
764}
765
774CommandCost CmdBuildRailVehicle(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
775{
776 const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
777
778 if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildRailWagon(flags, tile, e, ret);
779
780 /* Check if depot and new engine uses the same kind of tracks *
781 * We need to see if the engine got power on the tile to avoid electric engines in non-electric depots */
782 if (!HasPowerOnRail(rvi->railtypes, GetRailType(tile))) return CMD_ERROR;
783
784 if (flags.Test(DoCommandFlag::Execute)) {
786 int x = TileX(tile) * TILE_SIZE + _vehicle_initial_x_fract[dir];
787 int y = TileY(tile) * TILE_SIZE + _vehicle_initial_y_fract[dir];
788
789 Train *v = Train::Create();
790 *ret = v;
791 v->direction = DiagDirToDir(dir);
792 v->tile = tile;
794 v->x_pos = x;
795 v->y_pos = y;
796 v->z_pos = GetSlopePixelZ(x, y, true);
797 v->track = TRACK_BIT_DEPOT;
799 v->spritenum = rvi->image_index;
801 assert(IsValidCargoType(v->cargo_type));
802 v->cargo_cap = rvi->capacity;
803 v->refit_cap = 0;
804 v->last_station_visited = StationID::Invalid();
805 v->last_loading_station = StationID::Invalid();
806
807 v->engine_type = e->index;
808 v->gcache.first_engine = EngineID::Invalid(); // needs to be set before first callback
809
810 v->reliability = e->reliability;
813
814 v->railtypes = rvi->railtypes;
815
816 v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_trains);
820 v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
821 v->random_bits = Random();
822
824 v->SetServiceIntervalIsPercent(Company::Get(_current_company)->settings.vehicle.servint_ispercent);
825
827
828 v->SetFrontEngine();
829 v->SetEngine();
830
832 if (prob.has_value()) v->flags.Set(VehicleRailFlag::Flipped, prob.value());
833 v->UpdatePosition();
834
835 if (rvi->railveh_type == RAILVEH_MULTIHEAD) {
836 AddRearEngineToMultiheadedTrain(v);
837 } else {
839 }
840
843
845 }
846
847 return CommandCost();
848}
849
850static Train *FindGoodVehiclePos(const Train *src)
851{
852 EngineID eng = src->engine_type;
853
854 for (VehicleID vehicle : GetFreeWagonsInDepot(src->tile)) {
855 Train *dst = Train::Get(vehicle);
856
857 /* check so all vehicles in the line have the same engine. */
858 Train *t = dst;
859 while (t->engine_type == eng) {
860 t = t->Next();
861 if (t == nullptr) return dst;
862 }
863 }
864
865 return nullptr;
866}
867
869typedef std::vector<Train *> TrainList;
870
876static void MakeTrainBackup(TrainList &list, Train *t)
877{
878 for (; t != nullptr; t = t->Next()) list.push_back(t);
879}
880
886{
887 /* No train, nothing to do. */
888 if (list.empty()) return;
889
890 Train *prev = nullptr;
891 /* Iterate over the list and rebuild it. */
892 for (Train *t : list) {
893 if (prev != nullptr) {
894 prev->SetNext(t);
895 } else if (t->Previous() != nullptr) {
896 /* Make sure the head of the train is always the first in the chain. */
897 t->Previous()->SetNext(nullptr);
898 }
899 prev = t;
900 }
901}
902
908static void RemoveFromConsist(Train *part, bool chain = false)
909{
910 Train *tail = chain ? part->Last() : part->GetLastEnginePart();
911
912 /* Unlink at the front, but make it point to the next
913 * vehicle after the to be remove part. */
914 if (part->Previous() != nullptr) part->Previous()->SetNext(tail->Next());
915
916 /* Unlink at the back */
917 tail->SetNext(nullptr);
918}
919
925static void InsertInConsist(Train *dst, Train *chain)
926{
927 /* We do not want to add something in the middle of an articulated part. */
928 assert(dst != nullptr && (dst->Next() == nullptr || !dst->Next()->IsArticulatedPart()));
929
930 chain->Last()->SetNext(dst->Next());
931 dst->SetNext(chain);
932}
933
940{
941 for (; t != nullptr; t = t->GetNextVehicle()) {
942 if (!t->IsMultiheaded() || !t->IsEngine()) continue;
943
944 /* Make sure that there are no free cars before next engine */
945 Train *u;
946 for (u = t; u->Next() != nullptr && !u->Next()->IsEngine(); u = u->Next()) {}
947
948 if (u == t->other_multiheaded_part) continue;
949
950 /* Remove the part from the 'wrong' train */
951 RemoveFromConsist(t->other_multiheaded_part);
952 /* And add it to the 'right' train */
953 InsertInConsist(u, t->other_multiheaded_part);
954 }
955}
956
961static void NormaliseSubtypes(Train *chain)
962{
963 /* Nothing to do */
964 if (chain == nullptr) return;
965
966 /* We must be the first in the chain. */
967 assert(chain->Previous() == nullptr);
968
969 /* Set the appropriate bits for the first in the chain. */
970 if (chain->IsWagon()) {
971 chain->SetFreeWagon();
972 } else {
973 assert(chain->IsEngine());
974 chain->SetFrontEngine();
975 }
976
977 /* Now clear the bits for the rest of the chain */
978 for (Train *t = chain->Next(); t != nullptr; t = t->Next()) {
979 t->ClearFreeWagon();
980 t->ClearFrontEngine();
981 }
982}
983
993static CommandCost CheckNewTrain(Train *original_dst, Train *dst, Train *original_src, Train *src)
994{
995 /* Just add 'new' engines and subtract the original ones.
996 * If that's less than or equal to 0 we can be sure we did
997 * not add any engines (read: trains) along the way. */
998 if ((src != nullptr && src->IsEngine() ? 1 : 0) +
999 (dst != nullptr && dst->IsEngine() ? 1 : 0) -
1000 (original_src != nullptr && original_src->IsEngine() ? 1 : 0) -
1001 (original_dst != nullptr && original_dst->IsEngine() ? 1 : 0) <= 0) {
1002 return CommandCost();
1003 }
1004
1005 /* Get a free unit number and check whether it's within the bounds.
1006 * There will always be a maximum of one new train. */
1007 if (GetFreeUnitNumber(VEH_TRAIN) <= _settings_game.vehicle.max_trains) return CommandCost();
1008
1009 return CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);
1010}
1011
1018{
1019 /* No multi-part train, no need to check. */
1020 if (t == nullptr || t->Next() == nullptr) return CommandCost();
1021
1022 /* The maximum length for a train. For each part we decrease this by one
1023 * and if the result is negative the train is simply too long. */
1024 int allowed_len = _settings_game.vehicle.max_train_length * TILE_SIZE - t->gcache.cached_veh_length;
1025
1026 /* For free-wagon chains, check if they are within the max_train_length limit. */
1027 if (!t->IsEngine()) {
1028 t = t->Next();
1029 while (t != nullptr) {
1030 allowed_len -= t->gcache.cached_veh_length;
1031
1032 t = t->Next();
1033 }
1034
1035 if (allowed_len < 0) return CommandCost(STR_ERROR_TRAIN_TOO_LONG);
1036 return CommandCost();
1037 }
1038
1039 Train *head = t;
1040 Train *prev = t;
1041
1042 /* Break the prev -> t link so it always holds within the loop. */
1043 t = t->Next();
1044 prev->SetNext(nullptr);
1045
1046 /* Make sure the cache is cleared. */
1047 head->InvalidateNewGRFCache();
1048
1049 while (t != nullptr) {
1050 allowed_len -= t->gcache.cached_veh_length;
1051
1052 Train *next = t->Next();
1053
1054 /* Unlink the to-be-added piece; it is already unlinked from the previous
1055 * part due to the fact that the prev -> t link is broken. */
1056 t->SetNext(nullptr);
1057
1058 /* Don't check callback for articulated or rear dual headed parts */
1059 if (!t->IsArticulatedPart() && !t->IsRearDualheaded()) {
1060 /* Back up and clear the first_engine data to avoid using wagon override group */
1061 EngineID first_engine = t->gcache.first_engine;
1062 t->gcache.first_engine = EngineID::Invalid();
1063
1064 /* We don't want the cache to interfere. head's cache is cleared before
1065 * the loop and after each callback does not need to be cleared here. */
1067
1068 std::array<int32_t, 1> regs100;
1069 uint16_t callback = GetVehicleCallbackParent(CBID_TRAIN_ALLOW_WAGON_ATTACH, 0, 0, head->engine_type, t, head, regs100);
1070
1071 /* Restore original first_engine data */
1072 t->gcache.first_engine = first_engine;
1073
1074 /* We do not want to remember any cached variables from the test run */
1076 head->InvalidateNewGRFCache();
1077
1078 if (callback != CALLBACK_FAILED) {
1079 /* A failing callback means everything is okay */
1080 StringID error = STR_NULL;
1081
1082 if (head->GetGRF()->grf_version < 8) {
1083 if (callback == 0xFD) error = STR_ERROR_INCOMPATIBLE_RAIL_TYPES;
1084 if (callback < 0xFD) error = GetGRFStringID(head->GetGRFID(), GRFSTR_MISC_GRF_TEXT + callback);
1085 if (callback >= 0x100) ErrorUnknownCallbackResult(head->GetGRFID(), CBID_TRAIN_ALLOW_WAGON_ATTACH, callback);
1086 } else {
1087 if (callback < 0x400) {
1088 error = GetGRFStringID(head->GetGRFID(), GRFSTR_MISC_GRF_TEXT + callback);
1089 } else {
1090 switch (callback) {
1091 case 0x400: // allow if railtypes match (always the case for OpenTTD)
1092 case 0x401: // allow
1093 break;
1094
1095 case 0x40F:
1096 error = GetGRFStringID(head->GetGRFID(), static_cast<GRFStringID>(regs100[0]));
1097 break;
1098
1099 default: // unknown reason -> disallow
1100 case 0x402: // disallow attaching
1101 error = STR_ERROR_INCOMPATIBLE_RAIL_TYPES;
1102 break;
1103 }
1104 }
1105 }
1106
1107 if (error != STR_NULL) return CommandCost(error);
1108 }
1109 }
1110
1111 /* And link it to the new part. */
1112 prev->SetNext(t);
1113 prev = t;
1114 t = next;
1115 }
1116
1117 if (allowed_len < 0) return CommandCost(STR_ERROR_TRAIN_TOO_LONG);
1118 return CommandCost();
1119}
1120
1131static CommandCost ValidateTrains(Train *original_dst, Train *dst, Train *original_src, Train *src, bool check_limit)
1132{
1133 /* Check whether we may actually construct the trains. */
1135 if (ret.Failed()) return ret;
1136 ret = CheckTrainAttachment(dst);
1137 if (ret.Failed()) return ret;
1138
1139 /* Check whether we need to build a new train. */
1140 return check_limit ? CheckNewTrain(original_dst, dst, original_src, src) : CommandCost();
1141}
1142
1151static void ArrangeTrains(Train **dst_head, Train *dst, Train **src_head, Train *src, bool move_chain)
1152{
1153 /* First determine the front of the two resulting trains */
1154 if (*src_head == *dst_head) {
1155 /* If we aren't moving part(s) to a new train, we are just moving the
1156 * front back and there is not destination head. */
1157 *dst_head = nullptr;
1158 } else if (*dst_head == nullptr) {
1159 /* If we are moving to a new train the head of the move train would become
1160 * the head of the new vehicle. */
1161 *dst_head = src;
1162 }
1163
1164 if (src == *src_head) {
1165 /* If we are moving the front of a train then we are, in effect, creating
1166 * a new head for the train. Point to that. Unless we are moving the whole
1167 * train in which case there is not 'source' train anymore.
1168 * In case we are a multiheaded part we want the complete thing to come
1169 * with us, so src->GetNextUnit(), however... when we are e.g. a wagon
1170 * that is followed by a rear multihead we do not want to include that. */
1171 *src_head = move_chain ? nullptr :
1172 (src->IsMultiheaded() ? src->GetNextUnit() : src->GetNextVehicle());
1173 }
1174
1175 /* Now it's just simply removing the part that we are going to move from the
1176 * source train and *if* the destination is a not a new train add the chain
1177 * at the destination location. */
1178 RemoveFromConsist(src, move_chain);
1179 if (*dst_head != src) InsertInConsist(dst, src);
1180
1181 /* Now normalise the dual heads, that is move the dual heads around in such
1182 * a way that the head and rear of a dual head are in the same train */
1183 NormaliseDualHeads(*src_head);
1184 NormaliseDualHeads(*dst_head);
1185}
1186
1192static void NormaliseTrainHead(Train *head)
1193{
1194 /* Not much to do! */
1195 if (head == nullptr) return;
1196
1197 /* Tell the 'world' the train changed. */
1199 UpdateTrainGroupID(head);
1200
1201 /* Not a front engine, i.e. a free wagon chain. No need to do more. */
1202 if (!head->IsFrontEngine()) return;
1203
1204 /* Update the refit button and window */
1207
1208 /* If we don't have a unit number yet, set one. */
1209 if (head->unitnumber != 0) return;
1210 head->unitnumber = Company::Get(head->owner)->freeunits[head->type].UseID(GetFreeUnitNumber(VEH_TRAIN));
1211}
1212
1222CommandCost CmdMoveRailVehicle(DoCommandFlags flags, VehicleID src_veh, VehicleID dest_veh, bool move_chain)
1223{
1224 Train *src = Train::GetIfValid(src_veh);
1225 if (src == nullptr) return CMD_ERROR;
1226
1227 CommandCost ret = CheckOwnership(src->owner);
1228 if (ret.Failed()) return ret;
1229
1230 /* Do not allow moving crashed vehicles inside the depot, it is likely to cause asserts later */
1231 if (src->vehstatus.Test(VehState::Crashed)) return CMD_ERROR;
1232
1233 /* if nothing is selected as destination, try and find a matching vehicle to drag to. */
1234 Train *dst;
1235 if (dest_veh == VehicleID::Invalid()) {
1236 dst = (src->IsEngine() || flags.Test(DoCommandFlag::AutoReplace)) ? nullptr : FindGoodVehiclePos(src);
1237 } else {
1238 dst = Train::GetIfValid(dest_veh);
1239 if (dst == nullptr) return CMD_ERROR;
1240
1241 ret = CheckOwnership(dst->owner);
1242 if (ret.Failed()) return ret;
1243
1244 /* Do not allow appending to crashed vehicles, too */
1245 if (dst->vehstatus.Test(VehState::Crashed)) return CMD_ERROR;
1246 }
1247
1248 /* if an articulated part is being handled, deal with its parent vehicle */
1249 src = src->GetFirstEnginePart();
1250 if (dst != nullptr) {
1251 dst = dst->GetFirstEnginePart();
1252 }
1253
1254 /* don't move the same vehicle.. */
1255 if (src == dst) return CommandCost();
1256
1257 /* locate the head of the two chains */
1258 Train *src_head = src->First();
1259 Train *dst_head;
1260 if (dst != nullptr) {
1261 dst_head = dst->First();
1262 if (dst_head->tile != src_head->tile) return CMD_ERROR;
1263 /* Now deal with articulated part of destination wagon */
1264 dst = dst->GetLastEnginePart();
1265 } else {
1266 dst_head = nullptr;
1267 }
1268
1269 if (src->IsRearDualheaded()) return CommandCost(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT);
1270
1271 /* When moving all wagons, we can't have the same src_head and dst_head */
1272 if (move_chain && src_head == dst_head) return CommandCost();
1273
1274 /* When moving a multiheaded part to be place after itself, bail out. */
1275 if (!move_chain && dst != nullptr && dst->IsRearDualheaded() && src == dst->other_multiheaded_part) return CommandCost();
1276
1277 /* Check if all vehicles in the source train are stopped inside a depot. */
1278 if (!src_head->IsStoppedInDepot()) return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
1279
1280 /* Check if all vehicles in the destination train are stopped inside a depot. */
1281 if (dst_head != nullptr && !dst_head->IsStoppedInDepot()) return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
1282
1283 /* First make a backup of the order of the trains. That way we can do
1284 * whatever we want with the order and later on easily revert. */
1285 TrainList original_src;
1286 TrainList original_dst;
1287
1288 MakeTrainBackup(original_src, src_head);
1289 MakeTrainBackup(original_dst, dst_head);
1290
1291 /* Also make backup of the original heads as ArrangeTrains can change them.
1292 * For the destination head we do not care if it is the same as the source
1293 * head because in that case it's just a copy. */
1294 Train *original_src_head = src_head;
1295 Train *original_dst_head = (dst_head == src_head ? nullptr : dst_head);
1296
1297 /* We want this information from before the rearrangement, but execute this after the validation.
1298 * original_src_head can't be nullptr; src is by definition != nullptr, so src_head can't be nullptr as
1299 * src->GetFirst() always yields non-nullptr, so eventually original_src_head != nullptr as well. */
1300 bool original_src_head_front_engine = original_src_head->IsFrontEngine();
1301 bool original_dst_head_front_engine = original_dst_head != nullptr && original_dst_head->IsFrontEngine();
1302
1303 /* (Re)arrange the trains in the wanted arrangement. */
1304 ArrangeTrains(&dst_head, dst, &src_head, src, move_chain);
1305
1306 if (!flags.Test(DoCommandFlag::AutoReplace)) {
1307 /* If the autoreplace flag is set we do not need to test for the validity
1308 * because we are going to revert the train to its original state. As we
1309 * assume the original state was correct autoreplace can skip this. */
1310 ret = ValidateTrains(original_dst_head, dst_head, original_src_head, src_head, true);
1311 if (ret.Failed()) {
1312 /* Restore the train we had. */
1313 RestoreTrainBackup(original_src);
1314 RestoreTrainBackup(original_dst);
1315 return ret;
1316 }
1317 }
1318
1319 /* do it? */
1320 if (flags.Test(DoCommandFlag::Execute)) {
1321 /* Remove old heads from the statistics */
1322 if (original_src_head_front_engine) GroupStatistics::CountVehicle(original_src_head, -1);
1323 if (original_dst_head_front_engine) GroupStatistics::CountVehicle(original_dst_head, -1);
1324
1325 /* First normalise the sub types of the chains. */
1326 NormaliseSubtypes(src_head);
1327 NormaliseSubtypes(dst_head);
1328
1329 /* There are 14 different cases:
1330 * 1) front engine gets moved to a new train, it stays a front engine.
1331 * a) the 'next' part is a wagon that becomes a free wagon chain.
1332 * b) the 'next' part is an engine that becomes a front engine.
1333 * c) there is no 'next' part, nothing else happens
1334 * 2) front engine gets moved to another train, it is not a front engine anymore
1335 * a) the 'next' part is a wagon that becomes a free wagon chain.
1336 * b) the 'next' part is an engine that becomes a front engine.
1337 * c) there is no 'next' part, nothing else happens
1338 * 3) front engine gets moved to later in the current train, it is not a front engine anymore.
1339 * a) the 'next' part is a wagon that becomes a free wagon chain.
1340 * b) the 'next' part is an engine that becomes a front engine.
1341 * 4) free wagon gets moved
1342 * a) the 'next' part is a wagon that becomes a free wagon chain.
1343 * b) the 'next' part is an engine that becomes a front engine.
1344 * c) there is no 'next' part, nothing else happens
1345 * 5) non front engine gets moved and becomes a new train, nothing else happens
1346 * 6) non front engine gets moved within a train / to another train, nothing happens
1347 * 7) wagon gets moved, nothing happens
1348 */
1349 if (src == original_src_head && src->IsEngine() && !src->IsFrontEngine()) {
1350 /* Cases #2 and #3: the front engine gets trashed. */
1351 CloseWindowById(WC_VEHICLE_VIEW, src->index);
1353 CloseWindowById(WC_VEHICLE_REFIT, src->index);
1356 DeleteNewGRFInspectWindow(GSF_TRAINS, src->index);
1358
1359 if (src_head != nullptr && src_head->IsFrontEngine()) {
1360 /* Cases #?b: Transfer order, unit number and other stuff
1361 * to the new front engine. */
1362 src_head->orders = src->orders;
1363 if (src_head->orders != nullptr) src_head->AddToShared(src);
1364 src_head->CopyVehicleConfigAndStatistics(src);
1365 }
1366 /* Remove stuff not valid anymore for non-front engines. */
1368 src->ReleaseUnitNumber();
1369 src->name.clear();
1370 }
1371
1372 /* We weren't a front engine but are becoming one. So
1373 * we should be put in the default group. */
1374 if (original_src_head != src && dst_head == src) {
1377 }
1378
1379 /* Handle 'new engine' part of cases #1b, #2b, #3b, #4b and #5 in NormaliseTrainHead. */
1380 NormaliseTrainHead(src_head);
1381 NormaliseTrainHead(dst_head);
1382
1383 /* Add new heads to statistics.
1384 * This should be done after NormaliseTrainHead due to engine total limit checks in GetFreeUnitNumber. */
1385 if (src_head != nullptr && src_head->IsFrontEngine()) GroupStatistics::CountVehicle(src_head, 1);
1386 if (dst_head != nullptr && dst_head->IsFrontEngine()) GroupStatistics::CountVehicle(dst_head, 1);
1387
1389 CheckCargoCapacity(src_head);
1390 CheckCargoCapacity(dst_head);
1391 }
1392
1393 if (src_head != nullptr) src_head->First()->MarkDirty();
1394 if (dst_head != nullptr) dst_head->First()->MarkDirty();
1395
1396 /* We are undoubtedly changing something in the depot and train list. */
1399 } else {
1400 /* We don't want to execute what we're just tried. */
1401 RestoreTrainBackup(original_src);
1402 RestoreTrainBackup(original_dst);
1403 }
1404
1405 return CommandCost();
1406}
1407
1420CommandCost CmdSellRailWagon(DoCommandFlags flags, Vehicle *t, bool sell_chain, bool backup_order, ClientID user)
1421{
1423 Train *first = v->First();
1424
1425 if (v->IsRearDualheaded()) return CommandCost(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT);
1426
1427 /* First make a backup of the order of the train. That way we can do
1428 * whatever we want with the order and later on easily revert. */
1429 TrainList original;
1430 MakeTrainBackup(original, first);
1431
1432 /* We need to keep track of the new head and the head of what we're going to sell. */
1433 Train *new_head = first;
1434 Train *sell_head = nullptr;
1435
1436 /* Split the train in the wanted way. */
1437 ArrangeTrains(&sell_head, nullptr, &new_head, v, sell_chain);
1438
1439 /* We don't need to validate the second train; it's going to be sold. */
1440 CommandCost ret = ValidateTrains(nullptr, nullptr, first, new_head, !flags.Test(DoCommandFlag::AutoReplace));
1441 if (ret.Failed()) {
1442 /* Restore the train we had. */
1443 RestoreTrainBackup(original);
1444 return ret;
1445 }
1446
1447 if (first->orders == nullptr && !OrderList::CanAllocateItem()) {
1448 /* Restore the train we had. */
1449 RestoreTrainBackup(original);
1450 return CommandCost(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS);
1451 }
1452
1454 for (Train *part = sell_head; part != nullptr; part = part->Next()) cost.AddCost(-part->value);
1455
1456 /* do it? */
1457 if (flags.Test(DoCommandFlag::Execute)) {
1458 /* First normalise the sub types of the chain. */
1459 NormaliseSubtypes(new_head);
1460
1461 if (v == first && !sell_chain && new_head != nullptr && new_head->IsFrontEngine()) {
1462 if (v->IsEngine()) {
1463 /* We are selling the front engine. In this case we want to
1464 * 'give' the order, unit number and such to the new head. */
1465 new_head->orders = first->orders;
1466 new_head->AddToShared(first);
1467 DeleteVehicleOrders(first);
1468
1469 /* Copy other important data from the front engine */
1470 new_head->CopyVehicleConfigAndStatistics(first);
1471 }
1472 GroupStatistics::CountVehicle(new_head, 1); // after copying over the profit, if required
1473 } else if (v->IsPrimaryVehicle() && backup_order) {
1474 OrderBackup::Backup(v, user);
1475 }
1476
1477 /* We need to update the information about the train. */
1478 NormaliseTrainHead(new_head);
1479
1480 /* We are undoubtedly changing something in the depot and train list. */
1483
1484 /* Actually delete the sold 'goods' */
1485 delete sell_head;
1486 } else {
1487 /* We don't want to execute what we're just tried. */
1488 RestoreTrainBackup(original);
1489 }
1490
1491 return cost;
1492}
1493
1495{
1496 /* Set common defaults. */
1497 this->bounds = {{-1, -1, 0}, {3, 3, 6}, {}};
1498
1499 /* Set if flipped and engine is NOT flagged with custom flip handling. */
1500 int flipped = this->flags.Test(VehicleRailFlag::Flipped) && !EngInfo(this->engine_type)->misc_flags.Test(EngineMiscFlag::RailFlips);
1501 /* If flipped and vehicle length is odd, we need to adjust the bounding box offset slightly. */
1502 int flip_offs = flipped && (this->gcache.cached_veh_length & 1);
1503
1504 Direction dir = this->direction;
1505 if (flipped) dir = ReverseDir(dir);
1506
1507 if (!IsDiagonalDirection(dir)) {
1508 static const Point _sign_table[] = {
1509 /* x, y */
1510 {-1, -1}, // DIR_N
1511 {-1, 1}, // DIR_E
1512 { 1, 1}, // DIR_S
1513 { 1, -1}, // DIR_W
1514 };
1515
1516 int half_shorten = (VEHICLE_LENGTH - this->gcache.cached_veh_length + flipped) / 2;
1517
1518 /* For all straight directions, move the bound box to the centre of the vehicle, but keep the size. */
1519 this->bounds.offset.x -= half_shorten * _sign_table[DirToDiagDir(dir)].x;
1520 this->bounds.offset.y -= half_shorten * _sign_table[DirToDiagDir(dir)].y;
1521 } else {
1522 switch (dir) {
1523 /* Shorten southern corner of the bounding box according the vehicle length
1524 * and center the bounding box on the vehicle. */
1525 case DIR_NE:
1526 this->bounds.origin.x = -(this->gcache.cached_veh_length + 1) / 2 + flip_offs;
1527 this->bounds.extent.x = this->gcache.cached_veh_length;
1528 this->bounds.offset.x = 1;
1529 break;
1530
1531 case DIR_NW:
1532 this->bounds.origin.y = -(this->gcache.cached_veh_length + 1) / 2 + flip_offs;
1533 this->bounds.extent.y = this->gcache.cached_veh_length;
1534 this->bounds.offset.y = 1;
1535 break;
1536
1537 /* Move northern corner of the bounding box down according to vehicle length
1538 * and center the bounding box on the vehicle. */
1539 case DIR_SW:
1540 this->bounds.origin.x = -(this->gcache.cached_veh_length) / 2 - flip_offs;
1541 this->bounds.extent.x = this->gcache.cached_veh_length;
1542 this->bounds.offset.x = 1 - (VEHICLE_LENGTH - this->gcache.cached_veh_length);
1543 break;
1544
1545 case DIR_SE:
1546 this->bounds.origin.y = -(this->gcache.cached_veh_length) / 2 - flip_offs;
1547 this->bounds.extent.y = this->gcache.cached_veh_length;
1548 this->bounds.offset.y = 1 - (VEHICLE_LENGTH - this->gcache.cached_veh_length);
1549 break;
1550
1551 default:
1552 NOT_REACHED();
1553 }
1554 }
1555}
1556
1562{
1563 if (!v->flags.Test(VehicleRailFlag::Stuck)) {
1564 /* It is the first time the problem occurred, set the "train stuck" flag. */
1565 v->flags.Set(VehicleRailFlag::Stuck);
1566
1567 v->wait_counter = 0;
1568
1569 /* Stop train */
1570 v->cur_speed = 0;
1571 v->subspeed = 0;
1572 v->SetLastSpeed();
1573
1575 }
1576}
1577
1585static void SwapTrainFlags(uint16_t *swap_flag1, uint16_t *swap_flag2)
1586{
1587 uint16_t flag1 = *swap_flag1;
1588 uint16_t flag2 = *swap_flag2;
1589
1590 /* Clear the flags */
1591 ClrBit(*swap_flag1, GVF_GOINGUP_BIT);
1592 ClrBit(*swap_flag1, GVF_GOINGDOWN_BIT);
1593 ClrBit(*swap_flag2, GVF_GOINGUP_BIT);
1594 ClrBit(*swap_flag2, GVF_GOINGDOWN_BIT);
1595
1596 /* Reverse the rail-flags (if needed) */
1597 if (HasBit(flag1, GVF_GOINGUP_BIT)) {
1598 SetBit(*swap_flag2, GVF_GOINGDOWN_BIT);
1599 } else if (HasBit(flag1, GVF_GOINGDOWN_BIT)) {
1600 SetBit(*swap_flag2, GVF_GOINGUP_BIT);
1601 }
1602 if (HasBit(flag2, GVF_GOINGUP_BIT)) {
1603 SetBit(*swap_flag1, GVF_GOINGDOWN_BIT);
1604 } else if (HasBit(flag2, GVF_GOINGDOWN_BIT)) {
1605 SetBit(*swap_flag1, GVF_GOINGUP_BIT);
1606 }
1607}
1608
1614{
1615 /* Reverse the direction. */
1616 if (v->track != TRACK_BIT_DEPOT) v->direction = ReverseDir(v->direction);
1617
1618 /* Call the proper EnterTile function unless we are in a wormhole. */
1619 if (v->track != TRACK_BIT_WORMHOLE) {
1620 VehicleEnterTile(v, v->tile, v->x_pos, v->y_pos);
1621 } else {
1622 /* VehicleEnter_TunnelBridge() sets TRACK_BIT_WORMHOLE when the vehicle
1623 * is on the last bit of the bridge head (frame == TILE_SIZE - 1).
1624 * If we were swapped with such a vehicle, we have set TRACK_BIT_WORMHOLE,
1625 * when we shouldn't have. Check if this is the case. */
1626 TileIndex vt = TileVirtXY(v->x_pos, v->y_pos);
1628 VehicleEnterTile(v, vt, v->x_pos, v->y_pos);
1629 if (v->track != TRACK_BIT_WORMHOLE && IsBridgeTile(v->tile)) {
1630 /* We have just left the wormhole, possibly set the
1631 * "goingdown" bit. UpdateInclination() can be used
1632 * because we are at the border of the tile. */
1633 v->UpdatePosition();
1634 v->UpdateInclination(true, true);
1635 return;
1636 }
1637 }
1638 }
1639
1640 v->UpdatePosition();
1641 v->UpdateViewport(true, true);
1642}
1643
1650void ReverseTrainSwapVeh(Train *v, int l, int r)
1651{
1652 Train *a, *b;
1653
1654 /* locate vehicles to swap */
1655 for (a = v; l != 0; l--) a = a->Next();
1656 for (b = v; r != 0; r--) b = b->Next();
1657
1658 if (a != b) {
1659 /* swap the hidden bits */
1660 {
1661 bool a_hidden = a->vehstatus.Test(VehState::Hidden);
1662 bool b_hidden = b->vehstatus.Test(VehState::Hidden);
1663 b->vehstatus.Set(VehState::Hidden, a_hidden);
1664 a->vehstatus.Set(VehState::Hidden, b_hidden);
1665 }
1666
1667 std::swap(a->track, b->track);
1668 std::swap(a->direction, b->direction);
1669 std::swap(a->x_pos, b->x_pos);
1670 std::swap(a->y_pos, b->y_pos);
1671 std::swap(a->tile, b->tile);
1672 std::swap(a->z_pos, b->z_pos);
1673
1675
1678 } else {
1679 /* Swap GVF_GOINGUP_BIT/GVF_GOINGDOWN_BIT.
1680 * This is a little bit redundant way, a->gv_flags will
1681 * be (re)set twice, but it reduces code duplication */
1684 }
1685}
1686
1692static bool IsTrain(const Vehicle *v)
1693{
1694 return v->type == VEH_TRAIN;
1695}
1696
1704{
1705 assert(IsLevelCrossingTile(tile));
1706
1707 return HasVehicleOnTile(tile, IsTrain);
1708}
1709
1717{
1718 if (v->type != VEH_TRAIN || v->vehstatus.Test(VehState::Crashed)) return false;
1719
1720 const Train *t = Train::From(v);
1721 if (!t->IsFrontEngine()) return false;
1722
1723 return TrainApproachingCrossingTile(t) == tile;
1724}
1725
1726
1734{
1735 assert(IsLevelCrossingTile(tile));
1736
1738 TileIndex tile_from = tile + TileOffsByDiagDir(dir);
1739
1740 if (HasVehicleOnTile(tile_from, [&](const Vehicle *v) {
1741 return TrainApproachingCrossingEnum(v, tile);
1742 })) return true;
1743
1744 dir = ReverseDiagDir(dir);
1745 tile_from = tile + TileOffsByDiagDir(dir);
1746
1747 return HasVehicleOnTile(tile_from, [&](const Vehicle *v) {
1748 return TrainApproachingCrossingEnum(v, tile);
1749 });
1750}
1751
1757static inline bool CheckLevelCrossing(TileIndex tile)
1758{
1759 /* reserved || train on crossing || train approaching crossing */
1761}
1762
1770static void UpdateLevelCrossingTile(TileIndex tile, bool sound, bool force_barred)
1771{
1772 assert(IsLevelCrossingTile(tile));
1773 bool set_barred;
1774
1775 /* We force the crossing to be barred when an adjacent crossing is barred, otherwise let it decide for itself. */
1776 set_barred = force_barred || CheckLevelCrossing(tile);
1777
1778 /* The state has changed */
1779 if (set_barred != IsCrossingBarred(tile)) {
1780 if (set_barred && sound && _settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, tile);
1781 SetCrossingBarred(tile, set_barred);
1782 MarkTileDirtyByTile(tile);
1783 }
1784}
1785
1792void UpdateLevelCrossing(TileIndex tile, bool sound, bool force_bar)
1793{
1794 if (!IsLevelCrossingTile(tile)) return;
1795
1796 bool forced_state = force_bar;
1797
1798 const Axis axis = GetCrossingRoadAxis(tile);
1799 const DiagDirection dir1 = AxisToDiagDir(axis);
1800 const DiagDirection dir2 = ReverseDiagDir(dir1);
1801
1802 /* Check if an adjacent crossing is barred. */
1803 for (DiagDirection dir : { dir1, dir2 }) {
1804 for (TileIndex t = tile; !forced_state && t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, dir)) {
1805 forced_state |= CheckLevelCrossing(t);
1806 }
1807 }
1808
1809 /* Now that we know whether all tiles in this crossing should be barred or open,
1810 * we need to update those tiles. We start with the tile itself, then look along the road axis. */
1811 UpdateLevelCrossingTile(tile, sound, forced_state);
1812 for (DiagDirection dir : { dir1, dir2 }) {
1813 for (TileIndex t = TileAddByDiagDir(tile, dir); t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, dir)) {
1814 UpdateLevelCrossingTile(t, sound, forced_state);
1815 }
1816 }
1817}
1818
1825{
1826 const DiagDirection dir1 = AxisToDiagDir(road_axis);
1827 const DiagDirection dir2 = ReverseDiagDir(dir1);
1828 for (DiagDirection dir : { dir1, dir2 }) {
1829 const TileIndex t = TileAddByDiagDir(tile, dir);
1830 if (t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis) {
1832 }
1833 }
1834}
1835
1842{
1843 const DiagDirection dir1 = AxisToDiagDir(road_axis);
1844 const DiagDirection dir2 = ReverseDiagDir(dir1);
1845 for (DiagDirection dir : { dir1, dir2 }) {
1846 const TileIndexDiff diff = TileOffsByDiagDir(dir);
1847 bool occupied = false;
1848 for (TileIndex t = tile + diff; t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis; t += diff) {
1849 occupied |= CheckLevelCrossing(t);
1850 }
1851 if (occupied) {
1852 /* Mark the immediately adjacent tile dirty */
1853 const TileIndex t = tile + diff;
1854 if (t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis) {
1856 }
1857 } else {
1858 /* Unbar the crossing tiles in this direction as necessary */
1859 for (TileIndex t = tile + diff; t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis; t += diff) {
1860 if (IsCrossingBarred(t)) {
1861 /* The crossing tile is barred, unbar it and continue to check the next tile */
1862 SetCrossingBarred(t, false);
1864 } else {
1865 /* The crossing tile is already unbarred, mark the tile dirty and stop checking */
1867 break;
1868 }
1869 }
1870 }
1871 }
1872}
1873
1879static inline void MaybeBarCrossingWithSound(TileIndex tile)
1880{
1881 if (!IsCrossingBarred(tile)) {
1882 SetCrossingReservation(tile, true);
1883 UpdateLevelCrossing(tile, true);
1884 }
1885}
1886
1887
1894{
1895 Train *base = v;
1896 Train *first = base; // first vehicle to move
1897 Train *last = v->Last(); // last vehicle to move
1898 uint length = CountVehiclesInChain(v);
1899
1900 while (length > 2) {
1901 last = last->Previous();
1902 first = first->Next();
1903
1904 int differential = base->CalcNextVehicleOffset() - last->CalcNextVehicleOffset();
1905
1906 /* do not update images now
1907 * negative differential will be handled in AdvanceWagonsAfterSwap() */
1908 for (int i = 0; i < differential; i++) TrainController(first, last->Next());
1909
1910 base = first; // == base->Next()
1911 length -= 2;
1912 }
1913}
1914
1915
1922{
1923 /* first of all, fix the situation when the train was entering a depot */
1924 Train *dep = v; // last vehicle in front of just left depot
1925 while (dep->Next() != nullptr && (dep->track == TRACK_BIT_DEPOT || dep->Next()->track != TRACK_BIT_DEPOT)) {
1926 dep = dep->Next(); // find first vehicle outside of a depot, with next vehicle inside a depot
1927 }
1928
1929 Train *leave = dep->Next(); // first vehicle in a depot we are leaving now
1930
1931 if (leave != nullptr) {
1932 /* 'pull' next wagon out of the depot, so we won't miss it (it could stay in depot forever) */
1933 int d = TicksToLeaveDepot(dep);
1934
1935 if (d <= 0) {
1936 leave->vehstatus.Reset(VehState::Hidden); // move it out of the depot
1937 leave->track = TrackToTrackBits(GetRailDepotTrack(leave->tile));
1938 for (int i = 0; i >= d; i--) TrainController(leave, nullptr); // maybe move it, and maybe let another wagon leave
1939 }
1940 } else {
1941 dep = nullptr; // no vehicle in a depot, so no vehicle leaving a depot
1942 }
1943
1944 Train *base = v;
1945 Train *first = base; // first vehicle to move
1946 Train *last = v->Last(); // last vehicle to move
1947 uint length = CountVehiclesInChain(v);
1948
1949 /* We have to make sure all wagons that leave a depot because of train reversing are moved correctly
1950 * they have already correct spacing, so we have to make sure they are moved how they should */
1951 bool nomove = (dep == nullptr); // If there is no vehicle leaving a depot, limit the number of wagons moved immediately.
1952
1953 while (length > 2) {
1954 /* we reached vehicle (originally) in front of a depot, stop now
1955 * (we would move wagons that are already moved with new wagon length). */
1956 if (base == dep) break;
1957
1958 /* the last wagon was that one leaving a depot, so do not move it anymore */
1959 if (last == dep) nomove = true;
1960
1961 last = last->Previous();
1962 first = first->Next();
1963
1964 int differential = last->CalcNextVehicleOffset() - base->CalcNextVehicleOffset();
1965
1966 /* do not update images now */
1967 for (int i = 0; i < differential; i++) TrainController(first, (nomove ? last->Next() : nullptr));
1968
1969 base = first; // == base->Next()
1970 length -= 2;
1971 }
1972}
1973
1974static bool IsWholeTrainInsideDepot(const Train *v)
1975{
1976 for (const Train *u = v; u != nullptr; u = u->Next()) {
1977 if (u->track != TRACK_BIT_DEPOT || u->tile != v->tile) return false;
1978 }
1979 return true;
1980}
1981
1987{
1988 if (IsRailDepotTile(v->tile)) {
1989 if (IsWholeTrainInsideDepot(v)) return;
1991 }
1992
1993 /* Clear path reservation in front if train is not stuck. */
1995
1996 /* Check if we were approaching a rail/road-crossing */
1998
1999 /* count number of vehicles */
2000 int r = CountVehiclesInChain(v) - 1; // number of vehicles - 1
2001
2003
2004 /* swap start<>end, start+1<>end-1, ... */
2005 int l = 0;
2006 do {
2007 ReverseTrainSwapVeh(v, l++, r--);
2008 } while (l <= r);
2009
2011
2012 if (IsRailDepotTile(v->tile)) {
2014 }
2015
2018
2019 /* recalculate cached data */
2021
2022 /* update all images */
2023 for (Train *u = v; u != nullptr; u = u->Next()) u->UpdateViewport(false, false);
2024
2025 /* update crossing we were approaching */
2026 if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing);
2027
2028 /* maybe we are approaching crossing now, after reversal */
2029 crossing = TrainApproachingCrossingTile(v);
2030 if (crossing != INVALID_TILE) MaybeBarCrossingWithSound(crossing);
2031
2032 /* If we are inside a depot after reversing, don't bother with path reserving. */
2033 if (v->track == TRACK_BIT_DEPOT) {
2034 /* Can't be stuck here as inside a depot is always a safe tile. */
2036 v->flags.Reset(VehicleRailFlag::Stuck);
2037 return;
2038 }
2039
2040 /* VehicleExitDir does not always produce the desired dir for depots and
2041 * tunnels/bridges that is needed for UpdateSignalsOnSegment. */
2042 DiagDirection dir = VehicleExitDir(v->direction, v->track);
2044
2045 if (UpdateSignalsOnSegment(v->tile, dir, v->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
2046 /* If we are currently on a tile with conventional signals, we can't treat the
2047 * current tile as a safe tile or we would enter a PBS block without a reservation. */
2048 bool first_tile_okay = !(IsTileType(v->tile, TileType::Railway) &&
2050 !IsPbsSignal(GetSignalType(v->tile, FindFirstTrack(v->track))));
2051
2052 /* If we are on a depot tile facing outwards, do not treat the current tile as safe. */
2053 if (IsRailDepotTile(v->tile) && TrackdirToExitdir(v->GetVehicleTrackdir()) == GetRailDepotDirection(v->tile)) first_tile_okay = false;
2054
2056 if (TryPathReserve(v, false, first_tile_okay)) {
2057 /* Do a look-ahead now in case our current tile was already a safe tile. */
2059 } else if (v->current_order.GetType() != OT_LOADING) {
2060 /* Do not wait for a way out when we're still loading */
2062 }
2063 } else if (v->flags.Test(VehicleRailFlag::Stuck)) {
2064 /* A train not inside a PBS block can't be stuck. */
2065 v->flags.Reset(VehicleRailFlag::Stuck);
2066 v->wait_counter = 0;
2067 }
2068}
2069
2077CommandCost CmdReverseTrainDirection(DoCommandFlags flags, VehicleID veh_id, bool reverse_single_veh)
2078{
2079 Train *v = Train::GetIfValid(veh_id);
2080 if (v == nullptr) return CMD_ERROR;
2081
2083 if (ret.Failed()) return ret;
2084
2085 if (reverse_single_veh) {
2086 /* turn a single unit around */
2087
2088 if (v->IsMultiheaded() || EngInfo(v->engine_type)->callback_mask.Test(VehicleCallbackMask::ArticEngine)) {
2089 return CommandCost(STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE_MULTIPLE_UNITS);
2090 }
2091
2092 Train *front = v->First();
2093 /* make sure the vehicle is stopped in the depot */
2094 if (!front->IsStoppedInDepot()) {
2095 return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
2096 }
2097
2098 if (flags.Test(DoCommandFlag::Execute)) {
2100
2103 SetWindowDirty(WC_VEHICLE_DETAILS, front->index);
2106 }
2107 } else {
2108 /* turn the whole train around */
2109 if (!v->IsPrimaryVehicle()) return CMD_ERROR;
2110 if (v->vehstatus.Test(VehState::Crashed) || v->breakdown_ctr != 0) return CMD_ERROR;
2111
2112 if (flags.Test(DoCommandFlag::Execute)) {
2113 /* Properly leave the station if we are loading and won't be loading anymore */
2114 if (v->current_order.IsType(OT_LOADING)) {
2115 const Vehicle *last = v;
2116 while (last->Next() != nullptr) last = last->Next();
2117
2118 /* not a station || different station --> leave the station */
2120 v->LeaveStation();
2121 }
2122 }
2123
2124 /* We cancel any 'skip signal at dangers' here */
2125 v->force_proceed = TFP_NONE;
2127
2128 if (_settings_game.vehicle.train_acceleration_model != AM_ORIGINAL && v->cur_speed != 0) {
2130 } else {
2131 v->cur_speed = 0;
2132 v->SetLastSpeed();
2135 }
2136
2137 /* Unbunching data is no longer valid. */
2139 }
2140 }
2141 return CommandCost();
2142}
2143
2155{
2156 if (t->vehstatus.Test(VehState::Crashed) || t->force_proceed == TFP_SIGNAL) return TFP_NONE;
2157 if (!t->flags.Test(VehicleRailFlag::Stuck)) return t->IsChainInDepot() ? TFP_STUCK : TFP_SIGNAL;
2158
2160 if (next_tile == INVALID_TILE || !IsTileType(next_tile, TileType::Railway) || !HasSignals(next_tile)) return TFP_STUCK;
2162 return new_tracks != TRACK_BIT_NONE && HasSignalOnTrack(next_tile, FindFirstTrack(new_tracks)) ? TFP_SIGNAL : TFP_STUCK;
2163}
2164
2171CommandCost CmdForceTrainProceed(DoCommandFlags flags, VehicleID veh_id)
2172{
2173 Train *t = Train::GetIfValid(veh_id);
2174 if (t == nullptr) return CMD_ERROR;
2175
2176 if (!t->IsPrimaryVehicle()) return CMD_ERROR;
2177
2179 if (ret.Failed()) return ret;
2180
2181
2182 if (flags.Test(DoCommandFlag::Execute)) {
2183 t->force_proceed = DetermineNextTrainForceProceeding(t);
2185
2186 /* Unbunching data is no longer valid. */
2188 }
2189
2190 return CommandCost();
2191}
2192
2200static FindDepotData FindClosestTrainDepot(Train *v, int max_distance)
2201{
2202 assert(!v->vehstatus.Test(VehState::Crashed));
2203
2204 return YapfTrainFindNearestDepot(v, max_distance);
2205}
2206
2208{
2209 FindDepotData tfdd = FindClosestTrainDepot(this, 0);
2210 if (tfdd.best_length == UINT_MAX) return ClosestDepot();
2211
2212 return ClosestDepot(tfdd.tile, GetDepotIndex(tfdd.tile), tfdd.reverse);
2213}
2214
2216void Train::PlayLeaveStationSound(bool force) const
2217{
2218 static const SoundFx sfx[] = {
2224 };
2225
2226 if (PlayVehicleSound(this, VSE_START, force)) return;
2227
2228 SndPlayVehicleFx(sfx[RailVehInfo(this->engine_type)->engclass], this);
2229}
2230
2236{
2237 /* Don't do any look-ahead if path_backoff_interval is 255. */
2238 if (_settings_game.pf.path_backoff_interval == 255) return;
2239
2240 /* Exit if we are inside a depot. */
2241 if (v->track == TRACK_BIT_DEPOT) return;
2242
2243 switch (v->current_order.GetType()) {
2244 /* Exit if we reached our destination depot. */
2245 case OT_GOTO_DEPOT:
2246 if (v->tile == v->dest_tile) return;
2247 break;
2248
2249 case OT_GOTO_WAYPOINT:
2250 /* If we reached our waypoint, make sure we see that. */
2252 break;
2253
2254 case OT_NOTHING:
2255 case OT_LEAVESTATION:
2256 case OT_LOADING:
2257 /* Exit if the current order doesn't have a destination, but the train has orders. */
2258 if (v->GetNumOrders() > 0) return;
2259 break;
2260
2261 default:
2262 break;
2263 }
2264 /* Exit if we are on a station tile and are going to stop. */
2266
2267 Trackdir td = v->GetVehicleTrackdir();
2268
2269 /* On a tile with a red non-pbs signal, don't look ahead. */
2271 !IsPbsSignal(GetSignalType(v->tile, TrackdirToTrack(td))) &&
2273
2274 CFollowTrackRail ft(v);
2275 if (!ft.Follow(v->tile, td)) return;
2276
2278 /* Next tile is not reserved. */
2281 /* If the next tile is a PBS signal, try to make a reservation. */
2285 }
2286 ChooseTrainTrack(v, ft.new_tile, ft.exitdir, tracks, false, nullptr, false);
2287 }
2288 }
2289 }
2290}
2291
2298{
2299 /* bail out if not all wagons are in the same depot or not in a depot at all */
2300 for (const Train *u = v; u != nullptr; u = u->Next()) {
2301 if (u->track != TRACK_BIT_DEPOT || u->tile != v->tile) return false;
2302 }
2303
2304 /* if the train got no power, then keep it in the depot */
2305 if (v->gcache.cached_power == 0) {
2308 return true;
2309 }
2310
2311 /* Check if we should wait here for unbunching. */
2312 if (v->IsWaitingForUnbunching()) return true;
2313
2314 SigSegState seg_state;
2315
2316 if (v->force_proceed == TFP_NONE) {
2317 /* force proceed was not pressed */
2318 if (++v->wait_counter < 37) {
2320 return true;
2321 }
2322
2323 v->wait_counter = 0;
2324
2325 seg_state = _settings_game.pf.reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
2326 if (seg_state == SIGSEG_FULL || HasDepotReservation(v->tile)) {
2327 /* Full and no PBS signal in block or depot reserved, can't exit. */
2329 return true;
2330 }
2331 } else {
2332 seg_state = _settings_game.pf.reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
2333 }
2334
2335 /* We are leaving a depot, but have to go to the exact same one; re-enter. */
2336 if (v->current_order.IsType(OT_GOTO_DEPOT) && v->tile == v->dest_tile) {
2337 /* Service when depot has no reservation. */
2339 return true;
2340 }
2341
2342 /* Only leave when we can reserve a path to our destination. */
2343 if (seg_state == SIGSEG_PBS && !TryPathReserve(v) && v->force_proceed == TFP_NONE) {
2344 /* No path and no force proceed. */
2347 return true;
2348 }
2349
2350 SetDepotReservation(v->tile, true);
2351 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
2352
2357
2358 v->track = TRACK_BIT_X;
2359 if (v->direction & 2) v->track = TRACK_BIT_Y;
2360
2362 v->cur_speed = 0;
2363
2364 v->UpdateViewport(true, true);
2365 v->UpdatePosition();
2367 v->UpdateAcceleration();
2369
2370 return false;
2371}
2372
2379static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_dir)
2380{
2381 DiagDirection dir = TrackdirToExitdir(track_dir);
2382
2384 /* Are we just leaving a tunnel/bridge? */
2385 if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) {
2387
2388 if (TunnelBridgeIsFree(tile, end, v).Succeeded()) {
2389 /* Free the reservation only if no other train is on the tiles. */
2390 SetTunnelBridgeReservation(tile, false);
2391 SetTunnelBridgeReservation(end, false);
2392
2393 if (_settings_client.gui.show_track_reservation) {
2394 if (IsBridge(tile)) {
2395 MarkBridgeDirty(tile);
2396 } else {
2397 MarkTileDirtyByTile(tile);
2399 }
2400 }
2401 }
2402 }
2403 } else if (IsRailStationTile(tile)) {
2404 TileIndex new_tile = TileAddByDiagDir(tile, dir);
2405 /* If the new tile is not a further tile of the same station, we
2406 * clear the reservation for the whole platform. */
2407 if (!IsCompatibleTrainStationTile(new_tile, tile)) {
2409 }
2410 } else {
2411 /* Any other tile */
2412 UnreserveRailTrack(tile, TrackdirToTrack(track_dir));
2413 }
2414}
2415
2421{
2422 assert(v->IsFrontEngine());
2423
2424 TileIndex tile = v->tile;
2425 Trackdir td = v->GetVehicleTrackdir();
2426 bool free_tile = !(IsRailStationTile(v->tile) || IsTileType(v->tile, TileType::TunnelBridge));
2427 StationID station_id = IsRailStationTile(v->tile) ? GetStationIndex(v->tile) : StationID::Invalid();
2428
2429 /* Can't be holding a reservation if we enter a depot. */
2430 if (IsRailDepotTile(tile) && TrackdirToExitdir(td) != GetRailDepotDirection(tile)) return;
2431 if (v->track == TRACK_BIT_DEPOT) {
2432 /* Front engine is in a depot. We enter if some part is not in the depot. */
2433 for (const Train *u = v; u != nullptr; u = u->Next()) {
2434 if (u->track != TRACK_BIT_DEPOT || u->tile != v->tile) return;
2435 }
2436 }
2437 /* Don't free reservation if it's not ours. */
2439
2440 CFollowTrackRail ft(v, GetAllCompatibleRailTypes(v->railtypes));
2441 while (ft.Follow(tile, td)) {
2442 tile = ft.new_tile;
2444 td = RemoveFirstTrackdir(&bits);
2445 assert(bits == TRACKDIR_BIT_NONE);
2446
2447 if (!IsValidTrackdir(td)) break;
2448
2449 if (IsTileType(tile, TileType::Railway)) {
2450 if (HasSignalOnTrackdir(tile, td) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(td)))) {
2451 /* Conventional signal along trackdir: remove reservation and stop. */
2453 break;
2454 }
2455 if (HasPbsSignalOnTrackdir(tile, td)) {
2456 if (GetSignalStateByTrackdir(tile, td) == SIGNAL_STATE_RED) {
2457 /* Red PBS signal? Can't be our reservation, would be green then. */
2458 break;
2459 } else {
2460 /* Turn the signal back to red. */
2462 MarkTileDirtyByTile(tile);
2463 }
2464 } else if (HasPbsSignalOnTrackdir(tile, ReverseTrackdir(td))) {
2465 /* Reservation passes an opposing path signal. Mark signal for update to re-establish the proper default state. */
2467 } else if (HasSignalOnTrackdir(tile, ReverseTrackdir(td)) && IsOnewaySignal(tile, TrackdirToTrack(td))) {
2468 break;
2469 }
2470 }
2471
2472 /* Don't free first station/bridge/tunnel if we are on it. */
2473 if (free_tile || (!(ft.is_station && GetStationIndex(ft.new_tile) == station_id) && !ft.is_tunnel && !ft.is_bridge)) ClearPathReservation(v, tile, td);
2474
2475 free_tile = true;
2476 }
2477
2479}
2480
2481static const uint8_t _initial_tile_subcoord[6][4][3] = {
2482{{ 15, 8, 1 }, { 0, 0, 0 }, { 0, 8, 5 }, { 0, 0, 0 }},
2483{{ 0, 0, 0 }, { 8, 0, 3 }, { 0, 0, 0 }, { 8, 15, 7 }},
2484{{ 0, 0, 0 }, { 7, 0, 2 }, { 0, 7, 6 }, { 0, 0, 0 }},
2485{{ 15, 8, 2 }, { 0, 0, 0 }, { 0, 0, 0 }, { 8, 15, 6 }},
2486{{ 15, 7, 0 }, { 8, 0, 4 }, { 0, 0, 0 }, { 0, 0, 0 }},
2487{{ 0, 0, 0 }, { 0, 0, 0 }, { 0, 8, 4 }, { 7, 15, 0 }},
2488};
2489
2503static Track DoTrainPathfind(const Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found, bool do_track_reservation, PBSTileInfo *dest, TileIndex *final_dest)
2504{
2505 if (final_dest != nullptr) *final_dest = INVALID_TILE;
2506 return YapfTrainChooseTrack(v, tile, enterdir, tracks, path_found, do_track_reservation, dest, final_dest);
2507}
2508
2514static PBSTileInfo ExtendTrainReservation(const Train *v, TrackBits *new_tracks, DiagDirection *enterdir)
2515{
2517
2518 CFollowTrackRail ft(v);
2519
2520 std::vector<std::pair<TileIndex, Trackdir>> signals_set_to_red;
2521
2522 TileIndex tile = origin.tile;
2523 Trackdir cur_td = origin.trackdir;
2524 while (ft.Follow(tile, cur_td)) {
2526 /* Possible signal tile. */
2528 }
2529
2532 if (ft.new_td_bits == TRACKDIR_BIT_NONE) break;
2533 }
2534
2535 /* Station, depot or waypoint are a possible target. */
2536 bool target_seen = ft.is_station || (IsTileType(ft.new_tile, TileType::Railway) && !IsPlainRail(ft.new_tile));
2537 if (target_seen || KillFirstBit(ft.new_td_bits) != TRACKDIR_BIT_NONE) {
2538 /* Choice found or possible target encountered.
2539 * On finding a possible target, we need to stop and let the pathfinder handle the
2540 * remaining path. This is because we don't know if this target is in one of our
2541 * orders, so we might cause pathfinding to fail later on if we find a choice.
2542 * This failure would cause a bogous call to TryReserveSafePath which might reserve
2543 * a wrong path not leading to our next destination. */
2545
2546 /* If we did skip some tiles, backtrack to the first skipped tile so the pathfinder
2547 * actually starts its search at the first unreserved tile. */
2548 if (ft.tiles_skipped != 0) ft.new_tile -= TileOffsByDiagDir(ft.exitdir) * ft.tiles_skipped;
2549
2550 /* Choice found, path valid but not okay. Save info about the choice tile as well. */
2551 if (new_tracks != nullptr) *new_tracks = TrackdirBitsToTrackBits(ft.new_td_bits);
2552 if (enterdir != nullptr) *enterdir = ft.exitdir;
2553 return PBSTileInfo(ft.new_tile, ft.old_td, false);
2554 }
2555
2556 tile = ft.new_tile;
2557 cur_td = FindFirstTrackdir(ft.new_td_bits);
2558
2559 Trackdir rev_td = ReverseTrackdir(cur_td);
2560 if (IsSafeWaitingPosition(v, tile, cur_td, true, _settings_game.pf.forbid_90_deg)) {
2561 bool wp_free = IsWaitingPositionFree(v, tile, cur_td, _settings_game.pf.forbid_90_deg);
2562 if (!(wp_free && TryReserveRailTrack(tile, TrackdirToTrack(cur_td)))) break;
2563 /* Green path signal opposing the path? Turn to red. */
2564 if (HasPbsSignalOnTrackdir(tile, rev_td) && GetSignalStateByTrackdir(tile, rev_td) == SIGNAL_STATE_GREEN) {
2565 signals_set_to_red.emplace_back(tile, rev_td);
2567 MarkTileDirtyByTile(tile);
2568 }
2569 /* Safe position is all good, path valid and okay. */
2570 return PBSTileInfo(tile, cur_td, true);
2571 }
2572
2573 if (!TryReserveRailTrack(tile, TrackdirToTrack(cur_td))) break;
2574
2575 /* Green path signal opposing the path? Turn to red. */
2576 if (HasPbsSignalOnTrackdir(tile, rev_td) && GetSignalStateByTrackdir(tile, rev_td) == SIGNAL_STATE_GREEN) {
2577 signals_set_to_red.emplace_back(tile, rev_td);
2579 MarkTileDirtyByTile(tile);
2580 }
2581 }
2582
2583 if (ft.err == CFollowTrackRail::EC_OWNER || ft.err == CFollowTrackRail::EC_NO_WAY) {
2584 /* End of line, path valid and okay. */
2585 return PBSTileInfo(ft.old_tile, ft.old_td, true);
2586 }
2587
2588 /* Sorry, can't reserve path, back out. */
2589 tile = origin.tile;
2590 cur_td = origin.trackdir;
2591 TileIndex stopped = ft.old_tile;
2592 Trackdir stopped_td = ft.old_td;
2593 while (tile != stopped || cur_td != stopped_td) {
2594 if (!ft.Follow(tile, cur_td)) break;
2595
2598 assert(ft.new_td_bits != TRACKDIR_BIT_NONE);
2599 }
2601
2602 tile = ft.new_tile;
2603 cur_td = FindFirstTrackdir(ft.new_td_bits);
2604
2605 UnreserveRailTrack(tile, TrackdirToTrack(cur_td));
2606 }
2607
2608 /* Re-instate green signals we turned to red. */
2609 for (auto [sig_tile, td] : signals_set_to_red) {
2611 }
2612
2613 /* Path invalid. */
2614 return PBSTileInfo();
2615}
2616
2627static bool TryReserveSafeTrack(const Train *v, TileIndex tile, Trackdir td, bool override_railtype)
2628{
2629 return YapfTrainFindNearestSafeTile(v, tile, td, override_railtype);
2630}
2631
2633class VehicleOrderSaver {
2634private:
2635 Train *v;
2636 Order old_order;
2637 TileIndex old_dest_tile;
2638 StationID old_last_station_visited;
2639 VehicleOrderID index;
2640 bool suppress_implicit_orders;
2641 bool restored;
2642
2643public:
2644 VehicleOrderSaver(Train *_v) :
2645 v(_v),
2646 old_order(_v->current_order),
2647 old_dest_tile(_v->dest_tile),
2648 old_last_station_visited(_v->last_station_visited),
2649 index(_v->cur_real_order_index),
2650 suppress_implicit_orders(HasBit(_v->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)),
2651 restored(false)
2652 {
2653 }
2654
2658 void Restore()
2659 {
2660 this->v->current_order = this->old_order;
2661 this->v->dest_tile = this->old_dest_tile;
2662 this->v->last_station_visited = this->old_last_station_visited;
2663 AssignBit(this->v->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS, suppress_implicit_orders);
2664 this->restored = true;
2665 }
2666
2671 {
2672 if (!this->restored) this->Restore();
2673 }
2674
2680 bool SwitchToNextOrder(bool skip_first)
2681 {
2682 if (this->v->GetNumOrders() == 0) return false;
2683
2684 if (skip_first) ++this->index;
2685
2686 int depth = 0;
2687
2688 do {
2689 /* Wrap around. */
2690 if (this->index >= this->v->GetNumOrders()) this->index = 0;
2691
2692 Order *order = this->v->GetOrder(this->index);
2693 assert(order != nullptr);
2694
2695 switch (order->GetType()) {
2696 case OT_GOTO_DEPOT:
2697 /* Skip service in depot orders when the train doesn't need service. */
2698 if (order->GetDepotOrderType().Test(OrderDepotTypeFlag::Service) && !this->v->NeedsServicing()) break;
2699 [[fallthrough]];
2700 case OT_GOTO_STATION:
2701 case OT_GOTO_WAYPOINT:
2702 this->v->current_order = *order;
2703 return UpdateOrderDest(this->v, order, 0, true);
2704 case OT_CONDITIONAL: {
2705 VehicleOrderID next = ProcessConditionalOrder(order, this->v);
2706 if (next != INVALID_VEH_ORDER_ID) {
2707 depth++;
2708 this->index = next;
2709 /* Don't increment next, so no break here. */
2710 continue;
2711 }
2712 break;
2713 }
2714 default:
2715 break;
2716 }
2717 /* Don't increment inside the while because otherwise conditional
2718 * orders can lead to an infinite loop. */
2719 ++this->index;
2720 depth++;
2721 } while (this->index != this->v->cur_real_order_index && depth < this->v->GetNumOrders());
2722
2723 return false;
2724 }
2725};
2726
2727/* choose a track */
2728static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck)
2729{
2730 Track best_track = INVALID_TRACK;
2731 bool do_track_reservation = _settings_game.pf.reserve_paths || force_res;
2732 bool changed_signal = false;
2733 TileIndex final_dest = INVALID_TILE;
2734
2735 assert((tracks & ~TRACK_BIT_MASK) == 0);
2736
2737 if (got_reservation != nullptr) *got_reservation = false;
2738
2739 /* Don't use tracks here as the setting to forbid 90 deg turns might have been switched between reservation and now. */
2740 TrackBits res_tracks = (TrackBits)(GetReservedTrackbits(tile) & DiagdirReachesTracks(enterdir));
2741 /* Do we have a suitable reserved track? */
2742 if (res_tracks != TRACK_BIT_NONE) return FindFirstTrack(res_tracks);
2743
2744 /* Quick return in case only one possible track is available */
2745 if (KillFirstBit(tracks) == TRACK_BIT_NONE) {
2746 Track track = FindFirstTrack(tracks);
2747 /* We need to check for signals only here, as a junction tile can't have signals. */
2748 if (track != INVALID_TRACK && HasPbsSignalOnTrackdir(tile, TrackEnterdirToTrackdir(track, enterdir))) {
2749 do_track_reservation = true;
2750 changed_signal = true;
2752 } else if (!do_track_reservation) {
2753 return track;
2754 }
2755 best_track = track;
2756 }
2757
2758 PBSTileInfo res_dest(tile, INVALID_TRACKDIR, false);
2759 DiagDirection dest_enterdir = enterdir;
2760 if (do_track_reservation) {
2761 res_dest = ExtendTrainReservation(v, &tracks, &dest_enterdir);
2762 if (res_dest.tile == INVALID_TILE) {
2763 /* Reservation failed? */
2764 if (mark_stuck) MarkTrainAsStuck(v);
2765 if (changed_signal) SetSignalStateByTrackdir(tile, TrackEnterdirToTrackdir(best_track, enterdir), SIGNAL_STATE_RED);
2766 return FindFirstTrack(tracks);
2767 }
2768 if (res_dest.okay) {
2769 /* Got a valid reservation that ends at a safe target, quick exit. */
2770 if (got_reservation != nullptr) *got_reservation = true;
2771 if (changed_signal) MarkTileDirtyByTile(tile);
2773 return best_track;
2774 }
2775
2776 /* Check if the train needs service here, so it has a chance to always find a depot.
2777 * Also check if the current order is a service order so we don't reserve a path to
2778 * the destination but instead to the next one if service isn't needed. */
2780 if (v->current_order.IsType(OT_DUMMY) || v->current_order.IsType(OT_CONDITIONAL) || v->current_order.IsType(OT_GOTO_DEPOT)) ProcessOrders(v);
2781 }
2782
2783 /* Save the current train order. The destructor will restore the old order on function exit. */
2784 VehicleOrderSaver orders(v);
2785
2786 /* If the current tile is the destination of the current order and
2787 * a reservation was requested, advance to the next order.
2788 * Don't advance on a depot order as depots are always safe end points
2789 * for a path and no look-ahead is necessary. This also avoids a
2790 * problem with depot orders not part of the order list when the
2791 * order list itself is empty. */
2792 if (v->current_order.IsType(OT_LEAVESTATION)) {
2793 orders.SwitchToNextOrder(false);
2794 } else if (v->current_order.IsType(OT_LOADING) || (!v->current_order.IsType(OT_GOTO_DEPOT) && (
2795 v->current_order.IsType(OT_GOTO_STATION) ?
2797 v->tile == v->dest_tile))) {
2798 orders.SwitchToNextOrder(true);
2799 }
2800
2801 if (res_dest.tile != INVALID_TILE && !res_dest.okay) {
2802 /* Pathfinders are able to tell that route was only 'guessed'. */
2803 bool path_found = true;
2804 TileIndex new_tile = res_dest.tile;
2805
2806 Track next_track = DoTrainPathfind(v, new_tile, dest_enterdir, tracks, path_found, do_track_reservation, &res_dest, &final_dest);
2807 if (new_tile == tile) best_track = next_track;
2808 v->HandlePathfindingResult(path_found);
2809 }
2810
2811 /* No track reservation requested -> finished. */
2812 if (!do_track_reservation) return best_track;
2813
2814 /* A path was found, but could not be reserved. */
2815 if (res_dest.tile != INVALID_TILE && !res_dest.okay) {
2816 if (mark_stuck) MarkTrainAsStuck(v);
2818 return best_track;
2819 }
2820
2821 /* No possible reservation target found, we are probably lost. */
2822 if (res_dest.tile == INVALID_TILE) {
2823 /* Try to find any safe destination. */
2825 if (TryReserveSafeTrack(v, origin.tile, origin.trackdir, false)) {
2826 TrackBits res = GetReservedTrackbits(tile) & DiagdirReachesTracks(enterdir);
2827 best_track = FindFirstTrack(res);
2829 if (got_reservation != nullptr) *got_reservation = true;
2830 if (changed_signal) MarkTileDirtyByTile(tile);
2831 } else {
2833 if (mark_stuck) MarkTrainAsStuck(v);
2834 }
2835 return best_track;
2836 }
2837
2838 if (got_reservation != nullptr) *got_reservation = true;
2839
2840 /* Reservation target found and free, check if it is safe. */
2841 while (!IsSafeWaitingPosition(v, res_dest.tile, res_dest.trackdir, true, _settings_game.pf.forbid_90_deg)) {
2842 /* Extend reservation until we have found a safe position. */
2843 DiagDirection exitdir = TrackdirToExitdir(res_dest.trackdir);
2844 TileIndex next_tile = TileAddByDiagDir(res_dest.tile, exitdir);
2846 if (Rail90DegTurnDisallowed(GetTileRailType(res_dest.tile), GetTileRailType(next_tile))) {
2847 reachable &= ~TrackCrossesTracks(TrackdirToTrack(res_dest.trackdir));
2848 }
2849
2850 /* Get next order with destination. */
2851 if (orders.SwitchToNextOrder(true)) {
2852 PBSTileInfo cur_dest;
2853 bool path_found;
2854 DoTrainPathfind(v, next_tile, exitdir, reachable, path_found, true, &cur_dest, nullptr);
2855 if (cur_dest.tile != INVALID_TILE) {
2856 res_dest = cur_dest;
2857 if (res_dest.okay) continue;
2858 /* Path found, but could not be reserved. */
2860 if (mark_stuck) MarkTrainAsStuck(v);
2861 if (got_reservation != nullptr) *got_reservation = false;
2862 changed_signal = false;
2863 break;
2864 }
2865 }
2866 /* No order or no safe position found, try any position. */
2867 if (!TryReserveSafeTrack(v, res_dest.tile, res_dest.trackdir, true)) {
2869 if (mark_stuck) MarkTrainAsStuck(v);
2870 if (got_reservation != nullptr) *got_reservation = false;
2871 changed_signal = false;
2872 }
2873 break;
2874 }
2875
2877
2878 if (changed_signal) MarkTileDirtyByTile(tile);
2879
2880 orders.Restore();
2881 if (v->current_order.IsType(OT_GOTO_DEPOT) &&
2883 final_dest != INVALID_TILE && IsRailDepotTile(final_dest)) {
2885 v->dest_tile = final_dest;
2887 }
2888
2889 return best_track;
2890}
2891
2900bool TryPathReserve(Train *v, bool mark_as_stuck, bool first_tile_okay)
2901{
2902 assert(v->IsFrontEngine());
2903
2904 /* We have to handle depots specially as the track follower won't look
2905 * at the depot tile itself but starts from the next tile. If we are still
2906 * inside the depot, a depot reservation can never be ours. */
2907 if (v->track == TRACK_BIT_DEPOT) {
2908 if (HasDepotReservation(v->tile)) {
2909 if (mark_as_stuck) MarkTrainAsStuck(v);
2910 return false;
2911 } else {
2912 /* Depot not reserved, but the next tile might be. */
2914 if (HasReservedTracks(next_tile, DiagdirReachesTracks(GetRailDepotDirection(v->tile)))) return false;
2915 }
2916 }
2917
2918 Vehicle *other_train = nullptr;
2919 PBSTileInfo origin = FollowTrainReservation(v, &other_train);
2920 /* The path we are driving on is already blocked by some other train.
2921 * This can only happen in certain situations when mixing path and
2922 * block signals or when changing tracks and/or signals.
2923 * Exit here as doing any further reservations will probably just
2924 * make matters worse. */
2925 if (other_train != nullptr && other_train->index != v->index) {
2926 if (mark_as_stuck) MarkTrainAsStuck(v);
2927 return false;
2928 }
2929 /* If we have a reserved path and the path ends at a safe tile, we are finished already. */
2930 if (origin.okay && (v->tile != origin.tile || first_tile_okay)) {
2931 /* Can't be stuck then. */
2933 v->flags.Reset(VehicleRailFlag::Stuck);
2934 return true;
2935 }
2936
2937 /* If we are in a depot, tentatively reserve the depot. */
2938 if (v->track == TRACK_BIT_DEPOT) {
2939 SetDepotReservation(v->tile, true);
2940 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
2941 }
2942
2943 DiagDirection exitdir = TrackdirToExitdir(origin.trackdir);
2944 TileIndex new_tile = TileAddByDiagDir(origin.tile, exitdir);
2946
2948
2949 bool res_made = false;
2950 ChooseTrainTrack(v, new_tile, exitdir, reachable, true, &res_made, mark_as_stuck);
2951
2952 if (!res_made) {
2953 /* Free the depot reservation as well. */
2954 if (v->track == TRACK_BIT_DEPOT) SetDepotReservation(v->tile, false);
2955 return false;
2956 }
2957
2958 if (v->flags.Test(VehicleRailFlag::Stuck)) {
2959 v->wait_counter = 0;
2961 }
2962 v->flags.Reset(VehicleRailFlag::Stuck);
2963 return true;
2964}
2965
2966
2967static bool CheckReverseTrain(const Train *v)
2968{
2969 if (_settings_game.difficulty.line_reverse_mode != 0 ||
2970 v->track == TRACK_BIT_DEPOT || v->track == TRACK_BIT_WORMHOLE ||
2971 !(v->direction & 1)) {
2972 return false;
2973 }
2974
2975 assert(v->track != TRACK_BIT_NONE);
2976
2977 return YapfTrainCheckReverse(v);
2978}
2979
2986{
2987 if (station == this->last_station_visited) this->last_station_visited = StationID::Invalid();
2988
2989 const Station *st = Station::Get(station);
2991 /* The destination station has no trainstation tiles. */
2993 return TileIndex{};
2994 }
2995
2996 return st->xy;
2997}
2998
3001{
3002 Train *v = this;
3003 do {
3004 v->colourmap = PAL_NONE;
3005 v->UpdateViewport(true, false);
3006 } while ((v = v->Next()) != nullptr);
3007
3008 /* need to update acceleration and cached values since the goods on the train changed. */
3009 this->CargoChanged();
3010 this->UpdateAcceleration();
3011}
3012
3021{
3022 switch (_settings_game.vehicle.train_acceleration_model) {
3023 default: NOT_REACHED();
3024 case AM_ORIGINAL:
3025 return this->DoUpdateSpeed(this->acceleration * (this->GetAccelerationStatus() == AS_BRAKE ? -4 : 2), 0, this->GetCurrentMaxSpeed());
3026
3027 case AM_REALISTIC:
3028 return this->DoUpdateSpeed(this->GetAcceleration(), this->GetAccelerationStatus() == AS_BRAKE ? 0 : 2, this->GetCurrentMaxSpeed());
3029 }
3030}
3031
3037static void TrainEnterStation(Train *v, StationID station)
3038{
3039 v->last_station_visited = station;
3040
3041 /* check if a train ever visited this station before */
3042 Station *st = Station::Get(station);
3043 if (!(st->had_vehicle_of_type & HVOT_TRAIN)) {
3044 st->had_vehicle_of_type |= HVOT_TRAIN;
3046 GetEncodedString(STR_NEWS_FIRST_TRAIN_ARRIVAL, st->index),
3048 v->index,
3049 st->index
3050 );
3051 AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
3052 Game::NewEvent(new ScriptEventStationFirstVehicle(st->index, v->index));
3053 }
3054
3055 v->force_proceed = TFP_NONE;
3057
3058 v->BeginLoading();
3059
3061 TriggerStationAnimation(st, v->tile, StationAnimationTrigger::VehicleArrives);
3062}
3063
3064/* Check if the vehicle is compatible with the specified tile */
3065static inline bool CheckCompatibleRail(const Train *v, TileIndex tile)
3066{
3067 return IsTileOwner(tile, v->owner) &&
3068 (!v->IsFrontEngine() || v->compatible_railtypes.Test(GetRailType(tile)));
3069}
3070
3073 uint8_t small_turn;
3074 uint8_t large_turn;
3075 uint8_t z_up;
3076 uint8_t z_down;
3077};
3078
3081 /* normal accel */
3082 {256 / 4, 256 / 2, 256 / 4, 2},
3083 {256 / 4, 256 / 2, 256 / 4, 2},
3084 {0, 256 / 2, 256 / 4, 2},
3085};
3086
3092static inline void AffectSpeedByZChange(Train *v, int old_z)
3093{
3094 if (old_z == v->z_pos || _settings_game.vehicle.train_acceleration_model != AM_ORIGINAL) return;
3095
3096 const AccelerationSlowdownParams *asp = &_accel_slowdown[static_cast<int>(v->GetAccelerationType())];
3097
3098 if (old_z < v->z_pos) {
3099 v->cur_speed -= (v->cur_speed * asp->z_up >> 8);
3100 } else {
3101 uint16_t spd = v->cur_speed + asp->z_down;
3102 if (spd <= v->gcache.cached_max_track_speed) v->cur_speed = spd;
3103 }
3104}
3105
3106static bool TrainMovedChangeSignals(TileIndex tile, DiagDirection dir)
3107{
3108 if (IsTileType(tile, TileType::Railway) &&
3111 Trackdir trackdir = FindFirstTrackdir(tracks);
3112 if (UpdateSignalsOnSegment(tile, TrackdirToExitdir(trackdir), GetTileOwner(tile)) == SIGSEG_PBS && HasSignalOnTrackdir(tile, trackdir)) {
3113 /* A PBS block with a non-PBS signal facing us? */
3114 if (!IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) return true;
3115 }
3116 }
3117 return false;
3118}
3119
3122{
3123 for (const Train *u = this; u != nullptr; u = u->Next()) {
3124 switch (u->track) {
3125 case TRACK_BIT_WORMHOLE:
3127 break;
3128 case TRACK_BIT_DEPOT:
3129 break;
3130 default:
3132 break;
3133 }
3134 }
3135}
3136
3143uint Train::Crash(bool flooded)
3144{
3145 uint victims = 0;
3146 if (this->IsFrontEngine()) {
3147 victims += 2; // driver
3148
3149 /* Remove the reserved path in front of the train if it is not stuck.
3150 * Also clear all reserved tracks the train is currently on. */
3151 if (!this->flags.Test(VehicleRailFlag::Stuck)) FreeTrainTrackReservation(this);
3152 for (const Train *v = this; v != nullptr; v = v->Next()) {
3155 /* ClearPathReservation will not free the wormhole exit
3156 * if the train has just entered the wormhole. */
3158 }
3159 }
3160
3161 /* we may need to update crossing we were approaching,
3162 * but must be updated after the train has been marked crashed */
3163 TileIndex crossing = TrainApproachingCrossingTile(this);
3164 if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing);
3165
3166 /* Remove the loading indicators (if any) */
3168 }
3169
3170 victims += this->GroundVehicleBase::Crash(flooded);
3171
3172 this->crash_anim_pos = flooded ? 4000 : 1; // max 4440, disappear pretty fast when flooded
3173 return victims;
3174}
3175
3182static uint TrainCrashed(Train *v)
3183{
3184 uint victims = 0;
3185
3186 /* do not crash train twice */
3187 if (!v->vehstatus.Test(VehState::Crashed)) {
3188 victims = v->Crash();
3189 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_TRAIN, victims, v->owner));
3190 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_TRAIN, victims, v->owner));
3191 }
3192
3193 /* Try to re-reserve track under already crashed train too.
3194 * Crash() clears the reservation! */
3196
3197 return victims;
3198}
3199
3207{
3208 /* not a train or in depot */
3209 if (v->type != VEH_TRAIN || Train::From(v)->track == TRACK_BIT_DEPOT) return 0;
3210
3211 /* do not crash into trains of another company. */
3212 if (v->owner != t->owner) return 0;
3213
3214 /* get first vehicle now to make most usual checks faster */
3215 Train *coll = Train::From(v)->First();
3216
3217 /* can't collide with own wagons */
3218 if (coll == t) return 0;
3219
3220 int x_diff = v->x_pos - t->x_pos;
3221 int y_diff = v->y_pos - t->y_pos;
3222
3223 /* Do fast calculation to check whether trains are not in close vicinity
3224 * and quickly reject trains distant enough for any collision.
3225 * Differences are shifted by 7, mapping range [-7 .. 8] into [0 .. 15]
3226 * Differences are then ORed and then we check for any higher bits */
3227 uint hash = (y_diff + 7) | (x_diff + 7);
3228 if (hash & ~15) return 0;
3229
3230 /* Slower check using multiplication */
3231 int min_diff = (Train::From(v)->gcache.cached_veh_length + 1) / 2 + (t->gcache.cached_veh_length + 1) / 2 - 1;
3232 if (x_diff * x_diff + y_diff * y_diff > min_diff * min_diff) return 0;
3233
3234 /* Happens when there is a train under bridge next to bridge head */
3235 if (abs(v->z_pos - t->z_pos) > 5) return 0;
3236
3237 /* crash both trains */
3238 return TrainCrashed(t) + TrainCrashed(coll);
3239}
3240
3249{
3250 /* can't collide in depot */
3251 if (v->track == TRACK_BIT_DEPOT) return false;
3252
3253 assert(v->track == TRACK_BIT_WORMHOLE || TileVirtXY(v->x_pos, v->y_pos) == v->tile);
3254
3255 uint num_victims = 0;
3256
3257 /* find colliding vehicles */
3258 if (v->track == TRACK_BIT_WORMHOLE) {
3259 for (Vehicle *u : VehiclesOnTile(v->tile)) {
3260 num_victims += CheckTrainCollision(u, v);
3261 }
3263 num_victims += CheckTrainCollision(u, v);
3264 }
3265 } else {
3266 for (Vehicle *u : VehiclesNearTileXY(v->x_pos, v->y_pos, 7)) {
3267 num_victims += CheckTrainCollision(u, v);
3268 }
3269 }
3270
3271 /* any dead -> no crash */
3272 if (num_victims == 0) return false;
3273
3274 AddTileNewsItem(GetEncodedString(STR_NEWS_TRAIN_CRASH, num_victims), NewsType::Accident, v->tile);
3275
3276 ModifyStationRatingAround(v->tile, v->owner, -160, 30);
3277 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_13_TRAIN_COLLISION, v);
3278 return true;
3279}
3280
3288bool TrainController(Train *v, Vehicle *nomove, bool reverse)
3289{
3290 Train *first = v->First();
3291 Train *prev;
3292 bool direction_changed = false; // has direction of any part changed?
3293
3294 /* For every vehicle after and including the given vehicle */
3295 for (prev = v->Previous(); v != nomove; prev = v, v = v->Next()) {
3296 DiagDirection enterdir = DIAGDIR_BEGIN;
3297 bool update_signals_crossing = false; // will we update signals or crossing state?
3298
3300 if (v->track != TRACK_BIT_WORMHOLE) {
3301 /* Not inside tunnel */
3302 if (gp.old_tile == gp.new_tile) {
3303 /* Staying in the old tile */
3304 if (v->track == TRACK_BIT_DEPOT) {
3305 /* Inside depot */
3306 gp.x = v->x_pos;
3307 gp.y = v->y_pos;
3308 } else {
3309 /* Not inside depot */
3310
3311 /* Reverse when we are at the end of the track already, do not move to the new position */
3312 if (v->IsFrontEngine() && !TrainCheckIfLineEnds(v, reverse)) return false;
3313
3314 auto vets = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
3315 if (vets.Test(VehicleEnterTileState::CannotEnter)) {
3316 goto invalid_rail;
3317 }
3319 /* The new position is the end of the platform */
3321 }
3322 }
3323 } else {
3324 /* A new tile is about to be entered. */
3325
3326 /* Determine what direction we're entering the new tile from */
3327 enterdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile);
3328 assert(IsValidDiagDirection(enterdir));
3329
3330 /* Get the status of the tracks in the new tile and mask
3331 * away the bits that aren't reachable. */
3332 TrackStatus ts = GetTileTrackStatus(gp.new_tile, TRANSPORT_RAIL, 0, ReverseDiagDir(enterdir));
3333 TrackdirBits reachable_trackdirs = DiagdirReachesTrackdirs(enterdir);
3334
3335 TrackdirBits trackdirbits = TrackStatusToTrackdirBits(ts) & reachable_trackdirs;
3336 TrackBits red_signals = TrackdirBitsToTrackBits(TrackStatusToRedSignals(ts) & reachable_trackdirs);
3337
3338 TrackBits bits = TrackdirBitsToTrackBits(trackdirbits);
3339 if (Rail90DegTurnDisallowed(GetTileRailType(gp.old_tile), GetTileRailType(gp.new_tile)) && prev == nullptr) {
3340 /* We allow wagons to make 90 deg turns, because forbid_90_deg
3341 * can be switched on halfway a turn */
3342 bits &= ~TrackCrossesTracks(FindFirstTrack(v->track));
3343 }
3344
3345 if (bits == TRACK_BIT_NONE) goto invalid_rail;
3346
3347 /* Check if the new tile constrains tracks that are compatible
3348 * with the current train, if not, bail out. */
3349 if (!CheckCompatibleRail(v, gp.new_tile)) goto invalid_rail;
3350
3351 TrackBits chosen_track;
3352 if (prev == nullptr) {
3353 /* Currently the locomotive is active. Determine which one of the
3354 * available tracks to choose */
3355 chosen_track = TrackToTrackBits(ChooseTrainTrack(v, gp.new_tile, enterdir, bits, false, nullptr, true));
3356 assert(chosen_track & (bits | GetReservedTrackbits(gp.new_tile)));
3357
3358 if (v->force_proceed != TFP_NONE && IsPlainRailTile(gp.new_tile) && HasSignals(gp.new_tile)) {
3359 /* For each signal we find decrease the counter by one.
3360 * We start at two, so the first signal we pass decreases
3361 * this to one, then if we reach the next signal it is
3362 * decreased to zero and we won't pass that new signal. */
3363 Trackdir dir = FindFirstTrackdir(trackdirbits);
3364 if (HasSignalOnTrackdir(gp.new_tile, dir) ||
3366 GetSignalType(gp.new_tile, TrackdirToTrack(dir)) != SIGTYPE_PBS)) {
3367 /* However, we do not want to be stopped by PBS signals
3368 * entered via the back. */
3369 v->force_proceed = (v->force_proceed == TFP_SIGNAL) ? TFP_STUCK : TFP_NONE;
3371 }
3372 }
3373
3374 /* Check if it's a red signal and that force proceed is not clicked. */
3375 if ((red_signals & chosen_track) && v->force_proceed == TFP_NONE) {
3376 /* In front of a red signal */
3377 Trackdir i = FindFirstTrackdir(trackdirbits);
3378
3379 /* Don't handle stuck trains here. */
3380 if (v->flags.Test(VehicleRailFlag::Stuck)) return false;
3381
3383 v->cur_speed = 0;
3384 v->subspeed = 0;
3385 v->progress = 255; // make sure that every bit of acceleration will hit the signal again, so speed stays 0.
3386 if (!_settings_game.pf.reverse_at_signals || ++v->wait_counter < _settings_game.pf.wait_oneway_signal * Ticks::DAY_TICKS * 2) return false;
3387 } else if (HasSignalOnTrackdir(gp.new_tile, i)) {
3388 v->cur_speed = 0;
3389 v->subspeed = 0;
3390 v->progress = 255; // make sure that every bit of acceleration will hit the signal again, so speed stays 0.
3391 if (!_settings_game.pf.reverse_at_signals || ++v->wait_counter < _settings_game.pf.wait_twoway_signal * Ticks::DAY_TICKS * 2) {
3392 DiagDirection exitdir = TrackdirToExitdir(i);
3393 TileIndex o_tile = TileAddByDiagDir(gp.new_tile, exitdir);
3394
3395 exitdir = ReverseDiagDir(exitdir);
3396
3397 /* check if a train is waiting on the other side */
3398 if (!HasVehicleOnTile(o_tile, [&exitdir](const Vehicle *u) {
3399 if (u->type != VEH_TRAIN || u->vehstatus.Test(VehState::Crashed)) return false;
3400 const Train *t = Train::From(u);
3401
3402 /* not front engine of a train, inside wormhole or depot, crashed */
3403 if (!t->IsFrontEngine() || !(t->track & TRACK_BIT_MASK)) return false;
3404
3405 if (t->cur_speed > 5 || VehicleExitDir(t->direction, t->track) != exitdir) return false;
3406
3407 return true;
3408 })) return false;
3409 }
3410 }
3411
3412 /* If we would reverse but are currently in a PBS block and
3413 * reversing of stuck trains is disabled, don't reverse.
3414 * This does not apply if the reason for reversing is a one-way
3415 * signal blocking us, because a train would then be stuck forever. */
3416 if (!_settings_game.pf.reverse_at_signals && !HasOnewaySignalBlockingTrackdir(gp.new_tile, i) &&
3417 UpdateSignalsOnSegment(v->tile, enterdir, v->owner) == SIGSEG_PBS) {
3418 v->wait_counter = 0;
3419 return false;
3420 }
3421 goto reverse_train_direction;
3422 } else {
3423 TryReserveRailTrack(gp.new_tile, TrackBitsToTrack(chosen_track), false);
3424 }
3425 } else {
3426 /* The wagon is active, simply follow the prev vehicle. */
3427 if (prev->tile == gp.new_tile) {
3428 /* Choose the same track as prev */
3429 if (prev->track == TRACK_BIT_WORMHOLE) {
3430 /* Vehicles entering tunnels enter the wormhole earlier than for bridges.
3431 * However, just choose the track into the wormhole. */
3432 assert(IsTunnel(prev->tile));
3433 chosen_track = bits;
3434 } else {
3435 chosen_track = prev->track;
3436 }
3437 } else {
3438 /* Choose the track that leads to the tile where prev is.
3439 * This case is active if 'prev' is already on the second next tile, when 'v' just enters the next tile.
3440 * I.e. when the tile between them has only space for a single vehicle like
3441 * 1) horizontal/vertical track tiles and
3442 * 2) some orientations of tunnel entries, where the vehicle is already inside the wormhole at 8/16 from the tile edge.
3443 * Is also the train just reversing, the wagon inside the tunnel is 'on' the tile of the opposite tunnel entry.
3444 */
3445 static const TrackBits _connecting_track[DIAGDIR_END][DIAGDIR_END] = {
3450 };
3451 DiagDirection exitdir = DiagdirBetweenTiles(gp.new_tile, prev->tile);
3452 assert(IsValidDiagDirection(exitdir));
3453 chosen_track = _connecting_track[enterdir][exitdir];
3454 }
3455 chosen_track &= bits;
3456 }
3457
3458 /* Make sure chosen track is a valid track */
3459 assert(
3460 chosen_track == TRACK_BIT_X || chosen_track == TRACK_BIT_Y ||
3461 chosen_track == TRACK_BIT_UPPER || chosen_track == TRACK_BIT_LOWER ||
3462 chosen_track == TRACK_BIT_LEFT || chosen_track == TRACK_BIT_RIGHT);
3463
3464 /* Update XY to reflect the entrance to the new tile, and select the direction to use */
3465 const uint8_t *b = _initial_tile_subcoord[FindFirstBit(chosen_track)][enterdir];
3466 gp.x = (gp.x & ~0xF) | b[0];
3467 gp.y = (gp.y & ~0xF) | b[1];
3468 Direction chosen_dir = (Direction)b[2];
3469
3470 /* Call the landscape function and tell it that the vehicle entered the tile */
3471 auto vets = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
3472 if (vets.Test(VehicleEnterTileState::CannotEnter)) {
3473 goto invalid_rail;
3474 }
3475
3477 Track track = FindFirstTrack(chosen_track);
3478 Trackdir tdir = TrackDirectionToTrackdir(track, chosen_dir);
3479 if (v->IsFrontEngine() && HasPbsSignalOnTrackdir(gp.new_tile, tdir)) {
3482 }
3483
3484 /* Clear any track reservation when the last vehicle leaves the tile */
3485 if (v->Next() == nullptr) ClearPathReservation(v, v->tile, v->GetVehicleTrackdir());
3486
3487 v->tile = gp.new_tile;
3488
3491 }
3492
3493 v->track = chosen_track;
3494 assert(v->track);
3495 }
3496
3497 /* We need to update signal status, but after the vehicle position hash
3498 * has been updated by UpdateInclination() */
3499 update_signals_crossing = true;
3500
3501 if (chosen_dir != v->direction) {
3502 if (prev == nullptr && _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) {
3503 const AccelerationSlowdownParams *asp = &_accel_slowdown[static_cast<int>(v->GetAccelerationType())];
3504 DirDiff diff = DirDifference(v->direction, chosen_dir);
3505 v->cur_speed -= (diff == DIRDIFF_45RIGHT || diff == DIRDIFF_45LEFT ? asp->small_turn : asp->large_turn) * v->cur_speed >> 8;
3506 }
3507 direction_changed = true;
3508 v->direction = chosen_dir;
3509 }
3510
3511 if (v->IsFrontEngine()) {
3512 v->wait_counter = 0;
3513
3514 /* If we are approaching a crossing that is reserved, play the sound now. */
3516 if (crossing != INVALID_TILE && HasCrossingReservation(crossing) && _settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, crossing);
3517
3518 /* Always try to extend the reservation when entering a tile. */
3520 }
3521
3523 /* The new position is the location where we want to stop */
3525 }
3526 }
3527 } else {
3529 /* Perform look-ahead on tunnel exit. */
3530 if (v->IsFrontEngine()) {
3533 }
3534 /* Prevent v->UpdateInclination() being called with wrong parameters.
3535 * This could happen if the train was reversed inside the tunnel/bridge. */
3536 if (gp.old_tile == gp.new_tile) {
3538 }
3539 } else {
3540 v->x_pos = gp.x;
3541 v->y_pos = gp.y;
3542 v->UpdatePosition();
3543 if (!v->vehstatus.Test(VehState::Hidden)) v->Vehicle::UpdateViewport(true);
3544 continue;
3545 }
3546 }
3547
3548 /* update image of train, as well as delta XY */
3549 v->UpdateDeltaXY();
3550
3551 v->x_pos = gp.x;
3552 v->y_pos = gp.y;
3553 v->UpdatePosition();
3554
3555 /* update the Z position of the vehicle */
3556 int old_z = v->UpdateInclination(gp.new_tile != gp.old_tile, false);
3557
3558 if (prev == nullptr) {
3559 /* This is the first vehicle in the train */
3560 AffectSpeedByZChange(v, old_z);
3561 }
3562
3563 if (update_signals_crossing) {
3564 if (v->IsFrontEngine()) {
3565 if (TrainMovedChangeSignals(gp.new_tile, enterdir)) {
3566 /* We are entering a block with PBS signals right now, but
3567 * not through a PBS signal. This means we don't have a
3568 * reservation right now. As a conventional signal will only
3569 * ever be green if no other train is in the block, getting
3570 * a path should always be possible. If the player built
3571 * such a strange network that it is not possible, the train
3572 * will be marked as stuck and the player has to deal with
3573 * the problem. */
3574 if ((!HasReservedTracks(gp.new_tile, v->track) &&
3575 !TryReserveRailTrack(gp.new_tile, FindFirstTrack(v->track))) ||
3576 !TryPathReserve(v)) {
3578 }
3579 }
3580 }
3581
3582 /* Signals can only change when the first
3583 * (above) or the last vehicle moves. */
3584 if (v->Next() == nullptr) {
3585 TrainMovedChangeSignals(gp.old_tile, ReverseDiagDir(enterdir));
3587 }
3588 }
3589
3590 /* Do not check on every tick to save some computing time. */
3591 if (v->IsFrontEngine() && v->tick_counter % _settings_game.pf.path_backoff_interval == 0) CheckNextTrainTile(v);
3592 }
3593
3594 if (direction_changed) first->tcache.cached_max_curve_speed = first->GetCurveSpeedLimit();
3595
3596 return true;
3597
3598invalid_rail:
3599 /* We've reached end of line?? */
3600 if (prev != nullptr) FatalError("Disconnecting train");
3601
3602reverse_train_direction:
3603 if (reverse) {
3604 v->wait_counter = 0;
3605 v->cur_speed = 0;
3606 v->subspeed = 0;
3608 }
3609
3610 return false;
3611}
3612
3613static bool IsRailStationPlatformOccupied(TileIndex tile)
3614{
3616
3617 for (TileIndex t = tile; IsCompatibleTrainStationTile(t, tile); t -= delta) {
3618 if (HasVehicleOnTile(t, IsTrain)) return true;
3619 }
3620 for (TileIndex t = tile + delta; IsCompatibleTrainStationTile(t, tile); t += delta) {
3621 if (HasVehicleOnTile(t, IsTrain)) return true;
3622 }
3623
3624 return false;
3625}
3626
3634static void DeleteLastWagon(Train *v)
3635{
3636 Train *first = v->First();
3637
3638 /* Go to the last wagon and delete the link pointing there
3639 * new_last is then the one-before-last wagon, and v the last
3640 * one which will physically be removed */
3641 Train *new_last = v;
3642 for (; v->Next() != nullptr; v = v->Next()) new_last = v;
3643 new_last->SetNext(nullptr);
3644
3645 if (first != v) {
3646 /* Recalculate cached train properties */
3648 /* Update the depot window if the first vehicle is in depot -
3649 * if v == first, then it is updated in PreDestructor() */
3650 if (first->track == TRACK_BIT_DEPOT) {
3652 }
3653 v->last_station_visited = first->last_station_visited; // for PreDestructor
3654 }
3655
3656 /* 'v' shouldn't be accessed after it has been deleted */
3657 TrackBits trackbits = v->track;
3658 TileIndex tile = v->tile;
3659 Owner owner = v->owner;
3660
3661 delete v;
3662 v = nullptr; // make sure nobody will try to read 'v' anymore
3663
3664 if (trackbits == TRACK_BIT_WORMHOLE) {
3665 /* Vehicle is inside a wormhole, v->track contains no useful value then. */
3667 }
3668
3669 Track track = TrackBitsToTrack(trackbits);
3670 if (HasReservedTracks(tile, trackbits)) {
3671 UnreserveRailTrack(tile, track);
3672
3673 /* If there are still crashed vehicles on the tile, give the track reservation to them */
3674 TrackBits remaining_trackbits = TRACK_BIT_NONE;
3675 for (const Vehicle *u : VehiclesOnTile(tile)) {
3676 if (u->type != VEH_TRAIN || !u->vehstatus.Test(VehState::Crashed)) continue;
3677 TrackBits train_tbits = Train::From(u)->track;
3678 if (train_tbits == TRACK_BIT_WORMHOLE) {
3679 /* Vehicle is inside a wormhole, u->track contains no useful value then. */
3680 remaining_trackbits |= DiagDirToDiagTrackBits(GetTunnelBridgeDirection(u->tile));
3681 } else if (train_tbits != TRACK_BIT_DEPOT) {
3682 remaining_trackbits |= train_tbits;
3683 }
3684 }
3685
3686 /* It is important that these two are the first in the loop, as reservation cannot deal with every trackbit combination */
3687 assert(TRACK_BEGIN == TRACK_X && TRACK_Y == TRACK_BEGIN + 1);
3688 for (Track t : SetTrackBitIterator(remaining_trackbits)) TryReserveRailTrack(tile, t);
3689 }
3690
3691 /* check if the wagon was on a road/rail-crossing */
3693
3694 if (IsRailStationTile(tile)) {
3695 bool occupied = IsRailStationPlatformOccupied(tile);
3697 SetRailStationPlatformReservation(tile, dir, occupied);
3699 }
3700
3701 /* Update signals */
3704 } else {
3705 SetSignalsOnBothDir(tile, track, owner);
3706 }
3707}
3708
3714{
3715 static const DirDiff delta[] = {
3717 };
3718
3719 do {
3720 /* We don't need to twist around vehicles if they're not visible */
3721 if (!v->vehstatus.Test(VehState::Hidden)) {
3722 v->direction = ChangeDir(v->direction, delta[GB(Random(), 0, 2)]);
3723 /* Refrain from updating the z position of the vehicle when on
3724 * a bridge, because UpdateInclination() will put the vehicle under
3725 * the bridge in that case */
3726 if (v->track != TRACK_BIT_WORMHOLE) {
3727 v->UpdatePosition();
3728 v->UpdateInclination(false, true);
3729 } else {
3730 v->UpdateViewport(false, true);
3731 }
3732 }
3733 } while ((v = v->Next()) != nullptr);
3734}
3735
3742{
3743 int state = ++v->crash_anim_pos;
3744
3745 if (state == 4 && !v->vehstatus.Test(VehState::Hidden)) {
3747 }
3748
3749 uint32_t r;
3750 if (state <= 200 && Chance16R(1, 7, r)) {
3751 int index = (r * 10 >> 16);
3752
3753 Vehicle *u = v;
3754 do {
3755 if (--index < 0) {
3756 r = Random();
3757
3759 GB(r, 8, 3) + 2,
3760 GB(r, 16, 3) + 2,
3761 GB(r, 0, 3) + 5,
3763 break;
3764 }
3765 } while ((u = u->Next()) != nullptr);
3766 }
3767
3768 if (state <= 240 && !(v->tick_counter & 3)) ChangeTrainDirRandomly(v);
3769
3770 if (state >= 4440 && !(v->tick_counter & 0x1F)) {
3771 bool ret = v->Next() != nullptr;
3772 DeleteLastWagon(v);
3773 return ret;
3774 }
3775
3776 return true;
3777}
3778
3780static const uint16_t _breakdown_speeds[16] = {
3781 225, 210, 195, 180, 165, 150, 135, 120, 105, 90, 75, 60, 45, 30, 15, 15
3782};
3783
3784
3793static bool TrainApproachingLineEnd(Train *v, bool signal, bool reverse)
3794{
3795 /* Calc position within the current tile */
3796 uint x = v->x_pos & 0xF;
3797 uint y = v->y_pos & 0xF;
3798
3799 /* for diagonal directions, 'x' will be 0..15 -
3800 * for other directions, it will be 1, 3, 5, ..., 15 */
3801 switch (v->direction) {
3802 case DIR_N : x = ~x + ~y + 25; break;
3803 case DIR_NW: x = y; [[fallthrough]];
3804 case DIR_NE: x = ~x + 16; break;
3805 case DIR_E : x = ~x + y + 9; break;
3806 case DIR_SE: x = y; break;
3807 case DIR_S : x = x + y - 7; break;
3808 case DIR_W : x = ~y + x + 9; break;
3809 default: break;
3810 }
3811
3812 /* Do not reverse when approaching red signal. Make sure the vehicle's front
3813 * does not cross the tile boundary when we do reverse, but as the vehicle's
3814 * location is based on their center, use half a vehicle's length as offset.
3815 * Multiply the half-length by two for straight directions to compensate that
3816 * we only get odd x offsets there. */
3817 if (!signal && x + (v->gcache.cached_veh_length + 1) / 2 * (IsDiagonalDirection(v->direction) ? 1 : 2) >= TILE_SIZE) {
3818 /* we are too near the tile end, reverse now */
3819 v->cur_speed = 0;
3820 if (reverse) ReverseTrainDirection(v);
3821 return false;
3822 }
3823
3824 /* slow down */
3826 uint16_t break_speed = _breakdown_speeds[x & 0xF];
3827 if (break_speed < v->cur_speed) v->cur_speed = break_speed;
3828
3829 return true;
3830}
3831
3832
3838static bool TrainCanLeaveTile(const Train *v)
3839{
3840 /* Exit if inside a tunnel/bridge or a depot */
3841 if (v->track == TRACK_BIT_WORMHOLE || v->track == TRACK_BIT_DEPOT) return false;
3842
3843 TileIndex tile = v->tile;
3844
3845 /* entering a tunnel/bridge? */
3848 if (DiagDirToDir(dir) == v->direction) return false;
3849 }
3850
3851 /* entering a depot? */
3852 if (IsRailDepotTile(tile)) {
3854 if (DiagDirToDir(dir) == v->direction) return false;
3855 }
3856
3857 return true;
3858}
3859
3860
3869{
3870 assert(v->IsFrontEngine());
3871 assert(!v->vehstatus.Test(VehState::Crashed));
3872
3873 if (!TrainCanLeaveTile(v)) return INVALID_TILE;
3874
3875 DiagDirection dir = VehicleExitDir(v->direction, v->track);
3876 TileIndex tile = v->tile + TileOffsByDiagDir(dir);
3877
3878 /* not a crossing || wrong axis || unusable rail (wrong type or owner) */
3879 if (!IsLevelCrossingTile(tile) || DiagDirToAxis(dir) == GetCrossingRoadAxis(tile) ||
3880 !CheckCompatibleRail(v, tile)) {
3881 return INVALID_TILE;
3882 }
3883
3884 return tile;
3885}
3886
3887
3895static bool TrainCheckIfLineEnds(Train *v, bool reverse)
3896{
3897 /* First, handle broken down train */
3898
3899 int t = v->breakdown_ctr;
3900 if (t > 1) {
3902
3903 uint16_t break_speed = _breakdown_speeds[GB(~t, 4, 4)];
3904 if (break_speed < v->cur_speed) v->cur_speed = break_speed;
3905 } else {
3907 }
3908
3909 if (!TrainCanLeaveTile(v)) return true;
3910
3911 /* Determine the non-diagonal direction in which we will exit this tile */
3912 DiagDirection dir = VehicleExitDir(v->direction, v->track);
3913 /* Calculate next tile */
3914 TileIndex tile = v->tile + TileOffsByDiagDir(dir);
3915
3916 /* Determine the track status on the next tile */
3917 TrackStatus ts = GetTileTrackStatus(tile, TRANSPORT_RAIL, 0, ReverseDiagDir(dir));
3918 TrackdirBits reachable_trackdirs = DiagdirReachesTrackdirs(dir);
3919
3920 TrackdirBits trackdirbits = TrackStatusToTrackdirBits(ts) & reachable_trackdirs;
3921 TrackdirBits red_signals = TrackStatusToRedSignals(ts) & reachable_trackdirs;
3922
3923 /* We are sure the train is not entering a depot, it is detected above */
3924
3925 /* mask unreachable track bits if we are forbidden to do 90deg turns */
3926 TrackBits bits = TrackdirBitsToTrackBits(trackdirbits);
3928 bits &= ~TrackCrossesTracks(FindFirstTrack(v->track));
3929 }
3930
3931 /* no suitable trackbits at all || unusable rail (wrong type or owner) */
3932 if (bits == TRACK_BIT_NONE || !CheckCompatibleRail(v, tile)) {
3933 return TrainApproachingLineEnd(v, false, reverse);
3934 }
3935
3936 /* approaching red signal */
3937 if ((trackdirbits & red_signals) != 0) return TrainApproachingLineEnd(v, true, reverse);
3938
3939 /* approaching a rail/road crossing? then make it red */
3941
3942 return true;
3943}
3944
3945
3946static bool TrainLocoHandler(Train *v, bool mode)
3947{
3948 /* train has crashed? */
3950 return mode ? true : HandleCrashedTrain(v); // 'this' can be deleted here
3951 }
3952
3953 if (v->force_proceed != TFP_NONE) {
3954 v->flags.Reset(VehicleRailFlag::Stuck);
3956 }
3957
3958 /* train is broken down? */
3959 if (v->HandleBreakdown()) return true;
3960
3961 if (v->flags.Test(VehicleRailFlag::Reversing) && v->cur_speed == 0) {
3963 }
3964
3965 /* exit if train is stopped */
3966 if (v->vehstatus.Test(VehState::Stopped) && v->cur_speed == 0) return true;
3967
3968 bool valid_order = !v->current_order.IsType(OT_NOTHING) && v->current_order.GetType() != OT_CONDITIONAL;
3969 if (ProcessOrders(v) && CheckReverseTrain(v)) {
3970 v->wait_counter = 0;
3971 v->cur_speed = 0;
3972 v->subspeed = 0;
3975 return true;
3976 } else if (v->flags.Test(VehicleRailFlag::LeavingStation)) {
3977 /* Try to reserve a path when leaving the station as we
3978 * might not be marked as wanting a reservation, e.g.
3979 * when an overlength train gets turned around in a station. */
3980 DiagDirection dir = VehicleExitDir(v->direction, v->track);
3982
3983 if (UpdateSignalsOnSegment(v->tile, dir, v->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
3984 TryPathReserve(v, true, true);
3985 }
3987 }
3988
3989 v->HandleLoading(mode);
3990
3991 if (v->current_order.IsType(OT_LOADING)) return true;
3992
3993 if (CheckTrainStayInDepot(v)) return true;
3994
3995 if (!mode) v->ShowVisualEffect();
3996
3997 /* We had no order but have an order now, do look ahead. */
3998 if (!valid_order && !v->current_order.IsType(OT_NOTHING)) {
4000 }
4001
4002 /* Handle stuck trains. */
4003 if (!mode && v->flags.Test(VehicleRailFlag::Stuck)) {
4004 ++v->wait_counter;
4005
4006 /* Should we try reversing this tick if still stuck? */
4007 bool turn_around = v->wait_counter % (_settings_game.pf.wait_for_pbs_path * Ticks::DAY_TICKS) == 0 && _settings_game.pf.reverse_at_signals;
4008
4009 if (!turn_around && v->wait_counter % _settings_game.pf.path_backoff_interval != 0 && v->force_proceed == TFP_NONE) return true;
4010 if (!TryPathReserve(v)) {
4011 /* Still stuck. */
4012 if (turn_around) ReverseTrainDirection(v);
4013
4014 if (v->flags.Test(VehicleRailFlag::Stuck) && v->wait_counter > 2 * _settings_game.pf.wait_for_pbs_path * Ticks::DAY_TICKS) {
4015 /* Show message to player. */
4016 if (_settings_client.gui.lost_vehicle_warn && v->owner == _local_company) {
4017 AddVehicleAdviceNewsItem(AdviceType::TrainStuck, GetEncodedString(STR_NEWS_TRAIN_IS_STUCK, v->index), v->index);
4018 }
4019 v->wait_counter = 0;
4020 }
4021 /* Exit if force proceed not pressed, else reset stuck flag anyway. */
4022 if (v->force_proceed == TFP_NONE) return true;
4023 v->flags.Reset(VehicleRailFlag::Stuck);
4024 v->wait_counter = 0;
4026 }
4027 }
4028
4029 if (v->current_order.IsType(OT_LEAVESTATION)) {
4030 v->current_order.Free();
4032 return true;
4033 }
4034
4035 int j = v->UpdateSpeed();
4036
4037 /* we need to invalidate the widget if we are stopping from 'Stopping 0 km/h' to 'Stopped' */
4038 if (v->cur_speed == 0 && v->vehstatus.Test(VehState::Stopped)) {
4039 /* If we manually stopped, we're not force-proceeding anymore. */
4040 v->force_proceed = TFP_NONE;
4042 }
4043
4044 int adv_spd = v->GetAdvanceDistance();
4045 if (j < adv_spd) {
4046 /* if the vehicle has speed 0, update the last_speed field. */
4047 if (v->cur_speed == 0) v->SetLastSpeed();
4048 } else {
4050 /* Loop until the train has finished moving. */
4051 for (;;) {
4052 j -= adv_spd;
4053 TrainController(v, nullptr);
4054 /* Don't continue to move if the train crashed. */
4055 if (CheckTrainCollision(v)) break;
4056 /* Determine distance to next map position */
4057 adv_spd = v->GetAdvanceDistance();
4058
4059 /* No more moving this tick */
4060 if (j < adv_spd || v->cur_speed == 0) break;
4061
4062 OrderType order_type = v->current_order.GetType();
4063 /* Do not skip waypoints (incl. 'via' stations) when passing through at full speed. */
4064 if ((order_type == OT_GOTO_WAYPOINT || order_type == OT_GOTO_STATION) &&
4068 ProcessOrders(v);
4069 }
4070 }
4071 v->SetLastSpeed();
4072 }
4073
4074 for (Train *u = v; u != nullptr; u = u->Next()) {
4075 if (u->vehstatus.Test(VehState::Hidden)) continue;
4076
4077 u->UpdateViewport(false, false);
4078 }
4079
4080 if (v->progress == 0) v->progress = j; // Save unused spd for next time, if TrainController didn't set progress
4081
4082 return true;
4083}
4084
4090{
4091 Money cost = 0;
4092 const Train *v = this;
4093
4094 do {
4095 const Engine *e = v->GetEngine();
4096 if (e->VehInfo<RailVehicleInfo>().running_cost_class == Price::Invalid) continue;
4097
4098 uint cost_factor = GetVehicleProperty(v, PROP_TRAIN_RUNNING_COST_FACTOR, e->VehInfo<RailVehicleInfo>().running_cost);
4099 if (cost_factor == 0) continue;
4100
4101 /* Halve running cost for multiheaded parts */
4102 if (v->IsMultiheaded()) cost_factor /= 2;
4103
4104 cost += GetPrice(e->VehInfo<RailVehicleInfo>().running_cost_class, cost_factor, e->GetGRF());
4105 } while ((v = v->GetNextVehicle()) != nullptr);
4106
4107 return cost;
4108}
4109
4115{
4116 this->tick_counter++;
4117
4118 if (this->IsFrontEngine()) {
4120
4121 if (!this->vehstatus.Test(VehState::Stopped) || this->cur_speed > 0) this->running_ticks++;
4122
4123 this->current_order_time++;
4124
4125 if (!TrainLocoHandler(this, false)) return false;
4126
4127 return TrainLocoHandler(this, true);
4128 } else if (this->IsFreeWagon() && this->vehstatus.Test(VehState::Crashed)) {
4129 /* Delete flooded standalone wagon chain */
4130 if (++this->crash_anim_pos >= 4400) {
4131 delete this;
4132 return false;
4133 }
4134 }
4135
4136 return true;
4137}
4138
4144{
4145 if (Company::Get(v->owner)->settings.vehicle.servint_trains == 0 || !v->NeedsAutomaticServicing()) return;
4146 if (v->IsChainInDepot()) {
4148 return;
4149 }
4150
4151 uint max_penalty = _settings_game.pf.yapf.maximum_go_to_depot_penalty;
4152
4153 FindDepotData tfdd = FindClosestTrainDepot(v, max_penalty);
4154 /* Only go to the depot if it is not too far out of our way. */
4155 if (tfdd.best_length == UINT_MAX || tfdd.best_length > max_penalty) {
4156 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
4157 /* If we were already heading for a depot but it has
4158 * suddenly moved farther away, we continue our normal
4159 * schedule? */
4162 }
4163 return;
4164 }
4165
4166 DepotID depot = GetDepotIndex(tfdd.tile);
4167
4168 if (v->current_order.IsType(OT_GOTO_DEPOT) &&
4169 v->current_order.GetDestination() != depot &&
4170 !Chance16(3, 16)) {
4171 return;
4172 }
4173
4176 v->dest_tile = tfdd.tile;
4178}
4179
4182{
4183 AgeVehicle(this);
4184}
4185
4188{
4189 EconomyAgeVehicle(this);
4190
4191 if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
4192
4193 if (this->IsFrontEngine()) {
4195
4197
4198 CheckOrders(this);
4199
4200 /* update destination */
4201 if (this->current_order.IsType(OT_GOTO_STATION)) {
4202 TileIndex tile = Station::Get(this->current_order.GetDestination().ToStationID())->train_station.tile;
4203 if (tile != INVALID_TILE) this->dest_tile = tile;
4204 }
4205
4206 if (this->running_ticks != 0) {
4207 /* running costs */
4209
4210 this->profit_this_year -= cost.GetCost();
4211 this->running_ticks = 0;
4212
4214
4217 }
4218 }
4219}
4220
4226{
4227 if (this->vehstatus.Test(VehState::Crashed)) return INVALID_TRACKDIR;
4228
4229 if (this->track == TRACK_BIT_DEPOT) {
4230 /* We'll assume the train is facing outwards */
4231 return DiagDirToDiagTrackdir(GetRailDepotDirection(this->tile)); // Train in depot
4232 }
4233
4234 if (this->track == TRACK_BIT_WORMHOLE) {
4235 /* train in tunnel or on bridge, so just use its direction and assume a diagonal track */
4236 return DiagDirToDiagTrackdir(DirToDiagDir(this->direction));
4237 }
4238
4239 return TrackDirectionToTrackdir(FindFirstTrack(this->track), this->direction);
4240}
4241
4242uint16_t Train::GetMaxWeight() const
4243{
4244 uint16_t weight = CargoSpec::Get(this->cargo_type)->WeightOfNUnitsInTrain(this->GetEngine()->DetermineCapacity(this));
4245
4246 /* Vehicle weight is not added for articulated parts. */
4247 if (!this->IsArticulatedPart()) {
4248 weight += GetVehicleProperty(this, PROP_TRAIN_WEIGHT, RailVehInfo(this->engine_type)->weight);
4249 }
4250
4251 /* Powered wagons have extra weight added. */
4252 if (this->flags.Test(VehicleRailFlag::PoweredWagon)) {
4253 weight += RailVehInfo(this->gcache.first_engine)->pow_wag_weight;
4254 }
4255
4256 return weight;
4257}
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).
constexpr T AssignBit(T &x, const uint8_t y, bool value)
Assigns a bit in a variable.
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr uint8_t FindFirstBit(T x)
Search the first set bit in a value.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T KillFirstBit(T value)
Clear the first bit in an integer.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
const BridgeSpec * GetBridgeSpec(BridgeType i)
Get the specification of a bridge type.
Definition bridge.h:60
bool IsBridgeTile(Tile t)
checks if there is a bridge on this tile
Definition bridge_map.h:35
BridgeType GetBridgeType(Tile t)
Determines the type of bridge on a tile.
Definition bridge_map.h:56
bool IsBridge(Tile t)
Checks if this is a bridge, instead of a tunnel.
Definition bridge_map.h:24
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:21
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:104
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition ai_core.cpp:235
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Flip(Tvalue_type value)
Flip the value-th bit.
constexpr Timpl & Set()
Set all bits.
Common return value for all commands.
void AddCost(const Money &cost)
Adds the given cost to the cost of the command.
Money GetCost() const
The costs as made up to this moment.
bool Failed() const
Did this command fail?
uint32_t GetGRFID() const
Retrieve the GRF ID of the NewGRF the engine is tied to.
Definition engine.cpp:160
uint16_t reliability_spd_dec
Speed of reliability decay between services (per day).
Definition engine_base.h:50
const GRFFile * GetGRF() const
Retrieve the NewGRF the engine is tied to.
uint DetermineCapacity(const Vehicle *v, uint16_t *mail_capacity=nullptr) const
Determines capacity of a given vehicle from scratch.
Definition engine.cpp:204
EngineFlags flags
Flags of the engine.
Definition engine_base.h:57
uint8_t original_image_index
Original vehicle image index, thus the image index of the overridden vehicle.
Definition engine_base.h:61
TimerGameCalendar::Date GetLifeLengthInDays() const
Returns the vehicle's (not model's!) life length in days.
Definition engine.cpp:446
CargoType GetDefaultCargoType() const
Determines the default cargo type of an engine.
Definition engine_base.h:94
uint16_t reliability
Current reliability of the engine.
Definition engine_base.h:49
static void NewEvent(class ScriptEvent *event)
Queue a new event for a Game Script.
RAII class for measuring multi-step elements of performance.
This struct contains all the info that is needed to draw and construct tracks.
Definition rail.h:115
uint8_t curve_speed
Multiplier for curve maximum speed advantage.
Definition rail.h:195
static constexpr TimerGameTick::Ticks DAY_TICKS
1 day is 74 ticks; TimerGameCalendar::date_fract used to be uint16_t and incremented by 885.
static Date date
Current date in days (day counter).
static Year year
Current year, starting at 0.
static Date date
Current date in days (day counter).
This class will save the current order of a vehicle and restore it on destruction.
void Restore()
Restore the saved order to the vehicle.
~VehicleOrderSaver()
Restore the saved order to the vehicle, if Restore() has not already been called.
bool SwitchToNextOrder(bool skip_first)
Set the current vehicle order to the next order in the order list.
Iterate over all vehicles near a given world coordinate.
Iterate over all vehicles on a tile.
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Execute
execute the given command
@ NoCargoCapacityCheck
when autoreplace/autorenew is in progress, this shall prevent truncating the amount of cargo in the v...
@ AutoReplace
autoreplace/autorenew is in progress, this shall disable vehicle limits when building,...
Definition of stuff that is very close to a company, like the company struct itself.
CommandCost CheckOwnership(Owner owner, TileIndex tile)
Check whether the current owner owns something.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
void SubtractMoneyFromCompanyFract(CompanyID company, const CommandCost &cst)
Subtract money from a company, including the money fraction.
Functions related to companies.
DepotID GetDepotIndex(Tile t)
Get the index of which depot is attached to the tile.
Definition depot_map.h:53
PoolID< uint16_t, struct DepotIDTag, 64000, 0xFFFF > DepotID
Type for the unique identifier of depots.
Definition depot_type.h:15
DirDiff DirDifference(Direction d0, Direction d1)
Calculate the difference between two directions.
Direction DiagDirToDir(DiagDirection dir)
Convert a DiagDirection to a Direction.
DiagDirection ReverseDiagDir(DiagDirection d)
Returns the reverse direction of the given DiagDirection.
Direction ReverseDir(Direction d)
Return the reverse of a direction.
bool IsValidDiagDirection(DiagDirection d)
Checks if an integer value is a valid DiagDirection.
Direction ChangeDir(Direction d, DirDiff delta)
Change a direction by a given difference.
DiagDirection AxisToDiagDir(Axis a)
Converts an Axis to a DiagDirection.
bool IsDiagonalDirection(Direction dir)
Checks if a given Direction is diagonal.
Axis DiagDirToAxis(DiagDirection d)
Convert a DiagDirection to the axis.
DiagDirection DirToDiagDir(Direction dir)
Convert a Direction to a DiagDirection.
DirDiff
Enumeration for the difference between two directions.
@ DIRDIFF_90LEFT
Angle of 90 degrees left.
@ DIRDIFF_45LEFT
Angle of 45 degrees left.
@ DIRDIFF_45RIGHT
Angle of 45 degrees right.
@ DIRDIFF_SAME
Both directions faces to the same direction.
@ DIRDIFF_90RIGHT
Angle of 90 degrees right.
Direction
Defines the 8 directions on the map.
@ DIR_SW
Southwest.
@ DIR_NW
Northwest.
@ DIR_N
North.
@ DIR_SE
Southeast.
@ DIR_S
South.
@ DIR_NE
Northeast.
@ DIR_W
West.
@ DIR_E
East.
Axis
Allow incrementing of DiagDirDiff variables.
DiagDirection
Enumeration for diagonal directions.
@ DIAGDIR_END
Used for iterations.
@ DIAGDIR_BEGIN
Used for iterations.
@ INVALID_DIAGDIR
Flag for an invalid DiagDirection.
Money GetPrice(Price index, uint cost_factor, const GRFFile *grf_file, int shift)
Determine a certain price.
Definition economy.cpp:940
@ EXPENSES_TRAIN_RUN
Running costs trains.
@ EXPENSES_NEW_VEHICLES
New vehicles.
@ Invalid
Invalid base price.
EffectVehicle * CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicleType type)
Create an effect vehicle above a particular vehicle.
Functions related to effect vehicles.
@ EV_EXPLOSION_SMALL
Various explosions.
@ EV_EXPLOSION_LARGE
Various explosions.
@ RailFlips
Rail vehicle has old depot-flip handling.
@ RailTilts
Rail vehicle tilts in curves.
@ VE_DISABLE_WAGON_POWER
Flag to disable wagon power.
Definition engine_type.h:68
PoolID< uint16_t, struct EngineIDTag, 64000, 0xFFFF > EngineID
Unique identification number of an engine.
Definition engine_type.h:26
@ ExclusivePreview
This vehicle is in the exclusive preview stage, either being used or being offered to a company.
@ RAILVEH_WAGON
simple wagon, not motorized
Definition engine_type.h:34
@ RAILVEH_MULTIHEAD
indicates a combination of two locomotives
Definition engine_type.h:33
Functions related to errors.
@ WL_CRITICAL
Critical errors, the MessageBox is shown in all cases.
Definition error.h:27
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
Error reporting related functions.
fluid_settings_t * settings
FluidSynth settings handle.
Types for recording game performance data.
@ PFE_GL_TRAINS
Time spent processing trains.
Base functions for all Games.
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
@ AS_BRAKE
We want to stop.
@ GVF_SUPPRESS_IMPLICIT_ORDERS
Disable insertion and removal of automatic orders until the vehicle completes the real order.
@ GVF_GOINGDOWN_BIT
Vehicle is currently going downhill. (Cached track information for acceleration).
@ GVF_GOINGUP_BIT
Vehicle is currently going uphill. (Cached track information for acceleration).
void UpdateTrainGroupID(Train *v)
Recalculates the groupID of a train.
void SetTrainGroupID(Train *v, GroupID grp)
Affect the groupID of a train to new_g.
void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
Mark a tile given by its index dirty for repaint.
static constexpr GroupID DEFAULT_GROUP
Ungrouped vehicles are in this group.
Definition group_type.h:18
TrackStatus GetTileTrackStatus(TileIndex tile, TransportType mode, uint 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:625
static TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:406
TileIndex TileAddByDiagDir(TileIndex tile, DiagDirection dir)
Adds a DiagDir to a tile.
Definition map_func.h:613
TileIndexDiff TileOffsByAxis(Axis axis)
Convert an Axis to a TileIndexDiff.
Definition map_func.h:557
static uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:427
static uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:417
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition map_func.h:572
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:587
bool _networking
are we in networking mode?
Definition network.cpp:66
Basic functions/variables used all over the place.
ClientID
'Unique' identifier to be given to clients
Base for the NewGRF implementation.
@ ArticEngine
Add articulated engines (trains and road vehicles).
@ Length
Vehicle length (trains and road vehicles).
@ CBID_VEHICLE_LENGTH
Vehicle length, returns the amount of 1/8's the vehicle is shorter for trains and RVs.
@ CBID_TRAIN_ALLOW_WAGON_ATTACH
Determine whether a wagon can be attached to an already existing train.
static const uint CALLBACK_FAILED
Different values for Callback result evaluations.
void ErrorUnknownCallbackResult(uint32_t grfid, uint16_t cbid, uint16_t cb_res)
Record that a NewGRF returned an unknown/invalid callback result.
@ VehCapacity
Capacity of vehicle changes when not refitting or arranging.
Functions/types related to NewGRF debugging.
void InvalidateNewGRFInspectWindow(GrfSpecFeature feature, uint index)
Invalidate the inspect window for a given feature and index.
void DeleteNewGRFInspectWindow(GrfSpecFeature feature, uint index)
Delete inspect window for a given feature and index.
uint16_t GetVehicleCallbackParent(CallbackID callback, uint32_t param1, uint32_t param2, EngineID engine, const Vehicle *v, const Vehicle *parent, std::span< int32_t > regs100)
Evaluate a newgrf callback for vehicles with a different vehicle for parent scope.
bool UsesWagonOverride(const Vehicle *v)
Check if a wagon is currently using a wagon override.
uint16_t GetVehicleCallback(CallbackID callback, uint32_t param1, uint32_t param2, EngineID engine, const Vehicle *v, std::span< int32_t > regs100)
Evaluate a newgrf callback for vehicles.
std::optional< bool > TestVehicleBuildProbability(Vehicle *v, BuildProbabilityType type)
Test for vehicle build probability type.
@ Reversed
Change the rail vehicle should be reversed when purchased.
@ PROP_TRAIN_SHORTEN_FACTOR
Shorter vehicles.
@ PROP_TRAIN_USER_DATA
User defined data for vehicle variable 0x42.
@ PROP_TRAIN_WEIGHT
Weight in t (if dualheaded: for each single vehicle).
@ PROP_TRAIN_CARGO_AGE_PERIOD
Number of ticks before carried cargo is aged.
@ PROP_TRAIN_RUNNING_COST_FACTOR
Yearly runningcost (if dualheaded: sum of both vehicles).
@ PROP_TRAIN_SPEED
Max. speed: 1 unit = 1/1.6 mph = 1 km-ish/h.
bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event, bool force)
Checks whether a NewGRF wants to play a different vehicle sound effect.
Functions related to NewGRF provided sounds.
@ VSE_START
Vehicle starting, i.e. leaving, the station.
void TriggerStationRandomisation(BaseStation *st, TileIndex trigger_tile, StationRandomTrigger trigger, CargoType cargo_type)
Trigger station randomisation.
Header file for NewGRF stations.
StringID GetGRFStringID(uint32_t grfid, GRFStringID stringid)
Returns the index for this stringid associated with its grfID.
Header of Action 04 "universal holder" structure and functions.
StrongType::Typedef< uint32_t, struct GRFStringIDTag, StrongType::Compare, StrongType::Integer > GRFStringID
Type for GRF-internal string IDs.
static constexpr GRFStringID GRFSTR_MISC_GRF_TEXT
Miscellaneous GRF text range.
Functions related to news.
void AddVehicleNewsItem(EncodedString &&headline, NewsType type, VehicleID vehicle, StationID station=StationID::Invalid())
Adds a newsitem referencing a vehicle.
Definition news_func.h:30
void AddVehicleAdviceNewsItem(AdviceType advice_type, EncodedString &&headline, VehicleID vehicle)
Adds a vehicle-advice news item.
Definition news_func.h:40
@ ArrivalCompany
First vehicle arrived for company.
Definition news_type.h:30
@ ArrivalOther
First vehicle arrived for competitor.
Definition news_type.h:31
@ Accident
An accident or disaster has occurred.
Definition news_type.h:32
@ TrainStuck
The train got stuck and needs to be unstuck manually.
Definition news_type.h:56
@ Error
A game paused because a (critical) error.
Definition openttd.h:72
Functions related to order backups.
bool ProcessOrders(Vehicle *v)
Handle the orders of a vehicle and determine the next place to go to if needed.
bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool pbs_look_ahead)
Update the vehicle's destination tile from an order.
void CheckOrders(const Vehicle *v)
Check the orders of a vehicle, to see if there are invalid orders and stuff.
void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist, bool reset_order_indices)
Delete all orders from a vehicle.
VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v)
Process a conditional order and determine the next order.
OrderStopLocation
Where to stop the trains.
Definition order_type.h:97
@ NearEnd
Stop at the near end of the platform.
Definition order_type.h:98
@ FarEnd
Stop at the far end of the platform.
Definition order_type.h:100
@ Middle
Stop at the middle of the platform.
Definition order_type.h:99
@ NoDestination
The vehicle will stop at any station it passes except the destination, aka via.
Definition order_type.h:89
@ NoIntermediate
The vehicle will not stop at any stations it passes except the destination, aka non-stop.
Definition order_type.h:88
uint8_t VehicleOrderID
The index of an order within its current vehicle (not pool related).
Definition order_type.h:18
@ NearestDepot
Send the vehicle to the nearest depot.
Definition order_type.h:119
@ Service
This depot order is because of the servicing limit.
Definition order_type.h:108
static const VehicleOrderID INVALID_VEH_ORDER_ID
Invalid vehicle order index (sentinel).
Definition order_type.h:39
OrderType
Order types.
Definition order_type.h:50
void SetRailStationPlatformReservation(TileIndex start, DiagDirection dir, bool b)
Set the reservation for a complete station platform.
Definition pbs.cpp:57
TrackBits GetReservedTrackbits(TileIndex t)
Get the reserved trackbits for any tile, regardless of type.
Definition pbs.cpp:24
PBSTileInfo FollowTrainReservation(const Train *v, Vehicle **train_on_res)
Follow a train reservation to the last tile.
Definition pbs.cpp:289
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:427
void UnreserveRailTrack(TileIndex tile, Track t)
Lift the reservation of a specific track on a tile.
Definition pbs.cpp:144
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:381
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:37
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:324
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:351
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:376
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:410
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:300
RailTypes GetAllCompatibleRailTypes(RailTypes railtypes)
Returns all compatible railtypes for a set of railtypes.
Definition rail.h:312
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:474
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:425
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
@ 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:448
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:412
bool HasPbsSignalOnTrackdir(Tile tile, Trackdir td)
Is a pbs signal present along the trackdir?
Definition rail_map.h:462
bool IsOnewaySignal(Tile t, Track track)
One-way signals can't be passed the 'wrong' way.
Definition rail_map.h:318
void SetDepotReservation(Tile t, bool b)
Set the reservation state of the depot.
Definition rail_map.h:269
bool HasDepotReservation(Tile t)
Get the reservation state of the depot.
Definition rail_map.h:257
bool HasSignals(Tile t)
Checks if a rail tile has signals.
Definition rail_map.h:72
SignalState GetSignalStateByTrackdir(Tile tile, Trackdir trackdir)
Gets the state of the signal along the given trackdir.
Definition rail_map.h:437
static bool IsRailDepotTile(Tile t)
Is this tile rail tile and a rail depot?
Definition rail_map.h:105
@ RAILTYPE_RAIL
Standard non-electric rails.
Definition rail_type.h:27
Pseudo random number generator.
bool Chance16(const uint32_t a, const uint32_t b, const std::source_location location=std::source_location::current())
Flips a coin with given probability.
bool Chance16R(const uint32_t a, const uint32_t b, uint32_t &r, const std::source_location location=std::source_location::current())
Flips a coin with a given probability and saves the randomize-number in a variable.
void UpdateLevelCrossing(TileIndex tile, bool sound=true, bool force_bar=false)
Update a level crossing to barred or open (crossing may include multiple adjacent tiles).
bool HasCrossingReservation(Tile t)
Get the reservation state of the rail crossing.
Definition road_map.h:364
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:309
void SetCrossingBarred(Tile t, bool barred)
Set the bar state of a level crossing.
Definition road_map.h:412
Axis GetCrossingRailAxis(Tile t)
Get the rail axis of a level crossing.
Definition road_map.h:321
bool IsCrossingBarred(Tile t)
Check if the level crossing is barred.
Definition road_map.h:400
void SetCrossingReservation(Tile t, bool b)
Set the reservation state of the rail crossing.
Definition road_map.h:377
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
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:647
void UpdateSignalsInBuffer()
Update signals in buffer Called from 'outside'.
Definition signal.cpp:573
void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner)
Add side of tile to signal update buffer.
Definition signal.cpp:621
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:665
SigSegState
State of the signal segment.
Definition signal_func.h:49
@ SIGSEG_PBS
Segment is a PBS segment.
Definition signal_func.h:52
@ SIGSEG_FULL
Occupied by a train.
Definition signal_func.h:51
@ SIGTYPE_PBS
normal pbs signal
Definition signal_type.h:28
@ SIGNAL_STATE_RED
The signal is red.
Definition signal_type.h:43
@ SIGNAL_STATE_GREEN
The signal is green.
Definition signal_type.h:44
Functions related to sound.
SoundFx
Sound effects from baseset.
Definition sound_type.h:46
@ SND_04_DEPARTURE_STEAM
2 == 0x02 Station departure: steam engine
Definition sound_type.h:50
@ SND_41_DEPARTURE_MAGLEV
65 == 0x41 Station departure: maglev engine
Definition sound_type.h:113
@ SND_13_TRAIN_COLLISION
15 == 0x11 Train+train crash
Definition sound_type.h:65
@ SND_0E_LEVEL_CROSSING
12 == 0x0C Train passes through level crossing
Definition sound_type.h:60
@ SND_0A_DEPARTURE_TRAIN
8 == 0x08 Station departure: diesel and electric engine
Definition sound_type.h:56
@ SND_47_DEPARTURE_MONORAIL
71 == 0x47 Station departure: monorail engine
Definition sound_type.h:119
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1619
void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
Forcibly modify station ratings near a given tile.
bool IsRailWaypointTile(Tile t)
Is this tile a station tile and a rail waypoint?
bool IsCompatibleTrainStationTile(Tile test_tile, Tile station_tile)
Check if a tile is a valid continuation to a railstation tile.
bool IsRailStationTile(Tile t)
Is this tile a station tile and a rail station?
StationID GetStationIndex(Tile t)
Get StationID from a tile.
Definition station_map.h:28
Axis GetRailStationAxis(Tile t)
Get the rail direction of a rail station.
@ HVOT_TRAIN
Station has seen a train.
@ Train
Station with train station.
@ VehicleArrives
Trigger platform when train arrives.
@ VehicleArrives
Trigger platform when train arrives.
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:271
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Data structure for storing engine speed changes of an acceleration type.
uint8_t large_turn
Speed change due to a large turn.
uint8_t z_up
Fraction to remove when moving up.
uint8_t small_turn
Speed change due to a small turn.
uint8_t z_down
Fraction to add when moving down.
std::string name
Name of vehicle.
TimerGameTick::Ticks current_order_time
How many ticks have passed since this order started.
VehicleOrderID cur_real_order_index
The index to the current real (non-implicit) order.
VehicleFlags vehicle_flags
Used for gradual loading and other miscellaneous things (.
void ResetDepotUnbunching()
Resets all the data used for depot unbunching.
TileIndex xy
Base tile of the station.
StationFacilities facilities
The facilities that this station has.
TileArea train_station
Tile area the train 'station' part covers.
VehicleType type
Type of vehicle.
uint16_t speed
maximum travel speed (1 unit = 1/1.6 mph = 1 km-ish/h)
Definition bridge.h:39
bool Follow(TileIndex old_tile, Trackdir old_td)
main follower routine.
bool is_tunnel
last turn passed tunnel
bool is_bridge
last turn passed bridge ramp
int tiles_skipped
number of skipped tunnel or station tiles
DiagDirection exitdir
exit direction (leaving the old tile)
TrackdirBits new_td_bits
the new set of available trackdirs
TileIndex new_tile
the new tile (the vehicle has entered)
Trackdir old_td
the trackdir (the vehicle was on) before move
bool is_station
last turn passed station
TileIndex old_tile
the origin (vehicle moved from) before move
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:137
Structure to return information about the closest depot location, and whether it could be found.
T y
Y coordinate.
T x
X coordinate.
uint16_t cargo_age_period
Number of ticks before carried cargo is aged.
EngineMiscFlags misc_flags
Miscellaneous flags.
VehicleCallbackMasks callback_mask
Bitmask of vehicle callbacks that have to be called.
Helper container to find a depot.
uint best_length
The distance towards the depot in penalty, or UINT_MAX if not found.
bool reverse
True if reversing is necessary for the train to get to this depot.
TileIndex tile
The tile of the depot.
uint traininfo_vehicle_width
Width (in pixels) of a 8/8 train vehicle in depot GUI and vehicle details.
Definition newgrf.h:158
int traininfo_vehicle_pitch
Vertical offset for drawing train images in depot GUI and vehicle details.
Definition newgrf.h:157
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.
void SetMultiheaded()
Set a vehicle as a multiheaded engine.
bool IsEngine() const
Check if a vehicle is an engine (can be first in a consist).
bool IsRearDualheaded() const
Tell if we are dealing with the rear end of a multiheaded engine.
bool IsMultiheaded() const
Check if the vehicle is a multiheaded engine.
void SetEngine()
Set engine status.
void SetWagon()
Set a vehicle to be a wagon.
void SetFrontEngine()
Set front engine state.
void ClearFreeWagon()
Clear a vehicle from being a free wagon.
bool IsWagon() const
Check if a vehicle is a wagon.
uint Crash(bool flooded) override
Common code executed for crashed ground vehicles.
uint DoUpdateSpeed(uint accel, int min_speed, int max_speed)
void ClearFrontEngine()
Remove the front engine state.
void SetLastSpeed()
Update the GUI variant of the current speed of the vehicle.
static void CountVehicle(const Vehicle *v, int delta)
Update num_vehicle when adding or removing a vehicle.
static uint Size()
Get the size of the map.
Definition map_func.h:280
VehicleSpriteSeq sprite_seq
Vehicle appearance.
static void Backup(const Vehicle *v, uint32_t user)
Create an order backup for the given vehicle.
OrderDepotTypeFlags GetDepotOrderType() const
What caused us going to the depot?
Definition order_base.h:150
DestinationID GetDestination() const
Gets the destination of this order.
Definition order_base.h:99
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:66
OrderStopLocation GetStopLocation() const
Where must we stop at the platform?
Definition order_base.h:148
OrderType GetType() const
Get the type of order of this order.
Definition order_base.h:72
void MakeDummy()
Makes this order a Dummy order.
void MakeGoToDepot(DestinationID destination, OrderDepotTypeFlags order, OrderNonStopFlags non_stop_type=OrderNonStopFlag::NoIntermediate, OrderDepotActionFlags action={}, CargoType cargo=CARGO_NO_REFIT)
Makes this order a Go To Depot order.
Definition order_cmd.cpp:73
void SetDestination(DestinationID destination)
Sets the destination of this order.
Definition order_base.h:106
OrderDepotActionFlags GetDepotActionType() const
What are we going to do when in the depot.
Definition order_base.h:152
void Free()
'Free' the order
Definition order_cmd.cpp:47
bool ShouldStopAtStation(const Vehicle *v, StationID station) const
Check whether the given vehicle should stop at the given station based on this order and the non-stop...
OrderNonStopFlags GetNonStopType() const
At which stations must we stop?
Definition order_base.h:146
TileIndex tile
The base tile of the area.
This struct contains information about the end of a reserved path.
Definition pbs.h:26
Trackdir trackdir
The reserved trackdir on the tile.
Definition pbs.h:28
TileIndex tile
Tile the path ends, INVALID_TILE if no valid path was found.
Definition pbs.h:27
bool okay
True if tile is a safe waiting position, false otherwise.
Definition pbs.h:29
static Pool::IterateWrapper< Vehicle > Iterate(size_t from=0)
static Engine * Get(auto index)
static T * Create(Targs &&... args)
static Vehicle * GetIfValid(auto index)
Information about a rail vehicle.
Definition engine_type.h:74
uint16_t power
Power of engine (hp); For multiheaded engines the sum of both engine powers.
Definition engine_type.h:82
uint8_t user_def_data
Property 0x25: "User-defined bit mask" Used only for (very few) NewGRF vehicles.
Definition engine_type.h:94
uint8_t running_cost
Running cost of engine; For multiheaded engines the sum of both running costs.
Definition engine_type.h:84
uint8_t shorten_factor
length on main map for this type is 8 - shorten_factor
Definition engine_type.h:91
RailTypes railtypes
Railtypes, mangled if elrail is disabled.
Definition engine_type.h:78
uint16_t pow_wag_power
Extra power applied to consist if wagon should be powered.
Definition engine_type.h:88
uint16_t max_speed
Maximum speed (1 unit = 1/1.6 mph = 1 km-ish/h).
Definition engine_type.h:81
uint8_t capacity
Cargo capacity of vehicle; For multiheaded engines the capacity of each single engine.
Definition engine_type.h:87
int Width() const
Get width of Rect.
int Height() const
Get height of Rect.
static Station * Get(auto index)
T * Next() const
Get next vehicle in the chain.
T * Previous() const
Get previous vehicle in the chain.
static Train * From(Vehicle *v)
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 * 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.
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:289
uint16_t cached_max_curve_speed
max consist speed limited by curves
Definition train.h:83
'Train' is either a loco or a wagon.
Definition train.h:91
void PlayLeaveStationSound(bool force=false) const override
Play a sound for a train 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:149
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.
uint16_t crash_anim_pos
Crash animation counter.
Definition train.h:93
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.
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...
void UpdateDeltaXY() override
Updates the x and y offsets and the size of the sprite used for this vehicle.
int CalcNextVehicleOffset() const
Calculate the offset from this vehicle's center to the following center taking the vehicle lengths in...
Definition train.h:173
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:117
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:186
AccelStatus GetAccelerationStatus() const
Checks the current acceleration status of this vehicle.
Definition train.h:283
void OnNewEconomyDay() override
Economy day handler.
void ConsistChanged(ConsistChangeFlags allowed_changes)
Recalculates the cached stuff of a train.
Money GetRunningCost() const override
Get running cost for the train consist.
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:94
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:114
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:142
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:719
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.
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:2341
void AddToShared(Vehicle *shared_chain)
Adds this vehicle to a shared vehicle chain.
Definition vehicle.cpp:2970
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.
uint16_t random_bits
Bits used for randomized variational spritegroups.
void ReleaseUnitNumber()
Release the vehicle's unit number.
Definition vehicle.cpp:2410
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:2421
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:2500
CargoType cargo_type
type of cargo this vehicle is carrying
uint8_t acceleration
used by train & aircraft
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:763
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:729
OrderList * orders
Pointer to the order list for this vehicle.
Money value
Value of the vehicle.
uint16_t refit_cap
Capacity left over from before last refit.
void InvalidateNewGRFCache()
Invalidates cached NewGRF variables.
VehicleCache vcache
Cache of often used vehicle values.
uint32_t GetGRFID() const
Retrieve the GRF ID of the NewGRF the vehicle is tied to.
Definition vehicle.cpp:739
SpriteBounds bounds
Bounding box of vehicle.
void BeginLoading()
Prepare everything to begin the loading when arriving at a station.
Definition vehicle.cpp:2196
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:2547
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:2941
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:1348
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:1674
StationID last_station_visited
The last station we stopped at.
void ShowVisualEffect() const
Draw visual effects (smoke and/or sparks) for a vehicle chain.
Definition vehicle.cpp:2791
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:283
uint8_t running_ticks
Number of ticks this vehicle was not stopped this day.
@ EnteredStation
The vehicle entered a station.
Definition tile_cmd.h:24
@ CannotEnter
The vehicle cannot enter the tile.
Definition tile_cmd.h:26
@ EnteredWormhole
The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/...
Definition tile_cmd.h:25
VehicleEnterTileStates VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
Call the tile callback function for a vehicle entering a tile.
Definition vehicle.cpp:1831
static bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
bool IsTileOwner(Tile tile, Owner owner)
Checks if a tile belongs to the given owner.
Definition tile_map.h:214
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition tile_map.h:178
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:100
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
@ TunnelBridge
Tunnel entry/exit and bridge heads.
Definition tile_type.h:58
@ Station
A tile of a station or airport.
Definition tile_type.h:54
@ Railway
A tile with railway.
Definition tile_type.h:50
Definition of the game-calendar-timer.
Definition of the game-economy-timer.
Trackdir RemoveFirstTrackdir(TrackdirBits *trackdirs)
Removes first Trackdir from TrackdirBits and returns it.
Definition track_func.h:156
Track TrackdirToTrack(Trackdir trackdir)
Returns the Track that a given Trackdir represents.
Definition track_func.h:262
TrackBits TrackToTrackBits(Track track)
Maps a Track to the corresponding TrackBits value.
Definition track_func.h:77
TrackdirBits TrackBitsToTrackdirBits(TrackBits bits)
Converts TrackBits to TrackdirBits while allowing both directions.
Definition track_func.h:319
DiagDirection VehicleExitDir(Direction direction, TrackBits track)
Determine the side in which the vehicle will leave the tile.
Definition track_func.h:714
TrackdirBits TrackStatusToTrackdirBits(TrackStatus ts)
Returns the present-trackdir-information of a TrackStatus.
Definition track_func.h:352
Track TrackBitsToTrack(TrackBits tracks)
Converts TrackBits to Track.
Definition track_func.h:193
TrackBits TrackCrossesTracks(Track track)
Maps a track to all tracks that make 90 deg turns with it.
Definition track_func.h:420
Trackdir ReverseTrackdir(Trackdir trackdir)
Maps a trackdir to the reverse trackdir.
Definition track_func.h:247
bool TracksOverlap(TrackBits bits)
Checks if the given tracks overlap, ie form a crossing.
Definition track_func.h:645
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:498
bool IsValidTrackdir(Trackdir trackdir)
Checks if a Trackdir is valid for non-road vehicles.
Definition track_func.h:52
Trackdir FindFirstTrackdir(TrackdirBits trackdirs)
Returns first Trackdir from TrackdirBits or INVALID_TRACKDIR.
Definition track_func.h:211
TrackdirBits TrackdirCrossesTrackdirs(Trackdir trackdir)
Maps a trackdir to all trackdirs that make 90 deg turns with it.
Definition track_func.h:606
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:584
TrackdirBits DiagdirReachesTrackdirs(DiagDirection diagdir)
Returns all trackdirs that can be reached when entering a tile from a given (diagonal) direction.
Definition track_func.h:555
Track FindFirstTrack(TrackBits tracks)
Returns first Track from TrackBits or INVALID_TRACK.
Definition track_func.h:177
Trackdir TrackEnterdirToTrackdir(Track track, DiagDirection diagdir)
Maps a track and an (4-way) dir to the trackdir that represents the track with the entry in the given...
Definition track_func.h:486
TrackBits DiagdirReachesTracks(DiagDirection diagdir)
Returns all tracks that can be reached when entering a tile from a given (diagonal) direction.
Definition track_func.h:573
TrackBits DiagDirToDiagTrackBits(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal track bits incidating with that diagdir.
Definition track_func.h:524
Trackdir DiagDirToDiagTrackdir(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal trackdir that runs in that direction.
Definition track_func.h:537
TrackdirBits TrackStatusToRedSignals(TrackStatus ts)
Returns the red-signal-information of a TrackStatus.
Definition track_func.h:376
DiagDirection TrackdirToExitdir(Trackdir trackdir)
Maps a trackdir to the (4-way) direction the tile is exited when following that trackdir.
Definition track_func.h:439
Track DiagDirToDiagTrack(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal track incidating with that diagdir.
Definition track_func.h:512
TrackBits TrackdirBitsToTrackBits(TrackdirBits bits)
Discards all directional information from a TrackdirBits value.
Definition track_func.h:308
TrackBits
Allow incrementing of Track variables.
Definition track_type.h:35
@ TRACK_BIT_WORMHOLE
Bitflag for a wormhole (used for tunnels).
Definition track_type.h:52
@ TRACK_BIT_UPPER
Upper track.
Definition track_type.h:39
@ TRACK_BIT_DEPOT
Bitflag for a depot.
Definition track_type.h:53
@ TRACK_BIT_LEFT
Left track.
Definition track_type.h:41
@ TRACK_BIT_Y
Y-axis track.
Definition track_type.h:38
@ TRACK_BIT_NONE
No track.
Definition track_type.h:36
@ TRACK_BIT_X
X-axis track.
Definition track_type.h:37
@ TRACK_BIT_MASK
Bitmask for the first 6 bits.
Definition track_type.h:51
@ TRACK_BIT_LOWER
Lower track.
Definition track_type.h:40
@ TRACK_BIT_RIGHT
Right track.
Definition track_type.h:42
Trackdir
Enumeration for tracks and directions.
Definition track_type.h:66
@ INVALID_TRACKDIR
Flag for an invalid trackdir.
Definition track_type.h:85
TrackdirBits
Allow incrementing of Trackdir variables.
Definition track_type.h:97
@ TRACKDIR_BIT_NONE
No track build.
Definition track_type.h:98
Track
These are used to specify a single track.
Definition track_type.h:19
@ INVALID_TRACK
Flag for an invalid track.
Definition track_type.h:28
@ TRACK_Y
Track along the y-axis (north-west to south-east).
Definition track_type.h:22
@ TRACK_BEGIN
Used for iterations.
Definition track_type.h:20
@ TRACK_X
Track along the x-axis (north-east to south-west).
Definition track_type.h:21
@ Capacity
Allow vehicles to change capacity.
Definition train.h:47
@ Length
Allow vehicles to change length.
Definition train.h:46
static constexpr ConsistChangeFlags CCF_TRACK
Valid changes while vehicle is driving, and possibly changing tracks.
Definition train.h:52
bool TryPathReserve(Train *v, bool mark_as_stuck=false, bool first_tile_okay=false)
Try to reserve a path to a safe position.
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
int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, 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.
TrainForceProceeding
Modes for ignoring signals.
Definition train.h:38
@ TFP_SIGNAL
Ignore next signal, after the signal ignore being stuck.
Definition train.h:41
@ TFP_NONE
Normal operation.
Definition train.h:39
@ TFP_STUCK
Proceed till next signal, but ignore being stuck till then. This includes force leaving depots.
Definition train.h:40
static constexpr ConsistChangeFlags CCF_ARRANGE
Valid changes for arranging the consist in a depot.
Definition train.h:56
static void AffectSpeedByZChange(Train *v, int old_z)
Modify the speed of the vehicle due to a change in altitude.
static CommandCost CmdBuildRailWagon(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
Build a railroad wagon.
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).
static uint CheckTrainCollision(Vehicle *v, Train *t)
Collision test function.
static void MakeTrainBackup(TrainList &list, Train *t)
Make a backup of a train into a train list.
static void CheckNextTrainTile(Train *v)
Check if the train is on the last reserved tile and try to extend the path then.
static void ArrangeTrains(Train **dst_head, Train *dst, Train **src_head, Train *src, bool move_chain)
Arrange the trains in the wanted way.
bool TryPathReserve(Train *v, bool mark_as_stuck, bool first_tile_okay)
Try to reserve a path to a safe position.
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 bool TrainApproachingLineEnd(Train *v, bool signal, bool reverse)
Train is approaching line end, slow down and possibly reverse.
static void InsertInConsist(Train *dst, Train *chain)
Inserts a chain into the train at dst.
void UpdateAdjacentLevelCrossingTilesOnLevelCrossingRemoval(TileIndex tile, Axis road_axis)
Update adjacent level crossing tiles in this multi-track crossing, due to removal of a level crossing...
static void SwapTrainFlags(uint16_t *swap_flag1, uint16_t *swap_flag2)
Swap the two up/down flags in two ways:
static void UpdateLevelCrossingTile(TileIndex tile, bool sound, bool force_barred)
Sets a level crossing tile to the correct state.
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.
static void AdvanceWagonsBeforeSwap(Train *v)
Advances wagons for train reversing, needed for variable length wagons.
CommandCost CmdBuildRailVehicle(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
Build a railroad vehicle.
static void AdvanceWagonsAfterSwap(Train *v)
Advances wagons for train reversing, needed for variable length wagons.
uint8_t FreightWagonMult(CargoType cargo)
Return the cargo weight multiplier to use for a rail vehicle.
Definition train_cmd.cpp:68
static bool TrainCheckIfLineEnds(Train *v, bool reverse=true)
Checks for line end.
void ReverseTrainDirection(Train *v)
Turn a train around.
void FreeTrainTrackReservation(const Train *v)
Free the reserved path in front of a vehicle.
static uint TrainCrashed(Train *v)
Marks train as crashed and creates an AI event.
static void NormaliseDualHeads(Train *t)
Normalise the dual heads in the train, i.e.
static CommandCost CheckTrainAttachment(Train *t)
Check whether the train parts can be attached.
static const AccelerationSlowdownParams _accel_slowdown[]
Speed update fractions for each acceleration type.
CommandCost CmdMoveRailVehicle(DoCommandFlags flags, VehicleID src_veh, VehicleID dest_veh, bool move_chain)
Move a rail vehicle around inside the depot.
static Track DoTrainPathfind(const Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found, bool do_track_reservation, PBSTileInfo *dest, TileIndex *final_dest)
Perform pathfinding for a train.
static void 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 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:75
static void MaybeBarCrossingWithSound(TileIndex tile)
Bars crossing and plays ding-ding sound if not barred already.
bool TrainOnCrossing(TileIndex tile)
Check if a level crossing tile has a train on it.
static void ChangeTrainDirRandomly(Train *v)
Rotate all vehicles of a (crashed) train chain randomly to animate the crash.
void MarkDirtyAdjacentLevelCrossingTiles(TileIndex tile, Axis road_axis)
Find adjacent level crossing tiles in this multi-track crossing and mark them dirty.
static void NormaliseSubtypes(Train *chain)
Normalise the sub types of the parts in this chain.
bool TrainController(Train *v, Vehicle *nomove, bool reverse=true)
Move a vehicle chain one movement stop forwards.
CommandCost CmdSellRailWagon(DoCommandFlags flags, Vehicle *t, bool sell_chain, bool backup_order, ClientID user)
Sell a (single) train wagon/engine.
static TileIndex TrainApproachingCrossingTile(const Train *v)
Determines whether train is approaching a rail-road crossing (thus making it barred).
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 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 TrainCanLeaveTile(const Train *v)
Determines whether train would like to leave the tile.
int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, 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 void TrainEnterStation(Train *v, StationID station)
Trains enters a station, send out a news item if it is the first train, and start loading.
static void MarkTrainAsStuck(Train *v)
Mark a train as stuck and stop it if it isn't stopped right now.
static bool TrainApproachingCrossing(TileIndex tile)
Finds a vehicle approaching rail-road crossing.
void ReverseTrainSwapVeh(Train *v, int l, int r)
Swap vehicles l and r in consist v, and reverse their direction.
static PBSTileInfo ExtendTrainReservation(const Train *v, TrackBits *new_tracks, DiagDirection *enterdir)
Extend a train path as far as possible.
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 void UpdateStatusAfterSwap(Train *v)
Updates some variables after swapping the vehicle.
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.
@ TRANSPORT_RAIL
Transport by train.
bool IsTunnel(Tile t)
Is this a tunnel (entrance)?
Definition tunnel_map.h:23
void MarkBridgeDirty(TileIndex begin, TileIndex end, DiagDirection direction, uint bridge_height)
Mark bridge tiles dirty.
DiagDirection GetTunnelBridgeDirection(Tile t)
Get the direction pointing to the other end.
TileIndex GetOtherTunnelBridgeEnd(Tile t)
Determines type of the wormhole and returns its other end.
void SetTunnelBridgeReservation(Tile t, bool b)
Set the reservation state of the rail tunnel/bridge.
void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBug bug_type, bool critical)
Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
Definition vehicle.cpp:328
void VehicleEnterDepot(Vehicle *v)
Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it,...
Definition vehicle.cpp:1536
UnitID GetFreeUnitNumber(VehicleType type)
Get an unused unit number for a vehicle (if allowed).
Definition vehicle.cpp:1888
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:354
void VehicleServiceInDepot(Vehicle *v)
Service a vehicle and all subsequent vehicles in the consist.
Definition vehicle.cpp:178
void CheckVehicleBreakdown(Vehicle *v)
Periodic check for a vehicle to maybe break down.
Definition vehicle.cpp:1292
GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
Get position information of a vehicle when moving one pixel in the direction it is facing.
Definition vehicle.cpp:1777
void DecreaseVehicleValue(Vehicle *v)
Decrease the value of a vehicle.
Definition vehicle.cpp:1271
void EconomyAgeVehicle(Vehicle *v)
Update economy age of a vehicle.
Definition vehicle.cpp:1414
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
Finds vehicle in tunnel / bridge.
Definition vehicle.cpp:552
void AgeVehicle(Vehicle *v)
Update age of a vehicle.
Definition vehicle.cpp:1426
@ Crashed
Vehicle is crashed.
@ TrainSlowing
Train is slowing down.
@ Hidden
Vehicle is not visible.
@ DefaultPalette
Use default vehicle palette.
@ Stopped
Vehicle is stopped by the player.
Functions related to vehicles.
bool IsValidImageIndex(uint8_t image_index)
Helper to check whether an image index is valid for a particular vehicle.
@ CUSTOM_VEHICLE_SPRITENUM_REVERSED
Vehicle sprite from NewGRF with reverse driving direction (from articulation callback).
bool HasVehicleOnTile(TileIndex tile, UnaryPred &&predicate)
Loop over vehicles on a tile, and check whether a predicate is true for any of them.
@ VIWD_CONSIST_CHANGED
Vehicle composition was changed.
Definition vehicle_gui.h:37
EngineImageType
Visualisation contexts of vehicles and engines.
PoolID< uint32_t, struct VehicleIDTag, 0xFF000, 0xFFFFF > VehicleID
The type all our vehicle IDs have.
@ VEH_TRAIN
Train vehicle type.
static const uint VEHICLE_LENGTH
The length of a vehicle in tile units.
@ WID_VV_REFIT
Open the refit window.
@ WID_VV_START_STOP
Start or stop this vehicle, and show information about the current state.
Functions related to (drawing on) viewports.
void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
Close a window by its class and window number (if it is open).
Definition window.cpp:1198
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting).
Definition window.cpp:3218
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:3310
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:3204
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3188
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:3328
@ WC_VEHICLE_ORDERS
Vehicle orders; Window numbers:
@ WC_VEHICLE_DEPOT
Depot view; Window numbers:
@ WC_TRAINS_LIST
Trains list; Window numbers:
@ WC_VEHICLE_REFIT
Vehicle refit; Window numbers:
@ WC_VEHICLE_DETAILS
Vehicle details; Window numbers:
@ WC_COMPANY
Company view; Window numbers:
@ WC_VEHICLE_VIEW
Vehicle view; Window numbers:
@ WC_VEHICLE_TIMETABLE
Vehicle timetable; Window numbers:
FindDepotData YapfTrainFindNearestDepot(const Train *v, int max_distance)
Used when user sends train to the nearest depot or if train needs servicing using YAPF.
bool YapfTrainFindNearestSafeTile(const Train *v, TileIndex tile, Trackdir td, bool override_railtype)
Try to extend the reserved path of a train to the nearest safe tile using YAPF.
bool YapfTrainCheckReverse(const Train *v)
Returns true if it is better to reverse the train before leaving station using YAPF.
Track YapfTrainChooseTrack(const Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found, bool reserve_track, struct PBSTileInfo *target, TileIndex *dest)
Finds the best path for given train using YAPF.
Base includes/functions for YAPF.
Functions related to zooming.
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
Definition zoom_func.h:107
int UnScaleGUI(int value)
Short-hand to apply GUI zoom level.
Definition zoom_func.h:77