OpenTTD Source 20241222-master-gc72542431a
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 "spritecache.h"
30#include "core/random_func.hpp"
31#include "company_base.h"
32#include "newgrf.h"
33#include "order_backup.h"
34#include "zoom_func.h"
35#include "newgrf_debug.h"
36#include "framerate_type.h"
37#include "train_cmd.h"
38#include "misc_cmd.h"
41
42#include "table/strings.h"
43#include "table/train_sprites.h"
44
45#include "safeguards.h"
46
47static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck);
48static bool TrainCheckIfLineEnds(Train *v, bool reverse = true);
49bool TrainController(Train *v, Vehicle *nomove, bool reverse = true); // Also used in vehicle_sl.cpp.
51static void CheckIfTrainNeedsService(Train *v);
52static void CheckNextTrainTile(Train *v);
53
54static const uint8_t _vehicle_initial_x_fract[4] = {10, 8, 4, 8};
55static const uint8_t _vehicle_initial_y_fract[4] = { 8, 4, 8, 10};
56
57template <>
58bool IsValidImageIndex<VEH_TRAIN>(uint8_t image_index)
59{
60 return image_index < lengthof(_engine_sprite_base);
61}
62
63
70{
71 if (!CargoSpec::Get(cargo)->is_freight) return 1;
73}
74
77{
78 bool first = true;
79
80 for (const Train *v : Train::Iterate()) {
81 if (v->First() == v && !(v->vehstatus & VS_CRASHED)) {
82 for (const Train *u = v, *w = v->Next(); w != nullptr; u = w, w = w->Next()) {
83 if (u->track != TRACK_BIT_DEPOT) {
84 if ((w->track != TRACK_BIT_DEPOT &&
85 std::max(abs(u->x_pos - w->x_pos), abs(u->y_pos - w->y_pos)) != u->CalcNextVehicleOffset()) ||
86 (w->track == TRACK_BIT_DEPOT && TicksToLeaveDepot(u) <= 0)) {
87 SetDParam(0, v->index);
88 SetDParam(1, v->owner);
89 ShowErrorMessage(STR_BROKEN_VEHICLE_LENGTH, INVALID_STRING_ID, WL_CRITICAL);
90
91 if (!_networking && first) {
92 first = false;
94 }
95 /* Break so we warn only once for each train. */
96 break;
97 }
98 }
99 }
100 }
101 }
102}
103
111{
112 uint16_t max_speed = UINT16_MAX;
113
114 assert(this->IsFrontEngine() || this->IsFreeWagon());
115
116 const RailVehicleInfo *rvi_v = RailVehInfo(this->engine_type);
117 EngineID first_engine = this->IsFrontEngine() ? this->engine_type : INVALID_ENGINE;
118 this->gcache.cached_total_length = 0;
119 this->compatible_railtypes = RAILTYPES_NONE;
120
121 bool train_can_tilt = true;
122 int16_t min_curve_speed_mod = INT16_MAX;
123
124 for (Train *u = this; u != nullptr; u = u->Next()) {
125 const RailVehicleInfo *rvi_u = RailVehInfo(u->engine_type);
126
127 /* Check the this->first cache. */
128 assert(u->First() == this);
129
130 /* update the 'first engine' */
131 u->gcache.first_engine = this == u ? INVALID_ENGINE : first_engine;
132 u->railtype = rvi_u->railtype;
133
134 if (u->IsEngine()) first_engine = u->engine_type;
135
136 /* Set user defined data to its default value */
137 u->tcache.user_def_data = rvi_u->user_def_data;
138 this->InvalidateNewGRFCache();
139 u->InvalidateNewGRFCache();
140 }
141
142 for (Train *u = this; u != nullptr; u = u->Next()) {
143 /* Update user defined data (must be done before other properties) */
144 u->tcache.user_def_data = GetVehicleProperty(u, PROP_TRAIN_USER_DATA, u->tcache.user_def_data);
145 this->InvalidateNewGRFCache();
146 u->InvalidateNewGRFCache();
147 }
148
149 for (Train *u = this; u != nullptr; u = u->Next()) {
150 const Engine *e_u = u->GetEngine();
151 const RailVehicleInfo *rvi_u = &e_u->u.rail;
152
153 if (!HasBit(e_u->info.misc_flags, EF_RAIL_TILTS)) train_can_tilt = false;
154 min_curve_speed_mod = std::min(min_curve_speed_mod, u->GetCurveSpeedModifier());
155
156 /* Cache wagon override sprite group. nullptr is returned if there is none */
157 u->tcache.cached_override = GetWagonOverrideSpriteSet(u->engine_type, u->cargo_type, u->gcache.first_engine);
158
159 /* Reset colour map */
160 u->colourmap = PAL_NONE;
161
162 /* Update powered-wagon-status and visual effect */
163 u->UpdateVisualEffect(true);
164
165 if (rvi_v->pow_wag_power != 0 && rvi_u->railveh_type == RAILVEH_WAGON &&
166 UsesWagonOverride(u) && !HasBit(u->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
167 /* wagon is powered */
168 SetBit(u->flags, VRF_POWEREDWAGON); // cache 'powered' status
169 } else {
170 ClrBit(u->flags, VRF_POWEREDWAGON);
171 }
172
173 if (!u->IsArticulatedPart()) {
174 /* Do not count powered wagons for the compatible railtypes, as wagons always
175 have railtype normal */
176 if (rvi_u->power > 0) {
177 this->compatible_railtypes |= GetRailTypeInfo(u->railtype)->powered_railtypes;
178 }
179
180 /* Some electric engines can be allowed to run on normal rail. It happens to all
181 * existing electric engines when elrails are disabled and then re-enabled */
183 u->railtype = RAILTYPE_RAIL;
184 u->compatible_railtypes |= RAILTYPES_RAIL;
185 }
186
187 /* max speed is the minimum of the speed limits of all vehicles in the consist */
188 if ((rvi_u->railveh_type != RAILVEH_WAGON || _settings_game.vehicle.wagon_speed_limits) && !UsesWagonOverride(u)) {
189 uint16_t speed = GetVehicleProperty(u, PROP_TRAIN_SPEED, rvi_u->max_speed);
190 if (speed != 0) max_speed = std::min(speed, max_speed);
191 }
192 }
193
194 uint16_t new_cap = e_u->DetermineCapacity(u);
195 if (allowed_changes & CCF_CAPACITY) {
196 /* Update vehicle capacity. */
197 if (u->cargo_cap > new_cap) u->cargo.Truncate(new_cap);
198 u->refit_cap = std::min(new_cap, u->refit_cap);
199 u->cargo_cap = new_cap;
200 } else {
201 /* Verify capacity hasn't changed. */
202 if (new_cap != u->cargo_cap) ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_CAPACITY, GBUG_VEH_CAPACITY, true);
203 }
204 u->vcache.cached_cargo_age_period = GetVehicleProperty(u, PROP_TRAIN_CARGO_AGE_PERIOD, e_u->info.cargo_age_period);
205
206 /* check the vehicle length (callback) */
207 uint16_t veh_len = CALLBACK_FAILED;
208 if (e_u->GetGRF() != nullptr && e_u->GetGRF()->grf_version >= 8) {
209 /* Use callback 36 */
210 veh_len = GetVehicleProperty(u, PROP_TRAIN_SHORTEN_FACTOR, CALLBACK_FAILED);
211
212 if (veh_len != CALLBACK_FAILED && veh_len >= VEHICLE_LENGTH) {
214 }
215 } else if (HasBit(e_u->info.callback_mask, CBM_VEHICLE_LENGTH)) {
216 /* Use callback 11 */
217 veh_len = GetVehicleCallback(CBID_VEHICLE_LENGTH, 0, 0, u->engine_type, u);
218 }
219 if (veh_len == CALLBACK_FAILED) veh_len = rvi_u->shorten_factor;
220 veh_len = VEHICLE_LENGTH - Clamp(veh_len, 0, VEHICLE_LENGTH - 1);
221
222 if (allowed_changes & CCF_LENGTH) {
223 /* Update vehicle length. */
224 u->gcache.cached_veh_length = veh_len;
225 } else {
226 /* Verify length hasn't changed. */
227 if (veh_len != u->gcache.cached_veh_length) VehicleLengthChanged(u);
228 }
229
230 this->gcache.cached_total_length += u->gcache.cached_veh_length;
231 this->InvalidateNewGRFCache();
232 u->InvalidateNewGRFCache();
233 }
234
235 /* store consist weight/max speed in cache */
236 this->vcache.cached_max_speed = max_speed;
237 this->tcache.cached_tilt = train_can_tilt;
238 this->tcache.cached_curve_speed_mod = min_curve_speed_mod;
239 this->tcache.cached_max_curve_speed = this->GetCurveSpeedLimit();
240
241 /* recalculate cached weights and power too (we do this *after* the rest, so it is known which wagons are powered and need extra weight added) */
242 this->CargoChanged();
243
244 if (this->IsFrontEngine()) {
245 this->UpdateAcceleration();
249 InvalidateNewGRFInspectWindow(GSF_TRAINS, this->index);
250 }
251}
252
263int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, int *station_ahead, int *station_length)
264{
265 const Station *st = Station::Get(station_id);
266 *station_ahead = st->GetPlatformLength(tile, DirToDiagDir(v->direction)) * TILE_SIZE;
267 *station_length = st->GetPlatformLength(tile) * TILE_SIZE;
268
269 /* Default to the middle of the station for stations stops that are not in
270 * the order list like intermediate stations when non-stop is disabled */
272 if (v->gcache.cached_total_length >= *station_length) {
273 /* The train is longer than the station, make it stop at the far end of the platform */
275 } else if (v->current_order.IsType(OT_GOTO_STATION) && v->current_order.GetDestination() == station_id) {
277 }
278
279 /* The stop location of the FRONT! of the train */
280 int stop;
281 switch (osl) {
282 default: NOT_REACHED();
283
285 stop = v->gcache.cached_total_length;
286 break;
287
289 stop = *station_length - (*station_length - v->gcache.cached_total_length) / 2;
290 break;
291
293 stop = *station_length;
294 break;
295 }
296
297 /* Subtract half the front vehicle length of the train so we get the real
298 * stop location of the train. */
299 return stop - (v->gcache.cached_veh_length + 1) / 2;
300}
301
302
308{
309 assert(this->First() == this);
310
311 static const int absolute_max_speed = UINT16_MAX;
312 int max_speed = absolute_max_speed;
313
314 if (_settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) return max_speed;
315
316 int curvecount[2] = {0, 0};
317
318 /* first find the curve speed limit */
319 int numcurve = 0;
320 int sum = 0;
321 int pos = 0;
322 int lastpos = -1;
323 for (const Train *u = this; u->Next() != nullptr; u = u->Next(), pos += u->gcache.cached_veh_length) {
324 Direction this_dir = u->direction;
325 Direction next_dir = u->Next()->direction;
326
327 DirDiff dirdiff = DirDifference(this_dir, next_dir);
328 if (dirdiff == DIRDIFF_SAME) continue;
329
330 if (dirdiff == DIRDIFF_45LEFT) curvecount[0]++;
331 if (dirdiff == DIRDIFF_45RIGHT) curvecount[1]++;
332 if (dirdiff == DIRDIFF_45LEFT || dirdiff == DIRDIFF_45RIGHT) {
333 if (lastpos != -1) {
334 numcurve++;
335 sum += pos - lastpos;
336 if (pos - lastpos <= static_cast<int>(VEHICLE_LENGTH) && max_speed > 88) {
337 max_speed = 88;
338 }
339 }
340 lastpos = pos;
341 }
342
343 /* if we have a 90 degree turn, fix the speed limit to 60 */
344 if (dirdiff == DIRDIFF_90LEFT || dirdiff == DIRDIFF_90RIGHT) {
345 max_speed = 61;
346 }
347 }
348
349 if (numcurve > 0 && max_speed > 88) {
350 if (curvecount[0] == 1 && curvecount[1] == 1) {
351 max_speed = absolute_max_speed;
352 } else {
353 sum = CeilDiv(sum, VEHICLE_LENGTH);
354 sum /= numcurve;
355 max_speed = 232 - (13 - Clamp(sum, 1, 12)) * (13 - Clamp(sum, 1, 12));
356 }
357 }
358
359 if (max_speed != absolute_max_speed) {
360 /* Apply the current railtype's curve speed advantage */
361 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(this->tile));
362 max_speed += (max_speed / 2) * rti->curve_speed;
363
364 if (this->tcache.cached_tilt) {
365 /* Apply max_speed bonus of 20% for a tilting train */
366 max_speed += max_speed / 5;
367 }
368
369 /* Apply max_speed modifier (cached value is fixed-point binary with 8 fractional bits)
370 * and clamp the result to an acceptable range. */
371 max_speed += (max_speed * this->tcache.cached_curve_speed_mod) / 256;
372 max_speed = Clamp(max_speed, 2, absolute_max_speed);
373 }
374
375 return static_cast<uint16_t>(max_speed);
376}
377
383{
384 int max_speed = _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL ?
386 this->tcache.cached_max_curve_speed;
387
389 StationID sid = GetStationIndex(this->tile);
390 if (this->current_order.ShouldStopAtStation(this, sid)) {
391 int station_ahead;
392 int station_length;
393 int stop_at = GetTrainStopLocation(sid, this->tile, this, &station_ahead, &station_length);
394
395 /* The distance to go is whatever is still ahead of the train minus the
396 * distance from the train's stop location to the end of the platform */
397 int distance_to_go = station_ahead / TILE_SIZE - (station_length - stop_at) / TILE_SIZE;
398
399 if (distance_to_go > 0) {
400 int st_max_speed = 120;
401
402 int delta_v = this->cur_speed / (distance_to_go + 1);
403 if (max_speed > (this->cur_speed - delta_v)) {
404 st_max_speed = this->cur_speed - (delta_v / 10);
405 }
406
407 st_max_speed = std::max(st_max_speed, 25 * distance_to_go);
408 max_speed = std::min(max_speed, st_max_speed);
409 }
410 }
411 }
412
413 for (const Train *u = this; u != nullptr; u = u->Next()) {
414 if (_settings_game.vehicle.train_acceleration_model == AM_REALISTIC && u->track == TRACK_BIT_DEPOT) {
415 max_speed = std::min(max_speed, 61);
416 break;
417 }
418
419 /* Vehicle is on the middle part of a bridge. */
420 if (u->track == TRACK_BIT_WORMHOLE && !(u->vehstatus & VS_HIDDEN)) {
421 max_speed = std::min<int>(max_speed, GetBridgeSpec(GetBridgeType(u->tile))->speed);
422 }
423 }
424
425 max_speed = std::min<int>(max_speed, this->current_order.GetMaxSpeed());
426 return std::min<int>(max_speed, this->gcache.cached_max_track_speed);
427}
428
431{
432 assert(this->IsFrontEngine() || this->IsFreeWagon());
433
434 uint power = this->gcache.cached_power;
435 uint weight = this->gcache.cached_weight;
436 assert(weight != 0);
437 this->acceleration = Clamp(power / weight * 4, 1, 255);
438}
439
440int Train::GetCursorImageOffset() const
441{
442 if (this->gcache.cached_veh_length != 8 && HasBit(this->flags, VRF_REVERSE_DIRECTION) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_RAIL_FLIPS)) {
443 int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
444
445 const Engine *e = this->GetEngine();
446 if (e->GetGRF() != nullptr && is_custom_sprite(e->u.rail.image_index)) {
447 reference_width = e->GetGRF()->traininfo_vehicle_width;
448 }
449
450 return ScaleSpriteTrad((this->gcache.cached_veh_length - (int)VEHICLE_LENGTH) * reference_width / (int)VEHICLE_LENGTH);
451 }
452 return 0;
453}
454
461{
462 int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
463 int vehicle_pitch = 0;
464
465 const Engine *e = this->GetEngine();
466 if (e->GetGRF() != nullptr && is_custom_sprite(e->u.rail.image_index)) {
467 reference_width = e->GetGRF()->traininfo_vehicle_width;
468 vehicle_pitch = e->GetGRF()->traininfo_vehicle_pitch;
469 }
470
471 if (offset != nullptr) {
472 if (HasBit(this->flags, VRF_REVERSE_DIRECTION) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_RAIL_FLIPS)) {
473 offset->x = ScaleSpriteTrad(((int)this->gcache.cached_veh_length - (int)VEHICLE_LENGTH / 2) * reference_width / (int)VEHICLE_LENGTH);
474 } else {
475 offset->x = ScaleSpriteTrad(reference_width) / 2;
476 }
477 offset->y = ScaleSpriteTrad(vehicle_pitch);
478 }
479 return ScaleSpriteTrad(this->gcache.cached_veh_length * reference_width / VEHICLE_LENGTH);
480}
481
482static SpriteID GetDefaultTrainSprite(uint8_t spritenum, Direction direction)
483{
484 assert(IsValidImageIndex<VEH_TRAIN>(spritenum));
485 return ((direction + _engine_sprite_add[spritenum]) & _engine_sprite_and[spritenum]) + _engine_sprite_base[spritenum];
486}
487
494void Train::GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const
495{
496 uint8_t spritenum = this->spritenum;
497
499
500 if (is_custom_sprite(spritenum)) {
501 GetCustomVehicleSprite(this, (Direction)(direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE(spritenum)), image_type, result);
502 if (result->IsValid()) return;
503
505 }
506
507 assert(IsValidImageIndex<VEH_TRAIN>(spritenum));
508 SpriteID sprite = GetDefaultTrainSprite(spritenum, direction);
509
510 if (this->cargo.StoredCount() >= this->cargo_cap / 2U) sprite += _wagon_full_adder[spritenum];
511
512 result->Set(sprite);
513}
514
515static void GetRailIcon(EngineID engine, bool rear_head, int &y, EngineImageType image_type, VehicleSpriteSeq *result)
516{
517 const Engine *e = Engine::Get(engine);
518 Direction dir = rear_head ? DIR_E : DIR_W;
519 uint8_t spritenum = e->u.rail.image_index;
520
521 if (is_custom_sprite(spritenum)) {
522 GetCustomVehicleIcon(engine, dir, image_type, result);
523 if (result->IsValid()) {
524 if (e->GetGRF() != nullptr) {
526 }
527 return;
528 }
529
530 spritenum = Engine::Get(engine)->original_image_index;
531 }
532
533 if (rear_head) spritenum++;
534
535 result->Set(GetDefaultTrainSprite(spritenum, DIR_W));
536}
537
538void DrawTrainEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
539{
540 if (RailVehInfo(engine)->railveh_type == RAILVEH_MULTIHEAD) {
541 int yf = y;
542 int yr = y;
543
544 VehicleSpriteSeq seqf, seqr;
545 GetRailIcon(engine, false, yf, image_type, &seqf);
546 GetRailIcon(engine, true, yr, image_type, &seqr);
547
548 Rect rectf, rectr;
549 seqf.GetBounds(&rectf);
550 seqr.GetBounds(&rectr);
551
552 preferred_x = Clamp(preferred_x,
553 left - UnScaleGUI(rectf.left) + ScaleSpriteTrad(14),
554 right - UnScaleGUI(rectr.right) - ScaleSpriteTrad(15));
555
556 seqf.Draw(preferred_x - ScaleSpriteTrad(14), yf, pal, pal == PALETTE_CRASH);
557 seqr.Draw(preferred_x + ScaleSpriteTrad(15), yr, pal, pal == PALETTE_CRASH);
558 } else {
560 GetRailIcon(engine, false, y, image_type, &seq);
561
562 Rect rect;
563 seq.GetBounds(&rect);
564 preferred_x = Clamp(preferred_x,
565 left - UnScaleGUI(rect.left),
566 right - UnScaleGUI(rect.right));
567
568 seq.Draw(preferred_x, y, pal, pal == PALETTE_CRASH);
569 }
570}
571
581void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type)
582{
583 int y = 0;
584
586 GetRailIcon(engine, false, y, image_type, &seq);
587
588 Rect rect;
589 seq.GetBounds(&rect);
590
591 width = UnScaleGUI(rect.Width());
592 height = UnScaleGUI(rect.Height());
593 xoffs = UnScaleGUI(rect.left);
594 yoffs = UnScaleGUI(rect.top);
595
596 if (RailVehInfo(engine)->railveh_type == RAILVEH_MULTIHEAD) {
597 GetRailIcon(engine, true, y, image_type, &seq);
598 seq.GetBounds(&rect);
599
600 /* Calculate values relative to an imaginary center between the two sprites. */
601 width = ScaleSpriteTrad(TRAININFO_DEFAULT_VEHICLE_WIDTH) + UnScaleGUI(rect.right) - xoffs;
602 height = std::max<uint>(height, UnScaleGUI(rect.Height()));
603 xoffs = xoffs - ScaleSpriteTrad(TRAININFO_DEFAULT_VEHICLE_WIDTH) / 2;
604 yoffs = std::min(yoffs, UnScaleGUI(rect.top));
605 }
606}
607
617{
618 const RailVehicleInfo *rvi = &e->u.rail;
619
620 /* Check that the wagon can drive on the track in question */
621 if (!IsCompatibleRail(rvi->railtype, GetRailType(tile))) return CMD_ERROR;
622
623 if (flags & DC_EXEC) {
624 Train *v = new Train();
625 *ret = v;
626 v->spritenum = rvi->image_index;
627
628 v->engine_type = e->index;
629 v->gcache.first_engine = INVALID_ENGINE; // needs to be set before first callback
630
632
633 v->direction = DiagDirToDir(dir);
634 v->tile = tile;
635
636 int x = TileX(tile) * TILE_SIZE | _vehicle_initial_x_fract[dir];
637 int y = TileY(tile) * TILE_SIZE | _vehicle_initial_y_fract[dir];
638
639 v->x_pos = x;
640 v->y_pos = y;
641 v->z_pos = GetSlopePixelZ(x, y, true);
643 v->track = TRACK_BIT_DEPOT;
645
646 v->SetWagon();
647
648 v->SetFreeWagon();
650
652 assert(IsValidCargoID(v->cargo_type));
653 v->cargo_cap = rvi->capacity;
654 v->refit_cap = 0;
655
656 v->railtype = rvi->railtype;
657
661 v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
662 v->random_bits = Random();
663
665
666 if (TestVehicleBuildProbability(v, v->engine_type, BuildProbabilityType::Reversed)) SetBit(v->flags, VRF_REVERSE_DIRECTION);
668
669 v->UpdatePosition();
670 v->First()->ConsistChanged(CCF_ARRANGE);
672
674
675 /* Try to connect the vehicle to one of free chains of wagons. */
676 for (Train *w : Train::Iterate()) {
677 if (w->tile == tile &&
678 w->IsFreeWagon() &&
679 w->engine_type == e->index &&
680 w->First() != v &&
681 !(w->vehstatus & VS_CRASHED)) {
682 if (Command<CMD_MOVE_RAIL_VEHICLE>::Do(DC_EXEC, v->index, w->Last()->index, true).Succeeded()) {
683 break;
684 }
685 }
686 }
687 }
688
689 return CommandCost();
690}
691
694{
695 assert(u->IsEngine());
696 for (const Train *v : Train::Iterate()) {
697 if (v->IsFreeWagon() && v->tile == u->tile &&
698 v->track == TRACK_BIT_DEPOT) {
699 if (Command<CMD_MOVE_RAIL_VEHICLE>::Do(DC_EXEC, v->index, u->index, true).Failed()) {
700 break;
701 }
702 }
703 }
704}
705
706static void AddRearEngineToMultiheadedTrain(Train *v)
707{
708 Train *u = new Train();
709 v->value >>= 1;
710 u->value = v->value;
711 u->direction = v->direction;
712 u->owner = v->owner;
713 u->tile = v->tile;
714 u->x_pos = v->x_pos;
715 u->y_pos = v->y_pos;
716 u->z_pos = v->z_pos;
717 u->track = TRACK_BIT_DEPOT;
718 u->vehstatus = v->vehstatus & ~VS_STOPPED;
719 u->spritenum = v->spritenum + 1;
720 u->cargo_type = v->cargo_type;
722 u->cargo_cap = v->cargo_cap;
723 u->refit_cap = v->refit_cap;
724 u->railtype = v->railtype;
725 u->engine_type = v->engine_type;
728 u->build_year = v->build_year;
729 u->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
730 u->random_bits = Random();
731 v->SetMultiheaded();
732 u->SetMultiheaded();
733 v->SetNext(u);
734 if (TestVehicleBuildProbability(u, u->engine_type, BuildProbabilityType::Reversed)) SetBit(u->flags, VRF_REVERSE_DIRECTION);
735 u->UpdatePosition();
736
737 /* Now we need to link the front and rear engines together */
738 v->other_multiheaded_part = u;
739 u->other_multiheaded_part = v;
740}
741
751{
752 const RailVehicleInfo *rvi = &e->u.rail;
753
754 if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildRailWagon(flags, tile, e, ret);
755
756 /* Check if depot and new engine uses the same kind of tracks *
757 * We need to see if the engine got power on the tile to avoid electric engines in non-electric depots */
758 if (!HasPowerOnRail(rvi->railtype, GetRailType(tile))) return CMD_ERROR;
759
760 if (flags & DC_EXEC) {
762 int x = TileX(tile) * TILE_SIZE + _vehicle_initial_x_fract[dir];
763 int y = TileY(tile) * TILE_SIZE + _vehicle_initial_y_fract[dir];
764
765 Train *v = new Train();
766 *ret = v;
767 v->direction = DiagDirToDir(dir);
768 v->tile = tile;
770 v->x_pos = x;
771 v->y_pos = y;
772 v->z_pos = GetSlopePixelZ(x, y, true);
773 v->track = TRACK_BIT_DEPOT;
775 v->spritenum = rvi->image_index;
777 assert(IsValidCargoID(v->cargo_type));
778 v->cargo_cap = rvi->capacity;
779 v->refit_cap = 0;
780 v->last_station_visited = INVALID_STATION;
781 v->last_loading_station = INVALID_STATION;
782
783 v->engine_type = e->index;
784 v->gcache.first_engine = INVALID_ENGINE; // needs to be set before first callback
785
786 v->reliability = e->reliability;
789
790 v->railtype = rvi->railtype;
791
792 v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_trains);
796 v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
797 v->random_bits = Random();
798
800 v->SetServiceIntervalIsPercent(Company::Get(_current_company)->settings.vehicle.servint_ispercent);
801
803
804 v->SetFrontEngine();
805 v->SetEngine();
806
807 if (TestVehicleBuildProbability(v, v->engine_type, BuildProbabilityType::Reversed)) SetBit(v->flags, VRF_REVERSE_DIRECTION);
808 v->UpdatePosition();
809
810 if (rvi->railveh_type == RAILVEH_MULTIHEAD) {
811 AddRearEngineToMultiheadedTrain(v);
812 } else {
814 }
815
818
820 }
821
822 return CommandCost();
823}
824
825static Train *FindGoodVehiclePos(const Train *src)
826{
827 EngineID eng = src->engine_type;
828 TileIndex tile = src->tile;
829
830 for (Train *dst : Train::Iterate()) {
831 if (dst->IsFreeWagon() && dst->tile == tile && !(dst->vehstatus & VS_CRASHED)) {
832 /* check so all vehicles in the line have the same engine. */
833 Train *t = dst;
834 while (t->engine_type == eng) {
835 t = t->Next();
836 if (t == nullptr) return dst;
837 }
838 }
839 }
840
841 return nullptr;
842}
843
845typedef std::vector<Train *> TrainList;
846
852static void MakeTrainBackup(TrainList &list, Train *t)
853{
854 for (; t != nullptr; t = t->Next()) list.push_back(t);
855}
856
862{
863 /* No train, nothing to do. */
864 if (list.empty()) return;
865
866 Train *prev = nullptr;
867 /* Iterate over the list and rebuild it. */
868 for (Train *t : list) {
869 if (prev != nullptr) {
870 prev->SetNext(t);
871 } else if (t->Previous() != nullptr) {
872 /* Make sure the head of the train is always the first in the chain. */
873 t->Previous()->SetNext(nullptr);
874 }
875 prev = t;
876 }
877}
878
884static void RemoveFromConsist(Train *part, bool chain = false)
885{
886 Train *tail = chain ? part->Last() : part->GetLastEnginePart();
887
888 /* Unlink at the front, but make it point to the next
889 * vehicle after the to be remove part. */
890 if (part->Previous() != nullptr) part->Previous()->SetNext(tail->Next());
891
892 /* Unlink at the back */
893 tail->SetNext(nullptr);
894}
895
901static void InsertInConsist(Train *dst, Train *chain)
902{
903 /* We do not want to add something in the middle of an articulated part. */
904 assert(dst != nullptr && (dst->Next() == nullptr || !dst->Next()->IsArticulatedPart()));
905
906 chain->Last()->SetNext(dst->Next());
907 dst->SetNext(chain);
908}
909
916{
917 for (; t != nullptr; t = t->GetNextVehicle()) {
918 if (!t->IsMultiheaded() || !t->IsEngine()) continue;
919
920 /* Make sure that there are no free cars before next engine */
921 Train *u;
922 for (u = t; u->Next() != nullptr && !u->Next()->IsEngine(); u = u->Next()) {}
923
924 if (u == t->other_multiheaded_part) continue;
925
926 /* Remove the part from the 'wrong' train */
927 RemoveFromConsist(t->other_multiheaded_part);
928 /* And add it to the 'right' train */
929 InsertInConsist(u, t->other_multiheaded_part);
930 }
931}
932
937static void NormaliseSubtypes(Train *chain)
938{
939 /* Nothing to do */
940 if (chain == nullptr) return;
941
942 /* We must be the first in the chain. */
943 assert(chain->Previous() == nullptr);
944
945 /* Set the appropriate bits for the first in the chain. */
946 if (chain->IsWagon()) {
947 chain->SetFreeWagon();
948 } else {
949 assert(chain->IsEngine());
950 chain->SetFrontEngine();
951 }
952
953 /* Now clear the bits for the rest of the chain */
954 for (Train *t = chain->Next(); t != nullptr; t = t->Next()) {
955 t->ClearFreeWagon();
956 t->ClearFrontEngine();
957 }
958}
959
969static CommandCost CheckNewTrain(Train *original_dst, Train *dst, Train *original_src, Train *src)
970{
971 /* Just add 'new' engines and subtract the original ones.
972 * If that's less than or equal to 0 we can be sure we did
973 * not add any engines (read: trains) along the way. */
974 if ((src != nullptr && src->IsEngine() ? 1 : 0) +
975 (dst != nullptr && dst->IsEngine() ? 1 : 0) -
976 (original_src != nullptr && original_src->IsEngine() ? 1 : 0) -
977 (original_dst != nullptr && original_dst->IsEngine() ? 1 : 0) <= 0) {
978 return CommandCost();
979 }
980
981 /* Get a free unit number and check whether it's within the bounds.
982 * There will always be a maximum of one new train. */
984
985 return CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);
986}
987
994{
995 /* No multi-part train, no need to check. */
996 if (t == nullptr || t->Next() == nullptr) return CommandCost();
997
998 /* The maximum length for a train. For each part we decrease this by one
999 * and if the result is negative the train is simply too long. */
1001
1002 /* For free-wagon chains, check if they are within the max_train_length limit. */
1003 if (!t->IsEngine()) {
1004 t = t->Next();
1005 while (t != nullptr) {
1006 allowed_len -= t->gcache.cached_veh_length;
1007
1008 t = t->Next();
1009 }
1010
1011 if (allowed_len < 0) return CommandCost(STR_ERROR_TRAIN_TOO_LONG);
1012 return CommandCost();
1013 }
1014
1015 Train *head = t;
1016 Train *prev = t;
1017
1018 /* Break the prev -> t link so it always holds within the loop. */
1019 t = t->Next();
1020 prev->SetNext(nullptr);
1021
1022 /* Make sure the cache is cleared. */
1023 head->InvalidateNewGRFCache();
1024
1025 while (t != nullptr) {
1026 allowed_len -= t->gcache.cached_veh_length;
1027
1028 Train *next = t->Next();
1029
1030 /* Unlink the to-be-added piece; it is already unlinked from the previous
1031 * part due to the fact that the prev -> t link is broken. */
1032 t->SetNext(nullptr);
1033
1034 /* Don't check callback for articulated or rear dual headed parts */
1035 if (!t->IsArticulatedPart() && !t->IsRearDualheaded()) {
1036 /* Back up and clear the first_engine data to avoid using wagon override group */
1037 EngineID first_engine = t->gcache.first_engine;
1039
1040 /* We don't want the cache to interfere. head's cache is cleared before
1041 * the loop and after each callback does not need to be cleared here. */
1043
1044 uint16_t callback = GetVehicleCallbackParent(CBID_TRAIN_ALLOW_WAGON_ATTACH, 0, 0, head->engine_type, t, head);
1045
1046 /* Restore original first_engine data */
1047 t->gcache.first_engine = first_engine;
1048
1049 /* We do not want to remember any cached variables from the test run */
1051 head->InvalidateNewGRFCache();
1052
1053 if (callback != CALLBACK_FAILED) {
1054 /* A failing callback means everything is okay */
1055 StringID error = STR_NULL;
1056
1057 if (head->GetGRF()->grf_version < 8) {
1058 if (callback == 0xFD) error = STR_ERROR_INCOMPATIBLE_RAIL_TYPES;
1059 if (callback < 0xFD) error = GetGRFStringID(head->GetGRFID(), 0xD000 + callback);
1060 if (callback >= 0x100) ErrorUnknownCallbackResult(head->GetGRFID(), CBID_TRAIN_ALLOW_WAGON_ATTACH, callback);
1061 } else {
1062 if (callback < 0x400) {
1063 error = GetGRFStringID(head->GetGRFID(), 0xD000 + callback);
1064 } else {
1065 switch (callback) {
1066 case 0x400: // allow if railtypes match (always the case for OpenTTD)
1067 case 0x401: // allow
1068 break;
1069
1070 default: // unknown reason -> disallow
1071 case 0x402: // disallow attaching
1072 error = STR_ERROR_INCOMPATIBLE_RAIL_TYPES;
1073 break;
1074 }
1075 }
1076 }
1077
1078 if (error != STR_NULL) return CommandCost(error);
1079 }
1080 }
1081
1082 /* And link it to the new part. */
1083 prev->SetNext(t);
1084 prev = t;
1085 t = next;
1086 }
1087
1088 if (allowed_len < 0) return CommandCost(STR_ERROR_TRAIN_TOO_LONG);
1089 return CommandCost();
1090}
1091
1102static CommandCost ValidateTrains(Train *original_dst, Train *dst, Train *original_src, Train *src, bool check_limit)
1103{
1104 /* Check whether we may actually construct the trains. */
1106 if (ret.Failed()) return ret;
1107 ret = CheckTrainAttachment(dst);
1108 if (ret.Failed()) return ret;
1109
1110 /* Check whether we need to build a new train. */
1111 return check_limit ? CheckNewTrain(original_dst, dst, original_src, src) : CommandCost();
1112}
1113
1122static void ArrangeTrains(Train **dst_head, Train *dst, Train **src_head, Train *src, bool move_chain)
1123{
1124 /* First determine the front of the two resulting trains */
1125 if (*src_head == *dst_head) {
1126 /* If we aren't moving part(s) to a new train, we are just moving the
1127 * front back and there is not destination head. */
1128 *dst_head = nullptr;
1129 } else if (*dst_head == nullptr) {
1130 /* If we are moving to a new train the head of the move train would become
1131 * the head of the new vehicle. */
1132 *dst_head = src;
1133 }
1134
1135 if (src == *src_head) {
1136 /* If we are moving the front of a train then we are, in effect, creating
1137 * a new head for the train. Point to that. Unless we are moving the whole
1138 * train in which case there is not 'source' train anymore.
1139 * In case we are a multiheaded part we want the complete thing to come
1140 * with us, so src->GetNextUnit(), however... when we are e.g. a wagon
1141 * that is followed by a rear multihead we do not want to include that. */
1142 *src_head = move_chain ? nullptr :
1143 (src->IsMultiheaded() ? src->GetNextUnit() : src->GetNextVehicle());
1144 }
1145
1146 /* Now it's just simply removing the part that we are going to move from the
1147 * source train and *if* the destination is a not a new train add the chain
1148 * at the destination location. */
1149 RemoveFromConsist(src, move_chain);
1150 if (*dst_head != src) InsertInConsist(dst, src);
1151
1152 /* Now normalise the dual heads, that is move the dual heads around in such
1153 * a way that the head and rear of a dual head are in the same train */
1154 NormaliseDualHeads(*src_head);
1155 NormaliseDualHeads(*dst_head);
1156}
1157
1163static void NormaliseTrainHead(Train *head)
1164{
1165 /* Not much to do! */
1166 if (head == nullptr) return;
1167
1168 /* Tell the 'world' the train changed. */
1170 UpdateTrainGroupID(head);
1171
1172 /* Not a front engine, i.e. a free wagon chain. No need to do more. */
1173 if (!head->IsFrontEngine()) return;
1174
1175 /* Update the refit button and window */
1178
1179 /* If we don't have a unit number yet, set one. */
1180 if (head->unitnumber != 0) return;
1181 head->unitnumber = Company::Get(head->owner)->freeunits[head->type].UseID(GetFreeUnitNumber(VEH_TRAIN));
1182}
1183
1193CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID dest_veh, bool move_chain)
1194{
1195 Train *src = Train::GetIfValid(src_veh);
1196 if (src == nullptr) return CMD_ERROR;
1197
1198 CommandCost ret = CheckOwnership(src->owner);
1199 if (ret.Failed()) return ret;
1200
1201 /* Do not allow moving crashed vehicles inside the depot, it is likely to cause asserts later */
1202 if (src->vehstatus & VS_CRASHED) return CMD_ERROR;
1203
1204 /* if nothing is selected as destination, try and find a matching vehicle to drag to. */
1205 Train *dst;
1206 if (dest_veh == INVALID_VEHICLE) {
1207 dst = (src->IsEngine() || (flags & DC_AUTOREPLACE)) ? nullptr : FindGoodVehiclePos(src);
1208 } else {
1209 dst = Train::GetIfValid(dest_veh);
1210 if (dst == nullptr) return CMD_ERROR;
1211
1212 ret = CheckOwnership(dst->owner);
1213 if (ret.Failed()) return ret;
1214
1215 /* Do not allow appending to crashed vehicles, too */
1216 if (dst->vehstatus & VS_CRASHED) return CMD_ERROR;
1217 }
1218
1219 /* if an articulated part is being handled, deal with its parent vehicle */
1220 src = src->GetFirstEnginePart();
1221 if (dst != nullptr) {
1222 dst = dst->GetFirstEnginePart();
1223 }
1224
1225 /* don't move the same vehicle.. */
1226 if (src == dst) return CommandCost();
1227
1228 /* locate the head of the two chains */
1229 Train *src_head = src->First();
1230 Train *dst_head;
1231 if (dst != nullptr) {
1232 dst_head = dst->First();
1233 if (dst_head->tile != src_head->tile) return CMD_ERROR;
1234 /* Now deal with articulated part of destination wagon */
1235 dst = dst->GetLastEnginePart();
1236 } else {
1237 dst_head = nullptr;
1238 }
1239
1240 if (src->IsRearDualheaded()) return CommandCost(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT);
1241
1242 /* When moving all wagons, we can't have the same src_head and dst_head */
1243 if (move_chain && src_head == dst_head) return CommandCost();
1244
1245 /* When moving a multiheaded part to be place after itself, bail out. */
1246 if (!move_chain && dst != nullptr && dst->IsRearDualheaded() && src == dst->other_multiheaded_part) return CommandCost();
1247
1248 /* Check if all vehicles in the source train are stopped inside a depot. */
1249 if (!src_head->IsStoppedInDepot()) return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
1250
1251 /* Check if all vehicles in the destination train are stopped inside a depot. */
1252 if (dst_head != nullptr && !dst_head->IsStoppedInDepot()) return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
1253
1254 /* First make a backup of the order of the trains. That way we can do
1255 * whatever we want with the order and later on easily revert. */
1256 TrainList original_src;
1257 TrainList original_dst;
1258
1259 MakeTrainBackup(original_src, src_head);
1260 MakeTrainBackup(original_dst, dst_head);
1261
1262 /* Also make backup of the original heads as ArrangeTrains can change them.
1263 * For the destination head we do not care if it is the same as the source
1264 * head because in that case it's just a copy. */
1265 Train *original_src_head = src_head;
1266 Train *original_dst_head = (dst_head == src_head ? nullptr : dst_head);
1267
1268 /* We want this information from before the rearrangement, but execute this after the validation.
1269 * original_src_head can't be nullptr; src is by definition != nullptr, so src_head can't be nullptr as
1270 * src->GetFirst() always yields non-nullptr, so eventually original_src_head != nullptr as well. */
1271 bool original_src_head_front_engine = original_src_head->IsFrontEngine();
1272 bool original_dst_head_front_engine = original_dst_head != nullptr && original_dst_head->IsFrontEngine();
1273
1274 /* (Re)arrange the trains in the wanted arrangement. */
1275 ArrangeTrains(&dst_head, dst, &src_head, src, move_chain);
1276
1277 if ((flags & DC_AUTOREPLACE) == 0) {
1278 /* If the autoreplace flag is set we do not need to test for the validity
1279 * because we are going to revert the train to its original state. As we
1280 * assume the original state was correct autoreplace can skip this. */
1281 ret = ValidateTrains(original_dst_head, dst_head, original_src_head, src_head, true);
1282 if (ret.Failed()) {
1283 /* Restore the train we had. */
1284 RestoreTrainBackup(original_src);
1285 RestoreTrainBackup(original_dst);
1286 return ret;
1287 }
1288 }
1289
1290 /* do it? */
1291 if (flags & DC_EXEC) {
1292 /* Remove old heads from the statistics */
1293 if (original_src_head_front_engine) GroupStatistics::CountVehicle(original_src_head, -1);
1294 if (original_dst_head_front_engine) GroupStatistics::CountVehicle(original_dst_head, -1);
1295
1296 /* First normalise the sub types of the chains. */
1297 NormaliseSubtypes(src_head);
1298 NormaliseSubtypes(dst_head);
1299
1300 /* There are 14 different cases:
1301 * 1) front engine gets moved to a new train, it stays a front engine.
1302 * a) the 'next' part is a wagon that becomes a free wagon chain.
1303 * b) the 'next' part is an engine that becomes a front engine.
1304 * c) there is no 'next' part, nothing else happens
1305 * 2) front engine gets moved to another train, it is not a front engine anymore
1306 * a) the 'next' part is a wagon that becomes a free wagon chain.
1307 * b) the 'next' part is an engine that becomes a front engine.
1308 * c) there is no 'next' part, nothing else happens
1309 * 3) front engine gets moved to later in the current train, it is not a front engine anymore.
1310 * a) the 'next' part is a wagon that becomes a free wagon chain.
1311 * b) the 'next' part is an engine that becomes a front engine.
1312 * 4) free wagon gets moved
1313 * a) the 'next' part is a wagon that becomes a free wagon chain.
1314 * b) the 'next' part is an engine that becomes a front engine.
1315 * c) there is no 'next' part, nothing else happens
1316 * 5) non front engine gets moved and becomes a new train, nothing else happens
1317 * 6) non front engine gets moved within a train / to another train, nothing happens
1318 * 7) wagon gets moved, nothing happens
1319 */
1320 if (src == original_src_head && src->IsEngine() && !src->IsFrontEngine()) {
1321 /* Cases #2 and #3: the front engine gets trashed. */
1327 DeleteNewGRFInspectWindow(GSF_TRAINS, src->index);
1329
1330 if (src_head != nullptr && src_head->IsFrontEngine()) {
1331 /* Cases #?b: Transfer order, unit number and other stuff
1332 * to the new front engine. */
1333 src_head->orders = src->orders;
1334 if (src_head->orders != nullptr) src_head->AddToShared(src);
1335 src_head->CopyVehicleConfigAndStatistics(src);
1336 }
1337 /* Remove stuff not valid anymore for non-front engines. */
1339 src->ReleaseUnitNumber();
1340 src->name.clear();
1341 }
1342
1343 /* We weren't a front engine but are becoming one. So
1344 * we should be put in the default group. */
1345 if (original_src_head != src && dst_head == src) {
1348 }
1349
1350 /* Handle 'new engine' part of cases #1b, #2b, #3b, #4b and #5 in NormaliseTrainHead. */
1351 NormaliseTrainHead(src_head);
1352 NormaliseTrainHead(dst_head);
1353
1354 /* Add new heads to statistics.
1355 * This should be done after NormaliseTrainHead due to engine total limit checks in GetFreeUnitNumber. */
1356 if (src_head != nullptr && src_head->IsFrontEngine()) GroupStatistics::CountVehicle(src_head, 1);
1357 if (dst_head != nullptr && dst_head->IsFrontEngine()) GroupStatistics::CountVehicle(dst_head, 1);
1358
1359 if ((flags & DC_NO_CARGO_CAP_CHECK) == 0) {
1360 CheckCargoCapacity(src_head);
1361 CheckCargoCapacity(dst_head);
1362 }
1363
1364 if (src_head != nullptr) src_head->First()->MarkDirty();
1365 if (dst_head != nullptr) dst_head->First()->MarkDirty();
1366
1367 /* We are undoubtedly changing something in the depot and train list. */
1370 } else {
1371 /* We don't want to execute what we're just tried. */
1372 RestoreTrainBackup(original_src);
1373 RestoreTrainBackup(original_dst);
1374 }
1375
1376 return CommandCost();
1377}
1378
1391CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, bool backup_order, ClientID user)
1392{
1394 Train *first = v->First();
1395
1396 if (v->IsRearDualheaded()) return CommandCost(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT);
1397
1398 /* First make a backup of the order of the train. That way we can do
1399 * whatever we want with the order and later on easily revert. */
1400 TrainList original;
1401 MakeTrainBackup(original, first);
1402
1403 /* We need to keep track of the new head and the head of what we're going to sell. */
1404 Train *new_head = first;
1405 Train *sell_head = nullptr;
1406
1407 /* Split the train in the wanted way. */
1408 ArrangeTrains(&sell_head, nullptr, &new_head, v, sell_chain);
1409
1410 /* We don't need to validate the second train; it's going to be sold. */
1411 CommandCost ret = ValidateTrains(nullptr, nullptr, first, new_head, (flags & DC_AUTOREPLACE) == 0);
1412 if (ret.Failed()) {
1413 /* Restore the train we had. */
1414 RestoreTrainBackup(original);
1415 return ret;
1416 }
1417
1418 if (first->orders == nullptr && !OrderList::CanAllocateItem()) {
1419 /* Restore the train we had. */
1420 RestoreTrainBackup(original);
1421 return CommandCost(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS);
1422 }
1423
1425 for (Train *part = sell_head; part != nullptr; part = part->Next()) cost.AddCost(-part->value);
1426
1427 /* do it? */
1428 if (flags & DC_EXEC) {
1429 /* First normalise the sub types of the chain. */
1430 NormaliseSubtypes(new_head);
1431
1432 if (v == first && !sell_chain && new_head != nullptr && new_head->IsFrontEngine()) {
1433 if (v->IsEngine()) {
1434 /* We are selling the front engine. In this case we want to
1435 * 'give' the order, unit number and such to the new head. */
1436 new_head->orders = first->orders;
1437 new_head->AddToShared(first);
1438 DeleteVehicleOrders(first);
1439
1440 /* Copy other important data from the front engine */
1441 new_head->CopyVehicleConfigAndStatistics(first);
1442 }
1443 GroupStatistics::CountVehicle(new_head, 1); // after copying over the profit, if required
1444 } else if (v->IsPrimaryVehicle() && backup_order) {
1445 OrderBackup::Backup(v, user);
1446 }
1447
1448 /* We need to update the information about the train. */
1449 NormaliseTrainHead(new_head);
1450
1451 /* We are undoubtedly changing something in the depot and train list. */
1454
1455 /* Actually delete the sold 'goods' */
1456 delete sell_head;
1457 } else {
1458 /* We don't want to execute what we're just tried. */
1459 RestoreTrainBackup(original);
1460 }
1461
1462 return cost;
1463}
1464
1466{
1467 /* Set common defaults. */
1468 this->x_offs = -1;
1469 this->y_offs = -1;
1470 this->x_extent = 3;
1471 this->y_extent = 3;
1472 this->z_extent = 6;
1473 this->x_bb_offs = 0;
1474 this->y_bb_offs = 0;
1475
1476 /* Set if flipped and engine is NOT flagged with custom flip handling. */
1477 int flipped = HasBit(this->flags, VRF_REVERSE_DIRECTION) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_RAIL_FLIPS);
1478 /* If flipped and vehicle length is odd, we need to adjust the bounding box offset slightly. */
1479 int flip_offs = flipped && (this->gcache.cached_veh_length & 1);
1480
1481 Direction dir = this->direction;
1482 if (flipped) dir = ReverseDir(dir);
1483
1484 if (!IsDiagonalDirection(dir)) {
1485 static const int _sign_table[] =
1486 {
1487 /* x, y */
1488 -1, -1, // DIR_N
1489 -1, 1, // DIR_E
1490 1, 1, // DIR_S
1491 1, -1, // DIR_W
1492 };
1493
1494 int half_shorten = (VEHICLE_LENGTH - this->gcache.cached_veh_length + flipped) / 2;
1495
1496 /* For all straight directions, move the bound box to the centre of the vehicle, but keep the size. */
1497 this->x_offs -= half_shorten * _sign_table[dir];
1498 this->y_offs -= half_shorten * _sign_table[dir + 1];
1499 this->x_extent += this->x_bb_offs = half_shorten * _sign_table[dir];
1500 this->y_extent += this->y_bb_offs = half_shorten * _sign_table[dir + 1];
1501 } else {
1502 switch (dir) {
1503 /* Shorten southern corner of the bounding box according the vehicle length
1504 * and center the bounding box on the vehicle. */
1505 case DIR_NE:
1506 this->x_offs = 1 - (this->gcache.cached_veh_length + 1) / 2 + flip_offs;
1507 this->x_extent = this->gcache.cached_veh_length - 1;
1508 this->x_bb_offs = -1;
1509 break;
1510
1511 case DIR_NW:
1512 this->y_offs = 1 - (this->gcache.cached_veh_length + 1) / 2 + flip_offs;
1513 this->y_extent = this->gcache.cached_veh_length - 1;
1514 this->y_bb_offs = -1;
1515 break;
1516
1517 /* Move northern corner of the bounding box down according to vehicle length
1518 * and center the bounding box on the vehicle. */
1519 case DIR_SW:
1520 this->x_offs = 1 + (this->gcache.cached_veh_length + 1) / 2 - VEHICLE_LENGTH - flip_offs;
1521 this->x_extent = VEHICLE_LENGTH - 1;
1523 break;
1524
1525 case DIR_SE:
1526 this->y_offs = 1 + (this->gcache.cached_veh_length + 1) / 2 - VEHICLE_LENGTH - flip_offs;
1527 this->y_extent = VEHICLE_LENGTH - 1;
1529 break;
1530
1531 default:
1532 NOT_REACHED();
1533 }
1534 }
1535}
1536
1542{
1543 if (!HasBit(v->flags, VRF_TRAIN_STUCK)) {
1544 /* It is the first time the problem occurred, set the "train stuck" flag. */
1545 SetBit(v->flags, VRF_TRAIN_STUCK);
1546
1547 v->wait_counter = 0;
1548
1549 /* Stop train */
1550 v->cur_speed = 0;
1551 v->subspeed = 0;
1552 v->SetLastSpeed();
1553
1555 }
1556}
1557
1565static void SwapTrainFlags(uint16_t *swap_flag1, uint16_t *swap_flag2)
1566{
1567 uint16_t flag1 = *swap_flag1;
1568 uint16_t flag2 = *swap_flag2;
1569
1570 /* Clear the flags */
1571 ClrBit(*swap_flag1, GVF_GOINGUP_BIT);
1572 ClrBit(*swap_flag1, GVF_GOINGDOWN_BIT);
1573 ClrBit(*swap_flag2, GVF_GOINGUP_BIT);
1574 ClrBit(*swap_flag2, GVF_GOINGDOWN_BIT);
1575
1576 /* Reverse the rail-flags (if needed) */
1577 if (HasBit(flag1, GVF_GOINGUP_BIT)) {
1578 SetBit(*swap_flag2, GVF_GOINGDOWN_BIT);
1579 } else if (HasBit(flag1, GVF_GOINGDOWN_BIT)) {
1580 SetBit(*swap_flag2, GVF_GOINGUP_BIT);
1581 }
1582 if (HasBit(flag2, GVF_GOINGUP_BIT)) {
1583 SetBit(*swap_flag1, GVF_GOINGDOWN_BIT);
1584 } else if (HasBit(flag2, GVF_GOINGDOWN_BIT)) {
1585 SetBit(*swap_flag1, GVF_GOINGUP_BIT);
1586 }
1587}
1588
1594{
1595 /* Reverse the direction. */
1596 if (v->track != TRACK_BIT_DEPOT) v->direction = ReverseDir(v->direction);
1597
1598 /* Call the proper EnterTile function unless we are in a wormhole. */
1599 if (v->track != TRACK_BIT_WORMHOLE) {
1600 VehicleEnterTile(v, v->tile, v->x_pos, v->y_pos);
1601 } else {
1602 /* VehicleEnter_TunnelBridge() sets TRACK_BIT_WORMHOLE when the vehicle
1603 * is on the last bit of the bridge head (frame == TILE_SIZE - 1).
1604 * If we were swapped with such a vehicle, we have set TRACK_BIT_WORMHOLE,
1605 * when we shouldn't have. Check if this is the case. */
1606 TileIndex vt = TileVirtXY(v->x_pos, v->y_pos);
1607 if (IsTileType(vt, MP_TUNNELBRIDGE)) {
1608 VehicleEnterTile(v, vt, v->x_pos, v->y_pos);
1609 if (v->track != TRACK_BIT_WORMHOLE && IsBridgeTile(v->tile)) {
1610 /* We have just left the wormhole, possibly set the
1611 * "goingdown" bit. UpdateInclination() can be used
1612 * because we are at the border of the tile. */
1613 v->UpdatePosition();
1614 v->UpdateInclination(true, true);
1615 return;
1616 }
1617 }
1618 }
1619
1620 v->UpdatePosition();
1621 v->UpdateViewport(true, true);
1622}
1623
1630void ReverseTrainSwapVeh(Train *v, int l, int r)
1631{
1632 Train *a, *b;
1633
1634 /* locate vehicles to swap */
1635 for (a = v; l != 0; l--) a = a->Next();
1636 for (b = v; r != 0; r--) b = b->Next();
1637
1638 if (a != b) {
1639 /* swap the hidden bits */
1640 {
1641 uint16_t tmp = (a->vehstatus & ~VS_HIDDEN) | (b->vehstatus & VS_HIDDEN);
1642 b->vehstatus = (b->vehstatus & ~VS_HIDDEN) | (a->vehstatus & VS_HIDDEN);
1643 a->vehstatus = tmp;
1644 }
1645
1646 Swap(a->track, b->track);
1647 Swap(a->direction, b->direction);
1648 Swap(a->x_pos, b->x_pos);
1649 Swap(a->y_pos, b->y_pos);
1650 Swap(a->tile, b->tile);
1651 Swap(a->z_pos, b->z_pos);
1652
1654
1657 } else {
1658 /* Swap GVF_GOINGUP_BIT/GVF_GOINGDOWN_BIT.
1659 * This is a little bit redundant way, a->gv_flags will
1660 * be (re)set twice, but it reduces code duplication */
1663 }
1664}
1665
1666
1673{
1674 return (v->type == VEH_TRAIN) ? v : nullptr;
1675}
1676
1684{
1685 assert(IsLevelCrossingTile(tile));
1686
1687 return HasVehicleOnPos(tile, nullptr, &TrainOnTileEnum);
1688}
1689
1690
1698{
1699 if (v->type != VEH_TRAIN || (v->vehstatus & VS_CRASHED)) return nullptr;
1700
1701 Train *t = Train::From(v);
1702 if (!t->IsFrontEngine()) return nullptr;
1703
1704 TileIndex tile = *(TileIndex *)data;
1705
1706 if (TrainApproachingCrossingTile(t) != tile) return nullptr;
1707
1708 return t;
1709}
1710
1711
1719{
1720 assert(IsLevelCrossingTile(tile));
1721
1723 TileIndex tile_from = tile + TileOffsByDiagDir(dir);
1724
1725 if (HasVehicleOnPos(tile_from, &tile, &TrainApproachingCrossingEnum)) return true;
1726
1727 dir = ReverseDiagDir(dir);
1728 tile_from = tile + TileOffsByDiagDir(dir);
1729
1730 return HasVehicleOnPos(tile_from, &tile, &TrainApproachingCrossingEnum);
1731}
1732
1738static inline bool CheckLevelCrossing(TileIndex tile)
1739{
1740 /* reserved || train on crossing || train approaching crossing */
1742}
1743
1751static void UpdateLevelCrossingTile(TileIndex tile, bool sound, bool force_barred)
1752{
1753 assert(IsLevelCrossingTile(tile));
1754 bool set_barred;
1755
1756 /* We force the crossing to be barred when an adjacent crossing is barred, otherwise let it decide for itself. */
1757 set_barred = force_barred || CheckLevelCrossing(tile);
1758
1759 /* The state has changed */
1760 if (set_barred != IsCrossingBarred(tile)) {
1761 if (set_barred && sound && _settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, tile);
1762 SetCrossingBarred(tile, set_barred);
1763 MarkTileDirtyByTile(tile);
1764 }
1765}
1766
1773void UpdateLevelCrossing(TileIndex tile, bool sound, bool force_bar)
1774{
1775 if (!IsLevelCrossingTile(tile)) return;
1776
1777 bool forced_state = force_bar;
1778
1779 const Axis axis = GetCrossingRoadAxis(tile);
1780 const DiagDirection dir1 = AxisToDiagDir(axis);
1781 const DiagDirection dir2 = ReverseDiagDir(dir1);
1782
1783 /* Check if an adjacent crossing is barred. */
1784 for (DiagDirection dir : { dir1, dir2 }) {
1785 for (TileIndex t = tile; !forced_state && t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, dir)) {
1786 forced_state |= CheckLevelCrossing(t);
1787 }
1788 }
1789
1790 /* Now that we know whether all tiles in this crossing should be barred or open,
1791 * we need to update those tiles. We start with the tile itself, then look along the road axis. */
1792 UpdateLevelCrossingTile(tile, sound, forced_state);
1793 for (DiagDirection dir : { dir1, dir2 }) {
1794 for (TileIndex t = TileAddByDiagDir(tile, dir); t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, dir)) {
1795 UpdateLevelCrossingTile(t, sound, forced_state);
1796 }
1797 }
1798}
1799
1806{
1807 const DiagDirection dir1 = AxisToDiagDir(road_axis);
1808 const DiagDirection dir2 = ReverseDiagDir(dir1);
1809 for (DiagDirection dir : { dir1, dir2 }) {
1810 const TileIndex t = TileAddByDiagDir(tile, dir);
1811 if (t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis) {
1813 }
1814 }
1815}
1816
1823{
1824 const DiagDirection dir1 = AxisToDiagDir(road_axis);
1825 const DiagDirection dir2 = ReverseDiagDir(dir1);
1826 for (DiagDirection dir : { dir1, dir2 }) {
1827 const TileIndexDiff diff = TileOffsByDiagDir(dir);
1828 bool occupied = false;
1829 for (TileIndex t = tile + diff; t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis; t += diff) {
1830 occupied |= CheckLevelCrossing(t);
1831 }
1832 if (occupied) {
1833 /* Mark the immediately adjacent tile dirty */
1834 const TileIndex t = tile + diff;
1835 if (t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis) {
1837 }
1838 } else {
1839 /* Unbar the crossing tiles in this direction as necessary */
1840 for (TileIndex t = tile + diff; t < Map::Size() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == road_axis; t += diff) {
1841 if (IsCrossingBarred(t)) {
1842 /* The crossing tile is barred, unbar it and continue to check the next tile */
1843 SetCrossingBarred(t, false);
1845 } else {
1846 /* The crossing tile is already unbarred, mark the tile dirty and stop checking */
1848 break;
1849 }
1850 }
1851 }
1852 }
1853}
1854
1860static inline void MaybeBarCrossingWithSound(TileIndex tile)
1861{
1862 if (!IsCrossingBarred(tile)) {
1863 SetCrossingReservation(tile, true);
1864 UpdateLevelCrossing(tile, true);
1865 }
1866}
1867
1868
1875{
1876 Train *base = v;
1877 Train *first = base; // first vehicle to move
1878 Train *last = v->Last(); // last vehicle to move
1879 uint length = CountVehiclesInChain(v);
1880
1881 while (length > 2) {
1882 last = last->Previous();
1883 first = first->Next();
1884
1885 int differential = base->CalcNextVehicleOffset() - last->CalcNextVehicleOffset();
1886
1887 /* do not update images now
1888 * negative differential will be handled in AdvanceWagonsAfterSwap() */
1889 for (int i = 0; i < differential; i++) TrainController(first, last->Next());
1890
1891 base = first; // == base->Next()
1892 length -= 2;
1893 }
1894}
1895
1896
1903{
1904 /* first of all, fix the situation when the train was entering a depot */
1905 Train *dep = v; // last vehicle in front of just left depot
1906 while (dep->Next() != nullptr && (dep->track == TRACK_BIT_DEPOT || dep->Next()->track != TRACK_BIT_DEPOT)) {
1907 dep = dep->Next(); // find first vehicle outside of a depot, with next vehicle inside a depot
1908 }
1909
1910 Train *leave = dep->Next(); // first vehicle in a depot we are leaving now
1911
1912 if (leave != nullptr) {
1913 /* 'pull' next wagon out of the depot, so we won't miss it (it could stay in depot forever) */
1914 int d = TicksToLeaveDepot(dep);
1915
1916 if (d <= 0) {
1917 leave->vehstatus &= ~VS_HIDDEN; // move it out of the depot
1918 leave->track = TrackToTrackBits(GetRailDepotTrack(leave->tile));
1919 for (int i = 0; i >= d; i--) TrainController(leave, nullptr); // maybe move it, and maybe let another wagon leave
1920 }
1921 } else {
1922 dep = nullptr; // no vehicle in a depot, so no vehicle leaving a depot
1923 }
1924
1925 Train *base = v;
1926 Train *first = base; // first vehicle to move
1927 Train *last = v->Last(); // last vehicle to move
1928 uint length = CountVehiclesInChain(v);
1929
1930 /* We have to make sure all wagons that leave a depot because of train reversing are moved correctly
1931 * they have already correct spacing, so we have to make sure they are moved how they should */
1932 bool nomove = (dep == nullptr); // If there is no vehicle leaving a depot, limit the number of wagons moved immediately.
1933
1934 while (length > 2) {
1935 /* we reached vehicle (originally) in front of a depot, stop now
1936 * (we would move wagons that are already moved with new wagon length). */
1937 if (base == dep) break;
1938
1939 /* the last wagon was that one leaving a depot, so do not move it anymore */
1940 if (last == dep) nomove = true;
1941
1942 last = last->Previous();
1943 first = first->Next();
1944
1945 int differential = last->CalcNextVehicleOffset() - base->CalcNextVehicleOffset();
1946
1947 /* do not update images now */
1948 for (int i = 0; i < differential; i++) TrainController(first, (nomove ? last->Next() : nullptr));
1949
1950 base = first; // == base->Next()
1951 length -= 2;
1952 }
1953}
1954
1955static bool IsWholeTrainInsideDepot(const Train *v)
1956{
1957 for (const Train *u = v; u != nullptr; u = u->Next()) {
1958 if (u->track != TRACK_BIT_DEPOT || u->tile != v->tile) return false;
1959 }
1960 return true;
1961}
1962
1968{
1969 if (IsRailDepotTile(v->tile)) {
1970 if (IsWholeTrainInsideDepot(v)) return;
1972 }
1973
1974 /* Clear path reservation in front if train is not stuck. */
1976
1977 /* Check if we were approaching a rail/road-crossing */
1979
1980 /* count number of vehicles */
1981 int r = CountVehiclesInChain(v) - 1; // number of vehicles - 1
1982
1984
1985 /* swap start<>end, start+1<>end-1, ... */
1986 int l = 0;
1987 do {
1988 ReverseTrainSwapVeh(v, l++, r--);
1989 } while (l <= r);
1990
1992
1993 if (IsRailDepotTile(v->tile)) {
1995 }
1996
1997 ToggleBit(v->flags, VRF_TOGGLE_REVERSE);
1998
1999 ClrBit(v->flags, VRF_REVERSING);
2000
2001 /* recalculate cached data */
2003
2004 /* update all images */
2005 for (Train *u = v; u != nullptr; u = u->Next()) u->UpdateViewport(false, false);
2006
2007 /* update crossing we were approaching */
2008 if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing);
2009
2010 /* maybe we are approaching crossing now, after reversal */
2011 crossing = TrainApproachingCrossingTile(v);
2012 if (crossing != INVALID_TILE) MaybeBarCrossingWithSound(crossing);
2013
2014 /* If we are inside a depot after reversing, don't bother with path reserving. */
2015 if (v->track == TRACK_BIT_DEPOT) {
2016 /* Can't be stuck here as inside a depot is always a safe tile. */
2018 ClrBit(v->flags, VRF_TRAIN_STUCK);
2019 return;
2020 }
2021
2022 /* VehicleExitDir does not always produce the desired dir for depots and
2023 * tunnels/bridges that is needed for UpdateSignalsOnSegment. */
2024 DiagDirection dir = VehicleExitDir(v->direction, v->track);
2026
2028 /* If we are currently on a tile with conventional signals, we can't treat the
2029 * current tile as a safe tile or we would enter a PBS block without a reservation. */
2030 bool first_tile_okay = !(IsTileType(v->tile, MP_RAILWAY) &&
2032 !IsPbsSignal(GetSignalType(v->tile, FindFirstTrack(v->track))));
2033
2034 /* If we are on a depot tile facing outwards, do not treat the current tile as safe. */
2035 if (IsRailDepotTile(v->tile) && TrackdirToExitdir(v->GetVehicleTrackdir()) == GetRailDepotDirection(v->tile)) first_tile_okay = false;
2036
2038 if (TryPathReserve(v, false, first_tile_okay)) {
2039 /* Do a look-ahead now in case our current tile was already a safe tile. */
2041 } else if (v->current_order.GetType() != OT_LOADING) {
2042 /* Do not wait for a way out when we're still loading */
2044 }
2045 } else if (HasBit(v->flags, VRF_TRAIN_STUCK)) {
2046 /* A train not inside a PBS block can't be stuck. */
2047 ClrBit(v->flags, VRF_TRAIN_STUCK);
2048 v->wait_counter = 0;
2049 }
2050}
2051
2059CommandCost CmdReverseTrainDirection(DoCommandFlag flags, VehicleID veh_id, bool reverse_single_veh)
2060{
2061 Train *v = Train::GetIfValid(veh_id);
2062 if (v == nullptr) return CMD_ERROR;
2063
2065 if (ret.Failed()) return ret;
2066
2067 if (reverse_single_veh) {
2068 /* turn a single unit around */
2069
2071 return CommandCost(STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE_MULTIPLE_UNITS);
2072 }
2073
2074 Train *front = v->First();
2075 /* make sure the vehicle is stopped in the depot */
2076 if (!front->IsStoppedInDepot()) {
2077 return CommandCost(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
2078 }
2079
2080 if (flags & DC_EXEC) {
2082
2088 }
2089 } else {
2090 /* turn the whole train around */
2091 if (!v->IsPrimaryVehicle()) return CMD_ERROR;
2092 if ((v->vehstatus & VS_CRASHED) || v->breakdown_ctr != 0) return CMD_ERROR;
2093
2094 if (flags & DC_EXEC) {
2095 /* Properly leave the station if we are loading and won't be loading anymore */
2096 if (v->current_order.IsType(OT_LOADING)) {
2097 const Vehicle *last = v;
2098 while (last->Next() != nullptr) last = last->Next();
2099
2100 /* not a station || different station --> leave the station */
2101 if (!IsTileType(last->tile, MP_STATION) || GetStationIndex(last->tile) != GetStationIndex(v->tile)) {
2102 v->LeaveStation();
2103 }
2104 }
2105
2106 /* We cancel any 'skip signal at dangers' here */
2107 v->force_proceed = TFP_NONE;
2109
2110 if (_settings_game.vehicle.train_acceleration_model != AM_ORIGINAL && v->cur_speed != 0) {
2111 ToggleBit(v->flags, VRF_REVERSING);
2112 } else {
2113 v->cur_speed = 0;
2114 v->SetLastSpeed();
2117 }
2118
2119 /* Unbunching data is no longer valid. */
2121 }
2122 }
2123 return CommandCost();
2124}
2125
2133{
2134 Train *t = Train::GetIfValid(veh_id);
2135 if (t == nullptr) return CMD_ERROR;
2136
2137 if (!t->IsPrimaryVehicle()) return CMD_ERROR;
2138
2140 if (ret.Failed()) return ret;
2141
2142
2143 if (flags & DC_EXEC) {
2144 /* If we are forced to proceed, cancel that order.
2145 * If we are marked stuck we would want to force the train
2146 * to proceed to the next signal. In the other cases we
2147 * would like to pass the signal at danger and run till the
2148 * next signal we encounter. */
2149 t->force_proceed = t->force_proceed == TFP_SIGNAL ? TFP_NONE : HasBit(t->flags, VRF_TRAIN_STUCK) || t->IsChainInDepot() ? TFP_STUCK : TFP_SIGNAL;
2151
2152 /* Unbunching data is no longer valid. */
2154 }
2155
2156 return CommandCost();
2157}
2158
2166static FindDepotData FindClosestTrainDepot(Train *v, int max_distance)
2167{
2168 assert(!(v->vehstatus & VS_CRASHED));
2169
2170 return YapfTrainFindNearestDepot(v, max_distance);
2171}
2172
2174{
2175 FindDepotData tfdd = FindClosestTrainDepot(this, 0);
2176 if (tfdd.best_length == UINT_MAX) return ClosestDepot();
2177
2178 return ClosestDepot(tfdd.tile, GetDepotIndex(tfdd.tile), tfdd.reverse);
2179}
2180
2182void Train::PlayLeaveStationSound(bool force) const
2183{
2184 static const SoundFx sfx[] = {
2190 };
2191
2192 if (PlayVehicleSound(this, VSE_START, force)) return;
2193
2194 SndPlayVehicleFx(sfx[RailVehInfo(this->engine_type)->engclass], this);
2195}
2196
2202{
2203 /* Don't do any look-ahead if path_backoff_interval is 255. */
2204 if (_settings_game.pf.path_backoff_interval == 255) return;
2205
2206 /* Exit if we are inside a depot. */
2207 if (v->track == TRACK_BIT_DEPOT) return;
2208
2209 switch (v->current_order.GetType()) {
2210 /* Exit if we reached our destination depot. */
2211 case OT_GOTO_DEPOT:
2212 if (v->tile == v->dest_tile) return;
2213 break;
2214
2215 case OT_GOTO_WAYPOINT:
2216 /* If we reached our waypoint, make sure we see that. */
2218 break;
2219
2220 case OT_NOTHING:
2221 case OT_LEAVESTATION:
2222 case OT_LOADING:
2223 /* Exit if the current order doesn't have a destination, but the train has orders. */
2224 if (v->GetNumOrders() > 0) return;
2225 break;
2226
2227 default:
2228 break;
2229 }
2230 /* Exit if we are on a station tile and are going to stop. */
2232
2233 Trackdir td = v->GetVehicleTrackdir();
2234
2235 /* On a tile with a red non-pbs signal, don't look ahead. */
2236 if (IsTileType(v->tile, MP_RAILWAY) && HasSignalOnTrackdir(v->tile, td) &&
2237 !IsPbsSignal(GetSignalType(v->tile, TrackdirToTrack(td))) &&
2239
2240 CFollowTrackRail ft(v);
2241 if (!ft.Follow(v->tile, td)) return;
2242
2244 /* Next tile is not reserved. */
2247 /* If the next tile is a PBS signal, try to make a reservation. */
2250 tracks &= ~TrackCrossesTracks(TrackdirToTrack(ft.old_td));
2251 }
2252 ChooseTrainTrack(v, ft.new_tile, ft.exitdir, tracks, false, nullptr, false);
2253 }
2254 }
2255 }
2256}
2257
2264{
2265 /* bail out if not all wagons are in the same depot or not in a depot at all */
2266 for (const Train *u = v; u != nullptr; u = u->Next()) {
2267 if (u->track != TRACK_BIT_DEPOT || u->tile != v->tile) return false;
2268 }
2269
2270 /* if the train got no power, then keep it in the depot */
2271 if (v->gcache.cached_power == 0) {
2272 v->vehstatus |= VS_STOPPED;
2274 return true;
2275 }
2276
2277 /* Check if we should wait here for unbunching. */
2278 if (v->IsWaitingForUnbunching()) return true;
2279
2280 SigSegState seg_state;
2281
2282 if (v->force_proceed == TFP_NONE) {
2283 /* force proceed was not pressed */
2284 if (++v->wait_counter < 37) {
2286 return true;
2287 }
2288
2289 v->wait_counter = 0;
2290
2292 if (seg_state == SIGSEG_FULL || HasDepotReservation(v->tile)) {
2293 /* Full and no PBS signal in block or depot reserved, can't exit. */
2295 return true;
2296 }
2297 } else {
2299 }
2300
2301 /* We are leaving a depot, but have to go to the exact same one; re-enter. */
2302 if (v->current_order.IsType(OT_GOTO_DEPOT) && v->tile == v->dest_tile) {
2303 /* Service when depot has no reservation. */
2305 return true;
2306 }
2307
2308 /* Only leave when we can reserve a path to our destination. */
2309 if (seg_state == SIGSEG_PBS && !TryPathReserve(v) && v->force_proceed == TFP_NONE) {
2310 /* No path and no force proceed. */
2313 return true;
2314 }
2315
2316 SetDepotReservation(v->tile, true);
2318
2323
2324 v->track = TRACK_BIT_X;
2325 if (v->direction & 2) v->track = TRACK_BIT_Y;
2326
2327 v->vehstatus &= ~VS_HIDDEN;
2328 v->cur_speed = 0;
2329
2330 v->UpdateViewport(true, true);
2331 v->UpdatePosition();
2333 v->UpdateAcceleration();
2335
2336 return false;
2337}
2338
2345static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_dir)
2346{
2347 DiagDirection dir = TrackdirToExitdir(track_dir);
2348
2349 if (IsTileType(tile, MP_TUNNELBRIDGE)) {
2350 /* Are we just leaving a tunnel/bridge? */
2351 if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) {
2353
2354 if (TunnelBridgeIsFree(tile, end, v).Succeeded()) {
2355 /* Free the reservation only if no other train is on the tiles. */
2356 SetTunnelBridgeReservation(tile, false);
2357 SetTunnelBridgeReservation(end, false);
2358
2360 if (IsBridge(tile)) {
2361 MarkBridgeDirty(tile);
2362 } else {
2363 MarkTileDirtyByTile(tile);
2365 }
2366 }
2367 }
2368 }
2369 } else if (IsRailStationTile(tile)) {
2370 TileIndex new_tile = TileAddByDiagDir(tile, dir);
2371 /* If the new tile is not a further tile of the same station, we
2372 * clear the reservation for the whole platform. */
2373 if (!IsCompatibleTrainStationTile(new_tile, tile)) {
2375 }
2376 } else {
2377 /* Any other tile */
2378 UnreserveRailTrack(tile, TrackdirToTrack(track_dir));
2379 }
2380}
2381
2387{
2388 assert(v->IsFrontEngine());
2389
2390 TileIndex tile = v->tile;
2391 Trackdir td = v->GetVehicleTrackdir();
2392 bool free_tile = !(IsRailStationTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE));
2393 StationID station_id = IsRailStationTile(v->tile) ? GetStationIndex(v->tile) : INVALID_STATION;
2394
2395 /* Can't be holding a reservation if we enter a depot. */
2396 if (IsRailDepotTile(tile) && TrackdirToExitdir(td) != GetRailDepotDirection(tile)) return;
2397 if (v->track == TRACK_BIT_DEPOT) {
2398 /* Front engine is in a depot. We enter if some part is not in the depot. */
2399 for (const Train *u = v; u != nullptr; u = u->Next()) {
2400 if (u->track != TRACK_BIT_DEPOT || u->tile != v->tile) return;
2401 }
2402 }
2403 /* Don't free reservation if it's not ours. */
2405
2407 while (ft.Follow(tile, td)) {
2408 tile = ft.new_tile;
2410 td = RemoveFirstTrackdir(&bits);
2411 assert(bits == TRACKDIR_BIT_NONE);
2412
2413 if (!IsValidTrackdir(td)) break;
2414
2415 if (IsTileType(tile, MP_RAILWAY)) {
2416 if (HasSignalOnTrackdir(tile, td) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(td)))) {
2417 /* Conventional signal along trackdir: remove reservation and stop. */
2419 break;
2420 }
2421 if (HasPbsSignalOnTrackdir(tile, td)) {
2422 if (GetSignalStateByTrackdir(tile, td) == SIGNAL_STATE_RED) {
2423 /* Red PBS signal? Can't be our reservation, would be green then. */
2424 break;
2425 } else {
2426 /* Turn the signal back to red. */
2428 MarkTileDirtyByTile(tile);
2429 }
2430 } else if (HasPbsSignalOnTrackdir(tile, ReverseTrackdir(td))) {
2431 /* Reservation passes an opposing path signal. Mark signal for update to re-establish the proper default state. */
2433 } else if (HasSignalOnTrackdir(tile, ReverseTrackdir(td)) && IsOnewaySignal(tile, TrackdirToTrack(td))) {
2434 break;
2435 }
2436 }
2437
2438 /* Don't free first station/bridge/tunnel if we are on it. */
2439 if (free_tile || (!(ft.is_station && GetStationIndex(ft.new_tile) == station_id) && !ft.is_tunnel && !ft.is_bridge)) ClearPathReservation(v, tile, td);
2440
2441 free_tile = true;
2442 }
2443
2445}
2446
2447static const uint8_t _initial_tile_subcoord[6][4][3] = {
2448{{ 15, 8, 1 }, { 0, 0, 0 }, { 0, 8, 5 }, { 0, 0, 0 }},
2449{{ 0, 0, 0 }, { 8, 0, 3 }, { 0, 0, 0 }, { 8, 15, 7 }},
2450{{ 0, 0, 0 }, { 7, 0, 2 }, { 0, 7, 6 }, { 0, 0, 0 }},
2451{{ 15, 8, 2 }, { 0, 0, 0 }, { 0, 0, 0 }, { 8, 15, 6 }},
2452{{ 15, 7, 0 }, { 8, 0, 4 }, { 0, 0, 0 }, { 0, 0, 0 }},
2453{{ 0, 0, 0 }, { 0, 0, 0 }, { 0, 8, 4 }, { 7, 15, 0 }},
2454};
2455
2469static Track DoTrainPathfind(const Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found, bool do_track_reservation, PBSTileInfo *dest, TileIndex *final_dest)
2470{
2471 if (final_dest != nullptr) *final_dest = INVALID_TILE;
2472 return YapfTrainChooseTrack(v, tile, enterdir, tracks, path_found, do_track_reservation, dest, final_dest);
2473}
2474
2480static PBSTileInfo ExtendTrainReservation(const Train *v, TrackBits *new_tracks, DiagDirection *enterdir)
2481{
2483
2484 CFollowTrackRail ft(v);
2485
2486 std::vector<std::pair<TileIndex, Trackdir>> signals_set_to_red;
2487
2488 TileIndex tile = origin.tile;
2489 Trackdir cur_td = origin.trackdir;
2490 while (ft.Follow(tile, cur_td)) {
2492 /* Possible signal tile. */
2494 }
2495
2497 ft.new_td_bits &= ~TrackdirCrossesTrackdirs(ft.old_td);
2498 if (ft.new_td_bits == TRACKDIR_BIT_NONE) break;
2499 }
2500
2501 /* Station, depot or waypoint are a possible target. */
2502 bool target_seen = ft.is_station || (IsTileType(ft.new_tile, MP_RAILWAY) && !IsPlainRail(ft.new_tile));
2503 if (target_seen || KillFirstBit(ft.new_td_bits) != TRACKDIR_BIT_NONE) {
2504 /* Choice found or possible target encountered.
2505 * On finding a possible target, we need to stop and let the pathfinder handle the
2506 * remaining path. This is because we don't know if this target is in one of our
2507 * orders, so we might cause pathfinding to fail later on if we find a choice.
2508 * This failure would cause a bogous call to TryReserveSafePath which might reserve
2509 * a wrong path not leading to our next destination. */
2511
2512 /* If we did skip some tiles, backtrack to the first skipped tile so the pathfinder
2513 * actually starts its search at the first unreserved tile. */
2514 if (ft.tiles_skipped != 0) ft.new_tile -= TileOffsByDiagDir(ft.exitdir) * ft.tiles_skipped;
2515
2516 /* Choice found, path valid but not okay. Save info about the choice tile as well. */
2517 if (new_tracks != nullptr) *new_tracks = TrackdirBitsToTrackBits(ft.new_td_bits);
2518 if (enterdir != nullptr) *enterdir = ft.exitdir;
2519 return PBSTileInfo(ft.new_tile, ft.old_td, false);
2520 }
2521
2522 tile = ft.new_tile;
2523 cur_td = FindFirstTrackdir(ft.new_td_bits);
2524
2525 Trackdir rev_td = ReverseTrackdir(cur_td);
2526 if (IsSafeWaitingPosition(v, tile, cur_td, true, _settings_game.pf.forbid_90_deg)) {
2527 bool wp_free = IsWaitingPositionFree(v, tile, cur_td, _settings_game.pf.forbid_90_deg);
2528 if (!(wp_free && TryReserveRailTrack(tile, TrackdirToTrack(cur_td)))) break;
2529 /* Green path signal opposing the path? Turn to red. */
2530 if (HasPbsSignalOnTrackdir(tile, rev_td) && GetSignalStateByTrackdir(tile, rev_td) == SIGNAL_STATE_GREEN) {
2531 signals_set_to_red.emplace_back(tile, rev_td);
2533 MarkTileDirtyByTile(tile);
2534 }
2535 /* Safe position is all good, path valid and okay. */
2536 return PBSTileInfo(tile, cur_td, true);
2537 }
2538
2539 if (!TryReserveRailTrack(tile, TrackdirToTrack(cur_td))) break;
2540
2541 /* Green path signal opposing the path? Turn to red. */
2542 if (HasPbsSignalOnTrackdir(tile, rev_td) && GetSignalStateByTrackdir(tile, rev_td) == SIGNAL_STATE_GREEN) {
2543 signals_set_to_red.emplace_back(tile, rev_td);
2545 MarkTileDirtyByTile(tile);
2546 }
2547 }
2548
2549 if (ft.err == CFollowTrackRail::EC_OWNER || ft.err == CFollowTrackRail::EC_NO_WAY) {
2550 /* End of line, path valid and okay. */
2551 return PBSTileInfo(ft.old_tile, ft.old_td, true);
2552 }
2553
2554 /* Sorry, can't reserve path, back out. */
2555 tile = origin.tile;
2556 cur_td = origin.trackdir;
2557 TileIndex stopped = ft.old_tile;
2558 Trackdir stopped_td = ft.old_td;
2559 while (tile != stopped || cur_td != stopped_td) {
2560 if (!ft.Follow(tile, cur_td)) break;
2561
2563 ft.new_td_bits &= ~TrackdirCrossesTrackdirs(ft.old_td);
2564 assert(ft.new_td_bits != TRACKDIR_BIT_NONE);
2565 }
2567
2568 tile = ft.new_tile;
2569 cur_td = FindFirstTrackdir(ft.new_td_bits);
2570
2571 UnreserveRailTrack(tile, TrackdirToTrack(cur_td));
2572 }
2573
2574 /* Re-instate green signals we turned to red. */
2575 for (auto [sig_tile, td] : signals_set_to_red) {
2577 }
2578
2579 /* Path invalid. */
2580 return PBSTileInfo();
2581}
2582
2593static bool TryReserveSafeTrack(const Train *v, TileIndex tile, Trackdir td, bool override_railtype)
2594{
2595 return YapfTrainFindNearestSafeTile(v, tile, td, override_railtype);
2596}
2597
2600private:
2601 Train *v;
2602 Order old_order;
2603 TileIndex old_dest_tile;
2604 StationID old_last_station_visited;
2605 VehicleOrderID index;
2606 bool suppress_implicit_orders;
2607 bool restored;
2608
2609public:
2611 v(_v),
2612 old_order(_v->current_order),
2613 old_dest_tile(_v->dest_tile),
2614 old_last_station_visited(_v->last_station_visited),
2615 index(_v->cur_real_order_index),
2616 suppress_implicit_orders(HasBit(_v->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)),
2617 restored(false)
2618 {
2619 }
2620
2624 void Restore()
2625 {
2626 this->v->current_order = this->old_order;
2627 this->v->dest_tile = this->old_dest_tile;
2628 this->v->last_station_visited = this->old_last_station_visited;
2629 AssignBit(this->v->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS, suppress_implicit_orders);
2630 this->restored = true;
2631 }
2632
2637 {
2638 if (!this->restored) this->Restore();
2639 }
2640
2646 bool SwitchToNextOrder(bool skip_first)
2647 {
2648 if (this->v->GetNumOrders() == 0) return false;
2649
2650 if (skip_first) ++this->index;
2651
2652 int depth = 0;
2653
2654 do {
2655 /* Wrap around. */
2656 if (this->index >= this->v->GetNumOrders()) this->index = 0;
2657
2658 Order *order = this->v->GetOrder(this->index);
2659 assert(order != nullptr);
2660
2661 switch (order->GetType()) {
2662 case OT_GOTO_DEPOT:
2663 /* Skip service in depot orders when the train doesn't need service. */
2664 if ((order->GetDepotOrderType() & ODTFB_SERVICE) && !this->v->NeedsServicing()) break;
2665 [[fallthrough]];
2666 case OT_GOTO_STATION:
2667 case OT_GOTO_WAYPOINT:
2668 this->v->current_order = *order;
2669 return UpdateOrderDest(this->v, order, 0, true);
2670 case OT_CONDITIONAL: {
2671 VehicleOrderID next = ProcessConditionalOrder(order, this->v);
2672 if (next != INVALID_VEH_ORDER_ID) {
2673 depth++;
2674 this->index = next;
2675 /* Don't increment next, so no break here. */
2676 continue;
2677 }
2678 break;
2679 }
2680 default:
2681 break;
2682 }
2683 /* Don't increment inside the while because otherwise conditional
2684 * orders can lead to an infinite loop. */
2685 ++this->index;
2686 depth++;
2687 } while (this->index != this->v->cur_real_order_index && depth < this->v->GetNumOrders());
2688
2689 return false;
2690 }
2691};
2692
2693/* choose a track */
2694static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck)
2695{
2696 Track best_track = INVALID_TRACK;
2697 bool do_track_reservation = _settings_game.pf.reserve_paths || force_res;
2698 bool changed_signal = false;
2699 TileIndex final_dest = INVALID_TILE;
2700
2701 assert((tracks & ~TRACK_BIT_MASK) == 0);
2702
2703 if (got_reservation != nullptr) *got_reservation = false;
2704
2705 /* Don't use tracks here as the setting to forbid 90 deg turns might have been switched between reservation and now. */
2706 TrackBits res_tracks = (TrackBits)(GetReservedTrackbits(tile) & DiagdirReachesTracks(enterdir));
2707 /* Do we have a suitable reserved track? */
2708 if (res_tracks != TRACK_BIT_NONE) return FindFirstTrack(res_tracks);
2709
2710 /* Quick return in case only one possible track is available */
2711 if (KillFirstBit(tracks) == TRACK_BIT_NONE) {
2712 Track track = FindFirstTrack(tracks);
2713 /* We need to check for signals only here, as a junction tile can't have signals. */
2714 if (track != INVALID_TRACK && HasPbsSignalOnTrackdir(tile, TrackEnterdirToTrackdir(track, enterdir))) {
2715 do_track_reservation = true;
2716 changed_signal = true;
2718 } else if (!do_track_reservation) {
2719 return track;
2720 }
2721 best_track = track;
2722 }
2723
2724 PBSTileInfo res_dest(tile, INVALID_TRACKDIR, false);
2725 DiagDirection dest_enterdir = enterdir;
2726 if (do_track_reservation) {
2727 res_dest = ExtendTrainReservation(v, &tracks, &dest_enterdir);
2728 if (res_dest.tile == INVALID_TILE) {
2729 /* Reservation failed? */
2730 if (mark_stuck) MarkTrainAsStuck(v);
2731 if (changed_signal) SetSignalStateByTrackdir(tile, TrackEnterdirToTrackdir(best_track, enterdir), SIGNAL_STATE_RED);
2732 return FindFirstTrack(tracks);
2733 }
2734 if (res_dest.okay) {
2735 /* Got a valid reservation that ends at a safe target, quick exit. */
2736 if (got_reservation != nullptr) *got_reservation = true;
2737 if (changed_signal) MarkTileDirtyByTile(tile);
2739 return best_track;
2740 }
2741
2742 /* Check if the train needs service here, so it has a chance to always find a depot.
2743 * Also check if the current order is a service order so we don't reserve a path to
2744 * the destination but instead to the next one if service isn't needed. */
2746 if (v->current_order.IsType(OT_DUMMY) || v->current_order.IsType(OT_CONDITIONAL) || v->current_order.IsType(OT_GOTO_DEPOT)) ProcessOrders(v);
2747 }
2748
2749 /* Save the current train order. The destructor will restore the old order on function exit. */
2750 VehicleOrderSaver orders(v);
2751
2752 /* If the current tile is the destination of the current order and
2753 * a reservation was requested, advance to the next order.
2754 * Don't advance on a depot order as depots are always safe end points
2755 * for a path and no look-ahead is necessary. This also avoids a
2756 * problem with depot orders not part of the order list when the
2757 * order list itself is empty. */
2758 if (v->current_order.IsType(OT_LEAVESTATION)) {
2759 orders.SwitchToNextOrder(false);
2760 } else if (v->current_order.IsType(OT_LOADING) || (!v->current_order.IsType(OT_GOTO_DEPOT) && (
2761 v->current_order.IsType(OT_GOTO_STATION) ?
2763 v->tile == v->dest_tile))) {
2764 orders.SwitchToNextOrder(true);
2765 }
2766
2767 if (res_dest.tile != INVALID_TILE && !res_dest.okay) {
2768 /* Pathfinders are able to tell that route was only 'guessed'. */
2769 bool path_found = true;
2770 TileIndex new_tile = res_dest.tile;
2771
2772 Track next_track = DoTrainPathfind(v, new_tile, dest_enterdir, tracks, path_found, do_track_reservation, &res_dest, &final_dest);
2773 if (new_tile == tile) best_track = next_track;
2774 v->HandlePathfindingResult(path_found);
2775 }
2776
2777 /* No track reservation requested -> finished. */
2778 if (!do_track_reservation) return best_track;
2779
2780 /* A path was found, but could not be reserved. */
2781 if (res_dest.tile != INVALID_TILE && !res_dest.okay) {
2782 if (mark_stuck) MarkTrainAsStuck(v);
2784 return best_track;
2785 }
2786
2787 /* No possible reservation target found, we are probably lost. */
2788 if (res_dest.tile == INVALID_TILE) {
2789 /* Try to find any safe destination. */
2791 if (TryReserveSafeTrack(v, origin.tile, origin.trackdir, false)) {
2792 TrackBits res = GetReservedTrackbits(tile) & DiagdirReachesTracks(enterdir);
2793 best_track = FindFirstTrack(res);
2795 if (got_reservation != nullptr) *got_reservation = true;
2796 if (changed_signal) MarkTileDirtyByTile(tile);
2797 } else {
2799 if (mark_stuck) MarkTrainAsStuck(v);
2800 }
2801 return best_track;
2802 }
2803
2804 if (got_reservation != nullptr) *got_reservation = true;
2805
2806 /* Reservation target found and free, check if it is safe. */
2807 while (!IsSafeWaitingPosition(v, res_dest.tile, res_dest.trackdir, true, _settings_game.pf.forbid_90_deg)) {
2808 /* Extend reservation until we have found a safe position. */
2809 DiagDirection exitdir = TrackdirToExitdir(res_dest.trackdir);
2810 TileIndex next_tile = TileAddByDiagDir(res_dest.tile, exitdir);
2812 if (Rail90DegTurnDisallowed(GetTileRailType(res_dest.tile), GetTileRailType(next_tile))) {
2813 reachable &= ~TrackCrossesTracks(TrackdirToTrack(res_dest.trackdir));
2814 }
2815
2816 /* Get next order with destination. */
2817 if (orders.SwitchToNextOrder(true)) {
2818 PBSTileInfo cur_dest;
2819 bool path_found;
2820 DoTrainPathfind(v, next_tile, exitdir, reachable, path_found, true, &cur_dest, nullptr);
2821 if (cur_dest.tile != INVALID_TILE) {
2822 res_dest = cur_dest;
2823 if (res_dest.okay) continue;
2824 /* Path found, but could not be reserved. */
2826 if (mark_stuck) MarkTrainAsStuck(v);
2827 if (got_reservation != nullptr) *got_reservation = false;
2828 changed_signal = false;
2829 break;
2830 }
2831 }
2832 /* No order or no safe position found, try any position. */
2833 if (!TryReserveSafeTrack(v, res_dest.tile, res_dest.trackdir, true)) {
2835 if (mark_stuck) MarkTrainAsStuck(v);
2836 if (got_reservation != nullptr) *got_reservation = false;
2837 changed_signal = false;
2838 }
2839 break;
2840 }
2841
2843
2844 if (changed_signal) MarkTileDirtyByTile(tile);
2845
2846 orders.Restore();
2847 if (v->current_order.IsType(OT_GOTO_DEPOT) &&
2849 final_dest != INVALID_TILE && IsRailDepotTile(final_dest)) {
2851 v->dest_tile = final_dest;
2853 }
2854
2855 return best_track;
2856}
2857
2866bool TryPathReserve(Train *v, bool mark_as_stuck, bool first_tile_okay)
2867{
2868 assert(v->IsFrontEngine());
2869
2870 /* We have to handle depots specially as the track follower won't look
2871 * at the depot tile itself but starts from the next tile. If we are still
2872 * inside the depot, a depot reservation can never be ours. */
2873 if (v->track == TRACK_BIT_DEPOT) {
2874 if (HasDepotReservation(v->tile)) {
2875 if (mark_as_stuck) MarkTrainAsStuck(v);
2876 return false;
2877 } else {
2878 /* Depot not reserved, but the next tile might be. */
2880 if (HasReservedTracks(next_tile, DiagdirReachesTracks(GetRailDepotDirection(v->tile)))) return false;
2881 }
2882 }
2883
2884 Vehicle *other_train = nullptr;
2885 PBSTileInfo origin = FollowTrainReservation(v, &other_train);
2886 /* The path we are driving on is already blocked by some other train.
2887 * This can only happen in certain situations when mixing path and
2888 * block signals or when changing tracks and/or signals.
2889 * Exit here as doing any further reservations will probably just
2890 * make matters worse. */
2891 if (other_train != nullptr && other_train->index != v->index) {
2892 if (mark_as_stuck) MarkTrainAsStuck(v);
2893 return false;
2894 }
2895 /* If we have a reserved path and the path ends at a safe tile, we are finished already. */
2896 if (origin.okay && (v->tile != origin.tile || first_tile_okay)) {
2897 /* Can't be stuck then. */
2899 ClrBit(v->flags, VRF_TRAIN_STUCK);
2900 return true;
2901 }
2902
2903 /* If we are in a depot, tentatively reserve the depot. */
2904 if (v->track == TRACK_BIT_DEPOT) {
2905 SetDepotReservation(v->tile, true);
2907 }
2908
2909 DiagDirection exitdir = TrackdirToExitdir(origin.trackdir);
2910 TileIndex new_tile = TileAddByDiagDir(origin.tile, exitdir);
2912
2913 if (Rail90DegTurnDisallowed(GetTileRailType(origin.tile), GetTileRailType(new_tile))) reachable &= ~TrackCrossesTracks(TrackdirToTrack(origin.trackdir));
2914
2915 bool res_made = false;
2916 ChooseTrainTrack(v, new_tile, exitdir, reachable, true, &res_made, mark_as_stuck);
2917
2918 if (!res_made) {
2919 /* Free the depot reservation as well. */
2920 if (v->track == TRACK_BIT_DEPOT) SetDepotReservation(v->tile, false);
2921 return false;
2922 }
2923
2924 if (HasBit(v->flags, VRF_TRAIN_STUCK)) {
2925 v->wait_counter = 0;
2927 }
2928 ClrBit(v->flags, VRF_TRAIN_STUCK);
2929 return true;
2930}
2931
2932
2933static bool CheckReverseTrain(const Train *v)
2934{
2936 v->track == TRACK_BIT_DEPOT || v->track == TRACK_BIT_WORMHOLE ||
2937 !(v->direction & 1)) {
2938 return false;
2939 }
2940
2941 assert(v->track != TRACK_BIT_NONE);
2942
2943 return YapfTrainCheckReverse(v);
2944}
2945
2952{
2953 if (station == this->last_station_visited) this->last_station_visited = INVALID_STATION;
2954
2955 const Station *st = Station::Get(station);
2956 if (!(st->facilities & FACIL_TRAIN)) {
2957 /* The destination station has no trainstation tiles. */
2959 return 0;
2960 }
2961
2962 return st->xy;
2963}
2964
2967{
2968 Train *v = this;
2969 do {
2970 v->colourmap = PAL_NONE;
2971 v->UpdateViewport(true, false);
2972 } while ((v = v->Next()) != nullptr);
2973
2974 /* need to update acceleration and cached values since the goods on the train changed. */
2975 this->CargoChanged();
2976 this->UpdateAcceleration();
2977}
2978
2987{
2989 default: NOT_REACHED();
2990 case AM_ORIGINAL:
2991 return this->DoUpdateSpeed(this->acceleration * (this->GetAccelerationStatus() == AS_BRAKE ? -4 : 2), 0, this->GetCurrentMaxSpeed());
2992
2993 case AM_REALISTIC:
2994 return this->DoUpdateSpeed(this->GetAcceleration(), this->GetAccelerationStatus() == AS_BRAKE ? 0 : 2, this->GetCurrentMaxSpeed());
2995 }
2996}
2997
3003static void TrainEnterStation(Train *v, StationID station)
3004{
3005 v->last_station_visited = station;
3006
3007 /* check if a train ever visited this station before */
3008 Station *st = Station::Get(station);
3009 if (!(st->had_vehicle_of_type & HVOT_TRAIN)) {
3010 st->had_vehicle_of_type |= HVOT_TRAIN;
3011 SetDParam(0, st->index);
3013 STR_NEWS_FIRST_TRAIN_ARRIVAL,
3015 v->index,
3016 st->index
3017 );
3018 AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
3019 Game::NewEvent(new ScriptEventStationFirstVehicle(st->index, v->index));
3020 }
3021
3022 v->force_proceed = TFP_NONE;
3024
3025 v->BeginLoading();
3026
3028 TriggerStationAnimation(st, v->tile, SAT_TRAIN_ARRIVES);
3029}
3030
3031/* Check if the vehicle is compatible with the specified tile */
3032static inline bool CheckCompatibleRail(const Train *v, TileIndex tile)
3033{
3034 return IsTileOwner(tile, v->owner) &&
3035 (!v->IsFrontEngine() || HasBit(v->compatible_railtypes, GetRailType(tile)));
3036}
3037
3040 uint8_t small_turn;
3041 uint8_t large_turn;
3042 uint8_t z_up;
3043 uint8_t z_down;
3044};
3045
3048 /* normal accel */
3049 {256 / 4, 256 / 2, 256 / 4, 2},
3050 {256 / 4, 256 / 2, 256 / 4, 2},
3051 {0, 256 / 2, 256 / 4, 2},
3052};
3053
3059static inline void AffectSpeedByZChange(Train *v, int old_z)
3060{
3061 if (old_z == v->z_pos || _settings_game.vehicle.train_acceleration_model != AM_ORIGINAL) return;
3062
3064
3065 if (old_z < v->z_pos) {
3066 v->cur_speed -= (v->cur_speed * asp->z_up >> 8);
3067 } else {
3068 uint16_t spd = v->cur_speed + asp->z_down;
3069 if (spd <= v->gcache.cached_max_track_speed) v->cur_speed = spd;
3070 }
3071}
3072
3073static bool TrainMovedChangeSignals(TileIndex tile, DiagDirection dir)
3074{
3075 if (IsTileType(tile, MP_RAILWAY) &&
3078 Trackdir trackdir = FindFirstTrackdir(tracks);
3079 if (UpdateSignalsOnSegment(tile, TrackdirToExitdir(trackdir), GetTileOwner(tile)) == SIGSEG_PBS && HasSignalOnTrackdir(tile, trackdir)) {
3080 /* A PBS block with a non-PBS signal facing us? */
3081 if (!IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) return true;
3082 }
3083 }
3084 return false;
3085}
3086
3089{
3090 for (const Train *u = this; u != nullptr; u = u->Next()) {
3091 switch (u->track) {
3092 case TRACK_BIT_WORMHOLE:
3094 break;
3095 case TRACK_BIT_DEPOT:
3096 break;
3097 default:
3099 break;
3100 }
3101 }
3102}
3103
3110uint Train::Crash(bool flooded)
3111{
3112 uint victims = 0;
3113 if (this->IsFrontEngine()) {
3114 victims += 2; // driver
3115
3116 /* Remove the reserved path in front of the train if it is not stuck.
3117 * Also clear all reserved tracks the train is currently on. */
3118 if (!HasBit(this->flags, VRF_TRAIN_STUCK)) FreeTrainTrackReservation(this);
3119 for (const Train *v = this; v != nullptr; v = v->Next()) {
3121 if (IsTileType(v->tile, MP_TUNNELBRIDGE)) {
3122 /* ClearPathReservation will not free the wormhole exit
3123 * if the train has just entered the wormhole. */
3125 }
3126 }
3127
3128 /* we may need to update crossing we were approaching,
3129 * but must be updated after the train has been marked crashed */
3130 TileIndex crossing = TrainApproachingCrossingTile(this);
3131 if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing);
3132
3133 /* Remove the loading indicators (if any) */
3135 }
3136
3137 victims += this->GroundVehicleBase::Crash(flooded);
3138
3139 this->crash_anim_pos = flooded ? 4000 : 1; // max 4440, disappear pretty fast when flooded
3140 return victims;
3141}
3142
3149static uint TrainCrashed(Train *v)
3150{
3151 uint victims = 0;
3152
3153 /* do not crash train twice */
3154 if (!(v->vehstatus & VS_CRASHED)) {
3155 victims = v->Crash();
3156 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_TRAIN, victims));
3157 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_TRAIN, victims));
3158 }
3159
3160 /* Try to re-reserve track under already crashed train too.
3161 * Crash() clears the reservation! */
3163
3164 return victims;
3165}
3166
3172
3179static Vehicle *FindTrainCollideEnum(Vehicle *v, void *data)
3180{
3182
3183 /* not a train or in depot */
3184 if (v->type != VEH_TRAIN || Train::From(v)->track == TRACK_BIT_DEPOT) return nullptr;
3185
3186 /* do not crash into trains of another company. */
3187 if (v->owner != tcc->v->owner) return nullptr;
3188
3189 /* get first vehicle now to make most usual checks faster */
3190 Train *coll = Train::From(v)->First();
3191
3192 /* can't collide with own wagons */
3193 if (coll == tcc->v) return nullptr;
3194
3195 int x_diff = v->x_pos - tcc->v->x_pos;
3196 int y_diff = v->y_pos - tcc->v->y_pos;
3197
3198 /* Do fast calculation to check whether trains are not in close vicinity
3199 * and quickly reject trains distant enough for any collision.
3200 * Differences are shifted by 7, mapping range [-7 .. 8] into [0 .. 15]
3201 * Differences are then ORed and then we check for any higher bits */
3202 uint hash = (y_diff + 7) | (x_diff + 7);
3203 if (hash & ~15) return nullptr;
3204
3205 /* Slower check using multiplication */
3206 int min_diff = (Train::From(v)->gcache.cached_veh_length + 1) / 2 + (tcc->v->gcache.cached_veh_length + 1) / 2 - 1;
3207 if (x_diff * x_diff + y_diff * y_diff > min_diff * min_diff) return nullptr;
3208
3209 /* Happens when there is a train under bridge next to bridge head */
3210 if (abs(v->z_pos - tcc->v->z_pos) > 5) return nullptr;
3211
3212 /* crash both trains */
3213 tcc->num += TrainCrashed(tcc->v);
3214 tcc->num += TrainCrashed(coll);
3215
3216 return nullptr; // continue searching
3217}
3218
3227{
3228 /* can't collide in depot */
3229 if (v->track == TRACK_BIT_DEPOT) return false;
3230
3231 assert(v->track == TRACK_BIT_WORMHOLE || TileVirtXY(v->x_pos, v->y_pos) == v->tile);
3232
3234 tcc.v = v;
3235 tcc.num = 0;
3236
3237 /* find colliding vehicles */
3238 if (v->track == TRACK_BIT_WORMHOLE) {
3241 } else {
3243 }
3244
3245 /* any dead -> no crash */
3246 if (tcc.num == 0) return false;
3247
3248 SetDParam(0, tcc.num);
3249 AddTileNewsItem(STR_NEWS_TRAIN_CRASH, NT_ACCIDENT, v->tile);
3250
3251 ModifyStationRatingAround(v->tile, v->owner, -160, 30);
3252 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_13_TRAIN_COLLISION, v);
3253 return true;
3254}
3255
3256static Vehicle *CheckTrainAtSignal(Vehicle *v, void *data)
3257{
3258 if (v->type != VEH_TRAIN || (v->vehstatus & VS_CRASHED)) return nullptr;
3259
3260 Train *t = Train::From(v);
3261 DiagDirection exitdir = *(DiagDirection *)data;
3262
3263 /* not front engine of a train, inside wormhole or depot, crashed */
3264 if (!t->IsFrontEngine() || !(t->track & TRACK_BIT_MASK)) return nullptr;
3265
3266 if (t->cur_speed > 5 || VehicleExitDir(t->direction, t->track) != exitdir) return nullptr;
3267
3268 return t;
3269}
3270
3278bool TrainController(Train *v, Vehicle *nomove, bool reverse)
3279{
3280 Train *first = v->First();
3281 Train *prev;
3282 bool direction_changed = false; // has direction of any part changed?
3283
3284 /* For every vehicle after and including the given vehicle */
3285 for (prev = v->Previous(); v != nomove; prev = v, v = v->Next()) {
3286 DiagDirection enterdir = DIAGDIR_BEGIN;
3287 bool update_signals_crossing = false; // will we update signals or crossing state?
3288
3290 if (v->track != TRACK_BIT_WORMHOLE) {
3291 /* Not inside tunnel */
3292 if (gp.old_tile == gp.new_tile) {
3293 /* Staying in the old tile */
3294 if (v->track == TRACK_BIT_DEPOT) {
3295 /* Inside depot */
3296 gp.x = v->x_pos;
3297 gp.y = v->y_pos;
3298 } else {
3299 /* Not inside depot */
3300
3301 /* Reverse when we are at the end of the track already, do not move to the new position */
3302 if (v->IsFrontEngine() && !TrainCheckIfLineEnds(v, reverse)) return false;
3303
3304 uint32_t r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
3305 if (HasBit(r, VETS_CANNOT_ENTER)) {
3306 goto invalid_rail;
3307 }
3308 if (HasBit(r, VETS_ENTERED_STATION)) {
3309 /* The new position is the end of the platform */
3311 }
3312 }
3313 } else {
3314 /* A new tile is about to be entered. */
3315
3316 /* Determine what direction we're entering the new tile from */
3317 enterdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile);
3318 assert(IsValidDiagDirection(enterdir));
3319
3320 /* Get the status of the tracks in the new tile and mask
3321 * away the bits that aren't reachable. */
3322 TrackStatus ts = GetTileTrackStatus(gp.new_tile, TRANSPORT_RAIL, 0, ReverseDiagDir(enterdir));
3323 TrackdirBits reachable_trackdirs = DiagdirReachesTrackdirs(enterdir);
3324
3325 TrackdirBits trackdirbits = TrackStatusToTrackdirBits(ts) & reachable_trackdirs;
3326 TrackBits red_signals = TrackdirBitsToTrackBits(TrackStatusToRedSignals(ts) & reachable_trackdirs);
3327
3328 TrackBits bits = TrackdirBitsToTrackBits(trackdirbits);
3329 if (Rail90DegTurnDisallowed(GetTileRailType(gp.old_tile), GetTileRailType(gp.new_tile)) && prev == nullptr) {
3330 /* We allow wagons to make 90 deg turns, because forbid_90_deg
3331 * can be switched on halfway a turn */
3332 bits &= ~TrackCrossesTracks(FindFirstTrack(v->track));
3333 }
3334
3335 if (bits == TRACK_BIT_NONE) goto invalid_rail;
3336
3337 /* Check if the new tile constrains tracks that are compatible
3338 * with the current train, if not, bail out. */
3339 if (!CheckCompatibleRail(v, gp.new_tile)) goto invalid_rail;
3340
3341 TrackBits chosen_track;
3342 if (prev == nullptr) {
3343 /* Currently the locomotive is active. Determine which one of the
3344 * available tracks to choose */
3345 chosen_track = TrackToTrackBits(ChooseTrainTrack(v, gp.new_tile, enterdir, bits, false, nullptr, true));
3346 assert(chosen_track & (bits | GetReservedTrackbits(gp.new_tile)));
3347
3348 if (v->force_proceed != TFP_NONE && IsPlainRailTile(gp.new_tile) && HasSignals(gp.new_tile)) {
3349 /* For each signal we find decrease the counter by one.
3350 * We start at two, so the first signal we pass decreases
3351 * this to one, then if we reach the next signal it is
3352 * decreased to zero and we won't pass that new signal. */
3353 Trackdir dir = FindFirstTrackdir(trackdirbits);
3354 if (HasSignalOnTrackdir(gp.new_tile, dir) ||
3356 GetSignalType(gp.new_tile, TrackdirToTrack(dir)) != SIGTYPE_PBS)) {
3357 /* However, we do not want to be stopped by PBS signals
3358 * entered via the back. */
3359 v->force_proceed = (v->force_proceed == TFP_SIGNAL) ? TFP_STUCK : TFP_NONE;
3361 }
3362 }
3363
3364 /* Check if it's a red signal and that force proceed is not clicked. */
3365 if ((red_signals & chosen_track) && v->force_proceed == TFP_NONE) {
3366 /* In front of a red signal */
3367 Trackdir i = FindFirstTrackdir(trackdirbits);
3368
3369 /* Don't handle stuck trains here. */
3370 if (HasBit(v->flags, VRF_TRAIN_STUCK)) return false;
3371
3373 v->cur_speed = 0;
3374 v->subspeed = 0;
3375 v->progress = 255; // make sure that every bit of acceleration will hit the signal again, so speed stays 0.
3377 } else if (HasSignalOnTrackdir(gp.new_tile, i)) {
3378 v->cur_speed = 0;
3379 v->subspeed = 0;
3380 v->progress = 255; // make sure that every bit of acceleration will hit the signal again, so speed stays 0.
3382 DiagDirection exitdir = TrackdirToExitdir(i);
3383 TileIndex o_tile = TileAddByDiagDir(gp.new_tile, exitdir);
3384
3385 exitdir = ReverseDiagDir(exitdir);
3386
3387 /* check if a train is waiting on the other side */
3388 if (!HasVehicleOnPos(o_tile, &exitdir, &CheckTrainAtSignal)) 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 uint32_t r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
3452 if (HasBit(r, VETS_CANNOT_ENTER)) {
3453 goto invalid_rail;
3454 }
3455
3456 if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
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) {
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
3502 if (HasBit(r, VETS_ENTERED_STATION)) {
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 & VS_HIDDEN) == 0) 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
3600{
3601 TrackBits *trackbits = (TrackBits *)data;
3602
3603 if (v->type == VEH_TRAIN && (v->vehstatus & VS_CRASHED) != 0) {
3604 TrackBits train_tbits = Train::From(v)->track;
3605 if (train_tbits == TRACK_BIT_WORMHOLE) {
3606 /* Vehicle is inside a wormhole, v->track contains no useful value then. */
3608 } else if (train_tbits != TRACK_BIT_DEPOT) {
3609 *trackbits |= train_tbits;
3610 }
3611 }
3612
3613 return nullptr;
3614}
3615
3616static bool IsRailStationPlatformOccupied(TileIndex tile)
3617{
3619
3620 for (TileIndex t = tile; IsCompatibleTrainStationTile(t, tile); t -= delta) {
3621 if (HasVehicleOnPos(t, nullptr, &TrainOnTileEnum)) return true;
3622 }
3623 for (TileIndex t = tile + delta; IsCompatibleTrainStationTile(t, tile); t += delta) {
3624 if (HasVehicleOnPos(t, nullptr, &TrainOnTileEnum)) return true;
3625 }
3626
3627 return false;
3628}
3629
3637static void DeleteLastWagon(Train *v)
3638{
3639 Train *first = v->First();
3640
3641 /* Go to the last wagon and delete the link pointing there
3642 * *u is then the one-before-last wagon, and *v the last
3643 * one which will physically be removed */
3644 Train *u = v;
3645 for (; v->Next() != nullptr; v = v->Next()) u = v;
3646 u->SetNext(nullptr);
3647
3648 if (first != v) {
3649 /* Recalculate cached train properties */
3651 /* Update the depot window if the first vehicle is in depot -
3652 * if v == first, then it is updated in PreDestructor() */
3653 if (first->track == TRACK_BIT_DEPOT) {
3655 }
3656 v->last_station_visited = first->last_station_visited; // for PreDestructor
3657 }
3658
3659 /* 'v' shouldn't be accessed after it has been deleted */
3660 TrackBits trackbits = v->track;
3661 TileIndex tile = v->tile;
3662 Owner owner = v->owner;
3663
3664 delete v;
3665 v = nullptr; // make sure nobody will try to read 'v' anymore
3666
3667 if (trackbits == TRACK_BIT_WORMHOLE) {
3668 /* Vehicle is inside a wormhole, v->track contains no useful value then. */
3670 }
3671
3672 Track track = TrackBitsToTrack(trackbits);
3673 if (HasReservedTracks(tile, trackbits)) {
3674 UnreserveRailTrack(tile, track);
3675
3676 /* If there are still crashed vehicles on the tile, give the track reservation to them */
3677 TrackBits remaining_trackbits = TRACK_BIT_NONE;
3678 FindVehicleOnPos(tile, &remaining_trackbits, CollectTrackbitsFromCrashedVehiclesEnum);
3679
3680 /* It is important that these two are the first in the loop, as reservation cannot deal with every trackbit combination */
3681 assert(TRACK_BEGIN == TRACK_X && TRACK_Y == TRACK_BEGIN + 1);
3682 for (Track t : SetTrackBitIterator(remaining_trackbits)) TryReserveRailTrack(tile, t);
3683 }
3684
3685 /* check if the wagon was on a road/rail-crossing */
3687
3688 if (IsRailStationTile(tile)) {
3689 bool occupied = IsRailStationPlatformOccupied(tile);
3691 SetRailStationPlatformReservation(tile, dir, occupied);
3693 }
3694
3695 /* Update signals */
3696 if (IsTileType(tile, MP_TUNNELBRIDGE) || IsRailDepotTile(tile)) {
3698 } else {
3699 SetSignalsOnBothDir(tile, track, owner);
3700 }
3701}
3702
3708{
3709 static const DirDiff delta[] = {
3711 };
3712
3713 do {
3714 /* We don't need to twist around vehicles if they're not visible */
3715 if (!(v->vehstatus & VS_HIDDEN)) {
3716 v->direction = ChangeDir(v->direction, delta[GB(Random(), 0, 2)]);
3717 /* Refrain from updating the z position of the vehicle when on
3718 * a bridge, because UpdateInclination() will put the vehicle under
3719 * the bridge in that case */
3720 if (v->track != TRACK_BIT_WORMHOLE) {
3721 v->UpdatePosition();
3722 v->UpdateInclination(false, true);
3723 } else {
3724 v->UpdateViewport(false, true);
3725 }
3726 }
3727 } while ((v = v->Next()) != nullptr);
3728}
3729
3736{
3737 int state = ++v->crash_anim_pos;
3738
3739 if (state == 4 && !(v->vehstatus & VS_HIDDEN)) {
3741 }
3742
3743 uint32_t r;
3744 if (state <= 200 && Chance16R(1, 7, r)) {
3745 int index = (r * 10 >> 16);
3746
3747 Vehicle *u = v;
3748 do {
3749 if (--index < 0) {
3750 r = Random();
3751
3753 GB(r, 8, 3) + 2,
3754 GB(r, 16, 3) + 2,
3755 GB(r, 0, 3) + 5,
3757 break;
3758 }
3759 } while ((u = u->Next()) != nullptr);
3760 }
3761
3762 if (state <= 240 && !(v->tick_counter & 3)) ChangeTrainDirRandomly(v);
3763
3764 if (state >= 4440 && !(v->tick_counter & 0x1F)) {
3765 bool ret = v->Next() != nullptr;
3766 DeleteLastWagon(v);
3767 return ret;
3768 }
3769
3770 return true;
3771}
3772
3774static const uint16_t _breakdown_speeds[16] = {
3775 225, 210, 195, 180, 165, 150, 135, 120, 105, 90, 75, 60, 45, 30, 15, 15
3776};
3777
3778
3787static bool TrainApproachingLineEnd(Train *v, bool signal, bool reverse)
3788{
3789 /* Calc position within the current tile */
3790 uint x = v->x_pos & 0xF;
3791 uint y = v->y_pos & 0xF;
3792
3793 /* for diagonal directions, 'x' will be 0..15 -
3794 * for other directions, it will be 1, 3, 5, ..., 15 */
3795 switch (v->direction) {
3796 case DIR_N : x = ~x + ~y + 25; break;
3797 case DIR_NW: x = y; [[fallthrough]];
3798 case DIR_NE: x = ~x + 16; break;
3799 case DIR_E : x = ~x + y + 9; break;
3800 case DIR_SE: x = y; break;
3801 case DIR_S : x = x + y - 7; break;
3802 case DIR_W : x = ~y + x + 9; break;
3803 default: break;
3804 }
3805
3806 /* Do not reverse when approaching red signal. Make sure the vehicle's front
3807 * does not cross the tile boundary when we do reverse, but as the vehicle's
3808 * location is based on their center, use half a vehicle's length as offset.
3809 * Multiply the half-length by two for straight directions to compensate that
3810 * we only get odd x offsets there. */
3811 if (!signal && x + (v->gcache.cached_veh_length + 1) / 2 * (IsDiagonalDirection(v->direction) ? 1 : 2) >= TILE_SIZE) {
3812 /* we are too near the tile end, reverse now */
3813 v->cur_speed = 0;
3814 if (reverse) ReverseTrainDirection(v);
3815 return false;
3816 }
3817
3818 /* slow down */
3820 uint16_t break_speed = _breakdown_speeds[x & 0xF];
3821 if (break_speed < v->cur_speed) v->cur_speed = break_speed;
3822
3823 return true;
3824}
3825
3826
3832static bool TrainCanLeaveTile(const Train *v)
3833{
3834 /* Exit if inside a tunnel/bridge or a depot */
3835 if (v->track == TRACK_BIT_WORMHOLE || v->track == TRACK_BIT_DEPOT) return false;
3836
3837 TileIndex tile = v->tile;
3838
3839 /* entering a tunnel/bridge? */
3840 if (IsTileType(tile, MP_TUNNELBRIDGE)) {
3842 if (DiagDirToDir(dir) == v->direction) return false;
3843 }
3844
3845 /* entering a depot? */
3846 if (IsRailDepotTile(tile)) {
3848 if (DiagDirToDir(dir) == v->direction) return false;
3849 }
3850
3851 return true;
3852}
3853
3854
3863{
3864 assert(v->IsFrontEngine());
3865 assert(!(v->vehstatus & VS_CRASHED));
3866
3867 if (!TrainCanLeaveTile(v)) return INVALID_TILE;
3868
3869 DiagDirection dir = VehicleExitDir(v->direction, v->track);
3870 TileIndex tile = v->tile + TileOffsByDiagDir(dir);
3871
3872 /* not a crossing || wrong axis || unusable rail (wrong type or owner) */
3873 if (!IsLevelCrossingTile(tile) || DiagDirToAxis(dir) == GetCrossingRoadAxis(tile) ||
3874 !CheckCompatibleRail(v, tile)) {
3875 return INVALID_TILE;
3876 }
3877
3878 return tile;
3879}
3880
3881
3889static bool TrainCheckIfLineEnds(Train *v, bool reverse)
3890{
3891 /* First, handle broken down train */
3892
3893 int t = v->breakdown_ctr;
3894 if (t > 1) {
3896
3897 uint16_t break_speed = _breakdown_speeds[GB(~t, 4, 4)];
3898 if (break_speed < v->cur_speed) v->cur_speed = break_speed;
3899 } else {
3900 v->vehstatus &= ~VS_TRAIN_SLOWING;
3901 }
3902
3903 if (!TrainCanLeaveTile(v)) return true;
3904
3905 /* Determine the non-diagonal direction in which we will exit this tile */
3906 DiagDirection dir = VehicleExitDir(v->direction, v->track);
3907 /* Calculate next tile */
3908 TileIndex tile = v->tile + TileOffsByDiagDir(dir);
3909
3910 /* Determine the track status on the next tile */
3911 TrackStatus ts = GetTileTrackStatus(tile, TRANSPORT_RAIL, 0, ReverseDiagDir(dir));
3912 TrackdirBits reachable_trackdirs = DiagdirReachesTrackdirs(dir);
3913
3914 TrackdirBits trackdirbits = TrackStatusToTrackdirBits(ts) & reachable_trackdirs;
3915 TrackdirBits red_signals = TrackStatusToRedSignals(ts) & reachable_trackdirs;
3916
3917 /* We are sure the train is not entering a depot, it is detected above */
3918
3919 /* mask unreachable track bits if we are forbidden to do 90deg turns */
3920 TrackBits bits = TrackdirBitsToTrackBits(trackdirbits);
3922 bits &= ~TrackCrossesTracks(FindFirstTrack(v->track));
3923 }
3924
3925 /* no suitable trackbits at all || unusable rail (wrong type or owner) */
3926 if (bits == TRACK_BIT_NONE || !CheckCompatibleRail(v, tile)) {
3927 return TrainApproachingLineEnd(v, false, reverse);
3928 }
3929
3930 /* approaching red signal */
3931 if ((trackdirbits & red_signals) != 0) return TrainApproachingLineEnd(v, true, reverse);
3932
3933 /* approaching a rail/road crossing? then make it red */
3935
3936 return true;
3937}
3938
3939
3940static bool TrainLocoHandler(Train *v, bool mode)
3941{
3942 /* train has crashed? */
3943 if (v->vehstatus & VS_CRASHED) {
3944 return mode ? true : HandleCrashedTrain(v); // 'this' can be deleted here
3945 }
3946
3947 if (v->force_proceed != TFP_NONE) {
3948 ClrBit(v->flags, VRF_TRAIN_STUCK);
3950 }
3951
3952 /* train is broken down? */
3953 if (v->HandleBreakdown()) return true;
3954
3955 if (HasBit(v->flags, VRF_REVERSING) && v->cur_speed == 0) {
3957 }
3958
3959 /* exit if train is stopped */
3960 if ((v->vehstatus & VS_STOPPED) && v->cur_speed == 0) return true;
3961
3962 bool valid_order = !v->current_order.IsType(OT_NOTHING) && v->current_order.GetType() != OT_CONDITIONAL;
3963 if (ProcessOrders(v) && CheckReverseTrain(v)) {
3964 v->wait_counter = 0;
3965 v->cur_speed = 0;
3966 v->subspeed = 0;
3967 ClrBit(v->flags, VRF_LEAVING_STATION);
3969 return true;
3970 } else if (HasBit(v->flags, VRF_LEAVING_STATION)) {
3971 /* Try to reserve a path when leaving the station as we
3972 * might not be marked as wanting a reservation, e.g.
3973 * when an overlength train gets turned around in a station. */
3974 DiagDirection dir = VehicleExitDir(v->direction, v->track);
3976
3978 TryPathReserve(v, true, true);
3979 }
3980 ClrBit(v->flags, VRF_LEAVING_STATION);
3981 }
3982
3983 v->HandleLoading(mode);
3984
3985 if (v->current_order.IsType(OT_LOADING)) return true;
3986
3987 if (CheckTrainStayInDepot(v)) return true;
3988
3989 if (!mode) v->ShowVisualEffect();
3990
3991 /* We had no order but have an order now, do look ahead. */
3992 if (!valid_order && !v->current_order.IsType(OT_NOTHING)) {
3994 }
3995
3996 /* Handle stuck trains. */
3997 if (!mode && HasBit(v->flags, VRF_TRAIN_STUCK)) {
3998 ++v->wait_counter;
3999
4000 /* Should we try reversing this tick if still stuck? */
4002
4003 if (!turn_around && v->wait_counter % _settings_game.pf.path_backoff_interval != 0 && v->force_proceed == TFP_NONE) return true;
4004 if (!TryPathReserve(v)) {
4005 /* Still stuck. */
4006 if (turn_around) ReverseTrainDirection(v);
4007
4009 /* Show message to player. */
4011 SetDParam(0, v->index);
4012 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_STUCK, v->index);
4013 }
4014 v->wait_counter = 0;
4015 }
4016 /* Exit if force proceed not pressed, else reset stuck flag anyway. */
4017 if (v->force_proceed == TFP_NONE) return true;
4018 ClrBit(v->flags, VRF_TRAIN_STUCK);
4019 v->wait_counter = 0;
4021 }
4022 }
4023
4024 if (v->current_order.IsType(OT_LEAVESTATION)) {
4025 v->current_order.Free();
4027 return true;
4028 }
4029
4030 int j = v->UpdateSpeed();
4031
4032 /* we need to invalidate the widget if we are stopping from 'Stopping 0 km/h' to 'Stopped' */
4033 if (v->cur_speed == 0 && (v->vehstatus & VS_STOPPED)) {
4034 /* If we manually stopped, we're not force-proceeding anymore. */
4035 v->force_proceed = TFP_NONE;
4037 }
4038
4039 int adv_spd = v->GetAdvanceDistance();
4040 if (j < adv_spd) {
4041 /* if the vehicle has speed 0, update the last_speed field. */
4042 if (v->cur_speed == 0) v->SetLastSpeed();
4043 } else {
4045 /* Loop until the train has finished moving. */
4046 for (;;) {
4047 j -= adv_spd;
4048 TrainController(v, nullptr);
4049 /* Don't continue to move if the train crashed. */
4050 if (CheckTrainCollision(v)) break;
4051 /* Determine distance to next map position */
4052 adv_spd = v->GetAdvanceDistance();
4053
4054 /* No more moving this tick */
4055 if (j < adv_spd || v->cur_speed == 0) break;
4056
4057 OrderType order_type = v->current_order.GetType();
4058 /* Do not skip waypoints (incl. 'via' stations) when passing through at full speed. */
4059 if ((order_type == OT_GOTO_WAYPOINT || order_type == OT_GOTO_STATION) &&
4061 IsTileType(v->tile, MP_STATION) &&
4063 ProcessOrders(v);
4064 }
4065 }
4066 v->SetLastSpeed();
4067 }
4068
4069 for (Train *u = v; u != nullptr; u = u->Next()) {
4070 if ((u->vehstatus & VS_HIDDEN) != 0) continue;
4071
4072 u->UpdateViewport(false, false);
4073 }
4074
4075 if (v->progress == 0) v->progress = j; // Save unused spd for next time, if TrainController didn't set progress
4076
4077 return true;
4078}
4079
4085{
4086 Money cost = 0;
4087 const Train *v = this;
4088
4089 do {
4090 const Engine *e = v->GetEngine();
4091 if (e->u.rail.running_cost_class == INVALID_PRICE) continue;
4092
4093 uint cost_factor = GetVehicleProperty(v, PROP_TRAIN_RUNNING_COST_FACTOR, e->u.rail.running_cost);
4094 if (cost_factor == 0) continue;
4095
4096 /* Halve running cost for multiheaded parts */
4097 if (v->IsMultiheaded()) cost_factor /= 2;
4098
4099 cost += GetPrice(e->u.rail.running_cost_class, cost_factor, e->GetGRF());
4100 } while ((v = v->GetNextVehicle()) != nullptr);
4101
4102 return cost;
4103}
4104
4110{
4111 this->tick_counter++;
4112
4113 if (this->IsFrontEngine()) {
4115
4116 if (!(this->vehstatus & VS_STOPPED) || this->cur_speed > 0) this->running_ticks++;
4117
4118 this->current_order_time++;
4119
4120 if (!TrainLocoHandler(this, false)) return false;
4121
4122 return TrainLocoHandler(this, true);
4123 } else if (this->IsFreeWagon() && (this->vehstatus & VS_CRASHED)) {
4124 /* Delete flooded standalone wagon chain */
4125 if (++this->crash_anim_pos >= 4400) {
4126 delete this;
4127 return false;
4128 }
4129 }
4130
4131 return true;
4132}
4133
4139{
4140 if (Company::Get(v->owner)->settings.vehicle.servint_trains == 0 || !v->NeedsAutomaticServicing()) return;
4141 if (v->IsChainInDepot()) {
4143 return;
4144 }
4145
4147
4148 FindDepotData tfdd = FindClosestTrainDepot(v, max_penalty);
4149 /* Only go to the depot if it is not too far out of our way. */
4150 if (tfdd.best_length == UINT_MAX || tfdd.best_length > max_penalty) {
4151 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
4152 /* If we were already heading for a depot but it has
4153 * suddenly moved farther away, we continue our normal
4154 * schedule? */
4157 }
4158 return;
4159 }
4160
4161 DepotID depot = GetDepotIndex(tfdd.tile);
4162
4163 if (v->current_order.IsType(OT_GOTO_DEPOT) &&
4164 v->current_order.GetDestination() != depot &&
4165 !Chance16(3, 16)) {
4166 return;
4167 }
4168
4171 v->dest_tile = tfdd.tile;
4173}
4174
4177{
4178 AgeVehicle(this);
4179}
4180
4183{
4184 EconomyAgeVehicle(this);
4185
4186 if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
4187
4188 if (this->IsFrontEngine()) {
4189 CheckVehicleBreakdown(this);
4190
4192
4193 CheckOrders(this);
4194
4195 /* update destination */
4196 if (this->current_order.IsType(OT_GOTO_STATION)) {
4198 if (tile != INVALID_TILE) this->dest_tile = tile;
4199 }
4200
4201 if (this->running_ticks != 0) {
4202 /* running costs */
4204
4205 this->profit_this_year -= cost.GetCost();
4206 this->running_ticks = 0;
4207
4209
4212 }
4213 }
4214}
4215
4221{
4222 if (this->vehstatus & VS_CRASHED) return INVALID_TRACKDIR;
4223
4224 if (this->track == TRACK_BIT_DEPOT) {
4225 /* We'll assume the train is facing outwards */
4226 return DiagDirToDiagTrackdir(GetRailDepotDirection(this->tile)); // Train in depot
4227 }
4228
4229 if (this->track == TRACK_BIT_WORMHOLE) {
4230 /* train in tunnel or on bridge, so just use its direction and assume a diagonal track */
4231 return DiagDirToDiagTrackdir(DirToDiagDir(this->direction));
4232 }
4233
4234 return TrackDirectionToTrackdir(FindFirstTrack(this->track), this->direction);
4235}
4236
4237uint16_t Train::GetMaxWeight() const
4238{
4239 uint16_t weight = CargoSpec::Get(this->cargo_type)->WeightOfNUnitsInTrain(this->GetEngine()->DetermineCapacity(this));
4240
4241 /* Vehicle weight is not added for articulated parts. */
4242 if (!this->IsArticulatedPart()) {
4243 weight += GetVehicleProperty(this, PROP_TRAIN_WEIGHT, RailVehInfo(this->engine_type)->weight);
4244 }
4245
4246 /* Powered wagons have extra weight added. */
4247 if (HasBit(this->flags, VRF_POWEREDWAGON)) {
4248 weight += RailVehInfo(this->gcache.first_engine)->pow_wag_weight;
4249 }
4250
4251 return weight;
4252}
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.
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 ToggleBit(T &x, const uint8_t y)
Toggles a bit in a variable.
constexpr T KillFirstBit(T value)
Clear the first bit in an integer.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
const BridgeSpec * GetBridgeSpec(BridgeType i)
Get the specification of a bridge type.
Definition bridge.h:67
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 CargoID
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:22
bool IsValidCargoID(CargoID t)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:107
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition ai_core.cpp:243
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?
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:127
RailTypes powered_railtypes
bitmask to the OTHER railtypes on which an engine of THIS railtype generates power
Definition rail.h:188
RailTypes compatible_railtypes
bitmask to the OTHER railtypes on which an engine of THIS railtype can physically travel
Definition rail.h:191
uint8_t curve_speed
Multiplier for curve maximum speed advantage.
Definition rail.h:206
uint8_t acceleration_type
Acceleration type of this rail type.
Definition rail.h:226
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.
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
DoCommandFlag
List of flags for a command.
@ DC_AUTOREPLACE
autoreplace/autorenew is in progress, this shall disable vehicle limits when building,...
@ DC_NO_CARGO_CAP_CHECK
when autoreplace/autorenew is in progress, this shall prevent truncating the amount of cargo in the v...
@ DC_EXEC
execute the given command
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.
Owner
Enum for all companies/owners.
DepotID GetDepotIndex(Tile t)
Get the index of which depot is attached to the tile.
Definition depot_map.h:52
uint16_t DepotID
Type for the unique identifier of depots.
Definition depot_type.h:13
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.
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.
DirDiff
Allow incrementing of Direction variables.
@ 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.
Money GetPrice(Price index, uint cost_factor, const GRFFile *grf_file, int shift)
Determine a certain price.
Definition economy.cpp:966
@ 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.
static const EngineID INVALID_ENGINE
Constant denoting an invalid engine.
uint16_t EngineID
Unique identification number of an engine.
Definition engine_type.h:21
@ ENGINE_EXCLUSIVE_PREVIEW
This vehicle is in the exclusive preview stage, either being used or being offered to a company.
@ EF_RAIL_TILTS
Rail vehicle tilts in curves.
@ EF_RAIL_FLIPS
Rail vehicle has old depot-flip handling.
@ RAILVEH_WAGON
simple wagon, not motorized
Definition engine_type.h:29
@ RAILVEH_MULTIHEAD
indicates a combination of two locomotives
Definition engine_type.h:28
Functions related to errors.
void ShowErrorMessage(StringID summary_msg, int x, int y, CommandCost cc)
Display an error message in a window.
@ WL_CRITICAL
Critical errors, the MessageBox is shown in all cases.
Definition error.h:27
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:18
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:19
@ 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 const GroupID DEFAULT_GROUP
Ungrouped vehicles are in this group.
Definition group_type.h:17
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.
DiagDirection DiagdirBetweenTiles(TileIndex tile_from, TileIndex tile_to)
Determines the DiagDirection to get from one tile to another.
Definition map_func.h:620
TileIndex TileAddByDiagDir(TileIndex tile, DiagDirection dir)
Adds a DiagDir to a tile.
Definition map_func.h:608
TileIndexDiff TileOffsByAxis(Axis axis)
Convert an Axis to a TileIndexDiff.
Definition map_func.h:552
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:425
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:415
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition map_func.h:567
static debug_inline TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:404
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
constexpr void Swap(T &a, T &b)
Type safe swap operation.
Miscellaneous command definitions.
void HideFillingPercent(TextEffectID *te_id)
Hide vehicle loading indicators.
Definition misc_gui.cpp:628
bool _networking
are we in networking mode?
Definition network.cpp:65
Basic functions/variables used all over the place.
ClientID
'Unique' identifier to be given to clients
Base for the NewGRF implementation.
@ SAT_TRAIN_ARRIVES
Trigger platform when train arrives.
@ 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.
@ CBM_VEHICLE_LENGTH
Vehicle length (trains and road vehicles)
@ CBM_VEHICLE_ARTIC_ENGINE
Add articulated engines (trains and road vehicles)
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.
@ GBUG_VEH_CAPACITY
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)
Evaluate a newgrf callback for vehicles with a different vehicle for parent scope.
bool UsesWagonOverride(const Vehicle *v)
Check if a wagon is currently using a wagon override.
bool TestVehicleBuildProbability(Vehicle *v, EngineID engine, BuildProbabilityType type)
Test for vehicle build probablity type.
uint16_t GetVehicleCallback(CallbackID callback, uint32_t param1, uint32_t param2, EngineID engine, const Vehicle *v)
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(Station *st, TileIndex trigger_tile, StationRandomTrigger trigger, CargoID cargo_type)
Trigger station randomisation.
Header file for NewGRF stations.
@ SRT_TRAIN_ARRIVES
Trigger platform when train arrives.
StringID GetGRFStringID(uint32_t grfid, StringID stringid)
Returns the index for this stringid associated with its grfID.
Header of Action 04 "universal holder" structure and functions.
Functions related to news.
void AddVehicleAdviceNewsItem(StringID string, VehicleID vehicle)
Adds a vehicle-advice news item.
Definition news_func.h:40
void AddVehicleNewsItem(StringID string, NewsType type, VehicleID vehicle, StationID station=INVALID_STATION)
Adds a newsitem referencing a vehicle.
Definition news_func.h:30
@ NT_ARRIVAL_COMPANY
First vehicle arrived for company.
Definition news_type.h:24
@ NT_ARRIVAL_OTHER
First vehicle arrived for competitor.
Definition news_type.h:25
@ NT_ACCIDENT
An accident or disaster has occurred.
Definition news_type.h:26
@ PM_PAUSED_ERROR
A game paused because a (critical) error.
Definition openttd.h:73
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.
@ ODATFB_NEAREST_DEPOT
Send the vehicle to the nearest depot.
Definition order_type.h:105
OrderStopLocation
Where to stop the trains.
Definition order_type.h:83
@ OSL_PLATFORM_MIDDLE
Stop at the middle of the platform.
Definition order_type.h:85
@ OSL_PLATFORM_FAR_END
Stop at the far end of the platform.
Definition order_type.h:86
@ OSL_PLATFORM_NEAR_END
Stop at the near end of the platform.
Definition order_type.h:84
uint8_t VehicleOrderID
The index of an order within its current vehicle (not pool related)
Definition order_type.h:15
@ ONSF_NO_STOP_AT_DESTINATION_STATION
The vehicle will stop at any station it passes except the destination.
Definition order_type.h:75
@ ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS
The vehicle will not stop at any stations it passes except the destination.
Definition order_type.h:74
@ ODTFB_SERVICE
This depot order is because of the servicing limit.
Definition order_type.h:95
static const VehicleOrderID INVALID_VEH_ORDER_ID
Invalid vehicle order index (sentinel)
Definition order_type.h:21
OrderType
Order types.
Definition order_type.h:35
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:288
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:426
void UnreserveRailTrack(TileIndex tile, Track t)
Lift the reservation of a specific track on a tile.
Definition pbs.cpp:140
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:380
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:155
int TicksToLeaveDepot(const Train *v)
Compute number of ticks when next wagon will leave a depot.
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:322
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:335
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:357
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:307
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:475
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:426
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
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:449
bool HasPbsSignalOnTrackdir(Tile tile, Trackdir td)
Is a pbs signal present along the trackdir?
Definition rail_map.h:463
bool IsOnewaySignal(Tile t, Track track)
One-way signals can't be passed the 'wrong' way.
Definition rail_map.h:319
void SetDepotReservation(Tile t, bool b)
Set the reservation state of the depot.
Definition rail_map.h:270
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:258
@ RAIL_TILE_SIGNALS
Normal rail tile with signals.
Definition rail_map.h:25
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:438
@ RAILTYPES_RAIL
Non-electrified rails.
Definition rail_type.h:46
@ RAILTYPES_NONE
No rail types.
Definition rail_type.h:45
@ RAILTYPE_RAIL
Standard non-electric rails.
Definition rail_type.h:29
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:380
bool IsLevelCrossingTile(Tile t)
Return whether a tile is a level crossing tile.
Definition road_map.h:95
Axis GetCrossingRoadAxis(Tile t)
Get the road axis of a level crossing.
Definition road_map.h:325
void SetCrossingBarred(Tile t, bool barred)
Set the bar state of a level crossing.
Definition road_map.h:428
Axis GetCrossingRailAxis(Tile t)
Get the rail axis of a level crossing.
Definition road_map.h:337
bool IsCrossingBarred(Tile t)
Check if the level crossing is barred.
Definition road_map.h:416
void SetCrossingReservation(Tile t, bool b)
Set the reservation state of the rail crossing.
Definition road_map.h:393
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:57
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:56
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:650
void UpdateSignalsInBuffer()
Update signals in buffer Called from 'outside'.
Definition signal.cpp:576
void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner)
Add side of tile to signal update buffer.
Definition signal.cpp:624
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:668
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
Functions to cache sprites in memory.
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1605
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.
@ FACIL_TRAIN
Station with train station.
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:280
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition strings.cpp:104
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames)
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.
uint16_t vehicle_flags
Used for gradual loading and other miscellaneous things (.
void ResetDepotUnbunching()
Resets all the data used for depot unbunching.
StationFacility facilities
The facilities that this station has.
TileIndex xy
Base tile of the station.
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:48
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 ID.
Definition cargotype.h:139
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.
bool line_reverse_mode
reversing at stations or not
uint16_t cargo_age_period
Number of ticks before carried cargo is aged.
uint8_t misc_flags
Miscellaneous flags.
uint16_t callback_mask
Bitmask of vehicle callbacks that have to be called.
CargoID GetDefaultCargoType() const
Determines the default cargo type of an engine.
uint32_t GetGRFID() const
Retrieve the GRF ID of the NewGRF the engine is tied to.
Definition engine.cpp:157
uint16_t reliability_spd_dec
Speed of reliability decay between services (per day).
Definition engine_base.h:48
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:201
uint8_t original_image_index
Original vehicle image index, thus the image index of the overridden vehicle.
Definition engine_base.h:59
TimerGameCalendar::Date GetLifeLengthInDays() const
Returns the vehicle's (not model's!) life length in days.
Definition engine.cpp:443
uint16_t reliability
Current reliability of the engine.
Definition engine_base.h:47
uint8_t flags
Flags of the engine.
Definition engine_base.h:55
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:146
int traininfo_vehicle_pitch
Vertical offset for drawing train images in depot GUI and vehicle details.
Definition newgrf.h:145
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. INVALID_ENGINE 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:288
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:144
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:201
DestinationID GetDestination() const
Gets the destination of this order.
Definition order_base.h:103
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:70
OrderStopLocation GetStopLocation() const
Where must we stop at the platform?
Definition order_base.h:142
OrderType GetType() const
Get the type of order of this order.
Definition order_base.h:76
void MakeDummy()
Makes this order a Dummy order.
void MakeGoToDepot(DepotID destination, OrderDepotTypeFlags order, OrderNonStopFlags non_stop_type=ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS, OrderDepotActionFlags action=ODATF_SERVICE_ONLY, CargoID cargo=CARGO_NO_REFIT)
Makes this order a Go To Depot order.
Definition order_cmd.cpp:90
void SetDestination(DestinationID destination)
Sets the destination of this order.
Definition order_base.h:110
OrderDepotActionFlags GetDepotActionType() const
What are we going to do when in the depot.
Definition order_base.h:146
void Free()
'Free' the order
Definition order_cmd.cpp:63
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:140
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
Coordinates of a point in 2D.
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()
static Titem * Get(size_t index)
Returns Titem with given index.
Information about a rail vehicle.
Definition engine_type.h:42
uint16_t power
Power of engine (hp); For multiheaded engines the sum of both engine powers.
Definition engine_type.h:50
uint8_t user_def_data
Property 0x25: "User-defined bit mask" Used only for (very few) NewGRF vehicles.
Definition engine_type.h:62
uint8_t running_cost
Running cost of engine; For multiheaded engines the sum of both running costs.
Definition engine_type.h:52
uint8_t shorten_factor
length on main map for this type is 8 - shorten_factor
Definition engine_type.h:59
uint16_t pow_wag_power
Extra power applied to consist if wagon should be powered.
Definition engine_type.h:56
uint16_t max_speed
Maximum speed (1 unit = 1/1.6 mph = 1 km-ish/h)
Definition engine_type.h:49
uint8_t capacity
Cargo capacity of vehicle; For multiheaded engines the capacity of each single engine.
Definition engine_type.h:55
RailType railtype
Railtype, mangled if elrail is disabled.
Definition engine_type.h:46
uint8_t pow_wag_weight
Extra weight applied to consist if wagon should be powered.
Definition engine_type.h:57
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(size_t index)
Gets station with given index.
static T * GetIfValid(size_t index)
Returns vehicle if the index is a valid index for this vehicle type.
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.
T * First() const
Get the first vehicle in the chain.
T * GetNextVehicle() const
Get the next real (non-articulated part) vehicle in the consist.
void UpdateViewport(bool force_update, bool update_delta)
Update vehicle sprite- and position caches.
T * GetLastEnginePart()
Get the last part of an articulated engine.
T * GetFirstEnginePart()
Get the first part of an articulated engine.
T * Last()
Get the last vehicle in the chain.
Station data structure.
uint GetPlatformLength(TileIndex tile, DiagDirection dir) const override
Determines the REMAINING length of a platform, starting at (and including) the given tile.
Definition station.cpp:285
uint16_t cached_max_curve_speed
max consist speed limited by curves
Definition train.h:81
int16_t cached_curve_speed_mod
curve speed modifier of the entire train
Definition train.h:80
bool cached_tilt
train can tilt; feature provides a bonus in curves
Definition train.h:77
Temporary data storage for testing collisions.
Train * v
Vehicle we are testing for collision.
uint num
Total number of victims if train collided.
'Train' is either a loco or a wagon.
Definition train.h:89
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:148
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:91
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:172
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:116
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.
AccelStatus GetAccelerationStatus() const
Checks the current acceleration status of this vehicle.
Definition train.h:273
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:92
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:103
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:131
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:747
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:2350
void AddToShared(Vehicle *shared_chain)
Adds this vehicle to a shared vehicle chain.
Definition vehicle.cpp:2966
VehicleCargoList cargo
The cargo this vehicle is carrying.
uint8_t x_extent
x-extent of vehicle bounding box
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:2419
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:2430
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.
uint8_t z_extent
z-extent of vehicle bounding box
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:2514
int8_t y_offs
y offset for vehicle sprite
debug_inline bool IsFrontEngine() const
Check if the vehicle is a front engine.
uint8_t acceleration
used by train & aircraft
int8_t x_bb_offs
x offset of vehicle bounding box
Order current_order
The current order (+ status, like: loading)
CargoID cargo_type
type of cargo this vehicle is carrying
void HandlePathfindingResult(bool path_found)
Handle the pathfinding result, especially the lost status.
Definition vehicle.cpp:791
Vehicle * Next() const
Get the next vehicle of this vehicle.
int8_t x_offs
x offset for vehicle sprite
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:757
OrderList * orders
Pointer to the order list for this vehicle.
uint8_t y_extent
y-extent of vehicle bounding box
Money value
Value of the vehicle.
uint16_t refit_cap
Capacity left over from before last refit.
void InvalidateNewGRFCache()
Invalidates cached NewGRF variables.
uint8_t vehstatus
Status.
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:767
int8_t y_bb_offs
y offset of vehicle bounding box
void BeginLoading()
Prepare everything to begin the loading when arriving at a station.
Definition vehicle.cpp:2203
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:2561
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:2937
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:1363
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:1693
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:2787
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:272
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.
@ VETS_CANNOT_ENTER
The vehicle cannot enter the tile.
Definition tile_cmd.h:24
@ VETS_ENTERED_WORMHOLE
The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/...
Definition tile_cmd.h:23
@ VETS_ENTERED_STATION
The vehicle entered a station.
Definition tile_cmd.h:22
@ VETS_STATION_ID_OFFSET
Shift the VehicleEnterTileStatus this many bits to the right to get the station ID when VETS_ENTERED_...
Definition tile_cmd.h:31
VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
Call the tile callback function for a vehicle entering a tile.
Definition vehicle.cpp:1838
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
static const uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
@ 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:67
@ INVALID_TRACKDIR
Flag for an invalid trackdir.
Definition track_type.h:86
TrackdirBits
Allow incrementing of Trackdir variables.
Definition track_type.h:98
@ TRACKDIR_BIT_NONE
No track build.
Definition track_type.h:99
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
@ VRF_POWEREDWAGON
Wagon is powered.
Definition train.h:27
@ VRF_LEAVING_STATION
Train is just leaving a station.
Definition train.h:33
@ VRF_TOGGLE_REVERSE
Used for vehicle var 0xFE bit 8 (toggled each time the train is reversed, accurate for first vehicle ...
Definition train.h:31
@ VRF_REVERSE_DIRECTION
Reverse the visible direction of the vehicle.
Definition train.h:28
@ VRF_EL_ENGINE_ALLOWED_NORMAL_RAIL
Electric train engine is allowed to run on normal rail. *‍/.
Definition train.h:30
@ VRF_TRAIN_STUCK
Train can't get a path reservation.
Definition train.h:32
void FreeTrainTrackReservation(const Train *v)
Free the reserved path in front of a vehicle.
ConsistChangeFlags
Flags for Train::ConsistChanged.
Definition train.h:44
@ CCF_CAPACITY
Allow vehicles to change capacity.
Definition train.h:46
@ CCF_ARRANGE
Valid changes for arranging the consist in a depot.
Definition train.h:52
@ CCF_LENGTH
Allow vehicles to change length.
Definition train.h:45
@ CCF_TRACK
Valid changes while vehicle is driving, and possibly changing tracks.
Definition train.h:48
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:40
@ TFP_NONE
Normal operation.
Definition train.h:38
@ TFP_STUCK
Proceed till next signal, but ignore being stuck till then. This includes force leaving depots.
Definition train.h:39
static void AffectSpeedByZChange(Train *v, int old_z)
Modify the speed of the vehicle due to a change in altitude.
CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret)
Build a railroad vehicle.
static void NormaliseTrainHead(Train *head)
Normalise the head of the train again, i.e.
static CommandCost ValidateTrains(Train *original_dst, Train *dst, Train *original_src, Train *src, bool check_limit)
Validate whether we are going to create valid trains.
static bool CheckTrainStayInDepot(Train *v)
Will the train stay in the depot the next tick?
static FindDepotData FindClosestTrainDepot(Train *v, int max_distance)
Try to find a depot nearby.
void UpdateLevelCrossing(TileIndex tile, bool sound, bool force_bar)
Update a level crossing to barred or open (crossing may include multiple adjacent tiles).
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 bool CheckTrainCollision(Train *v)
Checks whether the specified train has a collision with another vehicle.
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:
CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID dest_veh, bool move_chain)
Move a rail vehicle around inside the depot.
static void UpdateLevelCrossingTile(TileIndex tile, bool sound, bool force_barred)
Sets a level crossing tile to the correct state.
static Vehicle * CollectTrackbitsFromCrashedVehiclesEnum(Vehicle *v, void *data)
Collect trackbits of all crashed train vehicles on a tile.
static bool HandleCrashedTrain(Train *v)
Handle a crashed train.
void NormalizeTrainVehInDepot(const Train *u)
Move all free vehicles in the depot to the train.
static bool CheckLevelCrossing(TileIndex tile)
Check if a level crossing should be barred.
uint8_t FreightWagonMult(CargoID cargo)
Return the cargo weight multiplier to use for a rail vehicle.
Definition train_cmd.cpp:69
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.
static void AdvanceWagonsAfterSwap(Train *v)
Advances wagons for train reversing, needed for variable length wagons.
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.
CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, bool backup_order, ClientID user)
Sell a (single) train wagon/engine.
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.
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 CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret)
Build a railroad wagon.
static Vehicle * FindTrainCollideEnum(Vehicle *v, void *data)
Collision test function.
CommandCost CmdForceTrainProceed(DoCommandFlag flags, VehicleID veh_id)
Force a train through a red signal.
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.
void CheckTrainsLengths()
Checks if lengths of all rail vehicles are valid.
Definition train_cmd.cpp:76
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.
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.
static Vehicle * TrainOnTileEnum(Vehicle *v, void *)
Check if the vehicle is a train.
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 void UpdateStatusAfterSwap(Train *v)
Updates some variables after swapping the vehicle.
static Vehicle * TrainApproachingCrossingEnum(Vehicle *v, void *data)
Checks if a train is approaching a rail-road crossing.
CommandCost CmdReverseTrainDirection(DoCommandFlag flags, VehicleID veh_id, bool reverse_single_veh)
Reverse train.
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 VehicleEnterDepot(Vehicle *v)
Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it,...
Definition vehicle.cpp:1552
UnitID GetFreeUnitNumber(VehicleType type)
Get an unused unit number for a vehicle (if allowed).
Definition vehicle.cpp:1895
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:346
void VehicleServiceInDepot(Vehicle *v)
Service a vehicle and all subsequent vehicles in the consist.
Definition vehicle.cpp:167
void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
Find a vehicle from a specific location.
Definition vehicle.cpp:445
GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
Get position information of a vehicle when moving one pixel in the direction it is facing.
Definition vehicle.cpp:1784
bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
Checks whether a vehicle is on a specific location.
Definition vehicle.cpp:520
void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
Find a vehicle from a specific location.
Definition vehicle.cpp:505
void DecreaseVehicleValue(Vehicle *v)
Decrease the value of a vehicle.
Definition vehicle.cpp:1301
void EconomyAgeVehicle(Vehicle *v)
Update economy age of a vehicle.
Definition vehicle.cpp:1429
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
Finds vehicle in tunnel / bridge.
Definition vehicle.cpp:575
void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
Definition vehicle.cpp:317
void AgeVehicle(Vehicle *v)
Update age of a vehicle.
Definition vehicle.cpp:1441
@ VF_BUILT_AS_PROTOTYPE
Vehicle is a prototype (accepted as exclusive preview).
@ VS_TRAIN_SLOWING
Train is slowing down.
@ VS_STOPPED
Vehicle is stopped by the player.
@ VS_HIDDEN
Vehicle is not visible.
@ VS_CRASHED
Vehicle is crashed.
@ VS_DEFPAL
Use default vehicle palette.
@ VE_DISABLE_WAGON_POWER
Flag to disable wagon power.
Functions related to vehicles.
@ VIWD_CONSIST_CHANGED
Vehicle composition was changed.
Definition vehicle_gui.h:37
EngineImageType
Visualisation contexts of vehicles and engines.
@ VEH_TRAIN
Train vehicle type.
uint32_t VehicleID
The type all our vehicle IDs have.
static const VehicleID INVALID_VEHICLE
Constant representing a non-existing vehicle.
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:1140
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3127
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:3219
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:3114
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3101
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:3236
@ 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