OpenTTD: src/vehicle.cpp Source File

OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

vehicle.cpp
Go to the documentation of this file.
1 /* $Id: vehicle.cpp 27904 2017-08-31 06:47:17Z adf88 $ */
2
3 /*
4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
8 */
9
12 #include "stdafx.h"
13 #include "error.h"
14 #include "roadveh.h"
15 #include "ship.h"
16 #include "spritecache.h"
17 #include "timetable.h"
18 #include "viewport_func.h"
19 #include "news_func.h"
20 #include "command_func.h"
21 #include "company_func.h"
22 #include "train.h"
23 #include "aircraft.h"
24 #include "newgrf_debug.h"
25 #include "newgrf_sound.h"
26 #include "newgrf_station.h"
27 #include "group_gui.h"
28 #include "strings_func.h"
29 #include "zoom_func.h"
30 #include "date_func.h"
31 #include "vehicle_func.h"
32 #include "autoreplace_func.h"
33 #include "autoreplace_gui.h"
34 #include "station_base.h"
35 #include "ai/ai.hpp"
36 #include "depot_func.h"
37 #include "network/network.h"
38 #include "core/pool_func.hpp"
39 #include "economy_base.h"
40 #include "articulated_vehicles.h"
41 #include "roadstop_base.h"
42 #include "core/random_func.hpp"
43 #include "core/backup_type.hpp"
44 #include "order_backup.h"
45 #include "sound_func.h"
46 #include "effectvehicle_func.h"
47 #include "effectvehicle_base.h"
48 #include "vehiclelist.h"
49 #include "bridge_map.h"
50 #include "tunnel_map.h"
51 #include "depot_map.h"
52 #include "gamelog.h"
53 #include "linkgraph/linkgraph.h"
54 #include "linkgraph/refresh.h"
55
56 #include "table/strings.h"
57
58 #include "safeguards.h"
59
60 #define GEN_HASH(x, y) ((GB((y), 6 + ZOOM_LVL_SHIFT, 6) << 6) + GB((x), 7 + ZOOM_LVL_SHIFT, 6))
61
62 VehicleID _new_vehicle_id;
63 uint16 _returned_refit_capacity;
64 uint16 _returned_mail_refit_capacity;
65
66
68 VehiclePool _vehicle_pool("Vehicle");
69 INSTANTIATE_POOL_METHODS(Vehicle)
70
71
72
76 void VehicleSpriteSeq::GetBounds(Rect *bounds) const
77 {
78 bounds->left = bounds->top = bounds->right = bounds->bottom = 0;
79 for (uint i = 0; i < this->count; ++i) {
80 const Sprite *spr = GetSprite(this->seq[i].sprite, ST_NORMAL);
81 if (i == 0) {
82 bounds->left = spr->x_offs;
83 bounds->top = spr->y_offs;
84 bounds->right = spr->width + spr->x_offs - 1;
85 bounds->bottom = spr->height + spr->y_offs - 1;
86 } else {
87 if (spr->x_offs < bounds->left) bounds->left = spr->x_offs;
88 if (spr->y_offs < bounds->top) bounds->top = spr->y_offs;
89 int right = spr->width + spr->x_offs - 1;
90 int bottom = spr->height + spr->y_offs - 1;
91 if (right > bounds->right) bounds->right = right;
92 if (bottom > bounds->bottom) bounds->bottom = bottom;
93 }
94 }
95 }
96
104 void VehicleSpriteSeq::Draw(int x, int y, PaletteID default_pal, bool force_pal) const
105 {
106 for (uint i = 0; i < this->count; ++i) {
107 PaletteID pal = force_pal || !this->seq[i].pal ? default_pal : this->seq[i].pal;
108 DrawSprite(this->seq[i].sprite, pal, x, y);
109 }
110 }
111
118 bool Vehicle::NeedsAutorenewing(const Company *c, bool use_renew_setting) const
119 {
120 /* We can always generate the Company pointer when we have the vehicle.
121 * However this takes time and since the Company pointer is often present
122 * when this function is called then it's faster to pass the pointer as an
123 * argument rather than finding it again. */
124 assert(c == Company::Get(this->owner));
125
126 if (use_renew_setting && !c->settings.engine_renew) return false;
127 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
128
129 /* Only engines need renewing */
130 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
131
132 return true;
133 }
134
140 void VehicleServiceInDepot(Vehicle *v)
141 {
142 assert(v != NULL);
143 SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
144
145 do {
146 v->date_of_last_service = _date;
147 v->breakdowns_since_last_service = 0;
148 v->reliability = v->GetEngine()->reliability;
149 /* Prevent vehicles from breaking down directly after exiting the depot. */
150 v->breakdown_chance /= 4;
151 v = v->Next();
152 } while (v != NULL && v->HasEngineType());
153 }
154
161 bool Vehicle::NeedsServicing() const
162 {
163 /* Stopped or crashed vehicles will not move, as such making unmovable
164 * vehicles to go for service is lame. */
165 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
166
167 /* Are we ready for the next service cycle? */
168 const Company *c = Company::Get(this->owner);
169 if (this->ServiceIntervalIsPercent() ?
170 (this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) :
171 (this->date_of_last_service + this->GetServiceInterval() >= _date)) {
172 return false;
173 }
174
175 /* If we're servicing anyway, because we have not disabled servicing when
176 * there are no breakdowns or we are playing with breakdowns, bail out. */
177 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
178 _settings_game.difficulty.vehicle_breakdowns != 0) {
179 return true;
180 }
181
182 /* Test whether there is some pending autoreplace.
183 * Note: We do this after the service-interval test.
184 * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
185 bool pending_replace = false;
186 Money needed_money = c->settings.engine_renew_money;
187 if (needed_money > c->money) return false;
188
189 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
190 bool replace_when_old = false;
191 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
192
193 /* Check engine availability */
194 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
195 /* Is the vehicle old if we are not always replacing? */
196 if (replace_when_old && !v->NeedsAutorenewing(c, false)) continue;
197
198 /* Check refittability */
199 uint32 available_cargo_types, union_mask;
200 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
201 /* Is there anything to refit? */
202 if (union_mask != 0) {
203 CargoID cargo_type;
204 /* We cannot refit to mixed cargoes in an automated way */
205 if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
206
207 /* Did the old vehicle carry anything? */
208 if (cargo_type != CT_INVALID) {
209 /* We can't refit the vehicle to carry the cargo we want */
210 if (!HasBit(available_cargo_types, cargo_type)) continue;
211 }
212 }
213
214 /* Check money.
215 * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
216 pending_replace = true;
217 needed_money += 2 * Engine::Get(new_engine)->GetCost();
218 if (needed_money > c->money) return false;
219 }
220
221 return pending_replace;
222 }
223
229 bool Vehicle::NeedsAutomaticServicing() const
230 {
231 if (this->HasDepotOrder()) return false;
232 if (this->current_order.IsType(OT_LOADING)) return false;
233 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
234 return NeedsServicing();
235 }
236
237 uint Vehicle::Crash(bool flooded)
238 {
239 assert((this->vehstatus & VS_CRASHED) == 0);
240 assert(this->Previous() == NULL); // IsPrimaryVehicle fails for free-wagon-chains
241
242 uint pass = 0;
243 /* Stop the vehicle. */
244 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
245 /* crash all wagons, and count passengers */
246 for (Vehicle *v = this; v != NULL; v = v->Next()) {
247 /* We do not transfer reserver cargo back, so TotalCount() instead of StoredCount() */
248 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.TotalCount();
249 v->vehstatus |= VS_CRASHED;
250 v->MarkAllViewportsDirty();
251 }
252
253 /* Dirty some windows */
254 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
255 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
256 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
257 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
258
259 delete this->cargo_payment;
260 assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
261
262 return RandomRange(pass + 1); // Randomise deceased passengers.
263 }
264
265
274 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
275 {
276 const Engine *e = Engine::Get(engine);
277 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
278
279 /* Missing GRF. Nothing useful can be done in this situation. */
280 if (grfconfig == NULL) return;
281
282 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
283 SetBit(grfconfig->grf_bugs, bug_type);
284 SetDParamStr(0, grfconfig->GetName());
285 SetDParam(1, engine);
286 ShowErrorMessage(part1, part2, WL_CRITICAL);
287 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
288 }
289
290 /* debug output */
291 char buffer[512];
292
293 SetDParamStr(0, grfconfig->GetName());
294 GetString(buffer, part1, lastof(buffer));
295 DEBUG(grf, 0, "%s", buffer + 3);
296
297 SetDParam(1, engine);
298 GetString(buffer, part2, lastof(buffer));
299 DEBUG(grf, 0, "%s", buffer + 3);
300 }
301
307 void VehicleLengthChanged(const Vehicle *u)
308 {
309 /* show a warning once for each engine in whole game and once for each GRF after each game load */
310 const Engine *engine = u->GetEngine();
311 uint32 grfid = engine->grf_prop.grffile->grfid;
312 GRFConfig *grfconfig = GetGRFConfig(grfid);
313 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
314 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
315 }
316 }
317
322 Vehicle::Vehicle(VehicleType type)
323 {
324 this->type = type;
325 this->coord.left = INVALID_COORD;
326 this->group_id = DEFAULT_GROUP;
327 this->fill_percent_te_id = INVALID_TE_ID;
328 this->first = this;
329 this->colourmap = PAL_NONE;
330 this->cargo_age_counter = 1;
331 this->last_station_visited = INVALID_STATION;
332 this->last_loading_station = INVALID_STATION;
333 }
334
339 byte VehicleRandomBits()
340 {
341 return GB(Random(), 0, 8);
342 }
343
344 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
345 * lookup times at the expense of memory usage. */
346 const int HASH_BITS = 7;
347 const int HASH_SIZE = 1 << HASH_BITS;
348 const int HASH_MASK = HASH_SIZE - 1;
349 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
350 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
351
352 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
353 * Profiling results show that 0 is fastest. */
354 const int HASH_RES = 0;
355
356 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
357
358 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
359 {
360 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
361 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
362 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
363 for (; v != NULL; v = v->hash_tile_next) {
364 Vehicle *a = proc(v, data);
365 if (find_first && a != NULL) return a;
366 }
367 if (x == xu) break;
368 }
369 if (y == yu) break;
370 }
371
372 return NULL;
373 }
374
375
387 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
388 {
389 const int COLL_DIST = 6;
390
391 /* Hash area to scan is from xl,yl to xu,yu */
392 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
393 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
394 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
395 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
396
397 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
398 }
399
414 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
415 {
416 VehicleFromPosXY(x, y, data, proc, false);
417 }
418
430 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
431 {
432 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
433 }
434
445 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
446 {
447 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
448 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
449
450 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
451 for (; v != NULL; v = v->hash_tile_next) {
452 if (v->tile != tile) continue;
453
454 Vehicle *a = proc(v, data);
455 if (find_first && a != NULL) return a;
456 }
457
458 return NULL;
459 }
460
474 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
475 {
476 VehicleFromPos(tile, data, proc, false);
477 }
478
489 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
490 {
491 return VehicleFromPos(tile, data, proc, true) != NULL;
492 }
493
500 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
501 {
502 int z = *(int*)data;
503
504 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
505 if (v->z_pos > z) return NULL;
506
507 return v;
508 }
509
515 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
516 {
517 int z = GetTileMaxPixelZ(tile);
518
519 /* Value v is not safe in MP games, however, it is used to generate a local
520 * error message only (which may be different for different machines).
521 * Such a message does not affect MP synchronisation.
522 */
523 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
524 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
525 return CommandCost();
526 }
527
529 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
530 {
531 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
532 if (v == (const Vehicle *)data) return NULL;
533
534 return v;
535 }
536
544 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
545 {
546 /* Value v is not safe in MP games, however, it is used to generate a local
547 * error message only (which may be different for different machines).
548 * Such a message does not affect MP synchronisation.
549 */
550 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
551 if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
552
553 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
554 return CommandCost();
555 }
556
557 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
558 {
559 TrackBits rail_bits = *(TrackBits *)data;
560
561 if (v->type != VEH_TRAIN) return NULL;
562
563 Train *t = Train::From(v);
564 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
565
566 return v;
567 }
568
577 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
578 {
579 /* Value v is not safe in MP games, however, it is used to generate a local
580 * error message only (which may be different for different machines).
581 * Such a message does not affect MP synchronisation.
582 */
583 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
584 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
585 return CommandCost();
586 }
587
588 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
589 {
590 Vehicle **old_hash = v->hash_tile_current;
591 Vehicle **new_hash;
592
593 if (remove) {
594 new_hash = NULL;
595 } else {
596 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
597 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
598 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
599 }
600
601 if (old_hash == new_hash) return;
602
603 /* Remove from the old position in the hash table */
604 if (old_hash != NULL) {
605 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
606 *v->hash_tile_prev = v->hash_tile_next;
607 }
608
609 /* Insert vehicle at beginning of the new position in the hash table */
610 if (new_hash != NULL) {
611 v->hash_tile_next = *new_hash;
612 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
613 v->hash_tile_prev = new_hash;
614 *new_hash = v;
615 }
616
617 /* Remember current hash position */
618 v->hash_tile_current = new_hash;
619 }
620
621 static Vehicle *_vehicle_viewport_hash[0x1000];
622
623 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
624 {
625 Vehicle **old_hash, **new_hash;
626 int old_x = v->coord.left;
627 int old_y = v->coord.top;
628
629 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
630 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
631
632 if (old_hash == new_hash) return;
633
634 /* remove from hash table? */
635 if (old_hash != NULL) {
636 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
637 *v->hash_viewport_prev = v->hash_viewport_next;
638 }
639
640 /* insert into hash table? */
641 if (new_hash != NULL) {
642 v->hash_viewport_next = *new_hash;
643 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
644 v->hash_viewport_prev = new_hash;
645 *new_hash = v;
646 }
647 }
648
649 void ResetVehicleHash()
650 {
651 Vehicle *v;
652 FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
653 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
654 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
655 }
656
657 void ResetVehicleColourMap()
658 {
659 Vehicle *v;
660 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
661 }
662
667 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
668 static AutoreplaceMap _vehicles_to_autoreplace;
669
670 void InitializeVehicles()
671 {
672 _vehicles_to_autoreplace.Reset();
673 ResetVehicleHash();
674 }
675
676 uint CountVehiclesInChain(const Vehicle *v)
677 {
678 uint count = 0;
679 do count++; while ((v = v->Next()) != NULL);
680 return count;
681 }
682
687 bool Vehicle::IsEngineCountable() const
688 {
689 switch (this->type) {
690 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
691 case VEH_TRAIN:
692 return !this->IsArticulatedPart() && // tenders and other articulated parts
693 !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
694 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
695 case VEH_SHIP: return true;
696 default: return false; // Only count company buildable vehicles
697 }
698 }
699
704 bool Vehicle::HasEngineType() const
705 {
706 switch (this->type) {
707 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
708 case VEH_TRAIN:
709 case VEH_ROAD:
710 case VEH_SHIP: return true;
711 default: return false;
712 }
713 }
714
720 const Engine *Vehicle::GetEngine() const
721 {
722 return Engine::Get(this->engine_type);
723 }
724
730 const GRFFile *Vehicle::GetGRF() const
731 {
732 return this->GetEngine()->GetGRF();
733 }
734
740 uint32 Vehicle::GetGRFID() const
741 {
742 return this->GetEngine()->GetGRFID();
743 }
744
752 void Vehicle::HandlePathfindingResult(bool path_found)
753 {
754 if (path_found) {
755 /* Route found, is the vehicle marked with "lost" flag? */
756 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
757
758 /* Clear the flag as the PF's problem was solved. */
759 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
760 /* Delete the news item. */
761 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
762 return;
763 }
764
765 /* Were we already lost? */
766 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
767
768 /* It is first time the problem occurred, set the "lost" flag. */
769 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
770 /* Notify user about the event. */
771 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
772 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
773 SetDParam(0, this->index);
774 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST, this->index);
775 }
776 }
777
779 void Vehicle::PreDestructor()
780 {
781 if (CleaningPool()) return;
782
783 if (Station::IsValidID(this->last_station_visited)) {
784 Station *st = Station::Get(this->last_station_visited);
785 st->loading_vehicles.remove(this);
786
787 HideFillingPercent(&this->fill_percent_te_id);
788 this->CancelReservation(INVALID_STATION, st);
789 delete this->cargo_payment;
790 assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
791 }
792
793 if (this->IsEngineCountable()) {
794 GroupStatistics::CountEngine(this, -1);
795 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
796 GroupStatistics::UpdateAutoreplace(this->owner);
797
798 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
799 DeleteGroupHighlightOfVehicle(this);
800 }
801
802 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
803 Aircraft *a = Aircraft::From(this);
804 Station *st = GetTargetAirportIfValid(a);
805 if (st != NULL) {
806 const AirportFTA *layout = st->airport.GetFTA()->layout;
807 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
808 }
809 }
810
811
812 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
813 RoadVehicle *v = RoadVehicle::From(this);
814 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
815 /* Leave the drive through roadstop, when you have not already left it. */
816 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
817 }
818 }
819
820 if (this->Previous() == NULL) {
821 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
822 }
823
824 if (this->IsPrimaryVehicle()) {
825 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
826 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
827 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
828 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
829 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
830 SetWindowDirty(WC_COMPANY, this->owner);
831 OrderBackup::ClearVehicle(this);
832 }
833 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
834
835 this->cargo.Truncate();
836 DeleteVehicleOrders(this);
837 DeleteDepotHighlightOfVehicle(this);
838
839 extern void StopGlobalFollowVehicle(const Vehicle *v);
840 StopGlobalFollowVehicle(this);
841
842 ReleaseDisastersTargetingVehicle(this->index);
843 }
844
845 Vehicle::~Vehicle()
846 {
847 if (CleaningPool()) {
848 this->cargo.OnCleanPool();
849 return;
850 }
851
852 /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
853 * it may happen that vehicle chain is deleted when visible */
854 if (!(this->vehstatus & VS_HIDDEN)) this->MarkAllViewportsDirty();
855
856 Vehicle *v = this->Next();
857 this->SetNext(NULL);
858
859 delete v;
860
861 UpdateVehicleTileHash(this, true);
862 UpdateVehicleViewportHash(this, INVALID_COORD, 0);
863 DeleteVehicleNews(this->index, INVALID_STRING_ID);
864 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
865 }
866
871 void VehicleEnteredDepotThisTick(Vehicle *v)
872 {
873 /* Vehicle should stop in the depot if it was in 'stopping' state */
874 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
875
876 /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
877 * stopping in the depot, so we stop it to ensure that it will not reserve
878 * the path out of the depot before we might autoreplace it to a different
879 * engine. The new engine would not own the reserved path we store that we
880 * stopped the vehicle, so autoreplace can start it again */
881 v->vehstatus |= VS_STOPPED;
882 }
883
889 static void RunVehicleDayProc()
890 {
891 if (_game_mode != GM_NORMAL) return;
892
893 /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
894 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
895 Vehicle *v = Vehicle::Get(i);
896 if (v == NULL) continue;
897
898 /* Call the 32-day callback if needed */
899 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
900 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
901 if (callback != CALLBACK_FAILED) {
902 if (HasBit(callback, 0)) {
903 TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
904 }
905
906 /* After a vehicle trigger, the graphics and properties of the vehicle could change.
907 * Note: MarkDirty also invalidates the palette, which is the meaning of bit 1. So, nothing special there. */
908 if (callback != 0) v->First()->MarkDirty();
909
910 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
911 }
912 }
913
914 /* This is called once per day for each vehicle, but not in the first tick of the day */
915 v->OnNewDay();
916 }
917 }
918
919 void CallVehicleTicks()
920 {
921 _vehicles_to_autoreplace.Clear();
922
923 RunVehicleDayProc();
924
925 Station *st;
926 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
927
928 Vehicle *v;
929 FOR_ALL_VEHICLES(v) {
930 /* Vehicle could be deleted in this tick */
931 if (!v->Tick()) {
932 assert(Vehicle::Get(vehicle_index) == NULL);
933 continue;
934 }
935
936 assert(Vehicle::Get(vehicle_index) == v);
937
938 switch (v->type) {
939 default: break;
940
941 case VEH_TRAIN:
942 case VEH_ROAD:
943 case VEH_AIRCRAFT:
944 case VEH_SHIP: {
945 Vehicle *front = v->First();
946
947 if (v->vcache.cached_cargo_age_period != 0) {
948 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
949 if (--v->cargo_age_counter == 0) {
950 v->cargo.AgeCargo();
951 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
952 }
953 }
954
955 /* Do not play any sound when crashed */
956 if (front->vehstatus & VS_CRASHED) continue;
957
958 /* Do not play any sound when in depot or tunnel */
959 if (v->vehstatus & VS_HIDDEN) continue;
960
961 /* Do not play any sound when stopped */
962 if ((front->vehstatus & VS_STOPPED) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue;
963
964 /* Check vehicle type specifics */
965 switch (v->type) {
966 case VEH_TRAIN:
967 if (Train::From(v)->IsWagon()) continue;
968 break;
969
970 case VEH_ROAD:
971 if (!RoadVehicle::From(v)->IsFrontEngine()) continue;
972 break;
973
974 case VEH_AIRCRAFT:
975 if (!Aircraft::From(v)->IsNormalAircraft()) continue;
976 break;
977
978 default:
979 break;
980 }
981
982 v->motion_counter += front->cur_speed;
983 /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
984 if (GB(v->motion_counter, 0, 8) < front->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
985
986 /* Play an alternating running sound every 16 ticks */
987 if (GB(v->tick_counter, 0, 4) == 0) {
988 /* Play running sound when speed > 0 and not braking */
989 bool running = (front->cur_speed > 0) && !(front->vehstatus & (VS_STOPPED | VS_TRAIN_SLOWING));
990 PlayVehicleSound(v, running ? VSE_RUNNING_16 : VSE_STOPPED_16);
991 }
992
993 break;
994 }
995 }
996 }
997
998 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
999 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
1000 v = it->first;
1001 /* Autoreplace needs the current company set as the vehicle owner */
1002 cur_company.Change(v->owner);
1003
1004 /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
1005 * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
1006 * they are already leaving the depot again before being replaced. */
1007 if (it->second) v->vehstatus &= ~VS_STOPPED;
1008
1009 /* Store the position of the effect as the vehicle pointer will become invalid later */
1010 int x = v->x_pos;
1011 int y = v->y_pos;
1012 int z = v->z_pos;
1013
1014 const Company *c = Company::Get(_current_company);
1015 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
1016 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
1017 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
1018
1019 if (!IsLocalCompany()) continue;
1020
1021 if (res.Succeeded()) {
1022 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
1023 continue;
1024 }
1025
1026 StringID error_message = res.GetErrorMessage();
1027 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
1028
1029 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
1030
1031 StringID message;
1032 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
1033 message = error_message;
1034 } else {
1035 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
1036 }
1037
1038 SetDParam(0, v->index);
1039 SetDParam(1, error_message);
1040 AddVehicleAdviceNewsItem(message, v->index);
1041 }
1042
1043 cur_company.Restore();
1044 }
1045
1050 static void DoDrawVehicle(const Vehicle *v)
1051 {
1052 PaletteID pal = PAL_NONE;
1053
1054 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
1055
1056 /* Check whether the vehicle shall be transparent due to the game state */
1057 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
1058
1059 if (v->type == VEH_EFFECT) {
1060 /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
1061 * However, transparent smoke and bubbles look weird, so always hide them. */
1062 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
1063 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
1064 }
1065
1066 StartSpriteCombine();
1067 for (uint i = 0; i < v->sprite_seq.count; ++i) {
1068 PaletteID pal2 = v->sprite_seq.seq[i].pal;
1069 if (!pal2 || (v->vehstatus & VS_CRASHED)) pal2 = pal;
1070 AddSortableSpriteToDraw(v->sprite_seq.seq[i].sprite, pal2, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
1071 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
1072 }
1073 EndSpriteCombine();
1074 }
1075
1080 void ViewportAddVehicles(DrawPixelInfo *dpi)
1081 {
1082 /* The bounding rectangle */
1083 const int l = dpi->left;
1084 const int r = dpi->left + dpi->width;
1085 const int t = dpi->top;
1086 const int b = dpi->top + dpi->height;
1087
1088 /* The hash area to scan */
1089 int xl, xu, yl, yu;
1090
1091 if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
1092 xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
1093 xu = GB(r, 7 + ZOOM_LVL_SHIFT, 6);
1094 } else {
1095 /* scan whole hash row */
1096 xl = 0;
1097 xu = 0x3F;
1098 }
1099
1100 if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
1101 yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
1102 yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6;
1103 } else {
1104 /* scan whole column */
1105 yl = 0;
1106 yu = 0x3F << 6;
1107 }
1108
1109 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
1110 for (int x = xl;; x = (x + 1) & 0x3F) {
1111 const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
1112
1113 while (v != NULL) {
1114 if (!(v->vehstatus & VS_HIDDEN) &&
1115 l <= v->coord.right &&
1116 t <= v->coord.bottom &&
1117 r >= v->coord.left &&
1118 b >= v->coord.top) {
1119 DoDrawVehicle(v);
1120 }
1121 v = v->hash_viewport_next;
1122 }
1123
1124 if (x == xu) break;
1125 }
1126
1127 if (y == yu) break;
1128 }
1129 }
1130
1138 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
1139 {
1140 Vehicle *found = NULL, *v;
1141 uint dist, best_dist = UINT_MAX;
1142
1143 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
1144
1145 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
1146 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
1147
1148 FOR_ALL_VEHICLES(v) {
1149 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
1150 x >= v->coord.left && x <= v->coord.right &&
1151 y >= v->coord.top && y <= v->coord.bottom) {
1152
1153 dist = max(
1154 abs(((v->coord.left + v->coord.right) >> 1) - x),
1155 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
1156 );
1157
1158 if (dist < best_dist) {
1159 found = v;
1160 best_dist = dist;
1161 }
1162 }
1163 }
1164
1165 return found;
1166 }
1167
1172 void DecreaseVehicleValue(Vehicle *v)
1173 {
1174 v->value -= v->value >> 8;
1175 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1176 }
1177
1178 static const byte _breakdown_chance[64] = {
1179 3, 3, 3, 3, 3, 3, 3, 3,
1180 4, 4, 5, 5, 6, 6, 7, 7,
1181 8, 8, 9, 9, 10, 10, 11, 11,
1182 12, 13, 13, 13, 13, 14, 15, 16,
1183 17, 19, 21, 25, 28, 31, 34, 37,
1184 40, 44, 48, 52, 56, 60, 64, 68,
1185 72, 80, 90, 100, 110, 120, 130, 140,
1186 150, 170, 190, 210, 230, 250, 250, 250,
1187 };
1188
1189 void CheckVehicleBreakdown(Vehicle *v)
1190 {
1191 int rel, rel_old;
1192
1193 /* decrease reliability */
1194 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
1195 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1196
1197 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
1198 _settings_game.difficulty.vehicle_breakdowns < 1 ||
1199 v->cur_speed < 5 || _game_mode == GM_MENU) {
1200 return;
1201 }
1202
1203 uint32 r = Random();
1204
1205 /* increase chance of failure */
1206 int chance = v->breakdown_chance + 1;
1207 if (Chance16I(1, 25, r)) chance += 25;
1208 v->breakdown_chance = min(255, chance);
1209
1210 /* calculate reliability value to use in comparison */
1211 rel = v->reliability;
1212 if (v->type == VEH_SHIP) rel += 0x6666;
1213
1214 /* reduced breakdowns? */
1215 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
1216
1217 /* check if to break down */
1218 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
1219 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
1220 v->breakdown_delay = GB(r, 24, 7) + 0x80;
1221 v->breakdown_chance = 0;
1222 }
1223 }
1224
1231 bool Vehicle::HandleBreakdown()
1232 {
1233 /* Possible states for Vehicle::breakdown_ctr
1234 * 0 - vehicle is running normally
1235 * 1 - vehicle is currently broken down
1236 * 2 - vehicle is going to break down now
1237 * >2 - vehicle is counting down to the actual breakdown event */
1238 switch (this->breakdown_ctr) {
1239 case 0:
1240 return false;
1241
1242 case 2:
1243 this->breakdown_ctr = 1;
1244
1245 if (this->breakdowns_since_last_service != 255) {
1246 this->breakdowns_since_last_service++;
1247 }
1248
1249 if (this->type == VEH_AIRCRAFT) {
1250 /* Aircraft just need this flag, the rest is handled elsewhere */
1251 this->vehstatus |= VS_AIRCRAFT_BROKEN;
1252 } else {
1253 this->cur_speed = 0;
1254
1255 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
1256 bool train_or_ship = this->type == VEH_TRAIN || this->type == VEH_SHIP;
1257 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
1258 (train_or_ship ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
1259 (train_or_ship ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
1260 }
1261
1262 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
1263 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
1264 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
1265 }
1266 }
1267
1268 this->MarkDirty(); // Update graphics after speed is zeroed
1269 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1270 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
1271
1272 FALLTHROUGH;
1273 case 1:
1274 /* Aircraft breakdowns end only when arriving at the airport */
1275 if (this->type == VEH_AIRCRAFT) return false;
1276
1277 /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
1278 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
1279 if (--this->breakdown_delay == 0) {
1280 this->breakdown_ctr = 0;
1281 this->MarkDirty();
1282 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1283 }
1284 }
1285 return true;
1286
1287 default:
1288 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
1289 return false;
1290 }
1291 }
1292
1297 void AgeVehicle(Vehicle *v)
1298 {
1299 if (v->age < MAX_DAY) {
1300 v->age++;
1301 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
1302 }
1303
1304 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
1305
1306 int age = v->age - v->max_age;
1307 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
1308 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
1309 v->reliability_spd_dec <<= 1;
1310 }
1311
1312 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1313
1314 /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
1315 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
1316
1317 /* Don't warn if a renew is active */
1318 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
1319
1320 StringID str;
1321 if (age == -DAYS_IN_LEAP_YEAR) {
1322 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
1323 } else if (age == 0) {
1324 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
1325 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
1326 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
1327 } else {
1328 return;
1329 }
1330
1331 SetDParam(0, v->index);
1332 AddVehicleAdviceNewsItem(str, v->index);
1333 }
1334
1344 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
1345 {
1346 int count = 0;
1347 int max = 0;
1348 int cars = 0;
1349 int unloading = 0;
1350 bool loading = false;
1351
1352 bool is_loading = front->current_order.IsType(OT_LOADING);
1353
1354 /* The station may be NULL when the (colour) string does not need to be set. */
1355 const Station *st = Station::GetIfValid(front->last_station_visited);
1356 assert(colour == NULL || (st != NULL && is_loading));
1357
1358 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
1359 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
1360
1361 /* Count up max and used */
1362 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
1363 count += v->cargo.StoredCount();
1364 max += v->cargo_cap;
1365 if (v->cargo_cap != 0 && colour != NULL) {
1366 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
1367 loading |= !order_no_load &&
1368 (order_full_load || st->goods[v->cargo_type].HasRating()) &&
1369 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
1370 cars++;
1371 }
1372 }
1373
1374 if (colour != NULL) {
1375 if (unloading == 0 && loading) {
1376 *colour = STR_PERCENT_UP;
1377 } else if (unloading == 0 && !loading) {
1378 *colour = STR_PERCENT_NONE;
1379 } else if (cars == unloading || !loading) {
1380 *colour = STR_PERCENT_DOWN;
1381 } else {
1382 *colour = STR_PERCENT_UP_DOWN;
1383 }
1384 }
1385
1386 /* Train without capacity */
1387 if (max == 0) return 100;
1388
1389 /* Return the percentage */
1390 if (count * 2 < max) {
1391 /* Less than 50%; round up, so that 0% means really empty. */
1392 return CeilDiv(count * 100, max);
1393 } else {
1394 /* More than 50%; round down, so that 100% means really full. */
1395 return (count * 100) / max;
1396 }
1397 }
1398
1403 void VehicleEnterDepot(Vehicle *v)
1404 {
1405 /* Always work with the front of the vehicle */
1406 assert(v == v->First());
1407
1408 switch (v->type) {
1409 case VEH_TRAIN: {
1410 Train *t = Train::From(v);
1411 SetWindowClassesDirty(WC_TRAINS_LIST);
1412 /* Clear path reservation */
1413 SetDepotReservation(t->tile, false);
1414 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
1415
1416 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
1417 t->wait_counter = 0;
1418 t->force_proceed = TFP_NONE;
1419 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
1420 t->ConsistChanged(CCF_ARRANGE);
1421 break;
1422 }
1423
1424 case VEH_ROAD:
1425 SetWindowClassesDirty(WC_ROADVEH_LIST);
1426 break;
1427
1428 case VEH_SHIP: {
1429 SetWindowClassesDirty(WC_SHIPS_LIST);
1430 Ship *ship = Ship::From(v);
1431 ship->state = TRACK_BIT_DEPOT;
1432 ship->UpdateCache();
1433 ship->UpdateViewport(true, true);
1434 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1435 break;
1436 }
1437
1438 case VEH_AIRCRAFT:
1439 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
1440 HandleAircraftEnterHangar(Aircraft::From(v));
1441 break;
1442 default: NOT_REACHED();
1443 }
1444 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1445
1446 if (v->type != VEH_TRAIN) {
1447 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
1448 * We only increase the number of vehicles when the first one enters, so we will not need to search for more vehicles in the depot */
1449 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
1450 }
1451 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1452
1453 v->vehstatus |= VS_HIDDEN;
1454 v->cur_speed = 0;
1455
1456 VehicleServiceInDepot(v);
1457
1458 /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
1459 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
1460 v->MarkDirty();
1461
1462 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
1463 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1464
1465 const Order *real_order = v->GetOrder(v->cur_real_order_index);
1466
1467 /* Test whether we are heading for this depot. If not, do nothing.
1468 * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
1469 if ((v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
1470 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
1471 (v->type == VEH_AIRCRAFT ? v->current_order.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
1472 /* We are heading for another depot, keep driving. */
1473 return;
1474 }
1475
1476 if (v->current_order.IsRefit()) {
1477 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
1478 CommandCost cost = DoCommand(v->tile, v->index, v->current_order.GetRefitCargo() | 0xFF << 8, DC_EXEC, GetCmdRefitVeh(v));
1479 cur_company.Restore();
1480
1481 if (cost.Failed()) {
1482 _vehicles_to_autoreplace[v] = false;
1483 if (v->owner == _local_company) {
1484 /* Notify the user that we stopped the vehicle */
1485 SetDParam(0, v->index);
1486 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
1487 }
1488 } else if (cost.GetCost() != 0) {
1489 v->profit_this_year -= cost.GetCost() << 8;
1490 if (v->owner == _local_company) {
1491 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
1492 }
1493 }
1494 }
1495
1496 if (v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
1497 /* Part of orders */
1498 v->DeleteUnreachedImplicitOrders();
1499 UpdateVehicleTimetable(v, true);
1500 v->IncrementImplicitOrderIndex();
1501 }
1502 if (v->current_order.GetDepotActionType() & ODATFB_HALT) {
1503 /* Vehicles are always stopped on entering depots. Do not restart this one. */
1504 _vehicles_to_autoreplace[v] = false;
1505 /* Invalidate last_loading_station. As the link from the station
1506 * before the stop to the station after the stop can't be predicted
1507 * we shouldn't construct it when the vehicle visits the next stop. */
1508 v->last_loading_station = INVALID_STATION;
1509 if (v->owner == _local_company) {
1510 SetDParam(0, v->index);
1511 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
1512 }
1513 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
1514 }
1515 v->current_order.MakeDummy();
1516 }
1517 }
1518
1519
1524 void Vehicle::UpdatePosition()
1525 {
1526 UpdateVehicleTileHash(this, false);
1527 }
1528
1534 void Vehicle::UpdateViewport(bool dirty)
1535 {
1536 Rect new_coord;
1537 this->sprite_seq.GetBounds(&new_coord);
1538
1539 Point pt = RemapCoords(this->x_pos + this->x_offs, this->y_pos + this->y_offs, this->z_pos);
1540 new_coord.left += pt.x;
1541 new_coord.top += pt.y;
1542 new_coord.right += pt.x + 2 * ZOOM_LVL_BASE;
1543 new_coord.bottom += pt.y + 2 * ZOOM_LVL_BASE;
1544
1545 UpdateVehicleViewportHash(this, new_coord.left, new_coord.top);
1546
1547 Rect old_coord = this->coord;
1548 this->coord = new_coord;
1549
1550 if (dirty) {
1551 if (old_coord.left == INVALID_COORD) {
1552 this->MarkAllViewportsDirty();
1553 } else {
1554 ::MarkAllViewportsDirty(
1555 min(old_coord.left, this->coord.left),
1556 min(old_coord.top, this->coord.top),
1557 max(old_coord.right, this->coord.right),
1558 max(old_coord.bottom, this->coord.bottom));
1559 }
1560 }
1561 }
1562
1566 void Vehicle::UpdatePositionAndViewport()
1567 {
1568 this->UpdatePosition();
1569 this->UpdateViewport(true);
1570 }
1571
1575 void Vehicle::MarkAllViewportsDirty() const
1576 {
1577 ::MarkAllViewportsDirty(this->coord.left, this->coord.top, this->coord.right, this->coord.bottom);
1578 }
1579
1585 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
1586 {
1587 static const int8 _delta_coord[16] = {
1588 -1,-1,-1, 0, 1, 1, 1, 0, /* x */
1589 -1, 0, 1, 1, 1, 0,-1,-1, /* y */
1590 };
1591
1592 int x = v->x_pos + _delta_coord[v->direction];
1593 int y = v->y_pos + _delta_coord[v->direction + 8];
1594
1595 GetNewVehiclePosResult gp;
1596 gp.x = x;
1597 gp.y = y;
1598 gp.old_tile = v->tile;
1599 gp.new_tile = TileVirtXY(x, y);
1600 return gp;
1601 }
1602
1603 static const Direction _new_direction_table[] = {
1604 DIR_N, DIR_NW, DIR_W,
1605 DIR_NE, DIR_SE, DIR_SW,
1606 DIR_E, DIR_SE, DIR_S
1607 };
1608
1609 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
1610 {
1611 int i = 0;
1612
1613 if (y >= v->y_pos) {
1614 if (y != v->y_pos) i += 3;
1615 i += 3;
1616 }
1617
1618 if (x >= v->x_pos) {
1619 if (x != v->x_pos) i++;
1620 i++;
1621 }
1622
1623 Direction dir = v->direction;
1624
1625 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
1626 if (dirdiff == DIRDIFF_SAME) return dir;
1627 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
1628 }
1629
1639 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
1640 {
1641 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
1642 }
1643
1651 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
1652 {
1653 /* Find maximum */
1654 const Vehicle *v;
1655 FOR_ALL_VEHICLES(v) {
1656 if (v->type == type && v->owner == owner) {
1657 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
1658 }
1659 }
1660
1661 if (this->maxid == 0) return;
1662
1663 /* Reserving 'maxid + 2' because we need:
1664 * - space for the last item (with v->unitnumber == maxid)
1665 * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
1666 this->cache = CallocT<bool>(this->maxid + 2);
1667
1668 /* Fill the cache */
1669 FOR_ALL_VEHICLES(v) {
1670 if (v->type == type && v->owner == owner) {
1671 this->cache[v->unitnumber] = true;
1672 }
1673 }
1674 }
1675
1677 UnitID FreeUnitIDGenerator::NextID()
1678 {
1679 if (this->maxid <= this->curid) return ++this->curid;
1680
1681 while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
1682
1683 return this->curid;
1684 }
1685
1691 UnitID GetFreeUnitNumber(VehicleType type)
1692 {
1693 /* Check whether it is allowed to build another vehicle. */
1694 uint max_veh;
1695 switch (type) {
1696 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
1697 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
1698 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
1699 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
1700 default: NOT_REACHED();
1701 }
1702
1703 const Company *c = Company::Get(_current_company);
1704 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
1705
1706 FreeUnitIDGenerator gen(type, _current_company);
1707
1708 return gen.NextID();
1709 }
1710
1711
1720 bool CanBuildVehicleInfrastructure(VehicleType type)
1721 {
1722 assert(IsCompanyBuildableVehicleType(type));
1723
1724 if (!Company::IsValidID(_local_company)) return false;
1725 if (!_settings_client.gui.disable_unsuitable_building) return true;
1726
1727 UnitID max;
1728 switch (type) {
1729 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
1730 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
1731 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
1732 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
1733 default: NOT_REACHED();
1734 }
1735
1736 /* We can build vehicle infrastructure when we may build the vehicle type */
1737 if (max > 0) {
1738 /* Can we actually build the vehicle type? */
1739 const Engine *e;
1740 FOR_ALL_ENGINES_OF_TYPE(e, type) {
1741 if (HasBit(e->company_avail, _local_company)) return true;
1742 }
1743 return false;
1744 }
1745
1746 /* We should be able to build infrastructure when we have the actual vehicle type */
1747 const Vehicle *v;
1748 FOR_ALL_VEHICLES(v) {
1749 if (v->owner == _local_company && v->type == type) return true;
1750 }
1751
1752 return false;
1753 }
1754
1755
1763 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
1764 {
1765 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
1766 const Engine *e = Engine::Get(engine_type);
1767 switch (e->type) {
1768 default: NOT_REACHED();
1769 case VEH_TRAIN:
1770 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
1771 /* Wagonoverrides use the colour scheme of the front engine.
1772 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
1773 engine_type = parent_engine_type;
1774 e = Engine::Get(engine_type);
1775 /* Note: Luckily cargo_type is not needed for engines */
1776 }
1777
1778 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1779 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1780 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
1781 if (!CargoSpec::Get(cargo_type)->is_freight) {
1782 if (parent_engine_type == INVALID_ENGINE) {
1783 return LS_PASSENGER_WAGON_STEAM;
1784 } else {
1785 switch (RailVehInfo(parent_engine_type)->engclass) {
1786 default: NOT_REACHED();
1787 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
1788 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
1789 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
1790 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
1791 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
1792 }
1793 }
1794 } else {
1795 return LS_FREIGHT_WAGON;
1796 }
1797 } else {
1798 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
1799
1800 switch (e->u.rail.engclass) {
1801 default: NOT_REACHED();
1802 case EC_STEAM: return LS_STEAM;
1803 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
1804 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
1805 case EC_MONORAIL: return LS_MONORAIL;
1806 case EC_MAGLEV: return LS_MAGLEV;
1807 }
1808 }
1809
1810 case VEH_ROAD:
1811 /* Always use the livery of the front */
1812 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
1813 engine_type = parent_engine_type;
1814 e = Engine::Get(engine_type);
1815 cargo_type = v->First()->cargo_type;
1816 }
1817 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1818 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1819
1820 /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
1821 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
1822 /* Tram */
1823 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
1824 } else {
1825 /* Bus or truck */
1826 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
1827 }
1828
1829 case VEH_SHIP:
1830 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1831 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1832 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
1833
1834 case VEH_AIRCRAFT:
1835 switch (e->u.air.subtype) {
1836 case AIR_HELI: return LS_HELICOPTER;
1837 case AIR_CTOL: return LS_SMALL_PLANE;
1838 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
1839 default: NOT_REACHED();
1840 }
1841 }
1842 }
1843
1853 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
1854 {
1855 const Company *c = Company::Get(company);
1856 LiveryScheme scheme = LS_DEFAULT;
1857
1858 /* The default livery is always available for use, but its in_use flag determines
1859 * whether any _other_ liveries are in use. */
1860 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
1861 /* Determine the livery scheme to use */
1862 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
1863
1864 /* Switch back to the default scheme if the resolved scheme is not in use */
1865 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
1866 }
1867
1868 return &c->livery[scheme];
1869 }
1870
1871
1872 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
1873 {
1874 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
1875
1876 /* Return cached value if any */
1877 if (map != PAL_NONE) return map;
1878
1879 const Engine *e = Engine::Get(engine_type);
1880
1881 /* Check if we should use the colour map callback */
1882 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
1883 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
1884 /* Failure means "use the default two-colour" */
1885 if (callback != CALLBACK_FAILED) {
1886 assert_compile(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
1887 map = GB(callback, 0, 14);
1888 /* If bit 14 is set, then the company colours are applied to the
1889 * map else it's returned as-is. */
1890 if (!HasBit(callback, 14)) {
1891 /* Update cache */
1892 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
1893 return map;
1894 }
1895 }
1896 }
1897
1898 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
1899
1900 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
1901
1902 /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
1903 if (!Company::IsValidID(company)) return map;
1904
1905 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
1906
1907 map += livery->colour1;
1908 if (twocc) map += livery->colour2 * 16;
1909
1910 /* Update cache */
1911 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
1912 return map;
1913 }
1914
1921 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
1922 {
1923 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
1924 }
1925
1931 PaletteID GetVehiclePalette(const Vehicle *v)
1932 {
1933 if (v->IsGroundVehicle()) {
1934 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
1935 }
1936
1937 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
1938 }
1939
1943 void Vehicle::DeleteUnreachedImplicitOrders()
1944 {
1945 if (this->IsGroundVehicle()) {
1946 uint16 &gv_flags = this->GetGroundVehicleFlags();
1947 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
1948 /* Do not delete orders, only skip them */
1949 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
1950 this->cur_implicit_order_index = this->cur_real_order_index;
1951 InvalidateVehicleOrder(this, 0);
1952 return;
1953 }
1954 }
1955
1956 const Order *order = this->GetOrder(this->cur_implicit_order_index);
1957 while (order != NULL) {
1958 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
1959
1960 if (order->IsType(OT_IMPLICIT)) {
1961 DeleteOrder(this, this->cur_implicit_order_index);
1962 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
1963 order = this->GetOrder(this->cur_implicit_order_index);
1964 } else {
1965 /* Skip non-implicit orders, e.g. service-orders */
1966 order = order->next;
1967 this->cur_implicit_order_index++;
1968 }
1969
1970 /* Wrap around */
1971 if (order == NULL) {
1972 order = this->GetOrder(0);
1973 this->cur_implicit_order_index = 0;
1974 }
1975 }
1976 }
1977
1982 void Vehicle::BeginLoading()
1983 {
1984 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
1985
1986 if (this->current_order.IsType(OT_GOTO_STATION) &&
1987 this->current_order.GetDestination() == this->last_station_visited) {
1988 this->DeleteUnreachedImplicitOrders();
1989
1990 /* Now both order indices point to the destination station, and we can start loading */
1991 this->current_order.MakeLoading(true);
1992 UpdateVehicleTimetable(this, true);
1993
1994 /* Furthermore add the Non Stop flag to mark that this station
1995 * is the actual destination of the vehicle, which is (for example)
1996 * necessary to be known for HandleTrainLoading to determine
1997 * whether the train is lost or not; not marking a train lost
1998 * that arrives at random stations is bad. */
1999 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
2000
2001 } else {
2002 /* We weren't scheduled to stop here. Insert an implicit order
2003 * to show that we are stopping here.
2004 * While only groundvehicles have implicit orders, e.g. aircraft might still enter
2005 * the 'wrong' terminal when skipping orders etc. */
2006 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
2007 if (this->IsGroundVehicle() &&
2008 (in_list == NULL || !in_list->IsType(OT_IMPLICIT) ||
2009 in_list->GetDestination() != this->last_station_visited)) {
2010 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
2011 /* Do not create consecutive duplicates of implicit orders */
2012 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
2013 if (prev_order == NULL ||
2014 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
2015 prev_order->GetDestination() != this->last_station_visited) {
2016
2017 /* Prefer deleting implicit orders instead of inserting new ones,
2018 * so test whether the right order follows later. In case of only
2019 * implicit orders treat the last order in the list like an
2020 * explicit one, except if the overall number of orders surpasses
2021 * IMPLICIT_ORDER_ONLY_CAP. */
2022 int target_index = this->cur_implicit_order_index;
2023 bool found = false;
2024 while (target_index != this->cur_real_order_index || this->GetNumManualOrders() == 0) {
2025 const Order *order = this->GetOrder(target_index);
2026 if (order == NULL) break; // No orders.
2027 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
2028 found = true;
2029 break;
2030 }
2031 target_index++;
2032 if (target_index >= this->orders.list->GetNumOrders()) {
2033 if (this->GetNumManualOrders() == 0 &&
2034 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP) {
2035 break;
2036 }
2037 target_index = 0;
2038 }
2039 if (target_index == this->cur_implicit_order_index) break; // Avoid infinite loop.
2040 }
2041
2042 if (found) {
2043 if (suppress_implicit_orders) {
2044 /* Skip to the found order */
2045 this->cur_implicit_order_index = target_index;
2046 InvalidateVehicleOrder(this, 0);
2047 } else {
2048 /* Delete all implicit orders up to the station we just reached */
2049 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2050 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
2051 if (order->IsType(OT_IMPLICIT)) {
2052 DeleteOrder(this, this->cur_implicit_order_index);
2053 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2054 order = this->GetOrder(this->cur_implicit_order_index);
2055 } else {
2056 /* Skip non-implicit orders, e.g. service-orders */
2057 order = order->next;
2058 this->cur_implicit_order_index++;
2059 }
2060
2061 /* Wrap around */
2062 if (order == NULL) {
2063 order = this->GetOrder(0);
2064 this->cur_implicit_order_index = 0;
2065 }
2066 assert(order != NULL);
2067 }
2068 }
2069 } else if (!suppress_implicit_orders &&
2070 ((this->orders.list == NULL ? OrderList::CanAllocateItem() : this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID)) &&
2071 Order::CanAllocateItem()) {
2072 /* Insert new implicit order */
2073 Order *implicit_order = new Order();
2074 implicit_order->MakeImplicit(this->last_station_visited);
2075 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
2076 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
2077
2078 /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
2079 * Reenable it for this vehicle */
2080 uint16 &gv_flags = this->GetGroundVehicleFlags();
2081 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2082 }
2083 }
2084 }
2085 this->current_order.MakeLoading(false);
2086 }
2087
2088 if (this->last_loading_station != INVALID_STATION &&
2089 this->last_loading_station != this->last_station_visited &&
2090 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2091 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
2092 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
2093 }
2094
2095 PrepareUnload(this);
2096
2097 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
2098 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2099 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
2100 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
2101
2102 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
2103 this->cur_speed = 0;
2104 this->MarkDirty();
2105 }
2106
2112 void Vehicle::CancelReservation(StationID next, Station *st)
2113 {
2114 for (Vehicle *v = this; v != NULL; v = v->next) {
2115 VehicleCargoList &cargo = v->cargo;
2116 if (cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
2117 DEBUG(misc, 1, "cancelling cargo reservation");
2118 cargo.Return(UINT_MAX, &st->goods[v->cargo_type].cargo, next);
2119 cargo.SetTransferLoadPlace(st->xy);
2120 }
2121 cargo.KeepAll();
2122 }
2123 }
2124
2129 void Vehicle::LeaveStation()
2130 {
2131 assert(this->current_order.IsType(OT_LOADING));
2132
2133 delete this->cargo_payment;
2134 assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
2135
2136 /* Only update the timetable if the vehicle was supposed to stop here. */
2137 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
2138
2139 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2140 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
2141 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
2142 /* Refresh next hop stats to make sure we've done that at least once
2143 * during the stop and that refit_cap == cargo_cap for each vehicle in
2144 * the consist. */
2145 this->ResetRefitCaps();
2146 LinkRefresher::Run(this);
2147
2148 /* if the vehicle could load here or could stop with cargo loaded set the last loading station */
2149 this->last_loading_station = this->last_station_visited;
2150 } else {
2151 /* if the vehicle couldn't load and had to unload or transfer everything
2152 * set the last loading station to invalid as it will leave empty. */
2153 this->last_loading_station = INVALID_STATION;
2154 }
2155 }
2156
2157 this->current_order.MakeLeaveStation();
2158 Station *st = Station::Get(this->last_station_visited);
2159 this->CancelReservation(INVALID_STATION, st);
2160 st->loading_vehicles.remove(this);
2161
2162 HideFillingPercent(&this->fill_percent_te_id);
2163 trip_occupancy = CalcPercentVehicleFilled(this, NULL);
2164
2165 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
2166 /* Trigger station animation (trains only) */
2167 if (IsTileType(this->tile, MP_STATION)) {
2168 TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
2169 TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
2170 }
2171
2172 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
2173 }
2174
2175 this->MarkDirty();
2176 }
2177
2181 void Vehicle::ResetRefitCaps()
2182 {
2183 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
2184 }
2185
2191 void Vehicle::HandleLoading(bool mode)
2192 {
2193 switch (this->current_order.GetType()) {
2194 case OT_LOADING: {
2195 uint wait_time = max(this->current_order.GetTimetabledWait() - this->lateness_counter, 0);
2196
2197 /* Not the first call for this tick, or still loading */
2198 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
2199
2200 this->PlayLeaveStationSound();
2201
2202 this->LeaveStation();
2203
2204 /* Only advance to next order if we just loaded at the current one */
2205 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2206 if (order == NULL ||
2207 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
2208 order->GetDestination() != this->last_station_visited) {
2209 return;
2210 }
2211 break;
2212 }
2213
2214 case OT_DUMMY: break;
2215
2216 default: return;
2217 }
2218
2219 this->IncrementImplicitOrderIndex();
2220 }
2221
2226 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
2227 {
2228 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
2229 if (v->cargo_cap == 0) continue;
2230 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
2231 if (pair == capacities.End()) {
2232 pair = capacities.Append();
2233 pair->first = v->cargo_type;
2234 pair->second = v->cargo_cap - v->cargo.StoredCount();
2235 } else {
2236 pair->second += v->cargo_cap - v->cargo.StoredCount();
2237 }
2238 }
2239 }
2240
2241 uint Vehicle::GetConsistTotalCapacity() const
2242 {
2243 uint result = 0;
2244 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
2245 result += v->cargo_cap;
2246 }
2247 return result;
2248 }
2249
2256 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
2257 {
2258 CommandCost ret = CheckOwnership(this->owner);
2259 if (ret.Failed()) return ret;
2260
2261 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
2262 if (this->IsStoppedInDepot()) return CMD_ERROR;
2263
2264 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
2265 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
2266 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
2267 /* We called with a different DEPOT_SERVICE setting.
2268 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2269 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2270 if (flags & DC_EXEC) {
2271 this->current_order.SetDepotOrderType(ODTF_MANUAL);
2272 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
2273 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2274 }
2275 return CommandCost();
2276 }
2277
2278 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
2279 if (flags & DC_EXEC) {
2280 /* If the orders to 'goto depot' are in the orders list (forced servicing),
2281 * then skip to the next order; effectively cancelling this forced service */
2282 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
2283
2284 if (this->IsGroundVehicle()) {
2285 uint16 &gv_flags = this->GetGroundVehicleFlags();
2286 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2287 }
2288
2289 this->current_order.MakeDummy();
2290 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2291 }
2292 return CommandCost();
2293 }
2294
2295 TileIndex location;
2296 DestinationID destination;
2297 bool reverse;
2298 static const StringID no_depot[] = {STR_ERROR_UNABLE_TO_FIND_ROUTE_TO, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT, STR_ERROR_CAN_T_SEND_AIRCRAFT_TO_HANGAR};
2299 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
2300
2301 if (flags & DC_EXEC) {
2302 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
2303
2304 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
2305 uint16 &gv_flags = this->GetGroundVehicleFlags();
2306 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2307 }
2308
2309 this->dest_tile = location;
2310 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
2311 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
2312 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2313
2314 /* If there is no depot in front, reverse automatically (trains only) */
2315 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
2316
2317 if (this->type == VEH_AIRCRAFT) {
2318 Aircraft *a = Aircraft::From(this);
2319 if (a->state == FLYING && a->targetairport != destination) {
2320 /* The aircraft is now heading for a different hangar than the next in the orders */
2321 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
2322 AircraftNextAirportPos_and_Order(a);
2323 }
2324 }
2325 }
2326
2327 return CommandCost();
2328
2329 }
2330
2335 void Vehicle::UpdateVisualEffect(bool allow_power_change)
2336 {
2337 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2338 const Engine *e = this->GetEngine();
2339
2340 /* Evaluate properties */
2341 byte visual_effect;
2342 switch (e->type) {
2343 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
2344 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
2345 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
2346 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
2347 }
2348
2349 /* Check powered wagon / visual effect callback */
2350 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
2351 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
2352
2353 if (callback != CALLBACK_FAILED) {
2354 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
2355
2356 callback = GB(callback, 0, 8);
2357 /* Avoid accidentally setting 'visual_effect' to the default value
2358 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
2359 if (callback == VE_DEFAULT) {
2360 assert(HasBit(callback, VE_DISABLE_EFFECT));
2361 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
2362 }
2363 visual_effect = callback;
2364 }
2365 }
2366
2367 /* Apply default values */
2368 if (visual_effect == VE_DEFAULT ||
2369 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
2370 /* Only train engines have default effects.
2371 * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
2372 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
2373 if (visual_effect == VE_DEFAULT) {
2374 visual_effect = 1 << VE_DISABLE_EFFECT;
2375 } else {
2376 SetBit(visual_effect, VE_DISABLE_EFFECT);
2377 }
2378 } else {
2379 if (visual_effect == VE_DEFAULT) {
2380 /* Also set the offset */
2381 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
2382 }
2383 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
2384 }
2385 }
2386
2387 this->vcache.cached_vis_effect = visual_effect;
2388
2389 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
2390 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2391 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
2392 }
2393 }
2394
2395 static const int8 _vehicle_smoke_pos[8] = {
2396 1, 1, 1, 0, -1, -1, -1, 0
2397 };
2398
2403 static void SpawnAdvancedVisualEffect(const Vehicle *v)
2404 {
2405 uint16 callback = GetVehicleCallback(CBID_VEHICLE_SPAWN_VISUAL_EFFECT, 0, Random(), v->engine_type, v);
2406 if (callback == CALLBACK_FAILED) return;
2407
2408 uint count = GB(callback, 0, 2);
2409 bool auto_center = HasBit(callback, 13);
2410 bool auto_rotate = !HasBit(callback, 14);
2411
2412 int8 l_center = 0;
2413 if (auto_center) {
2414 /* For road vehicles: Compute offset from vehicle position to vehicle center */
2415 if (v->type == VEH_ROAD) l_center = -(int)(VEHICLE_LENGTH - RoadVehicle::From(v)->gcache.cached_veh_length) / 2;
2416 } else {
2417 /* For trains: Compute offset from vehicle position to sprite position */
2418 if (v->type == VEH_TRAIN) l_center = (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2419 }
2420
2421 Direction l_dir = v->direction;
2422 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) l_dir = ReverseDir(l_dir);
2423 Direction t_dir = ChangeDir(l_dir, DIRDIFF_90RIGHT);
2424
2425 int8 x_center = _vehicle_smoke_pos[l_dir] * l_center;
2426 int8 y_center = _vehicle_smoke_pos[t_dir] * l_center;
2427
2428 for (uint i = 0; i < count; i++) {
2429 uint32 reg = GetRegister(0x100 + i);
2430 uint type = GB(reg, 0, 8);
2431 int8 x = GB(reg, 8, 8);
2432 int8 y = GB(reg, 16, 8);
2433 int8 z = GB(reg, 24, 8);
2434
2435 if (auto_rotate) {
2436 int8 l = x;
2437 int8 t = y;
2438 x = _vehicle_smoke_pos[l_dir] * l + _vehicle_smoke_pos[t_dir] * t;
2439 y = _vehicle_smoke_pos[t_dir] * l - _vehicle_smoke_pos[l_dir] * t;
2440 }
2441
2442 if (type >= 0xF0) {
2443 switch (type) {
2444 case 0xF1: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_STEAM_SMOKE); break;
2445 case 0xF2: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_DIESEL_SMOKE); break;
2446 case 0xF3: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_ELECTRIC_SPARK); break;
2447 case 0xFA: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_BREAKDOWN_SMOKE_AIRCRAFT); break;
2448 default: break;
2449 }
2450 }
2451 }
2452 }
2453
2458 void Vehicle::ShowVisualEffect() const
2459 {
2460 assert(this->IsPrimaryVehicle());
2461 bool sound = false;
2462
2463 /* Do not show any smoke when:
2464 * - vehicle smoke is disabled by the player
2465 * - the vehicle is slowing down or stopped (by the player)
2466 * - the vehicle is moving very slowly
2467 */
2468 if (_settings_game.vehicle.smoke_amount == 0 ||
2469 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
2470 this->cur_speed < 2) {
2471 return;
2472 }
2473
2474 /* Use the speed as limited by underground and orders. */
2475 uint max_speed = this->GetCurrentMaxSpeed();
2476
2477 if (this->type == VEH_TRAIN) {
2478 const Train *t = Train::From(this);
2479 /* For trains, do not show any smoke when:
2480 * - the train is reversing
2481 * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
2482 */
2483 if (HasBit(t->flags, VRF_REVERSING) ||
2484 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
2485 t->cur_speed >= max_speed)) {
2486 return;
2487 }
2488 }
2489
2490 const Vehicle *v = this;
2491
2492 do {
2493 bool advanced = HasBit(v->vcache.cached_vis_effect, VE_ADVANCED_EFFECT);
2494 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
2495 VisualEffectSpawnModel effect_model = VESM_NONE;
2496 if (advanced) {
2497 effect_offset = VE_OFFSET_CENTRE;
2498 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, 0, VE_ADVANCED_EFFECT);
2499 if (effect_model >= VESM_END) effect_model = VESM_NONE; // unknown spawning model
2500 } else {
2501 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
2502 assert(effect_model != (VisualEffectSpawnModel)VE_TYPE_DEFAULT); // should have been resolved by UpdateVisualEffect
2503 assert_compile((uint)VESM_STEAM == (uint)VE_TYPE_STEAM);
2504 assert_compile((uint)VESM_DIESEL == (uint)VE_TYPE_DIESEL);
2505 assert_compile((uint)VESM_ELECTRIC == (uint)VE_TYPE_ELECTRIC);
2506 }
2507
2508 /* Show no smoke when:
2509 * - Smoke has been disabled for this vehicle
2510 * - The vehicle is not visible
2511 * - The vehicle is under a bridge
2512 * - The vehicle is on a depot tile
2513 * - The vehicle is on a tunnel tile
2514 * - The vehicle is a train engine that is currently unpowered */
2515 if (effect_model == VESM_NONE ||
2516 v->vehstatus & VS_HIDDEN ||
2517 IsBridgeAbove(v->tile) ||
2518 IsDepotTile(v->tile) ||
2519 IsTunnelTile(v->tile) ||
2520 (v->type == VEH_TRAIN &&
2521 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
2522 continue;
2523 }
2524
2525 EffectVehicleType evt = EV_END;
2526 switch (effect_model) {
2527 case VESM_STEAM:
2528 /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
2529 * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
2530 * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
2531 * REGULATION:
2532 * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
2533 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
2534 evt = EV_STEAM_SMOKE;
2535 }
2536 break;
2537
2538 case VESM_DIESEL: {
2539 /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
2540 * when smoke emission stops.
2541 * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
2542 * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
2543 * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
2544 * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
2545 * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
2546 * maximum speed no diesel_smoke is emitted.
2547 * REGULATION:
2548 * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
2549 * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
2550 int power_weight_effect = 0;
2551 if (v->type == VEH_TRAIN) {
2552 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
2553 }
2554 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
2555 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
2556 evt = EV_DIESEL_SMOKE;
2557 }
2558 break;
2559 }
2560
2561 case VESM_ELECTRIC:
2562 /* Electric train's spark - more often occurs when train is departing (more load)
2563 * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
2564 * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
2565 * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
2566 * REGULATION:
2567 * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
2568 if (GB(v->tick_counter, 0, 2) == 0 &&
2569 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
2570 evt = EV_ELECTRIC_SPARK;
2571 }
2572 break;
2573
2574 default:
2575 NOT_REACHED();
2576 }
2577
2578 if (evt != EV_END && advanced) {
2579 sound = true;
2580 SpawnAdvancedVisualEffect(v);
2581 } else if (evt != EV_END) {
2582 sound = true;
2583
2584 /* The effect offset is relative to a point 4 units behind the vehicle's
2585 * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
2586 * correction factor. */
2587 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2588
2589 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
2590 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
2591
2592 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
2593 x = -x;
2594 y = -y;
2595 }
2596
2597 CreateEffectVehicleRel(v, x, y, 10, evt);
2598 }
2599 } while ((v = v->Next()) != NULL);
2600
2601 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
2602 }
2603
2608 void Vehicle::SetNext(Vehicle *next)
2609 {
2610 assert(this != next);
2611
2612 if (this->next != NULL) {
2613 /* We had an old next vehicle. Update the first and previous pointers */
2614 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
2615 v->first = this->next;
2616 }
2617 this->next->previous = NULL;
2618 }
2619
2620 this->next = next;
2621
2622 if (this->next != NULL) {
2623 /* A new next vehicle. Update the first and previous pointers */
2624 if (this->next->previous != NULL) this->next->previous->next = NULL;
2625 this->next->previous = this;
2626 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
2627 v->first = this->first;
2628 }
2629 }
2630 }
2631
2637 void Vehicle::AddToShared(Vehicle *shared_chain)
2638 {
2639 assert(this->previous_shared == NULL && this->next_shared == NULL);
2640
2641 if (shared_chain->orders.list == NULL) {
2642 assert(shared_chain->previous_shared == NULL);
2643 assert(shared_chain->next_shared == NULL);
2644 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
2645 }
2646
2647 this->next_shared = shared_chain->next_shared;
2648 this->previous_shared = shared_chain;
2649
2650 shared_chain->next_shared = this;
2651
2652 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
2653
2654 shared_chain->orders.list->AddVehicle(this);
2655 }
2656
2660 void Vehicle::RemoveFromShared()
2661 {
2662 /* Remember if we were first and the old window number before RemoveVehicle()
2663 * as this changes first if needed. */
2664 bool were_first = (this->FirstShared() == this);
2665 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
2666
2667 this->orders.list->RemoveVehicle(this);
2668
2669 if (!were_first) {
2670 /* We are not the first shared one, so only relink our previous one. */
2671 this->previous_shared->next_shared = this->NextShared();
2672 }
2673
2674 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
2675
2676
2677 if (this->orders.list->GetNumVehicles() == 1) {
2678 /* When there is only one vehicle, remove the shared order list window. */
2679 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
2680 InvalidateVehicleOrder(this->FirstShared(), VIWD_MODIFY_ORDERS);
2681 } else if (were_first) {
2682 /* If we were the first one, update to the new first one.
2683 * Note: FirstShared() is already the new first */
2684 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
2685 }
2686
2687 this->next_shared = NULL;
2688 this->previous_shared = NULL;
2689 }
2690
2691 void VehiclesYearlyLoop()
2692 {
2693 Vehicle *v;
2694 FOR_ALL_VEHICLES(v) {
2695 if (v->IsPrimaryVehicle()) {
2696 /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
2697 Money profit = v->GetDisplayProfitThisYear();
2698 if (v->age >= 730 && profit < 0) {
2699 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
2700 SetDParam(0, v->index);
2701 SetDParam(1, profit);
2702 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
2703 }
2704 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
2705 }
2706
2707 v->profit_last_year = v->profit_this_year;
2708 v->profit_this_year = 0;
2709 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
2710 }
2711 }
2712 GroupStatistics::UpdateProfits();
2713 SetWindowClassesDirty(WC_TRAINS_LIST);
2714 SetWindowClassesDirty(WC_SHIPS_LIST);
2715 SetWindowClassesDirty(WC_ROADVEH_LIST);
2716 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
2717 }
2718
2719
2729 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
2730 {
2731 const Engine *e = Engine::GetIfValid(engine_type);
2732 assert(e != NULL);
2733
2734 switch (e->type) {
2735 case VEH_TRAIN:
2736 return (st->facilities & FACIL_TRAIN) != 0;
2737
2738 case VEH_ROAD:
2739 /* For road vehicles we need the vehicle to know whether it can actually
2740 * use the station, but if it doesn't have facilities for RVs it is
2741 * certainly not possible that the station can be used. */
2742 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
2743
2744 case VEH_SHIP:
2745 return (st->facilities & FACIL_DOCK) != 0;
2746
2747 case VEH_AIRCRAFT:
2748 return (st->facilities & FACIL_AIRPORT) != 0 &&
2749 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
2750
2751 default:
2752 return false;
2753 }
2754 }
2755
2762 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
2763 {
2764 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
2765
2766 return CanVehicleUseStation(v->engine_type, st);
2767 }
2768
2774 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
2775 {
2776 assert(this->IsGroundVehicle());
2777 if (this->type == VEH_TRAIN) {
2778 return &Train::From(this)->gcache;
2779 } else {
2780 return &RoadVehicle::From(this)->gcache;
2781 }
2782 }
2783
2789 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
2790 {
2791 assert(this->IsGroundVehicle());
2792 if (this->type == VEH_TRAIN) {
2793 return &Train::From(this)->gcache;
2794 } else {
2795 return &RoadVehicle::From(this)->gcache;
2796 }
2797 }
2798
2804 uint16 &Vehicle::GetGroundVehicleFlags()
2805 {
2806 assert(this->IsGroundVehicle());
2807 if (this->type == VEH_TRAIN) {
2808 return Train::From(this)->gv_flags;
2809 } else {
2810 return RoadVehicle::From(this)->gv_flags;
2811 }
2812 }
2813
2819 const uint16 &Vehicle::GetGroundVehicleFlags() const
2820 {
2821 assert(this->IsGroundVehicle());
2822 if (this->type == VEH_TRAIN) {
2823 return Train::From(this)->gv_flags;
2824 } else {
2825 return RoadVehicle::From(this)->gv_flags;
2826 }
2827 }
2828
2837 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
2838 {
2839 if (v->type == VEH_TRAIN) {
2840 Train *u = Train::From(v);
2841 /* Only include whole vehicles, so start with the first articulated part */
2842 u = u->GetFirstEnginePart();
2843
2844 /* Include num_vehicles vehicles, not counting articulated parts */
2845 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
2846 do {
2847 /* Include current vehicle in the selection. */
2848 set.Include(u->index);
2849
2850 /* If the vehicle is multiheaded, add the other part too. */
2851 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
2852
2853 u = u->Next();
2854 } while (u != NULL && u->IsArticulatedPart());
2855 }
2856 }
2857 }