OpenTTD Source 20251019-master-g9f7f314f81
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 <http://www.gnu.org/licenses/>.
6 */
7
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;
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;
91 }
92 /* Break so we warn only once for each train. */
93 break;
94 }
95 }
96 }
97 }
98 }
99}
100
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}
249
260int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, int *station_ahead, int *station_length)
261{
262 const Station *st = Station::Get(station_id);
263 *station_ahead = st->GetPlatformLength(tile, DirToDiagDir(v->direction)) * TILE_SIZE;
264 *station_length = st->GetPlatformLength(tile) * TILE_SIZE;
265
266 /* Default to the middle of the station for stations stops that are not in
267 * the order list like intermediate stations when non-stop is disabled */
269 if (v->gcache.cached_total_length >= *station_length) {
270 /* The train is longer than the station, make it stop at the far end of the platform */
272 } else if (v->current_order.IsType(OT_GOTO_STATION) && v->current_order.GetDestination() == station_id) {
274 }
275
276 /* The stop location of the FRONT! of the train */
277 int stop;
278 switch (osl) {
279 default: NOT_REACHED();
280
282 stop = v->gcache.cached_total_length;
283 break;
284
286 stop = *station_length - (*station_length - v->gcache.cached_total_length) / 2;
287 break;
288
290 stop = *station_length;
291 break;
292 }
293
294 /* Subtract half the front vehicle length of the train so we get the real
295 * stop location of the train. */
296 return stop - (v->gcache.cached_veh_length + 1) / 2;
297}
298
299
305{
306 assert(this->First() == this);
307
308 static const int absolute_max_speed = UINT16_MAX;
309 int max_speed = absolute_max_speed;
310
311 if (_settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) return max_speed;
312
313 int curvecount[2] = {0, 0};
314
315 /* first find the curve speed limit */
316 int numcurve = 0;
317 int sum = 0;
318 int pos = 0;
319 int lastpos = -1;
320 for (const Train *u = this; u->Next() != nullptr; u = u->Next(), pos += u->gcache.cached_veh_length) {
321 Direction this_dir = u->direction;
322 Direction next_dir = u->Next()->direction;
323
324 DirDiff dirdiff = DirDifference(this_dir, next_dir);
325 if (dirdiff == DIRDIFF_SAME) continue;
326
327 if (dirdiff == DIRDIFF_45LEFT) curvecount[0]++;
328 if (dirdiff == DIRDIFF_45RIGHT) curvecount[1]++;
329 if (dirdiff == DIRDIFF_45LEFT || dirdiff == DIRDIFF_45RIGHT) {
330 if (lastpos != -1) {
331 numcurve++;
332 sum += pos - lastpos;
333 if (pos - lastpos <= static_cast<int>(VEHICLE_LENGTH) && max_speed > 88) {
334 max_speed = 88;
335 }
336 }
337 lastpos = pos;
338 }
339
340 /* if we have a 90 degree turn, fix the speed limit to 60 */
341 if (dirdiff == DIRDIFF_90LEFT || dirdiff == DIRDIFF_90RIGHT) {
342 max_speed = 61;
343 }
344 }
345
346 if (numcurve > 0 && max_speed > 88) {
347 if (curvecount[0] == 1 && curvecount[1] == 1) {
348 max_speed = absolute_max_speed;
349 } else {
350 sum = CeilDiv(sum, VEHICLE_LENGTH);
351 sum /= numcurve;
352 max_speed = 232 - (13 - Clamp(sum, 1, 12)) * (13 - Clamp(sum, 1, 12));
353 }
354 }
355
356 if (max_speed != absolute_max_speed) {
357 /* Apply the current railtype's curve speed advantage */
358 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(this->tile));
359 max_speed += (max_speed / 2) * rti->curve_speed;
360
361 if (this->tcache.cached_tilt) {
362 /* Apply max_speed bonus of 20% for a tilting train */
363 max_speed += max_speed / 5;
364 }
365
366 /* Apply max_speed modifier (cached value is fixed-point binary with 8 fractional bits)
367 * and clamp the result to an acceptable range. */
368 max_speed += (max_speed * this->tcache.cached_curve_speed_mod) / 256;
369 max_speed = Clamp(max_speed, 2, absolute_max_speed);
370 }
371
372 return static_cast<uint16_t>(max_speed);
373}
374
380{
381 int max_speed = _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL ?
383 this->tcache.cached_max_curve_speed;
384
386 StationID sid = GetStationIndex(this->tile);
387 if (this->current_order.ShouldStopAtStation(this, sid)) {
388 int station_ahead;
389 int station_length;
390 int stop_at = GetTrainStopLocation(sid, this->tile, this, &station_ahead, &station_length);
391
392 /* The distance to go is whatever is still ahead of the train minus the
393 * distance from the train's stop location to the end of the platform */
394 int distance_to_go = station_ahead / TILE_SIZE - (station_length - stop_at) / TILE_SIZE;
395
396 if (distance_to_go > 0) {
397 int st_max_speed = 120;
398
399 int delta_v = this->cur_speed / (distance_to_go + 1);
400 if (max_speed > (this->cur_speed - delta_v)) {
401 st_max_speed = this->cur_speed - (delta_v / 10);
402 }
403
404 st_max_speed = std::max(st_max_speed, 25 * distance_to_go);
405 max_speed = std::min(max_speed, st_max_speed);
406 }
407 }
408 }
409
410 for (const Train *u = this; u != nullptr; u = u->Next()) {
411 if (_settings_game.vehicle.train_acceleration_model == AM_REALISTIC && u->track == TRACK_BIT_DEPOT) {
412 max_speed = std::min(max_speed, 61);
413 break;
414 }
415
416 /* Vehicle is on the middle part of a bridge. */
417 if (u->track == TRACK_BIT_WORMHOLE && !u->vehstatus.Test(VehState::Hidden)) {
418 max_speed = std::min<int>(max_speed, GetBridgeSpec(GetBridgeType(u->tile))->speed);
419 }
420 }
421
422 max_speed = std::min<int>(max_speed, this->current_order.GetMaxSpeed());
423 return std::min<int>(max_speed, this->gcache.cached_max_track_speed);
424}
425
428{
429 assert(this->IsFrontEngine() || this->IsFreeWagon());
430
431 uint power = this->gcache.cached_power;
432 uint weight = this->gcache.cached_weight;
433 assert(weight != 0);
434 this->acceleration = Clamp(power / weight * 4, 1, 255);
435}
436
437int Train::GetCursorImageOffset() const
438{
439 if (this->gcache.cached_veh_length != 8 && this->flags.Test(VehicleRailFlag::Flipped) && !EngInfo(this->engine_type)->misc_flags.Test(EngineMiscFlag::RailFlips)) {
440 int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
441
442 const Engine *e = this->GetEngine();
443 if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->VehInfo<RailVehicleInfo>().image_index)) {
444 reference_width = e->GetGRF()->traininfo_vehicle_width;
445 }
446
447 return ScaleSpriteTrad((this->gcache.cached_veh_length - (int)VEHICLE_LENGTH) * reference_width / (int)VEHICLE_LENGTH);
448 }
449 return 0;
450}
451
458{
459 int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
460 int vehicle_pitch = 0;
461
462 const Engine *e = this->GetEngine();
463 if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->VehInfo<RailVehicleInfo>().image_index)) {
464 reference_width = e->GetGRF()->traininfo_vehicle_width;
465 vehicle_pitch = e->GetGRF()->traininfo_vehicle_pitch;
466 }
467
468 if (offset != nullptr) {
469 if (this->flags.Test(VehicleRailFlag::Flipped) && !EngInfo(this->engine_type)->misc_flags.Test(EngineMiscFlag::RailFlips)) {
470 offset->x = ScaleSpriteTrad(((int)this->gcache.cached_veh_length - (int)VEHICLE_LENGTH / 2) * reference_width / (int)VEHICLE_LENGTH);
471 } else {
472 offset->x = ScaleSpriteTrad(reference_width) / 2;
473 }
474 offset->y = ScaleSpriteTrad(vehicle_pitch);
475 }
476 return ScaleSpriteTrad(this->gcache.cached_veh_length * reference_width / VEHICLE_LENGTH);
477}
478
479static SpriteID GetDefaultTrainSprite(uint8_t spritenum, Direction direction)
480{
481 assert(IsValidImageIndex<VEH_TRAIN>(spritenum));
482 return ((direction + _engine_sprite_add[spritenum]) & _engine_sprite_and[spritenum]) + _engine_sprite_base[spritenum];
483}
484
491void Train::GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const
492{
493 uint8_t spritenum = this->spritenum;
494
496
497 if (IsCustomVehicleSpriteNum(spritenum)) {
499 GetCustomVehicleSprite(this, direction, image_type, result);
500 if (result->IsValid()) return;
501
503 }
504
505 assert(IsValidImageIndex<VEH_TRAIN>(spritenum));
506 SpriteID sprite = GetDefaultTrainSprite(spritenum, direction);
507
508 if (this->cargo.StoredCount() >= this->cargo_cap / 2U) sprite += _wagon_full_adder[spritenum];
509
510 result->Set(sprite);
511}
512
513static void GetRailIcon(EngineID engine, bool rear_head, int &y, EngineImageType image_type, VehicleSpriteSeq *result)
514{
515 const Engine *e = Engine::Get(engine);
516 Direction dir = rear_head ? DIR_E : DIR_W;
517 uint8_t spritenum = e->VehInfo<RailVehicleInfo>().image_index;
518
519 if (IsCustomVehicleSpriteNum(spritenum)) {
520 GetCustomVehicleIcon(engine, dir, image_type, result);
521 if (result->IsValid()) {
522 if (e->GetGRF() != nullptr) {
524 }
525 return;
526 }
527
528 spritenum = Engine::Get(engine)->original_image_index;
529 }
530
531 if (rear_head) spritenum++;
532
533 result->Set(GetDefaultTrainSprite(spritenum, DIR_W));
534}
535
536void DrawTrainEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
537{
538 if (RailVehInfo(engine)->railveh_type == RAILVEH_MULTIHEAD) {
539 int yf = y;
540 int yr = y;
541
542 VehicleSpriteSeq seqf, seqr;
543 GetRailIcon(engine, false, yf, image_type, &seqf);
544 GetRailIcon(engine, true, yr, image_type, &seqr);
545
546 Rect rectf, rectr;
547 seqf.GetBounds(&rectf);
548 seqr.GetBounds(&rectr);
549
550 preferred_x = Clamp(preferred_x,
551 left - UnScaleGUI(rectf.left) + ScaleSpriteTrad(14),
552 right - UnScaleGUI(rectr.right) - ScaleSpriteTrad(15));
553
554 seqf.Draw(preferred_x - ScaleSpriteTrad(14), yf, pal, pal == PALETTE_CRASH);
555 seqr.Draw(preferred_x + ScaleSpriteTrad(15), yr, pal, pal == PALETTE_CRASH);
556 } else {
558 GetRailIcon(engine, false, y, image_type, &seq);
559
560 Rect rect;
561 seq.GetBounds(&rect);
562 preferred_x = Clamp(preferred_x,
563 left - UnScaleGUI(rect.left),
564 right - UnScaleGUI(rect.right));
565
566 seq.Draw(preferred_x, y, pal, pal == PALETTE_CRASH);
567 }
568}
569
579void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type)
580{
581 int y = 0;
582
584 GetRailIcon(engine, false, y, image_type, &seq);
585
586 Rect rect;
587 seq.GetBounds(&rect);
588
589 width = UnScaleGUI(rect.Width());
590 height = UnScaleGUI(rect.Height());
591 xoffs = UnScaleGUI(rect.left);
592 yoffs = UnScaleGUI(rect.top);
593
594 if (RailVehInfo(engine)->railveh_type == RAILVEH_MULTIHEAD) {
595 GetRailIcon(engine, true, y, image_type, &seq);
596 seq.GetBounds(&rect);
597
598 /* Calculate values relative to an imaginary center between the two sprites. */
599 width = ScaleSpriteTrad(TRAININFO_DEFAULT_VEHICLE_WIDTH) + UnScaleGUI(rect.right) - xoffs;
600 height = std::max<uint>(height, UnScaleGUI(rect.Height()));
601 xoffs = xoffs - ScaleSpriteTrad(TRAININFO_DEFAULT_VEHICLE_WIDTH) / 2;
602 yoffs = std::min(yoffs, UnScaleGUI(rect.top));
603 }
604}
605
611static std::vector<VehicleID> GetFreeWagonsInDepot(TileIndex tile)
612{
613 std::vector<VehicleID> free_wagons;
614
615 for (Vehicle *v : VehiclesOnTile(tile)) {
616 if (v->type != VEH_TRAIN) continue;
617 if (v->vehstatus.Test(VehState::Crashed)) continue;
618 if (!Train::From(v)->IsFreeWagon()) continue;
619
620 free_wagons.push_back(v->index);
621 }
622
623 /* Sort by vehicle index for consistency across clients. */
624 std::ranges::sort(free_wagons);
625 return free_wagons;
626}
627
637{
638 const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
639
640 /* Check that the wagon can drive on the track in question */
641 if (!IsCompatibleRail(rvi->railtypes, GetRailType(tile))) return CMD_ERROR;
642
643 if (flags.Test(DoCommandFlag::Execute)) {
644 Train *v = new Train();
645 *ret = v;
646 v->spritenum = rvi->image_index;
647
648 v->engine_type = e->index;
649 v->gcache.first_engine = EngineID::Invalid(); // needs to be set before first callback
650
652
653 v->direction = DiagDirToDir(dir);
654 v->tile = tile;
655
656 int x = TileX(tile) * TILE_SIZE | _vehicle_initial_x_fract[dir];
657 int y = TileY(tile) * TILE_SIZE | _vehicle_initial_y_fract[dir];
658
659 v->x_pos = x;
660 v->y_pos = y;
661 v->z_pos = GetSlopePixelZ(x, y, true);
663 v->track = TRACK_BIT_DEPOT;
665
666 v->SetWagon();
667
668 v->SetFreeWagon();
670
672 assert(IsValidCargoType(v->cargo_type));
673 v->cargo_cap = rvi->capacity;
674 v->refit_cap = 0;
675
676 v->railtypes = rvi->railtypes;
677
681 v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
682 v->random_bits = Random();
683
685
686 auto prob = TestVehicleBuildProbability(v, v->engine_type, BuildProbabilityType::Reversed);
687 if (prob.has_value()) v->flags.Set(VehicleRailFlag::Flipped, prob.value());
689
690 v->UpdatePosition();
691 v->First()->ConsistChanged(CCF_ARRANGE);
693
695
696 /* Try to connect the vehicle to one of free chains of wagons. */
697 for (VehicleID vehicle : GetFreeWagonsInDepot(tile)) {
698 if (vehicle == v->index) continue;
699
700 const Train *w = Train::Get(vehicle);
701 if (w->engine_type != v->engine_type) continue;
702 if (w->First() == v) continue;
703
704 if (Command<CMD_MOVE_RAIL_VEHICLE>::Do(DoCommandFlag::Execute, v->index, w->Last()->index, true).Succeeded()) {
705 break;
706 }
707 }
708 }
709
710 return CommandCost();
711}
712
715{
716 assert(u->IsEngine());
717 for (VehicleID vehicle : GetFreeWagonsInDepot(u->tile)) {
718 if (Command<CMD_MOVE_RAIL_VEHICLE>::Do(DoCommandFlag::Execute, vehicle, u->index, true).Failed()) {
719 break;
720 }
721 }
722}
723
724static void AddRearEngineToMultiheadedTrain(Train *v)
725{
726 Train *u = new Train();
727 v->value >>= 1;
728 u->value = v->value;
729 u->direction = v->direction;
730 u->owner = v->owner;
731 u->tile = v->tile;
732 u->x_pos = v->x_pos;
733 u->y_pos = v->y_pos;
734 u->z_pos = v->z_pos;
735 u->track = TRACK_BIT_DEPOT;
736 u->vehstatus = v->vehstatus;
738 u->spritenum = v->spritenum + 1;
739 u->cargo_type = v->cargo_type;
741 u->cargo_cap = v->cargo_cap;
742 u->refit_cap = v->refit_cap;
743 u->railtypes = v->railtypes;
744 u->engine_type = v->engine_type;
747 u->build_year = v->build_year;
748 u->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
749 u->random_bits = Random();
750 v->SetMultiheaded();
751 u->SetMultiheaded();
752 v->SetNext(u);
753 auto prob = TestVehicleBuildProbability(u, u->engine_type, BuildProbabilityType::Reversed);
754 if (prob.has_value()) u->flags.Set(VehicleRailFlag::Flipped, prob.value());
755 u->UpdatePosition();
756
757 /* Now we need to link the front and rear engines together */
758 v->other_multiheaded_part = u;
759 u->other_multiheaded_part = v;
760}
761
771{
772 const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
773
774 if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildRailWagon(flags, tile, e, ret);
775
776 /* Check if depot and new engine uses the same kind of tracks *
777 * We need to see if the engine got power on the tile to avoid electric engines in non-electric depots */
778 if (!HasPowerOnRail(rvi->railtypes, GetRailType(tile))) return CMD_ERROR;
779
780 if (flags.Test(DoCommandFlag::Execute)) {
782 int x = TileX(tile) * TILE_SIZE + _vehicle_initial_x_fract[dir];
783 int y = TileY(tile) * TILE_SIZE + _vehicle_initial_y_fract[dir];
784
785 Train *v = new Train();
786 *ret = v;
787 v->direction = DiagDirToDir(dir);
788 v->tile = tile;
790 v->x_pos = x;
791 v->y_pos = y;
792 v->z_pos = GetSlopePixelZ(x, y, true);
793 v->track = TRACK_BIT_DEPOT;
795 v->spritenum = rvi->image_index;
797 assert(IsValidCargoType(v->cargo_type));
798 v->cargo_cap = rvi->capacity;
799 v->refit_cap = 0;
800 v->last_station_visited = StationID::Invalid();
801 v->last_loading_station = StationID::Invalid();
802
803 v->engine_type = e->index;
804 v->gcache.first_engine = EngineID::Invalid(); // needs to be set before first callback
805
806 v->reliability = e->reliability;
809
810 v->railtypes = rvi->railtypes;
811
812 v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_trains);
816 v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
817 v->random_bits = Random();
818
820 v->SetServiceIntervalIsPercent(Company::Get(_current_company)->settings.vehicle.servint_ispercent);
821
823
824 v->SetFrontEngine();
825 v->SetEngine();
826
827 auto prob = TestVehicleBuildProbability(v, v->engine_type, BuildProbabilityType::Reversed);
828 if (prob.has_value()) v->flags.Set(VehicleRailFlag::Flipped, prob.value());
829 v->UpdatePosition();
830
831 if (rvi->railveh_type == RAILVEH_MULTIHEAD) {
832 AddRearEngineToMultiheadedTrain(v);
833 } else {
835 }
836
839
841 }
842
843 return CommandCost();
844}
845
846static Train *FindGoodVehiclePos(const Train *src)
847{
848 EngineID eng = src->engine_type;
849
850 for (VehicleID vehicle : GetFreeWagonsInDepot(src->tile)) {
851 Train *dst = Train::Get(vehicle);
852
853 /* check so all vehicles in the line have the same engine. */
854 Train *t = dst;
855 while (t->engine_type == eng) {
856 t = t->Next();
857 if (t == nullptr) return dst;
858 }
859 }
860
861 return nullptr;
862}
863
865typedef std::vector<Train *> TrainList;
866
872static void MakeTrainBackup(TrainList &list, Train *t)
873{
874 for (; t != nullptr; t = t->Next()) list.push_back(t);
875}
876
882{
883 /* No train, nothing to do. */
884 if (list.empty()) return;
885
886 Train *prev = nullptr;
887 /* Iterate over the list and rebuild it. */
888 for (Train *t : list) {
889 if (prev != nullptr) {
890 prev->SetNext(t);
891 } else if (t->Previous() != nullptr) {
892 /* Make sure the head of the train is always the first in the chain. */
893 t->Previous()->SetNext(nullptr);
894 }
895 prev = t;
896 }
897}
898
904static void RemoveFromConsist(Train *part, bool chain = false)
905{
906 Train *tail = chain ? part->Last() : part->GetLastEnginePart();
907
908 /* Unlink at the front, but make it point to the next
909 * vehicle after the to be remove part. */
910 if (part->Previous() != nullptr) part->Previous()->SetNext(tail->Next());
911
912 /* Unlink at the back */
913 tail->SetNext(nullptr);
914}
915
921static void InsertInConsist(Train *dst, Train *chain)
922{
923 /* We do not want to add something in the middle of an articulated part. */
924 assert(dst != nullptr && (dst->Next() == nullptr || !dst->Next()->IsArticulatedPart()));
925
926 chain->Last()->SetNext(dst->Next());
927 dst->SetNext(chain);
928}
929
936{
937 for (; t != nullptr; t = t->GetNextVehicle()) {
938 if (!t->IsMultiheaded() || !t->IsEngine()) continue;
939
940 /* Make sure that there are no free cars before next engine */
941 Train *u;
942 for (u = t; u->Next() != nullptr && !u->Next()->IsEngine(); u = u->Next()) {}
943
944 if (u == t->other_multiheaded_part) continue;
945
946 /* Remove the part from the 'wrong' train */
947 RemoveFromConsist(t->other_multiheaded_part);
948 /* And add it to the 'right' train */
949 InsertInConsist(u, t->other_multiheaded_part);
950 }
951}
952
957static void NormaliseSubtypes(Train *chain)
958{
959 /* Nothing to do */
960 if (chain == nullptr) return;
961
962 /* We must be the first in the chain. */
963 assert(chain->Previous() == nullptr);
964
965 /* Set the appropriate bits for the first in the chain. */
966 if (chain->IsWagon()) {
967 chain->SetFreeWagon();
968 } else {
969 assert(chain->IsEngine());
970 chain->SetFrontEngine();
971 }
972
973 /* Now clear the bits for the rest of the chain */
974 for (Train *t = chain->Next(); t != nullptr; t = t->Next()) {
975 t->ClearFreeWagon();
976 t->ClearFrontEngine();
977 }
978}
979
989static CommandCost CheckNewTrain(Train *original_dst, Train *dst, Train *original_src, Train *src)
990{
991 /* Just add 'new' engines and subtract the original ones.
992 * If that's less than or equal to 0 we can be sure we did
993 * not add any engines (read: trains) along the way. */
994 if ((src != nullptr && src->IsEngine() ? 1 : 0) +
995 (dst != nullptr && dst->IsEngine() ? 1 : 0) -
996 (original_src != nullptr && original_src->IsEngine() ? 1 : 0) -
997 (original_dst != nullptr && original_dst->IsEngine() ? 1 : 0) <= 0) {
998 return CommandCost();
999 }
1000
1001 /* Get a free unit number and check whether it's within the bounds.
1002 * There will always be a maximum of one new train. */
1004
1005 return CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);
1006}
1007
1014{
1015 /* No multi-part train, no need to check. */
1016 if (t == nullptr || t->Next() == nullptr) return CommandCost();
1017
1018 /* The maximum length for a train. For each part we decrease this by one
1019 * and if the result is negative the train is simply too long. */
1021
1022 /* For free-wagon chains, check if they are within the max_train_length limit. */
1023 if (!t->IsEngine()) {
1024 t = t->Next();
1025 while (t != nullptr) {
1026 allowed_len -= t->gcache.cached_veh_length;
1027
1028 t = t->Next();
1029 }
1030
1031 if (allowed_len < 0) return CommandCost(STR_ERROR_TRAIN_TOO_LONG);
1032 return CommandCost();
1033 }
1034
1035 Train *head = t;
1036 Train *prev = t;
1037
1038 /* Break the prev -> t link so it always holds within the loop. */
1039 t = t->Next();
1040 prev->SetNext(nullptr);
1041
1042 /* Make sure the cache is cleared. */
1043 head->InvalidateNewGRFCache();
1044
1045 while (t != nullptr) {
1046 allowed_len -= t->gcache.cached_veh_length;
1047
1048 Train *next = t->Next();
1049
1050 /* Unlink the to-be-added piece; it is already unlinked from the previous
1051 * part due to the fact that the prev -> t link is broken. */
1052 t->SetNext(nullptr);
1053
1054 /* Don't check callback for articulated or rear dual headed parts */
1055 if (!t->IsArticulatedPart() && !t->IsRearDualheaded()) {
1056 /* Back up and clear the first_engine data to avoid using wagon override group */
1057 EngineID first_engine = t->gcache.first_engine;
1058 t->gcache.first_engine = EngineID::Invalid();
1059
1060 /* We don't want the cache to interfere. head's cache is cleared before
1061 * the loop and after each callback does not need to be cleared here. */
1063
1064 std::array<int32_t, 1> regs100;
1065 uint16_t callback = GetVehicleCallbackParent(CBID_TRAIN_ALLOW_WAGON_ATTACH, 0, 0, head->engine_type, t, head, regs100);
1066
1067 /* Restore original first_engine data */
1068 t->gcache.first_engine = first_engine;
1069
1070 /* We do not want to remember any cached variables from the test run */
1072 head->InvalidateNewGRFCache();
1073
1074 if (callback != CALLBACK_FAILED) {
1075 /* A failing callback means everything is okay */
1076 StringID error = STR_NULL;
1077
1078 if (head->GetGRF()->grf_version < 8) {
1079 if (callback == 0xFD) error = STR_ERROR_INCOMPATIBLE_RAIL_TYPES;
1080 if (callback < 0xFD) error = GetGRFStringID(head->GetGRFID(), GRFSTR_MISC_GRF_TEXT + callback);
1081 if (callback >= 0x100) ErrorUnknownCallbackResult(head->GetGRFID(), CBID_TRAIN_ALLOW_WAGON_ATTACH, callback);
1082 } else {
1083 if (callback < 0x400) {
1084 error = GetGRFStringID(head->GetGRFID(), GRFSTR_MISC_GRF_TEXT + callback);
1085 } else {
1086 switch (callback) {
1087 case 0x400: // allow if railtypes match (always the case for OpenTTD)
1088 case 0x401: // allow
1089 break;
1090
1091 case 0x40F:
1092 error = GetGRFStringID(head->GetGRFID(), static_cast<GRFStringID>(regs100[0]));
1093 break;
1094
1095 default: // unknown reason -> disallow
1096 case 0x402: // disallow attaching
1097 error = STR_ERROR_INCOMPATIBLE_RAIL_TYPES;
1098 break;
1099 }
1100 }
1101 }
1102
1103 if (error != STR_NULL) return CommandCost(error);
1104 }
1105 }
1106
1107 /* And link it to the new part. */
1108 prev->SetNext(t);
1109 prev = t;
1110 t = next;
1111 }
1112
1113 if (allowed_len < 0) return CommandCost(STR_ERROR_TRAIN_TOO_LONG);
1114 return CommandCost();
1115}
1116
1127static CommandCost ValidateTrains(Train *original_dst, Train *dst, Train *original_src, Train *src, bool check_limit)
1128{
1129 /* Check whether we may actually construct the trains. */
1131 if (ret.Failed()) return ret;
1132 ret = CheckTrainAttachment(dst);
1133 if (ret.Failed()) return ret;
1134
1135 /* Check whether we need to build a new train. */
1136 return check_limit ? CheckNewTrain(original_dst, dst, original_src, src) : CommandCost();
1137}
1138
1147static void ArrangeTrains(Train **dst_head, Train *dst, Train **src_head, Train *src, bool move_chain)
1148{
1149 /* First determine the front of the two resulting trains */
1150 if (*src_head == *dst_head) {
1151 /* If we aren't moving part(s) to a new train, we are just moving the
1152 * front back and there is not destination head. */
1153 *dst_head = nullptr;
1154 } else if (*dst_head == nullptr) {
1155 /* If we are moving to a new train the head of the move train would become
1156 * the head of the new vehicle. */
1157 *dst_head = src;
1158 }
1159
1160 if (src == *src_head) {
1161 /* If we are moving the front of a train then we are, in effect, creating
1162 * a new head for the train. Point to that. Unless we are moving the whole
1163 * train in which case there is not 'source' train anymore.
1164 * In case we are a multiheaded part we want the complete thing to come
1165 * with us, so src->GetNextUnit(), however... when we are e.g. a wagon
1166 * that is followed by a rear multihead we do not want to include that. */
1167 *src_head = move_chain ? nullptr :
1168 (src->IsMultiheaded() ? src->GetNextUnit() : src->GetNextVehicle());
1169 }
1170
1171 /* Now it's just simply removing the part that we are going to move from the
1172 * source train and *if* the destination is a not a new train add the chain
1173 * at the destination location. */
1174 RemoveFromConsist(src, move_chain);
1175 if (*dst_head != src) InsertInConsist(dst, src);
1176
1177 /* Now normalise the dual heads, that is move the dual heads around in such
1178 * a way that the head and rear of a dual head are in the same train */
1179 NormaliseDualHeads(*src_head);
1180 NormaliseDualHeads(*dst_head);
1181}
1182
1188static void NormaliseTrainHead(Train *head)
1189{
1190 /* Not much to do! */
1191 if (head == nullptr) return;
1192
1193 /* Tell the 'world' the train changed. */
1195 UpdateTrainGroupID(head);
1196
1197 /* Not a front engine, i.e. a free wagon chain. No need to do more. */
1198 if (!head->IsFrontEngine()) return;
1199
1200 /* Update the refit button and window */
1203
1204 /* If we don't have a unit number yet, set one. */
1205 if (head->unitnumber != 0) return;
1206 head->unitnumber = Company::Get(head->owner)->freeunits[head->type].UseID(GetFreeUnitNumber(VEH_TRAIN));
1207}
1208
1218CommandCost CmdMoveRailVehicle(DoCommandFlags flags, VehicleID src_veh, VehicleID dest_veh, bool move_chain)
1219{
1220 Train *src = Train::GetIfValid(src_veh);
1221 if (src == nullptr) return CMD_ERROR;
1222
1223 CommandCost ret = CheckOwnership(src->owner);
1224 if (ret.Failed()) return ret;
1225
1226 /* Do not allow moving crashed vehicles inside the depot, it is likely to cause asserts later */
1227 if (src->vehstatus.Test(VehState::Crashed)) return CMD_ERROR;
1228
1229 /* if nothing is selected as destination, try and find a matching vehicle to drag to. */
1230 Train *dst;
1231 if (dest_veh == VehicleID::Invalid()) {
1232 dst = (src->IsEngine() || flags.Test(DoCommandFlag::AutoReplace)) ? nullptr : FindGoodVehiclePos(src);
1233 } else {
1234 dst = Train::GetIfValid(dest_veh);
1235 if (dst == nullptr) return CMD_ERROR;
1236
1237 ret = CheckOwnership(dst->owner);
1238 if (ret.Failed()) return ret;
1239
1240 /* Do not allow appending to crashed vehicles, too */
1241 if (dst->vehstatus.Test(VehState::Crashed)) return CMD_ERROR;
1242 }
1243
1244 /* if an articulated part is being handled, deal with its parent vehicle */
1245 src = src->GetFirstEnginePart();
1246 if (dst != nullptr) {
1247 dst = dst->GetFirstEnginePart();
1248 }
1249
1250 /* don't move the same vehicle.. */
1251 if (src == dst) return CommandCost();
1252
1253 /* locate the head of the two chains */
1254 Train *src_head = src->First();
1255 Train *dst_head;
1256 if (dst != nullptr) {
1257 dst_head = dst->First();
1258 if (dst_head->tile != src_head->tile) return CMD_ERROR;
1259 /* Now deal with articulated part of destination wagon */
1260 dst = dst->GetLastEnginePart();
1261 } else {
1262 dst_head = nullptr;
1263 }
1264
1265 if (src->IsRearDualheaded()) return CommandCost(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT);
1266
1267 /* When moving all wagons, we can't have the same src_head and dst_head */
1268 if (move_chain && src_head == dst_head) return CommandCost();
1269
1270 /* When moving a multiheaded part to be place after itself, bail out. */
1271 if (!move_chain && dst != nullptr && dst->IsRearDualheaded() && src == dst->other_multiheaded_part) return CommandCost();
1272
1273 /* Check if all vehicles in the source train are stopped inside a depot. */
1274 if (!src_head->IsStoppedInDepot()) return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
1275
1276 /* Check if all vehicles in the destination train are stopped inside a depot. */
1277 if (dst_head != nullptr && !dst_head->IsStoppedInDepot()) return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
1278
1279 /* First make a backup of the order of the trains. That way we can do
1280 * whatever we want with the order and later on easily revert. */
1281 TrainList original_src;
1282 TrainList original_dst;
1283
1284 MakeTrainBackup(original_src, src_head);
1285 MakeTrainBackup(original_dst, dst_head);
1286
1287 /* Also make backup of the original heads as ArrangeTrains can change them.
1288 * For the destination head we do not care if it is the same as the source
1289 * head because in that case it's just a copy. */
1290 Train *original_src_head = src_head;
1291 Train *original_dst_head = (dst_head == src_head ? nullptr : dst_head);
1292
1293 /* We want this information from before the rearrangement, but execute this after the validation.
1294 * original_src_head can't be nullptr; src is by definition != nullptr, so src_head can't be nullptr as
1295 * src->GetFirst() always yields non-nullptr, so eventually original_src_head != nullptr as well. */
1296 bool original_src_head_front_engine = original_src_head->IsFrontEngine();
1297 bool original_dst_head_front_engine = original_dst_head != nullptr && original_dst_head->IsFrontEngine();
1298
1299 /* (Re)arrange the trains in the wanted arrangement. */
1300 ArrangeTrains(&dst_head, dst, &src_head, src, move_chain);
1301
1302 if (!flags.Test(DoCommandFlag::AutoReplace)) {
1303 /* If the autoreplace flag is set we do not need to test for the validity
1304 * because we are going to revert the train to its original state. As we
1305 * assume the original state was correct autoreplace can skip this. */
1306 ret = ValidateTrains(original_dst_head, dst_head, original_src_head, src_head, true);
1307 if (ret.Failed()) {
1308 /* Restore the train we had. */
1309 RestoreTrainBackup(original_src);
1310 RestoreTrainBackup(original_dst);
1311 return ret;
1312 }
1313 }
1314
1315 /* do it? */
1316 if (flags.Test(DoCommandFlag::Execute)) {
1317 /* Remove old heads from the statistics */
1318 if (original_src_head_front_engine) GroupStatistics::CountVehicle(original_src_head, -1);
1319 if (original_dst_head_front_engine) GroupStatistics::CountVehicle(original_dst_head, -1);
1320
1321 /* First normalise the sub types of the chains. */
1322 NormaliseSubtypes(src_head);
1323 NormaliseSubtypes(dst_head);
1324
1325 /* There are 14 different cases:
1326 * 1) front engine gets moved to a new train, it stays a front engine.
1327 * a) the 'next' part is a wagon that becomes a free wagon chain.
1328 * b) the 'next' part is an engine that becomes a front engine.
1329 * c) there is no 'next' part, nothing else happens
1330 * 2) front engine gets moved to another train, it is not a front engine anymore
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 * 3) front engine gets moved to later in the current 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 * 4) free wagon gets moved
1338 * a) the 'next' part is a wagon that becomes a free wagon chain.
1339 * b) the 'next' part is an engine that becomes a front engine.
1340 * c) there is no 'next' part, nothing else happens
1341 * 5) non front engine gets moved and becomes a new train, nothing else happens
1342 * 6) non front engine gets moved within a train / to another train, nothing happens
1343 * 7) wagon gets moved, nothing happens
1344 */
1345 if (src == original_src_head && src->IsEngine() && !src->IsFrontEngine()) {
1346 /* Cases #2 and #3: the front engine gets trashed. */
1352 DeleteNewGRFInspectWindow(GSF_TRAINS, src->index);
1354
1355 if (src_head != nullptr && src_head->IsFrontEngine()) {
1356 /* Cases #?b: Transfer order, unit number and other stuff
1357 * to the new front engine. */
1358 src_head->orders = src->orders;
1359 if (src_head->orders != nullptr) src_head->AddToShared(src);
1360 src_head->CopyVehicleConfigAndStatistics(src);
1361 }
1362 /* Remove stuff not valid anymore for non-front engines. */
1364 src->ReleaseUnitNumber();
1365 src->name.clear();
1366 }
1367
1368 /* We weren't a front engine but are becoming one. So
1369 * we should be put in the default group. */
1370 if (original_src_head != src && dst_head == src) {
1373 }
1374
1375 /* Handle 'new engine' part of cases #1b, #2b, #3b, #4b and #5 in NormaliseTrainHead. */
1376 NormaliseTrainHead(src_head);
1377 NormaliseTrainHead(dst_head);
1378
1379 /* Add new heads to statistics.
1380 * This should be done after NormaliseTrainHead due to engine total limit checks in GetFreeUnitNumber. */
1381 if (src_head != nullptr && src_head->IsFrontEngine()) GroupStatistics::CountVehicle(src_head, 1);
1382 if (dst_head != nullptr && dst_head->IsFrontEngine()) GroupStatistics::CountVehicle(dst_head, 1);
1383
1385 CheckCargoCapacity(src_head);
1386 CheckCargoCapacity(dst_head);
1387 }
1388
1389 if (src_head != nullptr) src_head->First()->MarkDirty();
1390 if (dst_head != nullptr) dst_head->First()->MarkDirty();
1391
1392 /* We are undoubtedly changing something in the depot and train list. */
1395 } else {
1396 /* We don't want to execute what we're just tried. */
1397 RestoreTrainBackup(original_src);
1398 RestoreTrainBackup(original_dst);
1399 }
1400
1401 return CommandCost();
1402}
1403
1416CommandCost CmdSellRailWagon(DoCommandFlags flags, Vehicle *t, bool sell_chain, bool backup_order, ClientID user)
1417{
1419 Train *first = v->First();
1420
1421 if (v->IsRearDualheaded()) return CommandCost(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT);
1422
1423 /* First make a backup of the order of the train. That way we can do
1424 * whatever we want with the order and later on easily revert. */
1425 TrainList original;
1426 MakeTrainBackup(original, first);
1427
1428 /* We need to keep track of the new head and the head of what we're going to sell. */
1429 Train *new_head = first;
1430 Train *sell_head = nullptr;
1431
1432 /* Split the train in the wanted way. */
1433 ArrangeTrains(&sell_head, nullptr, &new_head, v, sell_chain);
1434
1435 /* We don't need to validate the second train; it's going to be sold. */
1436 CommandCost ret = ValidateTrains(nullptr, nullptr, first, new_head, !flags.Test(DoCommandFlag::AutoReplace));
1437 if (ret.Failed()) {
1438 /* Restore the train we had. */
1439 RestoreTrainBackup(original);
1440 return ret;
1441 }
1442
1443 if (first->orders == nullptr && !OrderList::CanAllocateItem()) {
1444 /* Restore the train we had. */
1445 RestoreTrainBackup(original);
1446 return CommandCost(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS);
1447 }
1448
1450 for (Train *part = sell_head; part != nullptr; part = part->Next()) cost.AddCost(-part->value);
1451
1452 /* do it? */
1453 if (flags.Test(DoCommandFlag::Execute)) {
1454 /* First normalise the sub types of the chain. */
1455 NormaliseSubtypes(new_head);
1456
1457 if (v == first && !sell_chain && new_head != nullptr && new_head->IsFrontEngine()) {
1458 if (v->IsEngine()) {
1459 /* We are selling the front engine. In this case we want to
1460 * 'give' the order, unit number and such to the new head. */
1461 new_head->orders = first->orders;
1462 new_head->AddToShared(first);
1463 DeleteVehicleOrders(first);
1464
1465 /* Copy other important data from the front engine */
1466 new_head->CopyVehicleConfigAndStatistics(first);
1467 }
1468 GroupStatistics::CountVehicle(new_head, 1); // after copying over the profit, if required
1469 } else if (v->IsPrimaryVehicle() && backup_order) {
1470 OrderBackup::Backup(v, user);
1471 }
1472
1473 /* We need to update the information about the train. */
1474 NormaliseTrainHead(new_head);
1475
1476 /* We are undoubtedly changing something in the depot and train list. */
1479
1480 /* Actually delete the sold 'goods' */
1481 delete sell_head;
1482 } else {
1483 /* We don't want to execute what we're just tried. */
1484 RestoreTrainBackup(original);
1485 }
1486
1487 return cost;
1488}
1489
1491{
1492 /* Set common defaults. */
1493 this->bounds = {{-1, -1, 0}, {3, 3, 6}, {}};
1494
1495 /* Set if flipped and engine is NOT flagged with custom flip handling. */
1496 int flipped = this->flags.Test(VehicleRailFlag::Flipped) && !EngInfo(this->engine_type)->misc_flags.Test(EngineMiscFlag::RailFlips);
1497 /* If flipped and vehicle length is odd, we need to adjust the bounding box offset slightly. */
1498 int flip_offs = flipped && (this->gcache.cached_veh_length & 1);
1499
1500 Direction dir = this->direction;
1501 if (flipped) dir = ReverseDir(dir);
1502
1503 if (!IsDiagonalDirection(dir)) {
1504 static const Point _sign_table[] = {
1505 /* x, y */
1506 {-1, -1}, // DIR_N
1507 {-1, 1}, // DIR_E
1508 { 1, 1}, // DIR_S
1509 { 1, -1}, // DIR_W
1510 };
1511
1512 int half_shorten = (VEHICLE_LENGTH - this->gcache.cached_veh_length + flipped) / 2;
1513
1514 /* For all straight directions, move the bound box to the centre of the vehicle, but keep the size. */
1515 this->bounds.offset.x -= half_shorten * _sign_table[DirToDiagDir(dir)].x;
1516 this->bounds.offset.y -= half_shorten * _sign_table[DirToDiagDir(dir)].y;
1517 } else {
1518 switch (dir) {
1519 /* Shorten southern corner of the bounding box according the vehicle length
1520 * and center the bounding box on the vehicle. */
1521 case DIR_NE:
1522 this->bounds.origin.x = -(this->gcache.cached_veh_length + 1) / 2 + flip_offs;
1523 this->bounds.extent.x = this->gcache.cached_veh_length;
1524 this->bounds.offset.x = 1;
1525 break;
1526
1527 case DIR_NW:
1528 this->bounds.origin.y = -(this->gcache.cached_veh_length + 1) / 2 + flip_offs;
1529 this->bounds.extent.y = this->gcache.cached_veh_length;
1530 this->bounds.offset.y = 1;
1531 break;
1532
1533 /* Move northern corner of the bounding box down according to vehicle length
1534 * and center the bounding box on the vehicle. */
1535 case DIR_SW:
1536 this->bounds.origin.x = -(this->gcache.cached_veh_length) / 2 - flip_offs;
1537 this->bounds.extent.x = this->gcache.cached_veh_length;
1538 this->bounds.offset.x = 1 - (VEHICLE_LENGTH - this->gcache.cached_veh_length);
1539 break;
1540
1541 case DIR_SE:
1542 this->bounds.origin.y = -(this->gcache.cached_veh_length) / 2 - flip_offs;
1543 this->bounds.extent.y = this->gcache.cached_veh_length;
1544 this->bounds.offset.y = 1 - (VEHICLE_LENGTH - this->gcache.cached_veh_length);
1545 break;
1546
1547 default:
1548 NOT_REACHED();
1549 }
1550 }
1551}
1552
1558{
1559 if (!v->flags.Test(VehicleRailFlag::Stuck)) {
1560 /* It is the first time the problem occurred, set the "train stuck" flag. */
1561 v->flags.Set(VehicleRailFlag::Stuck);
1562
1563 v->wait_counter = 0;
1564
1565 /* Stop train */
1566 v->cur_speed = 0;
1567 v->subspeed = 0;
1568 v->SetLastSpeed();
1569
1571 }
1572}
1573
1581static void SwapTrainFlags(uint16_t *swap_flag1, uint16_t *swap_flag2)
1582{
1583 uint16_t flag1 = *swap_flag1;
1584 uint16_t flag2 = *swap_flag2;
1585
1586 /* Clear the flags */
1587 ClrBit(*swap_flag1, GVF_GOINGUP_BIT);
1588 ClrBit(*swap_flag1, GVF_GOINGDOWN_BIT);
1589 ClrBit(*swap_flag2, GVF_GOINGUP_BIT);
1590 ClrBit(*swap_flag2, GVF_GOINGDOWN_BIT);
1591
1592 /* Reverse the rail-flags (if needed) */
1593 if (HasBit(flag1, GVF_GOINGUP_BIT)) {
1594 SetBit(*swap_flag2, GVF_GOINGDOWN_BIT);
1595 } else if (HasBit(flag1, GVF_GOINGDOWN_BIT)) {
1596 SetBit(*swap_flag2, GVF_GOINGUP_BIT);
1597 }
1598 if (HasBit(flag2, GVF_GOINGUP_BIT)) {
1599 SetBit(*swap_flag1, GVF_GOINGDOWN_BIT);
1600 } else if (HasBit(flag2, GVF_GOINGDOWN_BIT)) {
1601 SetBit(*swap_flag1, GVF_GOINGUP_BIT);
1602 }
1603}
1604
1610{
1611 /* Reverse the direction. */
1612 if (v->track != TRACK_BIT_DEPOT) v->direction = ReverseDir(v->direction);
1613
1614 /* Call the proper EnterTile function unless we are in a wormhole. */
1615 if (v->track != TRACK_BIT_WORMHOLE) {
1616 VehicleEnterTile(v, v->tile, v->x_pos, v->y_pos);
1617 } else {
1618 /* VehicleEnter_TunnelBridge() sets TRACK_BIT_WORMHOLE when the vehicle
1619 * is on the last bit of the bridge head (frame == TILE_SIZE - 1).
1620 * If we were swapped with such a vehicle, we have set TRACK_BIT_WORMHOLE,
1621 * when we shouldn't have. Check if this is the case. */
1622 TileIndex vt = TileVirtXY(v->x_pos, v->y_pos);
1623 if (IsTileType(vt, MP_TUNNELBRIDGE)) {
1624 VehicleEnterTile(v, vt, v->x_pos, v->y_pos);
1625 if (v->track != TRACK_BIT_WORMHOLE && IsBridgeTile(v->tile)) {
1626 /* We have just left the wormhole, possibly set the
1627 * "goingdown" bit. UpdateInclination() can be used
1628 * because we are at the border of the tile. */
1629 v->UpdatePosition();
1630 v->UpdateInclination(true, true);
1631 return;
1632 }
1633 }
1634 }
1635
1636 v->UpdatePosition();
1637 v->UpdateViewport(true, true);
1638}
1639
1646void ReverseTrainSwapVeh(Train *v, int l, int r)
1647{
1648 Train *a, *b;
1649
1650 /* locate vehicles to swap */
1651 for (a = v; l != 0; l--) a = a->Next();
1652 for (b = v; r != 0; r--) b = b->Next();
1653
1654 if (a != b) {
1655 /* swap the hidden bits */
1656 {
1657 bool a_hidden = a->vehstatus.Test(VehState::Hidden);
1658 bool b_hidden = b->vehstatus.Test(VehState::Hidden);
1659 b->vehstatus.Set(VehState::Hidden, a_hidden);
1660 a->vehstatus.Set(VehState::Hidden, b_hidden);
1661 }
1662
1663 std::swap(a->track, b->track);
1664 std::swap(a->direction, b->direction);
1665 std::swap(a->x_pos, b->x_pos);
1666 std::swap(a->y_pos, b->y_pos);
1667 std::swap(a->tile, b->tile);
1668 std::swap(a->z_pos, b->z_pos);
1669
1671
1674 } else {
1675 /* Swap GVF_GOINGUP_BIT/GVF_GOINGDOWN_BIT.
1676 * This is a little bit redundant way, a->gv_flags will
1677 * be (re)set twice, but it reduces code duplication */
1680 }
1681}
1682
1688static bool IsTrain(const Vehicle *v)
1689{
1690 return v->type == VEH_TRAIN;
1691}
1692
1700{
1701 assert(IsLevelCrossingTile(tile));
1702
1703 return HasVehicleOnTile(tile, IsTrain);
1704}
1705
1713{
1714 if (v->type != VEH_TRAIN || v->vehstatus.Test(VehState::Crashed)) return false;
1715
1716 const Train *t = Train::From(v);
1717 if (!t->IsFrontEngine()) return false;
1718
1719 return TrainApproachingCrossingTile(t) == tile;
1720}
1721
1722
1730{
1731 assert(IsLevelCrossingTile(tile));
1732
1734 TileIndex tile_from = tile + TileOffsByDiagDir(dir);
1735
1736 if (HasVehicleOnTile(tile_from, [&](const Vehicle *v) {
1737 return TrainApproachingCrossingEnum(v, tile);
1738 })) return true;
1739
1740 dir = ReverseDiagDir(dir);
1741 tile_from = tile + TileOffsByDiagDir(dir);
1742
1743 return HasVehicleOnTile(tile_from, [&](const Vehicle *v) {
1744 return TrainApproachingCrossingEnum(v, tile);
1745 });
1746}
1747
1753static inline bool CheckLevelCrossing(TileIndex tile)
1754{
1755 /* reserved || train on crossing || train approaching crossing */
1757}
1758
1766static void UpdateLevelCrossingTile(TileIndex tile, bool sound, bool force_barred)
1767{
1768 assert(IsLevelCrossingTile(tile));
1769 bool set_barred;
1770
1771 /* We force the crossing to be barred when an adjacent crossing is barred, otherwise let it decide for itself. */
1772 set_barred = force_barred || CheckLevelCrossing(tile);
1773
1774 /* The state has changed */
1775 if (set_barred != IsCrossingBarred(tile)) {
1776 if (set_barred && sound && _settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, tile);
1777 SetCrossingBarred(tile, set_barred);
1778 MarkTileDirtyByTile(tile);
1779 }
1780}
1781
1788void UpdateLevelCrossing(TileIndex tile, bool sound, bool force_bar)
1789{
1790 if (!IsLevelCrossingTile(tile)) return;
1791
1792 bool forced_state = force_bar;
1793
1794 const Axis axis = GetCrossingRoadAxis(tile);
1795 const DiagDirection dir1 = AxisToDiagDir(axis);
1796 const DiagDirection dir2 = ReverseDiagDir(dir1);
1797
1798 /* Check if an adjacent crossing is barred. */
1799 for (DiagDirection dir : { dir1, dir2 }) {
1800 for (TileIndex t = tile; !forced_state && t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, dir)) {
1801 forced_state |= CheckLevelCrossing(t);
1802 }
1803 }
1804
1805 /* Now that we know whether all tiles in this crossing should be barred or open,
1806 * we need to update those tiles. We start with the tile itself, then look along the road axis. */
1807 UpdateLevelCrossingTile(tile, sound, forced_state);
1808 for (DiagDirection dir : { dir1, dir2 }) {
1809 for (TileIndex t = TileAddByDiagDir(tile, dir); t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, dir)) {
1810 UpdateLevelCrossingTile(t, sound, forced_state);
1811 }
1812 }
1813}
1814
1821{
1822 const DiagDirection dir1 = AxisToDiagDir(road_axis);
1823 const DiagDirection dir2 = ReverseDiagDir(dir1);
1824 for (DiagDirection dir : { dir1, dir2 }) {
1825 const TileIndex t = TileAddByDiagDir(tile, dir);
1826 if (t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis) {
1828 }
1829 }
1830}
1831
1838{
1839 const DiagDirection dir1 = AxisToDiagDir(road_axis);
1840 const DiagDirection dir2 = ReverseDiagDir(dir1);
1841 for (DiagDirection dir : { dir1, dir2 }) {
1842 const TileIndexDiff diff = TileOffsByDiagDir(dir);
1843 bool occupied = false;
1844 for (TileIndex t = tile + diff; t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis; t += diff) {
1845 occupied |= CheckLevelCrossing(t);
1846 }
1847 if (occupied) {
1848 /* Mark the immediately adjacent tile dirty */
1849 const TileIndex t = tile + diff;
1850 if (t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis) {
1852 }
1853 } else {
1854 /* Unbar the crossing tiles in this direction as necessary */
1855 for (TileIndex t = tile + diff; t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis; t += diff) {
1856 if (IsCrossingBarred(t)) {
1857 /* The crossing tile is barred, unbar it and continue to check the next tile */
1858 SetCrossingBarred(t, false);
1860 } else {
1861 /* The crossing tile is already unbarred, mark the tile dirty and stop checking */
1863 break;
1864 }
1865 }
1866 }
1867 }
1868}
1869
1875static inline void MaybeBarCrossingWithSound(TileIndex tile)
1876{
1877 if (!IsCrossingBarred(tile)) {
1878 SetCrossingReservation(tile, true);
1879 UpdateLevelCrossing(tile, true);
1880 }
1881}
1882
1883
1890{
1891 Train *base = v;
1892 Train *first = base; // first vehicle to move
1893 Train *last = v->Last(); // last vehicle to move
1894 uint length = CountVehiclesInChain(v);
1895
1896 while (length > 2) {
1897 last = last->Previous();
1898 first = first->Next();
1899
1900 int differential = base->CalcNextVehicleOffset() - last->CalcNextVehicleOffset();
1901
1902 /* do not update images now
1903 * negative differential will be handled in AdvanceWagonsAfterSwap() */
1904 for (int i = 0; i < differential; i++) TrainController(first, last->Next());
1905
1906 base = first; // == base->Next()
1907 length -= 2;
1908 }
1909}
1910
1911
1918{
1919 /* first of all, fix the situation when the train was entering a depot */
1920 Train *dep = v; // last vehicle in front of just left depot
1921 while (dep->Next() != nullptr && (dep->track == TRACK_BIT_DEPOT || dep->Next()->track != TRACK_BIT_DEPOT)) {
1922 dep = dep->Next(); // find first vehicle outside of a depot, with next vehicle inside a depot
1923 }
1924
1925 Train *leave = dep->Next(); // first vehicle in a depot we are leaving now
1926
1927 if (leave != nullptr) {
1928 /* 'pull' next wagon out of the depot, so we won't miss it (it could stay in depot forever) */
1929 int d = TicksToLeaveDepot(dep);
1930
1931 if (d <= 0) {
1932 leave->vehstatus.Reset(VehState::Hidden); // move it out of the depot
1933 leave->track = TrackToTrackBits(GetRailDepotTrack(leave->tile));
1934 for (int i = 0; i >= d; i--) TrainController(leave, nullptr); // maybe move it, and maybe let another wagon leave
1935 }
1936 } else {
1937 dep = nullptr; // no vehicle in a depot, so no vehicle leaving a depot
1938 }
1939
1940 Train *base = v;
1941 Train *first = base; // first vehicle to move
1942 Train *last = v->Last(); // last vehicle to move
1943 uint length = CountVehiclesInChain(v);
1944
1945 /* We have to make sure all wagons that leave a depot because of train reversing are moved correctly
1946 * they have already correct spacing, so we have to make sure they are moved how they should */
1947 bool nomove = (dep == nullptr); // If there is no vehicle leaving a depot, limit the number of wagons moved immediately.
1948
1949 while (length > 2) {
1950 /* we reached vehicle (originally) in front of a depot, stop now
1951 * (we would move wagons that are already moved with new wagon length). */
1952 if (base == dep) break;
1953
1954 /* the last wagon was that one leaving a depot, so do not move it anymore */
1955 if (last == dep) nomove = true;
1956
1957 last = last->Previous();
1958 first = first->Next();
1959
1960 int differential = last->CalcNextVehicleOffset() - base->CalcNextVehicleOffset();
1961
1962 /* do not update images now */
1963 for (int i = 0; i < differential; i++) TrainController(first, (nomove ? last->Next() : nullptr));
1964
1965 base = first; // == base->Next()
1966 length -= 2;
1967 }
1968}
1969
1970static bool IsWholeTrainInsideDepot(const Train *v)
1971{
1972 for (const Train *u = v; u != nullptr; u = u->Next()) {
1973 if (u->track != TRACK_BIT_DEPOT || u->tile != v->tile) return false;
1974 }
1975 return true;
1976}
1977
1983{
1984 if (IsRailDepotTile(v->tile)) {
1985 if (IsWholeTrainInsideDepot(v)) return;
1987 }
1988
1989 /* Clear path reservation in front if train is not stuck. */
1991
1992 /* Check if we were approaching a rail/road-crossing */
1994
1995 /* count number of vehicles */
1996 int r = CountVehiclesInChain(v) - 1; // number of vehicles - 1
1997
1999
2000 /* swap start<>end, start+1<>end-1, ... */
2001 int l = 0;
2002 do {
2003 ReverseTrainSwapVeh(v, l++, r--);
2004 } while (l <= r);
2005
2007
2008 if (IsRailDepotTile(v->tile)) {
2010 }
2011
2014
2015 /* recalculate cached data */
2017
2018 /* update all images */
2019 for (Train *u = v; u != nullptr; u = u->Next()) u->UpdateViewport(false, false);
2020
2021 /* update crossing we were approaching */
2022 if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing);
2023
2024 /* maybe we are approaching crossing now, after reversal */
2025 crossing = TrainApproachingCrossingTile(v);
2026 if (crossing != INVALID_TILE) MaybeBarCrossingWithSound(crossing);
2027
2028 /* If we are inside a depot after reversing, don't bother with path reserving. */
2029 if (v->track == TRACK_BIT_DEPOT) {
2030 /* Can't be stuck here as inside a depot is always a safe tile. */
2032 v->flags.Reset(VehicleRailFlag::Stuck);
2033 return;
2034 }
2035
2036 /* VehicleExitDir does not always produce the desired dir for depots and
2037 * tunnels/bridges that is needed for UpdateSignalsOnSegment. */
2038 DiagDirection dir = VehicleExitDir(v->direction, v->track);
2040
2042 /* If we are currently on a tile with conventional signals, we can't treat the
2043 * current tile as a safe tile or we would enter a PBS block without a reservation. */
2044 bool first_tile_okay = !(IsTileType(v->tile, MP_RAILWAY) &&
2046 !IsPbsSignal(GetSignalType(v->tile, FindFirstTrack(v->track))));
2047
2048 /* If we are on a depot tile facing outwards, do not treat the current tile as safe. */
2049 if (IsRailDepotTile(v->tile) && TrackdirToExitdir(v->GetVehicleTrackdir()) == GetRailDepotDirection(v->tile)) first_tile_okay = false;
2050
2052 if (TryPathReserve(v, false, first_tile_okay)) {
2053 /* Do a look-ahead now in case our current tile was already a safe tile. */
2055 } else if (v->current_order.GetType() != OT_LOADING) {
2056 /* Do not wait for a way out when we're still loading */
2058 }
2059 } else if (v->flags.Test(VehicleRailFlag::Stuck)) {
2060 /* A train not inside a PBS block can't be stuck. */
2061 v->flags.Reset(VehicleRailFlag::Stuck);
2062 v->wait_counter = 0;
2063 }
2064}
2065
2073CommandCost CmdReverseTrainDirection(DoCommandFlags flags, VehicleID veh_id, bool reverse_single_veh)
2074{
2075 Train *v = Train::GetIfValid(veh_id);
2076 if (v == nullptr) return CMD_ERROR;
2077
2079 if (ret.Failed()) return ret;
2080
2081 if (reverse_single_veh) {
2082 /* turn a single unit around */
2083
2085 return CommandCost(STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE_MULTIPLE_UNITS);
2086 }
2087
2088 Train *front = v->First();
2089 /* make sure the vehicle is stopped in the depot */
2090 if (!front->IsStoppedInDepot()) {
2091 return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
2092 }
2093
2094 if (flags.Test(DoCommandFlag::Execute)) {
2096
2102 }
2103 } else {
2104 /* turn the whole train around */
2105 if (!v->IsPrimaryVehicle()) return CMD_ERROR;
2106 if (v->vehstatus.Test(VehState::Crashed) || v->breakdown_ctr != 0) return CMD_ERROR;
2107
2108 if (flags.Test(DoCommandFlag::Execute)) {
2109 /* Properly leave the station if we are loading and won't be loading anymore */
2110 if (v->current_order.IsType(OT_LOADING)) {
2111 const Vehicle *last = v;
2112 while (last->Next() != nullptr) last = last->Next();
2113
2114 /* not a station || different station --> leave the station */
2115 if (!IsTileType(last->tile, MP_STATION) || GetStationIndex(last->tile) != GetStationIndex(v->tile)) {
2116 v->LeaveStation();
2117 }
2118 }
2119
2120 /* We cancel any 'skip signal at dangers' here */
2121 v->force_proceed = TFP_NONE;
2123
2124 if (_settings_game.vehicle.train_acceleration_model != AM_ORIGINAL && v->cur_speed != 0) {
2126 } else {
2127 v->cur_speed = 0;
2128 v->SetLastSpeed();
2131 }
2132
2133 /* Unbunching data is no longer valid. */
2135 }
2136 }
2137 return CommandCost();
2138}
2139
2147{
2148 Train *t = Train::GetIfValid(veh_id);
2149 if (t == nullptr) return CMD_ERROR;
2150
2151 if (!t->IsPrimaryVehicle()) return CMD_ERROR;
2152
2154 if (ret.Failed()) return ret;
2155
2156
2157 if (flags.Test(DoCommandFlag::Execute)) {
2158 /* If we are forced to proceed, cancel that order.
2159 * If we are marked stuck we would want to force the train
2160 * to proceed to the next signal. In the other cases we
2161 * would like to pass the signal at danger and run till the
2162 * next signal we encounter. */
2163 t->force_proceed = t->force_proceed == TFP_SIGNAL ? TFP_NONE : t->flags.Test(VehicleRailFlag::Stuck) || t->IsChainInDepot() ? TFP_STUCK : TFP_SIGNAL;
2165
2166 /* Unbunching data is no longer valid. */
2168 }
2169
2170 return CommandCost();
2171}
2172
2180static FindDepotData FindClosestTrainDepot(Train *v, int max_distance)
2181{
2182 assert(!v->vehstatus.Test(VehState::Crashed));
2183
2184 return YapfTrainFindNearestDepot(v, max_distance);
2185}
2186
2188{
2189 FindDepotData tfdd = FindClosestTrainDepot(this, 0);
2190 if (tfdd.best_length == UINT_MAX) return ClosestDepot();
2191
2192 return ClosestDepot(tfdd.tile, GetDepotIndex(tfdd.tile), tfdd.reverse);
2193}
2194
2196void Train::PlayLeaveStationSound(bool force) const
2197{
2198 static const SoundFx sfx[] = {
2204 };
2205
2206 if (PlayVehicleSound(this, VSE_START, force)) return;
2207
2208 SndPlayVehicleFx(sfx[RailVehInfo(this->engine_type)->engclass], this);
2209}
2210
2216{
2217 /* Don't do any look-ahead if path_backoff_interval is 255. */
2218 if (_settings_game.pf.path_backoff_interval == 255) return;
2219
2220 /* Exit if we are inside a depot. */
2221 if (v->track == TRACK_BIT_DEPOT) return;
2222
2223 switch (v->current_order.GetType()) {
2224 /* Exit if we reached our destination depot. */
2225 case OT_GOTO_DEPOT:
2226 if (v->tile == v->dest_tile) return;
2227 break;
2228
2229 case OT_GOTO_WAYPOINT:
2230 /* If we reached our waypoint, make sure we see that. */
2232 break;
2233
2234 case OT_NOTHING:
2235 case OT_LEAVESTATION:
2236 case OT_LOADING:
2237 /* Exit if the current order doesn't have a destination, but the train has orders. */
2238 if (v->GetNumOrders() > 0) return;
2239 break;
2240
2241 default:
2242 break;
2243 }
2244 /* Exit if we are on a station tile and are going to stop. */
2246
2247 Trackdir td = v->GetVehicleTrackdir();
2248
2249 /* On a tile with a red non-pbs signal, don't look ahead. */
2250 if (IsTileType(v->tile, MP_RAILWAY) && HasSignalOnTrackdir(v->tile, td) &&
2251 !IsPbsSignal(GetSignalType(v->tile, TrackdirToTrack(td))) &&
2253
2254 CFollowTrackRail ft(v);
2255 if (!ft.Follow(v->tile, td)) return;
2256
2258 /* Next tile is not reserved. */
2261 /* If the next tile is a PBS signal, try to make a reservation. */
2264 tracks &= ~TrackCrossesTracks(TrackdirToTrack(ft.old_td));
2265 }
2266 ChooseTrainTrack(v, ft.new_tile, ft.exitdir, tracks, false, nullptr, false);
2267 }
2268 }
2269 }
2270}
2271
2278{
2279 /* bail out if not all wagons are in the same depot or not in a depot at all */
2280 for (const Train *u = v; u != nullptr; u = u->Next()) {
2281 if (u->track != TRACK_BIT_DEPOT || u->tile != v->tile) return false;
2282 }
2283
2284 /* if the train got no power, then keep it in the depot */
2285 if (v->gcache.cached_power == 0) {
2288 return true;
2289 }
2290
2291 /* Check if we should wait here for unbunching. */
2292 if (v->IsWaitingForUnbunching()) return true;
2293
2294 SigSegState seg_state;
2295
2296 if (v->force_proceed == TFP_NONE) {
2297 /* force proceed was not pressed */
2298 if (++v->wait_counter < 37) {
2300 return true;
2301 }
2302
2303 v->wait_counter = 0;
2304
2306 if (seg_state == SIGSEG_FULL || HasDepotReservation(v->tile)) {
2307 /* Full and no PBS signal in block or depot reserved, can't exit. */
2309 return true;
2310 }
2311 } else {
2313 }
2314
2315 /* We are leaving a depot, but have to go to the exact same one; re-enter. */
2316 if (v->current_order.IsType(OT_GOTO_DEPOT) && v->tile == v->dest_tile) {
2317 /* Service when depot has no reservation. */
2319 return true;
2320 }
2321
2322 /* Only leave when we can reserve a path to our destination. */
2323 if (seg_state == SIGSEG_PBS && !TryPathReserve(v) && v->force_proceed == TFP_NONE) {
2324 /* No path and no force proceed. */
2327 return true;
2328 }
2329
2330 SetDepotReservation(v->tile, true);
2332
2337
2338 v->track = TRACK_BIT_X;
2339 if (v->direction & 2) v->track = TRACK_BIT_Y;
2340
2342 v->cur_speed = 0;
2343
2344 v->UpdateViewport(true, true);
2345 v->UpdatePosition();
2347 v->UpdateAcceleration();
2349
2350 return false;
2351}
2352
2359static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_dir)
2360{
2361 DiagDirection dir = TrackdirToExitdir(track_dir);
2362
2363 if (IsTileType(tile, MP_TUNNELBRIDGE)) {
2364 /* Are we just leaving a tunnel/bridge? */
2365 if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) {
2367
2368 if (TunnelBridgeIsFree(tile, end, v).Succeeded()) {
2369 /* Free the reservation only if no other train is on the tiles. */
2370 SetTunnelBridgeReservation(tile, false);
2371 SetTunnelBridgeReservation(end, false);
2372
2374 if (IsBridge(tile)) {
2375 MarkBridgeDirty(tile);
2376 } else {
2377 MarkTileDirtyByTile(tile);
2379 }
2380 }
2381 }
2382 }
2383 } else if (IsRailStationTile(tile)) {
2384 TileIndex new_tile = TileAddByDiagDir(tile, dir);
2385 /* If the new tile is not a further tile of the same station, we
2386 * clear the reservation for the whole platform. */
2387 if (!IsCompatibleTrainStationTile(new_tile, tile)) {
2389 }
2390 } else {
2391 /* Any other tile */
2392 UnreserveRailTrack(tile, TrackdirToTrack(track_dir));
2393 }
2394}
2395
2401{
2402 assert(v->IsFrontEngine());
2403
2404 TileIndex tile = v->tile;
2405 Trackdir td = v->GetVehicleTrackdir();
2406 bool free_tile = !(IsRailStationTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE));
2407 StationID station_id = IsRailStationTile(v->tile) ? GetStationIndex(v->tile) : StationID::Invalid();
2408
2409 /* Can't be holding a reservation if we enter a depot. */
2410 if (IsRailDepotTile(tile) && TrackdirToExitdir(td) != GetRailDepotDirection(tile)) return;
2411 if (v->track == TRACK_BIT_DEPOT) {
2412 /* Front engine is in a depot. We enter if some part is not in the depot. */
2413 for (const Train *u = v; u != nullptr; u = u->Next()) {
2414 if (u->track != TRACK_BIT_DEPOT || u->tile != v->tile) return;
2415 }
2416 }
2417 /* Don't free reservation if it's not ours. */
2419
2420 CFollowTrackRail ft(v, GetAllCompatibleRailTypes(v->railtypes));
2421 while (ft.Follow(tile, td)) {
2422 tile = ft.new_tile;
2424 td = RemoveFirstTrackdir(&bits);
2425 assert(bits == TRACKDIR_BIT_NONE);
2426
2427 if (!IsValidTrackdir(td)) break;
2428
2429 if (IsTileType(tile, MP_RAILWAY)) {
2430 if (HasSignalOnTrackdir(tile, td) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(td)))) {
2431 /* Conventional signal along trackdir: remove reservation and stop. */
2433 break;
2434 }
2435 if (HasPbsSignalOnTrackdir(tile, td)) {
2436 if (GetSignalStateByTrackdir(tile, td) == SIGNAL_STATE_RED) {
2437 /* Red PBS signal? Can't be our reservation, would be green then. */
2438 break;
2439 } else {
2440 /* Turn the signal back to red. */
2442 MarkTileDirtyByTile(tile);
2443 }
2444 } else if (HasPbsSignalOnTrackdir(tile, ReverseTrackdir(td))) {
2445 /* Reservation passes an opposing path signal. Mark signal for update to re-establish the proper default state. */
2447 } else if (HasSignalOnTrackdir(tile, ReverseTrackdir(td)) && IsOnewaySignal(tile, TrackdirToTrack(td))) {
2448 break;
2449 }
2450 }
2451
2452 /* Don't free first station/bridge/tunnel if we are on it. */
2453 if (free_tile || (!(ft.is_station && GetStationIndex(ft.new_tile) == station_id) && !ft.is_tunnel && !ft.is_bridge)) ClearPathReservation(v, tile, td);
2454
2455 free_tile = true;
2456 }
2457
2459}
2460
2461static const uint8_t _initial_tile_subcoord[6][4][3] = {
2462{{ 15, 8, 1 }, { 0, 0, 0 }, { 0, 8, 5 }, { 0, 0, 0 }},
2463{{ 0, 0, 0 }, { 8, 0, 3 }, { 0, 0, 0 }, { 8, 15, 7 }},
2464{{ 0, 0, 0 }, { 7, 0, 2 }, { 0, 7, 6 }, { 0, 0, 0 }},
2465{{ 15, 8, 2 }, { 0, 0, 0 }, { 0, 0, 0 }, { 8, 15, 6 }},
2466{{ 15, 7, 0 }, { 8, 0, 4 }, { 0, 0, 0 }, { 0, 0, 0 }},
2467{{ 0, 0, 0 }, { 0, 0, 0 }, { 0, 8, 4 }, { 7, 15, 0 }},
2468};
2469
2483static Track DoTrainPathfind(const Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found, bool do_track_reservation, PBSTileInfo *dest, TileIndex *final_dest)
2484{
2485 if (final_dest != nullptr) *final_dest = INVALID_TILE;
2486 return YapfTrainChooseTrack(v, tile, enterdir, tracks, path_found, do_track_reservation, dest, final_dest);
2487}
2488
2494static PBSTileInfo ExtendTrainReservation(const Train *v, TrackBits *new_tracks, DiagDirection *enterdir)
2495{
2497
2498 CFollowTrackRail ft(v);
2499
2500 std::vector<std::pair<TileIndex, Trackdir>> signals_set_to_red;
2501
2502 TileIndex tile = origin.tile;
2503 Trackdir cur_td = origin.trackdir;
2504 while (ft.Follow(tile, cur_td)) {
2506 /* Possible signal tile. */
2508 }
2509
2511 ft.new_td_bits &= ~TrackdirCrossesTrackdirs(ft.old_td);
2512 if (ft.new_td_bits == TRACKDIR_BIT_NONE) break;
2513 }
2514
2515 /* Station, depot or waypoint are a possible target. */
2516 bool target_seen = ft.is_station || (IsTileType(ft.new_tile, MP_RAILWAY) && !IsPlainRail(ft.new_tile));
2517 if (target_seen || KillFirstBit(ft.new_td_bits) != TRACKDIR_BIT_NONE) {
2518 /* Choice found or possible target encountered.
2519 * On finding a possible target, we need to stop and let the pathfinder handle the
2520 * remaining path. This is because we don't know if this target is in one of our
2521 * orders, so we might cause pathfinding to fail later on if we find a choice.
2522 * This failure would cause a bogous call to TryReserveSafePath which might reserve
2523 * a wrong path not leading to our next destination. */
2525
2526 /* If we did skip some tiles, backtrack to the first skipped tile so the pathfinder
2527 * actually starts its search at the first unreserved tile. */
2528 if (ft.tiles_skipped != 0) ft.new_tile -= TileOffsByDiagDir(ft.exitdir) * ft.tiles_skipped;
2529
2530 /* Choice found, path valid but not okay. Save info about the choice tile as well. */
2531 if (new_tracks != nullptr) *new_tracks = TrackdirBitsToTrackBits(ft.new_td_bits);
2532 if (enterdir != nullptr) *enterdir = ft.exitdir;
2533 return PBSTileInfo(ft.new_tile, ft.old_td, false);
2534 }
2535
2536 tile = ft.new_tile;
2537 cur_td = FindFirstTrackdir(ft.new_td_bits);
2538
2539 Trackdir rev_td = ReverseTrackdir(cur_td);
2540 if (IsSafeWaitingPosition(v, tile, cur_td, true, _settings_game.pf.forbid_90_deg)) {
2541 bool wp_free = IsWaitingPositionFree(v, tile, cur_td, _settings_game.pf.forbid_90_deg);
2542 if (!(wp_free && TryReserveRailTrack(tile, TrackdirToTrack(cur_td)))) break;
2543 /* Green path signal opposing the path? Turn to red. */
2544 if (HasPbsSignalOnTrackdir(tile, rev_td) && GetSignalStateByTrackdir(tile, rev_td) == SIGNAL_STATE_GREEN) {
2545 signals_set_to_red.emplace_back(tile, rev_td);
2547 MarkTileDirtyByTile(tile);
2548 }
2549 /* Safe position is all good, path valid and okay. */
2550 return PBSTileInfo(tile, cur_td, true);
2551 }
2552
2553 if (!TryReserveRailTrack(tile, TrackdirToTrack(cur_td))) break;
2554
2555 /* Green path signal opposing the path? Turn to red. */
2556 if (HasPbsSignalOnTrackdir(tile, rev_td) && GetSignalStateByTrackdir(tile, rev_td) == SIGNAL_STATE_GREEN) {
2557 signals_set_to_red.emplace_back(tile, rev_td);
2559 MarkTileDirtyByTile(tile);
2560 }
2561 }
2562
2563 if (ft.err == CFollowTrackRail::EC_OWNER || ft.err == CFollowTrackRail::EC_NO_WAY) {
2564 /* End of line, path valid and okay. */
2565 return PBSTileInfo(ft.old_tile, ft.old_td, true);
2566 }
2567
2568 /* Sorry, can't reserve path, back out. */
2569 tile = origin.tile;
2570 cur_td = origin.trackdir;
2571 TileIndex stopped = ft.old_tile;
2572 Trackdir stopped_td = ft.old_td;
2573 while (tile != stopped || cur_td != stopped_td) {
2574 if (!ft.Follow(tile, cur_td)) break;
2575
2577 ft.new_td_bits &= ~TrackdirCrossesTrackdirs(ft.old_td);
2578 assert(ft.new_td_bits != TRACKDIR_BIT_NONE);
2579 }
2581
2582 tile = ft.new_tile;
2583 cur_td = FindFirstTrackdir(ft.new_td_bits);
2584
2585 UnreserveRailTrack(tile, TrackdirToTrack(cur_td));
2586 }
2587
2588 /* Re-instate green signals we turned to red. */
2589 for (auto [sig_tile, td] : signals_set_to_red) {
2591 }
2592
2593 /* Path invalid. */
2594 return PBSTileInfo();
2595}
2596
2607static bool TryReserveSafeTrack(const Train *v, TileIndex tile, Trackdir td, bool override_railtype)
2608{
2609 return YapfTrainFindNearestSafeTile(v, tile, td, override_railtype);
2610}
2611
2614private:
2615 Train *v;
2616 Order old_order;
2617 TileIndex old_dest_tile;
2618 StationID old_last_station_visited;
2619 VehicleOrderID index;
2620 bool suppress_implicit_orders;
2621 bool restored;
2622
2623public:
2625 v(_v),
2626 old_order(_v->current_order),
2627 old_dest_tile(_v->dest_tile),
2628 old_last_station_visited(_v->last_station_visited),
2629 index(_v->cur_real_order_index),
2630 suppress_implicit_orders(HasBit(_v->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)),
2631 restored(false)
2632 {
2633 }
2634
2638 void Restore()
2639 {
2640 this->v->current_order = this->old_order;
2641 this->v->dest_tile = this->old_dest_tile;
2642 this->v->last_station_visited = this->old_last_station_visited;
2643 AssignBit(this->v->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS, suppress_implicit_orders);
2644 this->restored = true;
2645 }
2646
2651 {
2652 if (!this->restored) this->Restore();
2653 }
2654
2660 bool SwitchToNextOrder(bool skip_first)
2661 {
2662 if (this->v->GetNumOrders() == 0) return false;
2663
2664 if (skip_first) ++this->index;
2665
2666 int depth = 0;
2667
2668 do {
2669 /* Wrap around. */
2670 if (this->index >= this->v->GetNumOrders()) this->index = 0;
2671
2672 Order *order = this->v->GetOrder(this->index);
2673 assert(order != nullptr);
2674
2675 switch (order->GetType()) {
2676 case OT_GOTO_DEPOT:
2677 /* Skip service in depot orders when the train doesn't need service. */
2678 if ((order->GetDepotOrderType() & ODTFB_SERVICE) && !this->v->NeedsServicing()) break;
2679 [[fallthrough]];
2680 case OT_GOTO_STATION:
2681 case OT_GOTO_WAYPOINT:
2682 this->v->current_order = *order;
2683 return UpdateOrderDest(this->v, order, 0, true);
2684 case OT_CONDITIONAL: {
2685 VehicleOrderID next = ProcessConditionalOrder(order, this->v);
2686 if (next != INVALID_VEH_ORDER_ID) {
2687 depth++;
2688 this->index = next;
2689 /* Don't increment next, so no break here. */
2690 continue;
2691 }
2692 break;
2693 }
2694 default:
2695 break;
2696 }
2697 /* Don't increment inside the while because otherwise conditional
2698 * orders can lead to an infinite loop. */
2699 ++this->index;
2700 depth++;
2701 } while (this->index != this->v->cur_real_order_index && depth < this->v->GetNumOrders());
2702
2703 return false;
2704 }
2705};
2706
2707/* choose a track */
2708static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck)
2709{
2710 Track best_track = INVALID_TRACK;
2711 bool do_track_reservation = _settings_game.pf.reserve_paths || force_res;
2712 bool changed_signal = false;
2713 TileIndex final_dest = INVALID_TILE;
2714
2715 assert((tracks & ~TRACK_BIT_MASK) == 0);
2716
2717 if (got_reservation != nullptr) *got_reservation = false;
2718
2719 /* Don't use tracks here as the setting to forbid 90 deg turns might have been switched between reservation and now. */
2720 TrackBits res_tracks = (TrackBits)(GetReservedTrackbits(tile) & DiagdirReachesTracks(enterdir));
2721 /* Do we have a suitable reserved track? */
2722 if (res_tracks != TRACK_BIT_NONE) return FindFirstTrack(res_tracks);
2723
2724 /* Quick return in case only one possible track is available */
2725 if (KillFirstBit(tracks) == TRACK_BIT_NONE) {
2726 Track track = FindFirstTrack(tracks);
2727 /* We need to check for signals only here, as a junction tile can't have signals. */
2728 if (track != INVALID_TRACK && HasPbsSignalOnTrackdir(tile, TrackEnterdirToTrackdir(track, enterdir))) {
2729 do_track_reservation = true;
2730 changed_signal = true;
2732 } else if (!do_track_reservation) {
2733 return track;
2734 }
2735 best_track = track;
2736 }
2737
2738 PBSTileInfo res_dest(tile, INVALID_TRACKDIR, false);
2739 DiagDirection dest_enterdir = enterdir;
2740 if (do_track_reservation) {
2741 res_dest = ExtendTrainReservation(v, &tracks, &dest_enterdir);
2742 if (res_dest.tile == INVALID_TILE) {
2743 /* Reservation failed? */
2744 if (mark_stuck) MarkTrainAsStuck(v);
2745 if (changed_signal) SetSignalStateByTrackdir(tile, TrackEnterdirToTrackdir(best_track, enterdir), SIGNAL_STATE_RED);
2746 return FindFirstTrack(tracks);
2747 }
2748 if (res_dest.okay) {
2749 /* Got a valid reservation that ends at a safe target, quick exit. */
2750 if (got_reservation != nullptr) *got_reservation = true;
2751 if (changed_signal) MarkTileDirtyByTile(tile);
2753 return best_track;
2754 }
2755
2756 /* Check if the train needs service here, so it has a chance to always find a depot.
2757 * Also check if the current order is a service order so we don't reserve a path to
2758 * the destination but instead to the next one if service isn't needed. */
2760 if (v->current_order.IsType(OT_DUMMY) || v->current_order.IsType(OT_CONDITIONAL) || v->current_order.IsType(OT_GOTO_DEPOT)) ProcessOrders(v);
2761 }
2762
2763 /* Save the current train order. The destructor will restore the old order on function exit. */
2764 VehicleOrderSaver orders(v);
2765
2766 /* If the current tile is the destination of the current order and
2767 * a reservation was requested, advance to the next order.
2768 * Don't advance on a depot order as depots are always safe end points
2769 * for a path and no look-ahead is necessary. This also avoids a
2770 * problem with depot orders not part of the order list when the
2771 * order list itself is empty. */
2772 if (v->current_order.IsType(OT_LEAVESTATION)) {
2773 orders.SwitchToNextOrder(false);
2774 } else if (v->current_order.IsType(OT_LOADING) || (!v->current_order.IsType(OT_GOTO_DEPOT) && (
2775 v->current_order.IsType(OT_GOTO_STATION) ?
2777 v->tile == v->dest_tile))) {
2778 orders.SwitchToNextOrder(true);
2779 }
2780
2781 if (res_dest.tile != INVALID_TILE && !res_dest.okay) {
2782 /* Pathfinders are able to tell that route was only 'guessed'. */
2783 bool path_found = true;
2784 TileIndex new_tile = res_dest.tile;
2785
2786 Track next_track = DoTrainPathfind(v, new_tile, dest_enterdir, tracks, path_found, do_track_reservation, &res_dest, &final_dest);
2787 if (new_tile == tile) best_track = next_track;
2788 v->HandlePathfindingResult(path_found);
2789 }
2790
2791 /* No track reservation requested -> finished. */
2792 if (!do_track_reservation) return best_track;
2793
2794 /* A path was found, but could not be reserved. */
2795 if (res_dest.tile != INVALID_TILE && !res_dest.okay) {
2796 if (mark_stuck) MarkTrainAsStuck(v);
2798 return best_track;
2799 }
2800
2801 /* No possible reservation target found, we are probably lost. */
2802 if (res_dest.tile == INVALID_TILE) {
2803 /* Try to find any safe destination. */
2805 if (TryReserveSafeTrack(v, origin.tile, origin.trackdir, false)) {
2806 TrackBits res = GetReservedTrackbits(tile) & DiagdirReachesTracks(enterdir);
2807 best_track = FindFirstTrack(res);
2809 if (got_reservation != nullptr) *got_reservation = true;
2810 if (changed_signal) MarkTileDirtyByTile(tile);
2811 } else {
2813 if (mark_stuck) MarkTrainAsStuck(v);
2814 }
2815 return best_track;
2816 }
2817
2818 if (got_reservation != nullptr) *got_reservation = true;
2819
2820 /* Reservation target found and free, check if it is safe. */
2821 while (!IsSafeWaitingPosition(v, res_dest.tile, res_dest.trackdir, true, _settings_game.pf.forbid_90_deg)) {
2822 /* Extend reservation until we have found a safe position. */
2823 DiagDirection exitdir = TrackdirToExitdir(res_dest.trackdir);
2824 TileIndex next_tile = TileAddByDiagDir(res_dest.tile, exitdir);
2826 if (Rail90DegTurnDisallowed(GetTileRailType(res_dest.tile), GetTileRailType(next_tile))) {
2827 reachable &= ~TrackCrossesTracks(TrackdirToTrack(res_dest.trackdir));
2828 }
2829
2830 /* Get next order with destination. */
2831 if (orders.SwitchToNextOrder(true)) {
2832 PBSTileInfo cur_dest;
2833 bool path_found;
2834 DoTrainPathfind(v, next_tile, exitdir, reachable, path_found, true, &cur_dest, nullptr);
2835 if (cur_dest.tile != INVALID_TILE) {
2836 res_dest = cur_dest;
2837 if (res_dest.okay) continue;
2838 /* Path found, but could not be reserved. */
2840 if (mark_stuck) MarkTrainAsStuck(v);
2841 if (got_reservation != nullptr) *got_reservation = false;
2842 changed_signal = false;
2843 break;
2844 }
2845 }
2846 /* No order or no safe position found, try any position. */
2847 if (!TryReserveSafeTrack(v, res_dest.tile, res_dest.trackdir, true)) {
2849 if (mark_stuck) MarkTrainAsStuck(v);
2850 if (got_reservation != nullptr) *got_reservation = false;
2851 changed_signal = false;
2852 }
2853 break;
2854 }
2855
2857
2858 if (changed_signal) MarkTileDirtyByTile(tile);
2859
2860 orders.Restore();
2861 if (v->current_order.IsType(OT_GOTO_DEPOT) &&
2863 final_dest != INVALID_TILE && IsRailDepotTile(final_dest)) {
2865 v->dest_tile = final_dest;
2867 }
2868
2869 return best_track;
2870}
2871
2880bool TryPathReserve(Train *v, bool mark_as_stuck, bool first_tile_okay)
2881{
2882 assert(v->IsFrontEngine());
2883
2884 /* We have to handle depots specially as the track follower won't look
2885 * at the depot tile itself but starts from the next tile. If we are still
2886 * inside the depot, a depot reservation can never be ours. */
2887 if (v->track == TRACK_BIT_DEPOT) {
2888 if (HasDepotReservation(v->tile)) {
2889 if (mark_as_stuck) MarkTrainAsStuck(v);
2890 return false;
2891 } else {
2892 /* Depot not reserved, but the next tile might be. */
2894 if (HasReservedTracks(next_tile, DiagdirReachesTracks(GetRailDepotDirection(v->tile)))) return false;
2895 }
2896 }
2897
2898 Vehicle *other_train = nullptr;
2899 PBSTileInfo origin = FollowTrainReservation(v, &other_train);
2900 /* The path we are driving on is already blocked by some other train.
2901 * This can only happen in certain situations when mixing path and
2902 * block signals or when changing tracks and/or signals.
2903 * Exit here as doing any further reservations will probably just
2904 * make matters worse. */
2905 if (other_train != nullptr && other_train->index != v->index) {
2906 if (mark_as_stuck) MarkTrainAsStuck(v);
2907 return false;
2908 }
2909 /* If we have a reserved path and the path ends at a safe tile, we are finished already. */
2910 if (origin.okay && (v->tile != origin.tile || first_tile_okay)) {
2911 /* Can't be stuck then. */
2913 v->flags.Reset(VehicleRailFlag::Stuck);
2914 return true;
2915 }
2916
2917 /* If we are in a depot, tentatively reserve the depot. */
2918 if (v->track == TRACK_BIT_DEPOT) {
2919 SetDepotReservation(v->tile, true);
2921 }
2922
2923 DiagDirection exitdir = TrackdirToExitdir(origin.trackdir);
2924 TileIndex new_tile = TileAddByDiagDir(origin.tile, exitdir);
2926
2927 if (Rail90DegTurnDisallowed(GetTileRailType(origin.tile), GetTileRailType(new_tile))) reachable &= ~TrackCrossesTracks(TrackdirToTrack(origin.trackdir));
2928
2929 bool res_made = false;
2930 ChooseTrainTrack(v, new_tile, exitdir, reachable, true, &res_made, mark_as_stuck);
2931
2932 if (!res_made) {
2933 /* Free the depot reservation as well. */
2934 if (v->track == TRACK_BIT_DEPOT) SetDepotReservation(v->tile, false);
2935 return false;
2936 }
2937
2938 if (v->flags.Test(VehicleRailFlag::Stuck)) {
2939 v->wait_counter = 0;
2941 }
2942 v->flags.Reset(VehicleRailFlag::Stuck);
2943 return true;
2944}
2945
2946
2947static bool CheckReverseTrain(const Train *v)
2948{
2950 v->track == TRACK_BIT_DEPOT || v->track == TRACK_BIT_WORMHOLE ||
2951 !(v->direction & 1)) {
2952 return false;
2953 }
2954
2955 assert(v->track != TRACK_BIT_NONE);
2956
2957 return YapfTrainCheckReverse(v);
2958}
2959
2966{
2967 if (station == this->last_station_visited) this->last_station_visited = StationID::Invalid();
2968
2969 const Station *st = Station::Get(station);
2971 /* The destination station has no trainstation tiles. */
2973 return TileIndex{};
2974 }
2975
2976 return st->xy;
2977}
2978
2981{
2982 Train *v = this;
2983 do {
2984 v->colourmap = PAL_NONE;
2985 v->UpdateViewport(true, false);
2986 } while ((v = v->Next()) != nullptr);
2987
2988 /* need to update acceleration and cached values since the goods on the train changed. */
2989 this->CargoChanged();
2990 this->UpdateAcceleration();
2991}
2992
3001{
3003 default: NOT_REACHED();
3004 case AM_ORIGINAL:
3005 return this->DoUpdateSpeed(this->acceleration * (this->GetAccelerationStatus() == AS_BRAKE ? -4 : 2), 0, this->GetCurrentMaxSpeed());
3006
3007 case AM_REALISTIC:
3008 return this->DoUpdateSpeed(this->GetAcceleration(), this->GetAccelerationStatus() == AS_BRAKE ? 0 : 2, this->GetCurrentMaxSpeed());
3009 }
3010}
3011
3017static void TrainEnterStation(Train *v, StationID station)
3018{
3019 v->last_station_visited = station;
3020
3021 /* check if a train ever visited this station before */
3022 Station *st = Station::Get(station);
3023 if (!(st->had_vehicle_of_type & HVOT_TRAIN)) {
3024 st->had_vehicle_of_type |= HVOT_TRAIN;
3026 GetEncodedString(STR_NEWS_FIRST_TRAIN_ARRIVAL, st->index),
3028 v->index,
3029 st->index
3030 );
3031 AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
3032 Game::NewEvent(new ScriptEventStationFirstVehicle(st->index, v->index));
3033 }
3034
3035 v->force_proceed = TFP_NONE;
3037
3038 v->BeginLoading();
3039
3041 TriggerStationAnimation(st, v->tile, StationAnimationTrigger::VehicleArrives);
3042}
3043
3044/* Check if the vehicle is compatible with the specified tile */
3045static inline bool CheckCompatibleRail(const Train *v, TileIndex tile)
3046{
3047 return IsTileOwner(tile, v->owner) &&
3048 (!v->IsFrontEngine() || v->compatible_railtypes.Test(GetRailType(tile)));
3049}
3050
3053 uint8_t small_turn;
3054 uint8_t large_turn;
3055 uint8_t z_up;
3056 uint8_t z_down;
3057};
3058
3061 /* normal accel */
3062 {256 / 4, 256 / 2, 256 / 4, 2},
3063 {256 / 4, 256 / 2, 256 / 4, 2},
3064 {0, 256 / 2, 256 / 4, 2},
3065};
3066
3072static inline void AffectSpeedByZChange(Train *v, int old_z)
3073{
3074 if (old_z == v->z_pos || _settings_game.vehicle.train_acceleration_model != AM_ORIGINAL) return;
3075
3076 const AccelerationSlowdownParams *asp = &_accel_slowdown[static_cast<int>(v->GetAccelerationType())];
3077
3078 if (old_z < v->z_pos) {
3079 v->cur_speed -= (v->cur_speed * asp->z_up >> 8);
3080 } else {
3081 uint16_t spd = v->cur_speed + asp->z_down;
3082 if (spd <= v->gcache.cached_max_track_speed) v->cur_speed = spd;
3083 }
3084}
3085
3086static bool TrainMovedChangeSignals(TileIndex tile, DiagDirection dir)
3087{
3088 if (IsTileType(tile, MP_RAILWAY) &&
3091 Trackdir trackdir = FindFirstTrackdir(tracks);
3092 if (UpdateSignalsOnSegment(tile, TrackdirToExitdir(trackdir), GetTileOwner(tile)) == SIGSEG_PBS && HasSignalOnTrackdir(tile, trackdir)) {
3093 /* A PBS block with a non-PBS signal facing us? */
3094 if (!IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) return true;
3095 }
3096 }
3097 return false;
3098}
3099
3102{
3103 for (const Train *u = this; u != nullptr; u = u->Next()) {
3104 switch (u->track) {
3105 case TRACK_BIT_WORMHOLE:
3107 break;
3108 case TRACK_BIT_DEPOT:
3109 break;
3110 default:
3112 break;
3113 }
3114 }
3115}
3116
3123uint Train::Crash(bool flooded)
3124{
3125 uint victims = 0;
3126 if (this->IsFrontEngine()) {
3127 victims += 2; // driver
3128
3129 /* Remove the reserved path in front of the train if it is not stuck.
3130 * Also clear all reserved tracks the train is currently on. */
3131 if (!this->flags.Test(VehicleRailFlag::Stuck)) FreeTrainTrackReservation(this);
3132 for (const Train *v = this; v != nullptr; v = v->Next()) {
3134 if (IsTileType(v->tile, MP_TUNNELBRIDGE)) {
3135 /* ClearPathReservation will not free the wormhole exit
3136 * if the train has just entered the wormhole. */
3138 }
3139 }
3140
3141 /* we may need to update crossing we were approaching,
3142 * but must be updated after the train has been marked crashed */
3143 TileIndex crossing = TrainApproachingCrossingTile(this);
3144 if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing);
3145
3146 /* Remove the loading indicators (if any) */
3148 }
3149
3150 victims += this->GroundVehicleBase::Crash(flooded);
3151
3152 this->crash_anim_pos = flooded ? 4000 : 1; // max 4440, disappear pretty fast when flooded
3153 return victims;
3154}
3155
3162static uint TrainCrashed(Train *v)
3163{
3164 uint victims = 0;
3165
3166 /* do not crash train twice */
3167 if (!v->vehstatus.Test(VehState::Crashed)) {
3168 victims = v->Crash();
3169 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_TRAIN, victims, v->owner));
3170 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_TRAIN, victims, v->owner));
3171 }
3172
3173 /* Try to re-reserve track under already crashed train too.
3174 * Crash() clears the reservation! */
3176
3177 return victims;
3178}
3179
3187{
3188 /* not a train or in depot */
3189 if (v->type != VEH_TRAIN || Train::From(v)->track == TRACK_BIT_DEPOT) return 0;
3190
3191 /* do not crash into trains of another company. */
3192 if (v->owner != t->owner) return 0;
3193
3194 /* get first vehicle now to make most usual checks faster */
3195 Train *coll = Train::From(v)->First();
3196
3197 /* can't collide with own wagons */
3198 if (coll == t) return 0;
3199
3200 int x_diff = v->x_pos - t->x_pos;
3201 int y_diff = v->y_pos - t->y_pos;
3202
3203 /* Do fast calculation to check whether trains are not in close vicinity
3204 * and quickly reject trains distant enough for any collision.
3205 * Differences are shifted by 7, mapping range [-7 .. 8] into [0 .. 15]
3206 * Differences are then ORed and then we check for any higher bits */
3207 uint hash = (y_diff + 7) | (x_diff + 7);
3208 if (hash & ~15) return 0;
3209
3210 /* Slower check using multiplication */
3211 int min_diff = (Train::From(v)->gcache.cached_veh_length + 1) / 2 + (t->gcache.cached_veh_length + 1) / 2 - 1;
3212 if (x_diff * x_diff + y_diff * y_diff > min_diff * min_diff) return 0;
3213
3214 /* Happens when there is a train under bridge next to bridge head */
3215 if (abs(v->z_pos - t->z_pos) > 5) return 0;
3216
3217 /* crash both trains */
3218 return TrainCrashed(t) + TrainCrashed(coll);
3219}
3220
3229{
3230 /* can't collide in depot */
3231 if (v->track == TRACK_BIT_DEPOT) return false;
3232
3233 assert(v->track == TRACK_BIT_WORMHOLE || TileVirtXY(v->x_pos, v->y_pos) == v->tile);
3234
3235 uint num_victims = 0;
3236
3237 /* find colliding vehicles */
3238 if (v->track == TRACK_BIT_WORMHOLE) {
3239 for (Vehicle *u : VehiclesOnTile(v->tile)) {
3240 num_victims += CheckTrainCollision(u, v);
3241 }
3243 num_victims += CheckTrainCollision(u, v);
3244 }
3245 } else {
3246 for (Vehicle *u : VehiclesNearTileXY(v->x_pos, v->y_pos, 7)) {
3247 num_victims += CheckTrainCollision(u, v);
3248 }
3249 }
3250
3251 /* any dead -> no crash */
3252 if (num_victims == 0) return false;
3253
3254 AddTileNewsItem(GetEncodedString(STR_NEWS_TRAIN_CRASH, num_victims), NewsType::Accident, v->tile);
3255
3256 ModifyStationRatingAround(v->tile, v->owner, -160, 30);
3257 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_13_TRAIN_COLLISION, v);
3258 return true;
3259}
3260
3268bool TrainController(Train *v, Vehicle *nomove, bool reverse)
3269{
3270 Train *first = v->First();
3271 Train *prev;
3272 bool direction_changed = false; // has direction of any part changed?
3273
3274 /* For every vehicle after and including the given vehicle */
3275 for (prev = v->Previous(); v != nomove; prev = v, v = v->Next()) {
3276 DiagDirection enterdir = DIAGDIR_BEGIN;
3277 bool update_signals_crossing = false; // will we update signals or crossing state?
3278
3280 if (v->track != TRACK_BIT_WORMHOLE) {
3281 /* Not inside tunnel */
3282 if (gp.old_tile == gp.new_tile) {
3283 /* Staying in the old tile */
3284 if (v->track == TRACK_BIT_DEPOT) {
3285 /* Inside depot */
3286 gp.x = v->x_pos;
3287 gp.y = v->y_pos;
3288 } else {
3289 /* Not inside depot */
3290
3291 /* Reverse when we are at the end of the track already, do not move to the new position */
3292 if (v->IsFrontEngine() && !TrainCheckIfLineEnds(v, reverse)) return false;
3293
3294 auto vets = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
3295 if (vets.Test(VehicleEnterTileState::CannotEnter)) {
3296 goto invalid_rail;
3297 }
3299 /* The new position is the end of the platform */
3301 }
3302 }
3303 } else {
3304 /* A new tile is about to be entered. */
3305
3306 /* Determine what direction we're entering the new tile from */
3307 enterdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile);
3308 assert(IsValidDiagDirection(enterdir));
3309
3310 /* Get the status of the tracks in the new tile and mask
3311 * away the bits that aren't reachable. */
3312 TrackStatus ts = GetTileTrackStatus(gp.new_tile, TRANSPORT_RAIL, 0, ReverseDiagDir(enterdir));
3313 TrackdirBits reachable_trackdirs = DiagdirReachesTrackdirs(enterdir);
3314
3315 TrackdirBits trackdirbits = TrackStatusToTrackdirBits(ts) & reachable_trackdirs;
3316 TrackBits red_signals = TrackdirBitsToTrackBits(TrackStatusToRedSignals(ts) & reachable_trackdirs);
3317
3318 TrackBits bits = TrackdirBitsToTrackBits(trackdirbits);
3319 if (Rail90DegTurnDisallowed(GetTileRailType(gp.old_tile), GetTileRailType(gp.new_tile)) && prev == nullptr) {
3320 /* We allow wagons to make 90 deg turns, because forbid_90_deg
3321 * can be switched on halfway a turn */
3322 bits &= ~TrackCrossesTracks(FindFirstTrack(v->track));
3323 }
3324
3325 if (bits == TRACK_BIT_NONE) goto invalid_rail;
3326
3327 /* Check if the new tile constrains tracks that are compatible
3328 * with the current train, if not, bail out. */
3329 if (!CheckCompatibleRail(v, gp.new_tile)) goto invalid_rail;
3330
3331 TrackBits chosen_track;
3332 if (prev == nullptr) {
3333 /* Currently the locomotive is active. Determine which one of the
3334 * available tracks to choose */
3335 chosen_track = TrackToTrackBits(ChooseTrainTrack(v, gp.new_tile, enterdir, bits, false, nullptr, true));
3336 assert(chosen_track & (bits | GetReservedTrackbits(gp.new_tile)));
3337
3338 if (v->force_proceed != TFP_NONE && IsPlainRailTile(gp.new_tile) && HasSignals(gp.new_tile)) {
3339 /* For each signal we find decrease the counter by one.
3340 * We start at two, so the first signal we pass decreases
3341 * this to one, then if we reach the next signal it is
3342 * decreased to zero and we won't pass that new signal. */
3343 Trackdir dir = FindFirstTrackdir(trackdirbits);
3344 if (HasSignalOnTrackdir(gp.new_tile, dir) ||
3346 GetSignalType(gp.new_tile, TrackdirToTrack(dir)) != SIGTYPE_PBS)) {
3347 /* However, we do not want to be stopped by PBS signals
3348 * entered via the back. */
3349 v->force_proceed = (v->force_proceed == TFP_SIGNAL) ? TFP_STUCK : TFP_NONE;
3351 }
3352 }
3353
3354 /* Check if it's a red signal and that force proceed is not clicked. */
3355 if ((red_signals & chosen_track) && v->force_proceed == TFP_NONE) {
3356 /* In front of a red signal */
3357 Trackdir i = FindFirstTrackdir(trackdirbits);
3358
3359 /* Don't handle stuck trains here. */
3360 if (v->flags.Test(VehicleRailFlag::Stuck)) return false;
3361
3363 v->cur_speed = 0;
3364 v->subspeed = 0;
3365 v->progress = 255; // make sure that every bit of acceleration will hit the signal again, so speed stays 0.
3367 } else if (HasSignalOnTrackdir(gp.new_tile, i)) {
3368 v->cur_speed = 0;
3369 v->subspeed = 0;
3370 v->progress = 255; // make sure that every bit of acceleration will hit the signal again, so speed stays 0.
3372 DiagDirection exitdir = TrackdirToExitdir(i);
3373 TileIndex o_tile = TileAddByDiagDir(gp.new_tile, exitdir);
3374
3375 exitdir = ReverseDiagDir(exitdir);
3376
3377 /* check if a train is waiting on the other side */
3378 if (!HasVehicleOnTile(o_tile, [&exitdir](const Vehicle *u) {
3379 if (u->type != VEH_TRAIN || u->vehstatus.Test(VehState::Crashed)) return false;
3380 const Train *t = Train::From(u);
3381
3382 /* not front engine of a train, inside wormhole or depot, crashed */
3383 if (!t->IsFrontEngine() || !(t->track & TRACK_BIT_MASK)) return false;
3384
3385 if (t->cur_speed > 5 || VehicleExitDir(t->direction, t->track) != exitdir) return false;
3386
3387 return true;
3388 })) return false;
3389 }
3390 }
3391
3392 /* If we would reverse but are currently in a PBS block and
3393 * reversing of stuck trains is disabled, don't reverse.
3394 * This does not apply if the reason for reversing is a one-way
3395 * signal blocking us, because a train would then be stuck forever. */
3397 UpdateSignalsOnSegment(v->tile, enterdir, v->owner) == SIGSEG_PBS) {
3398 v->wait_counter = 0;
3399 return false;
3400 }
3401 goto reverse_train_direction;
3402 } else {
3403 TryReserveRailTrack(gp.new_tile, TrackBitsToTrack(chosen_track), false);
3404 }
3405 } else {
3406 /* The wagon is active, simply follow the prev vehicle. */
3407 if (prev->tile == gp.new_tile) {
3408 /* Choose the same track as prev */
3409 if (prev->track == TRACK_BIT_WORMHOLE) {
3410 /* Vehicles entering tunnels enter the wormhole earlier than for bridges.
3411 * However, just choose the track into the wormhole. */
3412 assert(IsTunnel(prev->tile));
3413 chosen_track = bits;
3414 } else {
3415 chosen_track = prev->track;
3416 }
3417 } else {
3418 /* Choose the track that leads to the tile where prev is.
3419 * This case is active if 'prev' is already on the second next tile, when 'v' just enters the next tile.
3420 * I.e. when the tile between them has only space for a single vehicle like
3421 * 1) horizontal/vertical track tiles and
3422 * 2) some orientations of tunnel entries, where the vehicle is already inside the wormhole at 8/16 from the tile edge.
3423 * Is also the train just reversing, the wagon inside the tunnel is 'on' the tile of the opposite tunnel entry.
3424 */
3425 static const TrackBits _connecting_track[DIAGDIR_END][DIAGDIR_END] = {
3430 };
3431 DiagDirection exitdir = DiagdirBetweenTiles(gp.new_tile, prev->tile);
3432 assert(IsValidDiagDirection(exitdir));
3433 chosen_track = _connecting_track[enterdir][exitdir];
3434 }
3435 chosen_track &= bits;
3436 }
3437
3438 /* Make sure chosen track is a valid track */
3439 assert(
3440 chosen_track == TRACK_BIT_X || chosen_track == TRACK_BIT_Y ||
3441 chosen_track == TRACK_BIT_UPPER || chosen_track == TRACK_BIT_LOWER ||
3442 chosen_track == TRACK_BIT_LEFT || chosen_track == TRACK_BIT_RIGHT);
3443
3444 /* Update XY to reflect the entrance to the new tile, and select the direction to use */
3445 const uint8_t *b = _initial_tile_subcoord[FindFirstBit(chosen_track)][enterdir];
3446 gp.x = (gp.x & ~0xF) | b[0];
3447 gp.y = (gp.y & ~0xF) | b[1];
3448 Direction chosen_dir = (Direction)b[2];
3449
3450 /* Call the landscape function and tell it that the vehicle entered the tile */
3451 auto vets = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
3452 if (vets.Test(VehicleEnterTileState::CannotEnter)) {
3453 goto invalid_rail;
3454 }
3455
3457 Track track = FindFirstTrack(chosen_track);
3458 Trackdir tdir = TrackDirectionToTrackdir(track, chosen_dir);
3459 if (v->IsFrontEngine() && HasPbsSignalOnTrackdir(gp.new_tile, tdir)) {
3462 }
3463
3464 /* Clear any track reservation when the last vehicle leaves the tile */
3465 if (v->Next() == nullptr) ClearPathReservation(v, v->tile, v->GetVehicleTrackdir());
3466
3467 v->tile = gp.new_tile;
3468
3470 v->First()->ConsistChanged(CCF_TRACK);
3471 }
3472
3473 v->track = chosen_track;
3474 assert(v->track);
3475 }
3476
3477 /* We need to update signal status, but after the vehicle position hash
3478 * has been updated by UpdateInclination() */
3479 update_signals_crossing = true;
3480
3481 if (chosen_dir != v->direction) {
3482 if (prev == nullptr && _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) {
3483 const AccelerationSlowdownParams *asp = &_accel_slowdown[static_cast<int>(v->GetAccelerationType())];
3484 DirDiff diff = DirDifference(v->direction, chosen_dir);
3485 v->cur_speed -= (diff == DIRDIFF_45RIGHT || diff == DIRDIFF_45LEFT ? asp->small_turn : asp->large_turn) * v->cur_speed >> 8;
3486 }
3487 direction_changed = true;
3488 v->direction = chosen_dir;
3489 }
3490
3491 if (v->IsFrontEngine()) {
3492 v->wait_counter = 0;
3493
3494 /* If we are approaching a crossing that is reserved, play the sound now. */
3496 if (crossing != INVALID_TILE && HasCrossingReservation(crossing) && _settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, crossing);
3497
3498 /* Always try to extend the reservation when entering a tile. */
3500 }
3501
3503 /* The new position is the location where we want to stop */
3505 }
3506 }
3507 } else {
3509 /* Perform look-ahead on tunnel exit. */
3510 if (v->IsFrontEngine()) {
3513 }
3514 /* Prevent v->UpdateInclination() being called with wrong parameters.
3515 * This could happen if the train was reversed inside the tunnel/bridge. */
3516 if (gp.old_tile == gp.new_tile) {
3518 }
3519 } else {
3520 v->x_pos = gp.x;
3521 v->y_pos = gp.y;
3522 v->UpdatePosition();
3523 if (!v->vehstatus.Test(VehState::Hidden)) v->Vehicle::UpdateViewport(true);
3524 continue;
3525 }
3526 }
3527
3528 /* update image of train, as well as delta XY */
3529 v->UpdateDeltaXY();
3530
3531 v->x_pos = gp.x;
3532 v->y_pos = gp.y;
3533 v->UpdatePosition();
3534
3535 /* update the Z position of the vehicle */
3536 int old_z = v->UpdateInclination(gp.new_tile != gp.old_tile, false);
3537
3538 if (prev == nullptr) {
3539 /* This is the first vehicle in the train */
3540 AffectSpeedByZChange(v, old_z);
3541 }
3542
3543 if (update_signals_crossing) {
3544 if (v->IsFrontEngine()) {
3545 if (TrainMovedChangeSignals(gp.new_tile, enterdir)) {
3546 /* We are entering a block with PBS signals right now, but
3547 * not through a PBS signal. This means we don't have a
3548 * reservation right now. As a conventional signal will only
3549 * ever be green if no other train is in the block, getting
3550 * a path should always be possible. If the player built
3551 * such a strange network that it is not possible, the train
3552 * will be marked as stuck and the player has to deal with
3553 * the problem. */
3554 if ((!HasReservedTracks(gp.new_tile, v->track) &&
3555 !TryReserveRailTrack(gp.new_tile, FindFirstTrack(v->track))) ||
3556 !TryPathReserve(v)) {
3558 }
3559 }
3560 }
3561
3562 /* Signals can only change when the first
3563 * (above) or the last vehicle moves. */
3564 if (v->Next() == nullptr) {
3565 TrainMovedChangeSignals(gp.old_tile, ReverseDiagDir(enterdir));
3567 }
3568 }
3569
3570 /* Do not check on every tick to save some computing time. */
3572 }
3573
3574 if (direction_changed) first->tcache.cached_max_curve_speed = first->GetCurveSpeedLimit();
3575
3576 return true;
3577
3578invalid_rail:
3579 /* We've reached end of line?? */
3580 if (prev != nullptr) FatalError("Disconnecting train");
3581
3582reverse_train_direction:
3583 if (reverse) {
3584 v->wait_counter = 0;
3585 v->cur_speed = 0;
3586 v->subspeed = 0;
3588 }
3589
3590 return false;
3591}
3592
3593static bool IsRailStationPlatformOccupied(TileIndex tile)
3594{
3596
3597 for (TileIndex t = tile; IsCompatibleTrainStationTile(t, tile); t -= delta) {
3598 if (HasVehicleOnTile(t, IsTrain)) return true;
3599 }
3600 for (TileIndex t = tile + delta; IsCompatibleTrainStationTile(t, tile); t += delta) {
3601 if (HasVehicleOnTile(t, IsTrain)) return true;
3602 }
3603
3604 return false;
3605}
3606
3614static void DeleteLastWagon(Train *v)
3615{
3616 Train *first = v->First();
3617
3618 /* Go to the last wagon and delete the link pointing there
3619 * new_last is then the one-before-last wagon, and v the last
3620 * one which will physically be removed */
3621 Train *new_last = v;
3622 for (; v->Next() != nullptr; v = v->Next()) new_last = v;
3623 new_last->SetNext(nullptr);
3624
3625 if (first != v) {
3626 /* Recalculate cached train properties */
3628 /* Update the depot window if the first vehicle is in depot -
3629 * if v == first, then it is updated in PreDestructor() */
3630 if (first->track == TRACK_BIT_DEPOT) {
3632 }
3633 v->last_station_visited = first->last_station_visited; // for PreDestructor
3634 }
3635
3636 /* 'v' shouldn't be accessed after it has been deleted */
3637 TrackBits trackbits = v->track;
3638 TileIndex tile = v->tile;
3639 Owner owner = v->owner;
3640
3641 delete v;
3642 v = nullptr; // make sure nobody will try to read 'v' anymore
3643
3644 if (trackbits == TRACK_BIT_WORMHOLE) {
3645 /* Vehicle is inside a wormhole, v->track contains no useful value then. */
3647 }
3648
3649 Track track = TrackBitsToTrack(trackbits);
3650 if (HasReservedTracks(tile, trackbits)) {
3651 UnreserveRailTrack(tile, track);
3652
3653 /* If there are still crashed vehicles on the tile, give the track reservation to them */
3654 TrackBits remaining_trackbits = TRACK_BIT_NONE;
3655 for (const Vehicle *u : VehiclesOnTile(tile)) {
3656 if (u->type != VEH_TRAIN || !u->vehstatus.Test(VehState::Crashed)) continue;
3657 TrackBits train_tbits = Train::From(u)->track;
3658 if (train_tbits == TRACK_BIT_WORMHOLE) {
3659 /* Vehicle is inside a wormhole, u->track contains no useful value then. */
3660 remaining_trackbits |= DiagDirToDiagTrackBits(GetTunnelBridgeDirection(u->tile));
3661 } else if (train_tbits != TRACK_BIT_DEPOT) {
3662 remaining_trackbits |= train_tbits;
3663 }
3664 }
3665
3666 /* It is important that these two are the first in the loop, as reservation cannot deal with every trackbit combination */
3667 assert(TRACK_BEGIN == TRACK_X && TRACK_Y == TRACK_BEGIN + 1);
3668 for (Track t : SetTrackBitIterator(remaining_trackbits)) TryReserveRailTrack(tile, t);
3669 }
3670
3671 /* check if the wagon was on a road/rail-crossing */
3673
3674 if (IsRailStationTile(tile)) {
3675 bool occupied = IsRailStationPlatformOccupied(tile);
3677 SetRailStationPlatformReservation(tile, dir, occupied);
3679 }
3680
3681 /* Update signals */
3682 if (IsTileType(tile, MP_TUNNELBRIDGE) || IsRailDepotTile(tile)) {
3684 } else {
3685 SetSignalsOnBothDir(tile, track, owner);
3686 }
3687}
3688
3694{
3695 static const DirDiff delta[] = {
3697 };
3698
3699 do {
3700 /* We don't need to twist around vehicles if they're not visible */
3701 if (!v->vehstatus.Test(VehState::Hidden)) {
3702 v->direction = ChangeDir(v->direction, delta[GB(Random(), 0, 2)]);
3703 /* Refrain from updating the z position of the vehicle when on
3704 * a bridge, because UpdateInclination() will put the vehicle under
3705 * the bridge in that case */
3706 if (v->track != TRACK_BIT_WORMHOLE) {
3707 v->UpdatePosition();
3708 v->UpdateInclination(false, true);
3709 } else {
3710 v->UpdateViewport(false, true);
3711 }
3712 }
3713 } while ((v = v->Next()) != nullptr);
3714}
3715
3722{
3723 int state = ++v->crash_anim_pos;
3724
3725 if (state == 4 && !v->vehstatus.Test(VehState::Hidden)) {
3727 }
3728
3729 uint32_t r;
3730 if (state <= 200 && Chance16R(1, 7, r)) {
3731 int index = (r * 10 >> 16);
3732
3733 Vehicle *u = v;
3734 do {
3735 if (--index < 0) {
3736 r = Random();
3737
3739 GB(r, 8, 3) + 2,
3740 GB(r, 16, 3) + 2,
3741 GB(r, 0, 3) + 5,
3743 break;
3744 }
3745 } while ((u = u->Next()) != nullptr);
3746 }
3747
3748 if (state <= 240 && !(v->tick_counter & 3)) ChangeTrainDirRandomly(v);
3749
3750 if (state >= 4440 && !(v->tick_counter & 0x1F)) {
3751 bool ret = v->Next() != nullptr;
3752 DeleteLastWagon(v);
3753 return ret;
3754 }
3755
3756 return true;
3757}
3758
3760static const uint16_t _breakdown_speeds[16] = {
3761 225, 210, 195, 180, 165, 150, 135, 120, 105, 90, 75, 60, 45, 30, 15, 15
3762};
3763
3764
3773static bool TrainApproachingLineEnd(Train *v, bool signal, bool reverse)
3774{
3775 /* Calc position within the current tile */
3776 uint x = v->x_pos & 0xF;
3777 uint y = v->y_pos & 0xF;
3778
3779 /* for diagonal directions, 'x' will be 0..15 -
3780 * for other directions, it will be 1, 3, 5, ..., 15 */
3781 switch (v->direction) {
3782 case DIR_N : x = ~x + ~y + 25; break;
3783 case DIR_NW: x = y; [[fallthrough]];
3784 case DIR_NE: x = ~x + 16; break;
3785 case DIR_E : x = ~x + y + 9; break;
3786 case DIR_SE: x = y; break;
3787 case DIR_S : x = x + y - 7; break;
3788 case DIR_W : x = ~y + x + 9; break;
3789 default: break;
3790 }
3791
3792 /* Do not reverse when approaching red signal. Make sure the vehicle's front
3793 * does not cross the tile boundary when we do reverse, but as the vehicle's
3794 * location is based on their center, use half a vehicle's length as offset.
3795 * Multiply the half-length by two for straight directions to compensate that
3796 * we only get odd x offsets there. */
3797 if (!signal && x + (v->gcache.cached_veh_length + 1) / 2 * (IsDiagonalDirection(v->direction) ? 1 : 2) >= TILE_SIZE) {
3798 /* we are too near the tile end, reverse now */
3799 v->cur_speed = 0;
3800 if (reverse) ReverseTrainDirection(v);
3801 return false;
3802 }
3803
3804 /* slow down */
3806 uint16_t break_speed = _breakdown_speeds[x & 0xF];
3807 if (break_speed < v->cur_speed) v->cur_speed = break_speed;
3808
3809 return true;
3810}
3811
3812
3818static bool TrainCanLeaveTile(const Train *v)
3819{
3820 /* Exit if inside a tunnel/bridge or a depot */
3821 if (v->track == TRACK_BIT_WORMHOLE || v->track == TRACK_BIT_DEPOT) return false;
3822
3823 TileIndex tile = v->tile;
3824
3825 /* entering a tunnel/bridge? */
3826 if (IsTileType(tile, MP_TUNNELBRIDGE)) {
3828 if (DiagDirToDir(dir) == v->direction) return false;
3829 }
3830
3831 /* entering a depot? */
3832 if (IsRailDepotTile(tile)) {
3834 if (DiagDirToDir(dir) == v->direction) return false;
3835 }
3836
3837 return true;
3838}
3839
3840
3849{
3850 assert(v->IsFrontEngine());
3851 assert(!v->vehstatus.Test(VehState::Crashed));
3852
3853 if (!TrainCanLeaveTile(v)) return INVALID_TILE;
3854
3855 DiagDirection dir = VehicleExitDir(v->direction, v->track);
3856 TileIndex tile = v->tile + TileOffsByDiagDir(dir);
3857
3858 /* not a crossing || wrong axis || unusable rail (wrong type or owner) */
3859 if (!IsLevelCrossingTile(tile) || DiagDirToAxis(dir) == GetCrossingRoadAxis(tile) ||
3860 !CheckCompatibleRail(v, tile)) {
3861 return INVALID_TILE;
3862 }
3863
3864 return tile;
3865}
3866
3867
3875static bool TrainCheckIfLineEnds(Train *v, bool reverse)
3876{
3877 /* First, handle broken down train */
3878
3879 int t = v->breakdown_ctr;
3880 if (t > 1) {
3882
3883 uint16_t break_speed = _breakdown_speeds[GB(~t, 4, 4)];
3884 if (break_speed < v->cur_speed) v->cur_speed = break_speed;
3885 } else {
3887 }
3888
3889 if (!TrainCanLeaveTile(v)) return true;
3890
3891 /* Determine the non-diagonal direction in which we will exit this tile */
3892 DiagDirection dir = VehicleExitDir(v->direction, v->track);
3893 /* Calculate next tile */
3894 TileIndex tile = v->tile + TileOffsByDiagDir(dir);
3895
3896 /* Determine the track status on the next tile */
3897 TrackStatus ts = GetTileTrackStatus(tile, TRANSPORT_RAIL, 0, ReverseDiagDir(dir));
3898 TrackdirBits reachable_trackdirs = DiagdirReachesTrackdirs(dir);
3899
3900 TrackdirBits trackdirbits = TrackStatusToTrackdirBits(ts) & reachable_trackdirs;
3901 TrackdirBits red_signals = TrackStatusToRedSignals(ts) & reachable_trackdirs;
3902
3903 /* We are sure the train is not entering a depot, it is detected above */
3904
3905 /* mask unreachable track bits if we are forbidden to do 90deg turns */
3906 TrackBits bits = TrackdirBitsToTrackBits(trackdirbits);
3908 bits &= ~TrackCrossesTracks(FindFirstTrack(v->track));
3909 }
3910
3911 /* no suitable trackbits at all || unusable rail (wrong type or owner) */
3912 if (bits == TRACK_BIT_NONE || !CheckCompatibleRail(v, tile)) {
3913 return TrainApproachingLineEnd(v, false, reverse);
3914 }
3915
3916 /* approaching red signal */
3917 if ((trackdirbits & red_signals) != 0) return TrainApproachingLineEnd(v, true, reverse);
3918
3919 /* approaching a rail/road crossing? then make it red */
3921
3922 return true;
3923}
3924
3925
3926static bool TrainLocoHandler(Train *v, bool mode)
3927{
3928 /* train has crashed? */
3930 return mode ? true : HandleCrashedTrain(v); // 'this' can be deleted here
3931 }
3932
3933 if (v->force_proceed != TFP_NONE) {
3934 v->flags.Reset(VehicleRailFlag::Stuck);
3936 }
3937
3938 /* train is broken down? */
3939 if (v->HandleBreakdown()) return true;
3940
3941 if (v->flags.Test(VehicleRailFlag::Reversing) && v->cur_speed == 0) {
3943 }
3944
3945 /* exit if train is stopped */
3946 if (v->vehstatus.Test(VehState::Stopped) && v->cur_speed == 0) return true;
3947
3948 bool valid_order = !v->current_order.IsType(OT_NOTHING) && v->current_order.GetType() != OT_CONDITIONAL;
3949 if (ProcessOrders(v) && CheckReverseTrain(v)) {
3950 v->wait_counter = 0;
3951 v->cur_speed = 0;
3952 v->subspeed = 0;
3955 return true;
3956 } else if (v->flags.Test(VehicleRailFlag::LeavingStation)) {
3957 /* Try to reserve a path when leaving the station as we
3958 * might not be marked as wanting a reservation, e.g.
3959 * when an overlength train gets turned around in a station. */
3960 DiagDirection dir = VehicleExitDir(v->direction, v->track);
3962
3964 TryPathReserve(v, true, true);
3965 }
3967 }
3968
3969 v->HandleLoading(mode);
3970
3971 if (v->current_order.IsType(OT_LOADING)) return true;
3972
3973 if (CheckTrainStayInDepot(v)) return true;
3974
3975 if (!mode) v->ShowVisualEffect();
3976
3977 /* We had no order but have an order now, do look ahead. */
3978 if (!valid_order && !v->current_order.IsType(OT_NOTHING)) {
3980 }
3981
3982 /* Handle stuck trains. */
3983 if (!mode && v->flags.Test(VehicleRailFlag::Stuck)) {
3984 ++v->wait_counter;
3985
3986 /* Should we try reversing this tick if still stuck? */
3988
3989 if (!turn_around && v->wait_counter % _settings_game.pf.path_backoff_interval != 0 && v->force_proceed == TFP_NONE) return true;
3990 if (!TryPathReserve(v)) {
3991 /* Still stuck. */
3992 if (turn_around) ReverseTrainDirection(v);
3993
3995 /* Show message to player. */
3998 }
3999 v->wait_counter = 0;
4000 }
4001 /* Exit if force proceed not pressed, else reset stuck flag anyway. */
4002 if (v->force_proceed == TFP_NONE) return true;
4003 v->flags.Reset(VehicleRailFlag::Stuck);
4004 v->wait_counter = 0;
4006 }
4007 }
4008
4009 if (v->current_order.IsType(OT_LEAVESTATION)) {
4010 v->current_order.Free();
4012 return true;
4013 }
4014
4015 int j = v->UpdateSpeed();
4016
4017 /* we need to invalidate the widget if we are stopping from 'Stopping 0 km/h' to 'Stopped' */
4018 if (v->cur_speed == 0 && v->vehstatus.Test(VehState::Stopped)) {
4019 /* If we manually stopped, we're not force-proceeding anymore. */
4020 v->force_proceed = TFP_NONE;
4022 }
4023
4024 int adv_spd = v->GetAdvanceDistance();
4025 if (j < adv_spd) {
4026 /* if the vehicle has speed 0, update the last_speed field. */
4027 if (v->cur_speed == 0) v->SetLastSpeed();
4028 } else {
4030 /* Loop until the train has finished moving. */
4031 for (;;) {
4032 j -= adv_spd;
4033 TrainController(v, nullptr);
4034 /* Don't continue to move if the train crashed. */
4035 if (CheckTrainCollision(v)) break;
4036 /* Determine distance to next map position */
4037 adv_spd = v->GetAdvanceDistance();
4038
4039 /* No more moving this tick */
4040 if (j < adv_spd || v->cur_speed == 0) break;
4041
4042 OrderType order_type = v->current_order.GetType();
4043 /* Do not skip waypoints (incl. 'via' stations) when passing through at full speed. */
4044 if ((order_type == OT_GOTO_WAYPOINT || order_type == OT_GOTO_STATION) &&
4046 IsTileType(v->tile, MP_STATION) &&
4048 ProcessOrders(v);
4049 }
4050 }
4051 v->SetLastSpeed();
4052 }
4053
4054 for (Train *u = v; u != nullptr; u = u->Next()) {
4055 if (u->vehstatus.Test(VehState::Hidden)) continue;
4056
4057 u->UpdateViewport(false, false);
4058 }
4059
4060 if (v->progress == 0) v->progress = j; // Save unused spd for next time, if TrainController didn't set progress
4061
4062 return true;
4063}
4064
4070{
4071 Money cost = 0;
4072 const Train *v = this;
4073
4074 do {
4075 const Engine *e = v->GetEngine();
4076 if (e->VehInfo<RailVehicleInfo>().running_cost_class == INVALID_PRICE) continue;
4077
4078 uint cost_factor = GetVehicleProperty(v, PROP_TRAIN_RUNNING_COST_FACTOR, e->VehInfo<RailVehicleInfo>().running_cost);
4079 if (cost_factor == 0) continue;
4080
4081 /* Halve running cost for multiheaded parts */
4082 if (v->IsMultiheaded()) cost_factor /= 2;
4083
4084 cost += GetPrice(e->VehInfo<RailVehicleInfo>().running_cost_class, cost_factor, e->GetGRF());
4085 } while ((v = v->GetNextVehicle()) != nullptr);
4086
4087 return cost;
4088}
4089
4095{
4096 this->tick_counter++;
4097
4098 if (this->IsFrontEngine()) {
4100
4101 if (!this->vehstatus.Test(VehState::Stopped) || this->cur_speed > 0) this->running_ticks++;
4102
4103 this->current_order_time++;
4104
4105 if (!TrainLocoHandler(this, false)) return false;
4106
4107 return TrainLocoHandler(this, true);
4108 } else if (this->IsFreeWagon() && this->vehstatus.Test(VehState::Crashed)) {
4109 /* Delete flooded standalone wagon chain */
4110 if (++this->crash_anim_pos >= 4400) {
4111 delete this;
4112 return false;
4113 }
4114 }
4115
4116 return true;
4117}
4118
4124{
4125 if (Company::Get(v->owner)->settings.vehicle.servint_trains == 0 || !v->NeedsAutomaticServicing()) return;
4126 if (v->IsChainInDepot()) {
4128 return;
4129 }
4130
4132
4133 FindDepotData tfdd = FindClosestTrainDepot(v, max_penalty);
4134 /* Only go to the depot if it is not too far out of our way. */
4135 if (tfdd.best_length == UINT_MAX || tfdd.best_length > max_penalty) {
4136 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
4137 /* If we were already heading for a depot but it has
4138 * suddenly moved farther away, we continue our normal
4139 * schedule? */
4142 }
4143 return;
4144 }
4145
4146 DepotID depot = GetDepotIndex(tfdd.tile);
4147
4148 if (v->current_order.IsType(OT_GOTO_DEPOT) &&
4149 v->current_order.GetDestination() != depot &&
4150 !Chance16(3, 16)) {
4151 return;
4152 }
4153
4156 v->dest_tile = tfdd.tile;
4158}
4159
4162{
4163 AgeVehicle(this);
4164}
4165
4168{
4169 EconomyAgeVehicle(this);
4170
4171 if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
4172
4173 if (this->IsFrontEngine()) {
4174 CheckVehicleBreakdown(this);
4175
4177
4178 CheckOrders(this);
4179
4180 /* update destination */
4181 if (this->current_order.IsType(OT_GOTO_STATION)) {
4183 if (tile != INVALID_TILE) this->dest_tile = tile;
4184 }
4185
4186 if (this->running_ticks != 0) {
4187 /* running costs */
4189
4190 this->profit_this_year -= cost.GetCost();
4191 this->running_ticks = 0;
4192
4194
4197 }
4198 }
4199}
4200
4206{
4208
4209 if (this->track == TRACK_BIT_DEPOT) {
4210 /* We'll assume the train is facing outwards */
4211 return DiagDirToDiagTrackdir(GetRailDepotDirection(this->tile)); // Train in depot
4212 }
4213
4214 if (this->track == TRACK_BIT_WORMHOLE) {
4215 /* train in tunnel or on bridge, so just use its direction and assume a diagonal track */
4216 return DiagDirToDiagTrackdir(DirToDiagDir(this->direction));
4217 }
4218
4219 return TrackDirectionToTrackdir(FindFirstTrack(this->track), this->direction);
4220}
4221
4222uint16_t Train::GetMaxWeight() const
4223{
4224 uint16_t weight = CargoSpec::Get(this->cargo_type)->WeightOfNUnitsInTrain(this->GetEngine()->DetermineCapacity(this));
4225
4226 /* Vehicle weight is not added for articulated parts. */
4227 if (!this->IsArticulatedPart()) {
4228 weight += GetVehicleProperty(this, PROP_TRAIN_WEIGHT, RailVehInfo(this->engine_type)->weight);
4229 }
4230
4231 /* Powered wagons have extra weight added. */
4232 if (this->flags.Test(VehicleRailFlag::PoweredWagon)) {
4233 weight += RailVehInfo(this->gcache.first_engine)->pow_wag_weight;
4234 }
4235
4236 return weight;
4237}
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).
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T AssignBit(T &x, const uint8_t y, bool value)
Assigns a bit in a variable.
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.
debug_inline 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 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:23
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:106
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:156
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:200
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:442
CargoType GetDefaultCargoType() const
Determines the default cargo type of an engine.
Definition engine_base.h:95
uint16_t reliability
Current reliability of the engine.
Definition engine_base.h:49
Enum-as-bit-set wrapper.
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:116
uint8_t curve_speed
Multiplier for curve maximum speed advantage.
Definition rail.h:196
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 constexpr int DAYS_IN_YEAR
days per year
static Date date
Current date in days (day counter).
uint StoredCount() const
Returns sum of cargo on board the vehicle (ie not only reserved).
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
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:949
@ EXPENSES_TRAIN_RUN
Running costs trains.
@ EXPENSES_NEW_VEHICLES
New vehicles.
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
@ 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.
@ Random
Randomise borders.
DiagDirection DiagdirBetweenTiles(TileIndex tile_from, TileIndex tile_to)
Determines the DiagDirection to get from one tile to another.
Definition map_func.h:622
TileIndex TileAddByDiagDir(TileIndex tile, DiagDirection dir)
Adds a DiagDir to a tile.
Definition map_func.h:610
TileIndexDiff TileOffsByAxis(Axis axis)
Convert an Axis to a TileIndexDiff.
Definition map_func.h:554
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:424
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:414
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition map_func.h:569
static debug_inline TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:403
int32_t TileIndexDiff
An offset value between two tiles.
Definition map_type.h:23
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition math_func.hpp:23
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
Miscellaneous command definitions.
void HideFillingPercent(TextEffectID *te_id)
Hide vehicle loading indicators.
Definition misc_gui.cpp:581
bool _networking
are we in networking mode?
Definition network.cpp:67
Basic functions/variables used all over the place.
ClientID
'Unique' identifier to be given to clients
Base for the NewGRF implementation.
@ 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.
std::optional< bool > TestVehicleBuildProbability(Vehicle *v, EngineID engine, BuildProbabilityType type)
Test for vehicle build probability type.
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.
@ 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.
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.
@ ArrivalOther
First vehicle arrived for competitor.
@ Accident
An accident or disaster has occurred.
@ TrainStuck
The train got stuck and needs to be unstuck manually.
@ Error
A game paused because a (critical) error.
Functions related to order backups.
bool ProcessOrders(Vehicle *v)
Handle the orders of a vehicle and determine the next place to go to if needed.
bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool pbs_look_ahead)
Update the vehicle's destination tile from an order.
void CheckOrders(const Vehicle *v)
Check the orders of a vehicle, to see if there are invalid orders and stuff.
void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist, bool reset_order_indices)
Delete all orders from a vehicle.
VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v)
Process a conditional order and determine the next order.
OrderStopLocation
Where to stop the trains.
Definition order_type.h:98
@ OSL_PLATFORM_MIDDLE
Stop at the middle of the platform.
Definition order_type.h:100
@ OSL_PLATFORM_FAR_END
Stop at the far end of the platform.
Definition order_type.h:101
@ OSL_PLATFORM_NEAR_END
Stop at the near end of the platform.
Definition order_type.h:99
@ ODTFB_SERVICE
This depot order is because of the servicing limit.
Definition order_type.h:110
uint8_t VehicleOrderID
The index of an order within its current vehicle (not pool related)
Definition order_type.h:18
@ ODATFB_NEAREST_DEPOT
Send the vehicle to the nearest depot.
Definition order_type.h:120
static const VehicleOrderID INVALID_VEH_ORDER_ID
Invalid vehicle order index (sentinel)
Definition order_type.h:39
@ ONSF_NO_STOP_AT_DESTINATION_STATION
The vehicle will stop at any station it passes except the destination.
Definition order_type.h:90
@ ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS
The vehicle will not stop at any stations it passes except the destination.
Definition order_type.h:89
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:325
bool IsCompatibleRail(RailType enginetype, RailType tiletype)
Checks if an engine of the given RailType can drive on a tile with a given RailType.
Definition rail.h:352
bool HasPowerOnRail(RailType enginetype, RailType tiletype)
Checks if an engine of the given RailType got power on a tile with a given RailType.
Definition rail.h:377
bool Rail90DegTurnDisallowed(RailType rt1, RailType rt2, bool def=_settings_game.pf.forbid_90_deg)
Test if 90 degree turns are disallowed between two railtypes.
Definition rail.h:411
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:301
RailTypes GetAllCompatibleRailTypes(RailTypes railtypes)
Returns all compatible railtypes for a set of railtypes.
Definition rail.h:313
std::vector< Train * > TrainList
Helper type for lists/vectors of trains.
Definition rail_cmd.cpp:44
bool HasOnewaySignalBlockingTrackdir(Tile tile, Trackdir td)
Is a one-way signal blocking the trackdir? A one-way signal on the trackdir against will block,...
Definition rail_map.h:474
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
static debug_inline RailTileType GetRailTileType(Tile t)
Returns the RailTileType (normal with or without signals, waypoint or depot).
Definition rail_map.h:36
TrackBits GetTrackBits(Tile tile)
Gets the track bits of the given tile.
Definition rail_map.h:136
@ RAIL_TILE_SIGNALS
Normal rail tile with signals.
Definition rail_map.h:25
static debug_inline bool IsRailDepotTile(Tile t)
Is this tile rail tile and a rail depot?
Definition rail_map.h:105
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 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
static debug_inline bool IsPlainRail(Tile t)
Returns whether this is plain rails, with or without signals.
Definition rail_map.h:49
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
static debug_inline bool IsPlainRailTile(Tile t)
Checks whether the tile is a rail tile or rail tile with signals.
Definition rail_map.h:60
SignalState GetSignalStateByTrackdir(Tile tile, Trackdir trackdir)
Gets the state of the signal along the given trackdir.
Definition rail_map.h:437
@ 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:45
@ SND_04_DEPARTURE_STEAM
2 == 0x02 Station departure: steam engine
Definition sound_type.h:49
@ SND_41_DEPARTURE_MAGLEV
65 == 0x41 Station departure: maglev engine
Definition sound_type.h:112
@ SND_13_TRAIN_COLLISION
15 == 0x11 Train+train crash
Definition sound_type.h:64
@ SND_0E_LEVEL_CROSSING
12 == 0x0C Train passes through level crossing
Definition sound_type.h:59
@ SND_0A_DEPARTURE_TRAIN
8 == 0x08 Station departure: diesel and electric engine
Definition sound_type.h:55
@ SND_47_DEPARTURE_MONORAIL
71 == 0x47 Station departure: monorail engine
Definition sound_type.h:118
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
Track follower helper template class (can serve pathfinders and vehicle controllers).
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
SoundSettings sound
sound effect settings
GUISettings gui
settings related to the GUI
Structure to return information about the closest depot location, and whether it could be found.
T y
Y coordinate.
T x
X coordinate.
T x
X coordinate.
T y
Y coordinate.
bool line_reverse_mode
reversing at stations or not
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:156
int traininfo_vehicle_pitch
Vertical offset for drawing train images in depot GUI and vehicle details.
Definition newgrf.h:155
bool lost_vehicle_warn
if a vehicle can't find its destination, show a warning
bool show_track_reservation
highlight reserved tracks.
PathfinderSettings pf
settings for all pathfinders
DifficultySettings difficulty
settings related to the difficulty
VehicleSettings vehicle
options for vehicles
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.
uint32_t cached_weight
Total weight of the consist (valid only for the first engine).
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...
uint16_t cached_max_track_speed
Maximum consist speed (in internal units) limited by track type (valid only for the first engine).
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.
GroundVehicleCache gcache
Cache of often calculated values.
void CargoChanged()
Recalculates the cached weight of a vehicle and its parts.
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.
bool IsFreeWagon() const
Check if the vehicle is a free wagon (got no engine in front of it).
uint DoUpdateSpeed(uint accel, int min_speed, int max_speed)
Update the speed of the vehicle.
int GetAcceleration() const
Calculates the acceleration of the vehicle under its current conditions.
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 debug_inline uint Size()
Get the size of the map.
Definition map_func.h:287
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:140
uint16_t GetMaxSpeed() const
Get the maxmimum speed in km-ish/h a vehicle is allowed to reach on the way to the destination.
Definition order_base.h:197
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:138
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 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:142
void Free()
'Free' the order
Definition order_cmd.cpp:48
bool ShouldStopAtStation(const Vehicle *v, StationID station) const
Check whether the given vehicle should stop at the given station based on this order and the non-stop...
OrderNonStopFlags GetNonStopType() const
At which stations must we stop?
Definition order_base.h:136
void MakeGoToDepot(DestinationID destination, OrderDepotTypeFlags order, OrderNonStopFlags non_stop_type=ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS, OrderDepotActionFlags action=ODATF_SERVICE_ONLY, CargoType cargo=CARGO_NO_REFIT)
Makes this order a Go To Depot order.
Definition order_cmd.cpp:74
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
uint8_t path_backoff_interval
ticks between checks for a free path.
uint8_t wait_for_pbs_path
how long to wait for a path reservation.
uint8_t wait_oneway_signal
waitingtime in days before a oneway signal
bool reserve_paths
always reserve paths regardless of signal type.
YAPFSettings yapf
pathfinder settings for the yet another pathfinder
bool reverse_at_signals
whether to reverse at signals at all
bool forbid_90_deg
forbid trains to make 90 deg turns
uint8_t wait_twoway_signal
waitingtime in days before a twoway signal
static Titem * Get(auto index)
Returns Titem with given index.
Tindex index
Index of this pool item.
static bool CanAllocateItem(size_t n=1)
Helper functions so we can use PoolItem::Function() instead of _poolitem_pool.Function()
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
uint8_t pow_wag_weight
Extra weight applied to consist if wagon should be powered.
Definition engine_type.h:89
Specification of a rectangle with absolute coordinates of all edges.
int Width() const
Get width of Rect.
int Height() const
Get height of Rect.
Iterable ensemble of each set bit in a value.
bool ambient
Play ambient, industry and town sounds.
bool disaster
Play disaster and accident sounds.
static Station * Get(auto index)
Gets station with given index.
T * Next() const
Get next vehicle in the chain.
static Pool::IterateWrapper< T > Iterate(size_t from=0)
Returns an iterable ensemble of all valid vehicles of type T.
T * Previous() const
Get previous vehicle in the chain.
static T * From(Vehicle *v)
Converts a Vehicle to SpecializedVehicle with type checking.
static T * GetIfValid(auto index)
Returns vehicle if the index is a valid index for this vehicle type.
T * First() const
Get the first vehicle in the chain.
T * GetNextVehicle() const
Get the next real (non-articulated part) vehicle in the consist.
static T * Get(auto index)
Gets vehicle with given index.
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.
Coord3D< int8_t > offset
Relative position of sprite from bounding box.
Definition sprite.h:21
Coord3D< int8_t > origin
Position of northern corner within tile.
Definition sprite.h:19
Coord3D< uint8_t > extent
Size of bounding box.
Definition sprite.h:20
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
int16_t cached_curve_speed_mod
curve speed modifier of the entire train
Definition train.h:82
bool cached_tilt
train can tilt; feature provides a bonus in curves
Definition train.h:79
'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:150
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:174
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:118
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! Update its status and other parts around it.
VehicleAccelerationModel GetAccelerationType() const
Allows to know the acceleration type of a vehicle.
Definition train.h:187
AccelStatus GetAccelerationStatus() const
Checks the current acceleration status of this vehicle.
Definition train.h:284
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.
uint16_t cached_max_speed
Maximum speed of the consist (minimum of the max speed of all vehicles in the consist).
UnitID max_trains
max trains in game per company
uint8_t train_acceleration_model
realistic acceleration for trains
bool wagon_speed_limits
enable wagon speed limits
uint8_t freight_trains
value to multiply the weight of cargo by
uint8_t max_train_length
maximum length for trains
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:718
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:2327
void AddToShared(Vehicle *shared_chain)
Adds this vehicle to a shared vehicle chain.
Definition vehicle.cpp:2956
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:2396
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:2407
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:2486
CargoType cargo_type
type of cargo this vehicle is carrying
debug_inline bool IsFrontEngine() const
Check if the vehicle is a front engine.
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:762
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:728
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:738
SpriteBounds bounds
Bounding box of vehicle.
void BeginLoading()
Prepare everything to begin the loading when arriving at a station.
Definition vehicle.cpp:2183
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 IsWaitingForUnbunching() const
Check whether a vehicle inside a depot is waiting for unbunching.
Definition vehicle.cpp:2533
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:2927
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:1332
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:1662
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:2777
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.
uint32_t maximum_go_to_depot_penalty
What is the maximum penalty that may be endured for going to a depot.
@ EnteredStation
The vehicle entered a station.
@ CannotEnter
The vehicle cannot enter the tile.
@ EnteredWormhole
The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/...
VehicleEnterTileStates VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
Call the tile callback function for a vehicle entering a tile.
Definition vehicle.cpp:1819
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
static debug_inline bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
@ MP_STATION
A tile of a station.
Definition tile_type.h:53
@ MP_TUNNELBRIDGE
Tunnel entry/exit and bridge heads.
Definition tile_type.h:57
@ MP_RAILWAY
A railway.
Definition tile_type.h:49
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
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 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.
@ Length
Allow vehicles to change length.
static constexpr ConsistChangeFlags CCF_TRACK
Valid changes while vehicle is driving, and possibly changing tracks.
Definition train.h:52
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
@ AllowedOnNormalRail
Electric train engine is allowed to run on normal rail. *‍/.
Definition train.h:30
@ Stuck
Train can't get a path reservation.
Definition train.h:32
@ Flipped
Reverse the visible direction of the vehicle.
Definition train.h:28
@ PoweredWagon
Wagon is powered.
Definition train.h:27
@ LeavingStation
Train is just leaving a station.
Definition train.h:33
@ Reversing
Train is slowing down to reverse.
Definition train.h:26
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.
@ 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 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.
std::vector< Train * > TrainList
Helper type for lists/vectors of trains.
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:1520
UnitID GetFreeUnitNumber(VehicleType type)
Get an unused unit number for a vehicle (if allowed).
Definition vehicle.cpp:1876
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
GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
Get position information of a vehicle when moving one pixel in the direction it is facing.
Definition vehicle.cpp:1765
void DecreaseVehicleValue(Vehicle *v)
Decrease the value of a vehicle.
Definition vehicle.cpp:1270
void EconomyAgeVehicle(Vehicle *v)
Update economy age of a vehicle.
Definition vehicle.cpp:1398
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
Finds vehicle in tunnel / bridge.
Definition vehicle.cpp:551
void AgeVehicle(Vehicle *v)
Update age of a vehicle.
Definition vehicle.cpp:1410
@ 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.
@ 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.
@ 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:1193
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3184
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:3276
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:3171
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3158
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:3293
@ 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