OpenTTD Source 20260802-master-g00efe6a4ba
autoreplace_cmd.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include "company_func.h"
12#include "train.h"
13#include "command_func.h"
14#include "engine_func.h"
15#include "vehicle_func.h"
16#include "autoreplace_func.h"
17#include "autoreplace_gui.h"
19#include "core/bitmath_func.hpp"
20#include "core/random_func.hpp"
21#include "vehiclelist.h"
22#include "road.h"
23#include "ai/ai.hpp"
24#include "news_func.h"
25#include "strings_func.h"
26#include "autoreplace_cmd.h"
27#include "group_cmd.h"
28#include "order_cmd.h"
29#include "train_cmd.h"
30#include "vehicle_cmd.h"
31#include "script/api/script_event_types.hpp"
32
33#include "table/strings.h"
34
35#include "safeguards.h"
36
37extern void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index);
38extern void ChangeVehicleNews(VehicleID from_index, VehicleID to_index);
39extern void ChangeVehicleViewWindow(VehicleID from_index, VehicleID to_index);
40
47static bool EnginesHaveCargoInCommon(EngineID engine_a, EngineID engine_b)
48{
49 CargoTypes available_cargoes_a = GetUnionOfArticulatedRefitMasks(engine_a, true);
50 CargoTypes available_cargoes_b = GetUnionOfArticulatedRefitMasks(engine_b, true);
51 return available_cargoes_a.None() || available_cargoes_b.None() || available_cargoes_a.Any(available_cargoes_b);
52}
53
61bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company)
62{
63 assert(Engine::IsValidID(from) && Engine::IsValidID(to));
64
65 const Engine *e_from = Engine::Get(from);
66 const Engine *e_to = Engine::Get(to);
67 VehicleType type = e_from->type;
68
69 /* check that the new vehicle type is available to the company and its type is the same as the original one */
70 if (!IsEngineBuildable(to, type, company)) return false;
71
72 switch (type) {
73 case VehicleType::Train: {
74 /* make sure the railtypes are compatible */
75 if (!GetAllCompatibleRailTypes(e_from->VehInfo<RailVehicleInfo>().railtypes).Any(GetAllCompatibleRailTypes(e_to->VehInfo<RailVehicleInfo>().railtypes))) return false;
76
77 /* make sure we do not replace wagons with engines or vice versa */
78 if ((e_from->VehInfo<RailVehicleInfo>().railveh_type == RailVehicleType::Wagon) != (e_to->VehInfo<RailVehicleInfo>().railveh_type == RailVehicleType::Wagon)) return false;
79 break;
80 }
81
83 /* make sure the roadtypes are compatible */
84 if (!GetRoadTypeInfo(e_from->VehInfo<RoadVehicleInfo>().roadtype)->powered_roadtypes.Any(GetRoadTypeInfo(e_to->VehInfo<RoadVehicleInfo>().roadtype)->powered_roadtypes)) return false;
85
86 /* make sure that we do not replace a tram with a normal road vehicles or vice versa */
87 if (e_from->info.misc_flags.Test(EngineMiscFlag::RoadIsTram) != e_to->info.misc_flags.Test(EngineMiscFlag::RoadIsTram)) return false;
88 break;
89
91 /* make sure that we do not replace a plane with a helicopter or vice versa */
92 if ((e_from->VehInfo<AircraftVehicleInfo>().subtype & AIR_CTOL) != (e_to->VehInfo<AircraftVehicleInfo>().subtype & AIR_CTOL)) return false;
93 break;
94
95 default: break;
96 }
97
98 /* the engines needs to be able to carry the same cargo */
99 return EnginesHaveCargoInCommon(from, to);
100}
101
109{
110 assert(v == nullptr || v->First() == v);
111
112 for (Vehicle *src = v; src != nullptr; src = src->Next()) {
113 assert(src->cargo.TotalCount() == src->cargo.ActionCount(VehicleCargoList::MoveToAction::Keep));
114
115 /* Do we need to more cargo away? */
116 if (src->cargo.TotalCount() <= src->cargo_cap) continue;
117
118 /* We need to move a particular amount. Try that on the other vehicles. */
119 uint to_spread = src->cargo.TotalCount() - src->cargo_cap;
120 for (Vehicle *dest = v; dest != nullptr && to_spread != 0; dest = dest->Next()) {
121 assert(dest->cargo.TotalCount() == dest->cargo.ActionCount(VehicleCargoList::MoveToAction::Keep));
122 if (dest->cargo.TotalCount() >= dest->cargo_cap || dest->cargo_type != src->cargo_type) continue;
123
124 uint amount = std::min(to_spread, dest->cargo_cap - dest->cargo.TotalCount());
125 src->cargo.Shift(amount, &dest->cargo);
126 to_spread -= amount;
127 }
128
129 /* Any left-overs will be thrown away, but not their feeder share. */
130 if (src->cargo_cap < src->cargo.TotalCount()) src->cargo.Truncate(src->cargo.TotalCount() - src->cargo_cap);
131 }
132}
133
143static void TransferCargo(Vehicle *old_veh, Vehicle *new_head, bool part_of_chain)
144{
145 assert(!part_of_chain || new_head->IsPrimaryVehicle());
146 /* Loop through source parts */
147 for (Vehicle *src = old_veh; src != nullptr; src = src->Next()) {
148 assert(src->cargo.TotalCount() == src->cargo.ActionCount(VehicleCargoList::MoveToAction::Keep));
149 if (!part_of_chain && src->type == VehicleType::Train && src != old_veh && src != Train::From(old_veh)->other_multiheaded_part && !src->IsArticulatedPart()) {
150 /* Skip vehicles, which do not belong to old_veh */
151 src = src->GetLastEnginePart();
152 continue;
153 }
154 if (src->cargo_type >= NUM_CARGO || src->cargo.TotalCount() == 0) continue;
155
156 /* Find free space in the new chain */
157 for (Vehicle *dest = new_head; dest != nullptr && src->cargo.TotalCount() > 0; dest = dest->Next()) {
158 assert(dest->cargo.TotalCount() == dest->cargo.ActionCount(VehicleCargoList::MoveToAction::Keep));
159 if (!part_of_chain && dest->type == VehicleType::Train && dest != new_head && dest != Train::From(new_head)->other_multiheaded_part && !dest->IsArticulatedPart()) {
160 /* Skip vehicles, which do not belong to new_head */
161 dest = dest->GetLastEnginePart();
162 continue;
163 }
164 if (dest->cargo_type != src->cargo_type) continue;
165
166 uint amount = std::min(src->cargo.TotalCount(), dest->cargo_cap - dest->cargo.TotalCount());
167 if (amount <= 0) continue;
168
169 src->cargo.Shift(amount, &dest->cargo);
170 }
171 }
172
173 /* Update train weight etc., the old vehicle will be sold anyway */
174 if (part_of_chain && new_head->type == VehicleType::Train) Train::From(new_head)->ConsistChanged(CCF_LOADUNLOAD);
175}
176
183static bool VerifyAutoreplaceRefitForOrders(const Vehicle *v, EngineID engine_type)
184{
185 CargoTypes union_refit_mask_a = GetUnionOfArticulatedRefitMasks(v->engine_type, false);
186 CargoTypes union_refit_mask_b = GetUnionOfArticulatedRefitMasks(engine_type, false);
187
188 const Vehicle *u = (v->type == VehicleType::Train) ? v->First() : v;
189 for (const Order &o : u->Orders()) {
190 if (!o.IsRefit() || o.IsAutoRefit()) continue;
191 CargoType cargo_type = o.GetRefitCargo();
192
193 if (!union_refit_mask_a.Test(cargo_type)) continue;
194 if (!union_refit_mask_b.Test(cargo_type)) return false;
195 }
196
197 return true;
198}
199
207{
208 CargoTypes union_refit_mask = GetUnionOfArticulatedRefitMasks(engine_type, false);
209
210 const Vehicle *u = (v->type == VehicleType::Train) ? v->First() : v;
211
212 const OrderList *orders = u->orders;
213 if (orders == nullptr) return -1;
214 for (VehicleOrderID i = 0; i < orders->GetNumOrders(); i++) {
215 const Order *o = orders->GetOrderAt(i);
216 if (!o->IsRefit()) continue;
217 if (!union_refit_mask.Test(o->GetRefitCargo())) return i;
218 }
219
220 return -1;
221}
222
232static CargoType GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type, bool part_of_chain)
233{
234 CargoTypes available_cargo_types, union_mask;
235 GetArticulatedRefitMasks(engine_type, true, &union_mask, &available_cargo_types);
236
237 if (union_mask.None()) return CARGO_NO_REFIT; // Don't try to refit an engine with no cargo capacity
238
239 CargoType cargo_type;
240 CargoTypes cargo_mask = GetCargoTypesOfArticulatedVehicle(v, &cargo_type);
241 if (!HasAtMostOneBit(cargo_mask.base())) {
242 CargoTypes new_engine_default_cargoes = GetCargoTypesOfArticulatedParts(engine_type);
243 if ((cargo_mask & new_engine_default_cargoes) == cargo_mask) {
244 return CARGO_NO_REFIT; // engine_type is already a mixed cargo type which matches the incoming vehicle by default, no refit required
245 }
246
247 return INVALID_CARGO; // We cannot refit to mixed cargoes in an automated way
248 }
249
250 if (!IsValidCargoType(cargo_type)) {
251 if (v->type != VehicleType::Train) return CARGO_NO_REFIT; // If the vehicle does not carry anything at all, every replacement is fine.
252
253 if (!part_of_chain) return CARGO_NO_REFIT;
254
255 /* the old engine didn't have cargo capacity, but the new one does
256 * now we will figure out what cargo the train is carrying and refit to fit this */
257
258 for (v = v->First(); v != nullptr; v = v->Next()) {
259 if (!v->GetEngine()->CanCarryCargo()) continue;
260 /* Now we found a cargo type being carried on the train and we will see if it is possible to carry to this one */
261 if (available_cargo_types.Test(v->cargo_type)) return v->cargo_type;
262 }
263
264 return CARGO_NO_REFIT; // We failed to find a cargo type on the old vehicle and we will not refit the new one
265 } else {
266 if (!available_cargo_types.Test(cargo_type)) return INVALID_CARGO; // We can't refit the vehicle to carry the cargo we want
267
268 if (part_of_chain && !VerifyAutoreplaceRefitForOrders(v, engine_type)) return INVALID_CARGO; // Some refit orders lose their effect
269
270 return cargo_type;
271 }
272}
273
282static CommandCost GetNewEngineType(const Vehicle *v, const Company *c, bool always_replace, EngineID &e)
283{
284 assert(v->type != VehicleType::Train || !v->IsArticulatedPart());
285
286 e = EngineID::Invalid();
287
288 if (v->type == VehicleType::Train && Train::From(v)->IsRearDualheaded()) {
289 /* we build the rear ends of multiheaded trains with the front ones */
290 return CommandCost();
291 }
292
293 bool replace_when_old;
294 e = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
295 if (!always_replace && replace_when_old && !v->NeedsAutorenewing(c, false)) e = EngineID::Invalid();
296
297 /* Autoreplace, if engine is available */
298 if (e != EngineID::Invalid() && IsEngineBuildable(e, v->type, _current_company)) {
299 return CommandCost();
300 }
301
302 /* Autorenew if needed */
303 if (v->NeedsAutorenewing(c)) e = v->engine_type;
304
305 /* Nothing to do or all is fine? */
306 if (e == EngineID::Invalid() || IsEngineBuildable(e, v->type, _current_company)) return CommandCost();
307
308 /* The engine we need is not available. Report error to user */
309 return CommandCost(STR_ERROR_RAIL_VEHICLE_NOT_AVAILABLE + to_underlying(v->type));
310}
311
321static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehicle, bool part_of_chain, DoCommandFlags flags)
322{
323 *new_vehicle = nullptr;
324
325 /* Shall the vehicle be replaced? */
327 EngineID e;
328 CommandCost cost = GetNewEngineType(old_veh, c, true, e);
329 if (cost.Failed()) return cost;
330 if (e == EngineID::Invalid()) return CommandCost(); // neither autoreplace is set, nor autorenew is triggered
331
332 /* Does it need to be refitted */
333 CargoType refit_cargo = GetNewCargoTypeForReplace(old_veh, e, part_of_chain);
334 if (!IsValidCargoType(refit_cargo)) {
335 if (!IsLocalCompany() || !flags.Test(DoCommandFlag::Execute)) return CommandCost();
336
337 VehicleID old_veh_id = (old_veh->type == VehicleType::Train) ? Train::From(old_veh)->First()->index : old_veh->index;
338 EncodedString headline;
339
340 int order_id = GetIncompatibleRefitOrderIdForAutoreplace(old_veh, e);
341 if (order_id != -1) {
342 /* Orders contained a refit order that is incompatible with the new vehicle. */
343 headline = GetEncodedString(STR_NEWS_VEHICLE_AUTORENEW_FAILED,
344 old_veh_id,
345 STR_ERROR_AUTOREPLACE_INCOMPATIBLE_REFIT,
346 order_id + 1); // 1-based indexing for display
347 } else {
348 /* Current cargo is incompatible with the new vehicle. */
349 headline = GetEncodedString(STR_NEWS_VEHICLE_AUTORENEW_FAILED,
350 old_veh_id,
351 STR_ERROR_AUTOREPLACE_INCOMPATIBLE_CARGO,
352 CargoSpec::Get(old_veh->cargo_type)->name);
353 }
354
355 AddVehicleAdviceNewsItem(AdviceType::AutorenewFailed, std::move(headline), old_veh_id);
356 return CommandCost();
357 }
358
359 /* Build the new vehicle */
360 VehicleID new_veh_id;
361 std::tie(cost, new_veh_id, std::ignore, std::ignore, std::ignore) = Command<Commands::BuildVehicle>::Do({DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, old_veh->tile, e, true, INVALID_CARGO, ClientID::Invalid);
362 if (cost.Failed()) return cost;
363
364 Vehicle *new_veh = Vehicle::Get(new_veh_id);
365 *new_vehicle = new_veh;
366
367 /* Refit the vehicle if needed */
368 if (refit_cargo != CARGO_NO_REFIT) {
369 uint8_t subtype = GetBestFittingSubType(old_veh, new_veh, refit_cargo);
370
371 cost.AddCost(ExtractCommandCost(Command<Commands::RefitVehicle>::Do(DoCommandFlag::Execute, new_veh->index, refit_cargo, subtype, false, false, 0)));
372 assert(cost.Succeeded()); // This should be ensured by GetNewCargoTypeForReplace()
373 }
374
375 /* Try to reverse the vehicle, but do not care if it fails as the new type might not be reversible */
376 if (new_veh->type == VehicleType::Train && Train::From(old_veh)->flags.Test(VehicleRailFlag::Flipped)) {
377 /* Only copy the reverse state if neither old or new vehicle implements reverse-on-build probability callback. */
380 Command<Commands::ReverseTrainDirection>::Do(DoCommandFlag::Execute, new_veh->index, true);
381 }
382 }
383
384 return cost;
385}
386
393static inline CommandCost DoCmdStartStopVehicle(const Vehicle *v, bool evaluate_callback)
394{
395 return Command<Commands::StartStopVehicle>::Do({DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, v->index, evaluate_callback);
396}
397
406static inline CommandCost CmdMoveVehicle(const Vehicle *v, const Vehicle *after, DoCommandFlags flags, bool whole_chain)
407{
408 return Command<Commands::MoveRailVehicle>::Do(flags.Set(DoCommandFlag::NoCargoCapacityCheck), v->index, after != nullptr ? after->index : VehicleID::Invalid(), whole_chain);
409}
410
419{
420 CommandCost cost = CommandCost();
421
422 /* Share orders */
423 if (cost.Succeeded() && old_head != new_head) cost.AddCost(Command<Commands::CloneOrder>::Do(DoCommandFlag::Execute, CO_SHARE, new_head->index, old_head->index));
424
425 /* Copy group membership */
426 if (cost.Succeeded() && old_head != new_head) cost.AddCost(ExtractCommandCost(Command<Commands::AddVehicleToGroup>::Do(DoCommandFlag::Execute, old_head->group_id, new_head->index, false, VehicleListIdentifier{})));
427
428 /* Perform start/stop check whether the new vehicle suits newgrf restrictions etc. */
429 if (cost.Succeeded()) {
430 /* Start the vehicle, might be denied by certain things */
431 assert(new_head->vehstatus.Test(VehState::Stopped));
432 cost.AddCost(DoCmdStartStopVehicle(new_head, true));
433
434 /* Stop the vehicle again, but do not care about evil newgrfs allowing starting but not stopping :p */
435 if (cost.Succeeded()) cost.AddCost(DoCmdStartStopVehicle(new_head, false));
436 }
437
438 /* Last do those things which do never fail (resp. we do not care about), but which are not undo-able */
439 if (cost.Succeeded() && old_head != new_head && flags.Test(DoCommandFlag::Execute)) {
440 /* Copy other things which cannot be copied by a command and which shall not stay reset from the build vehicle command */
441 new_head->CopyVehicleConfigAndStatistics(old_head);
443
444 /* Switch vehicle windows/news to the new vehicle, so they are not closed/deleted when the old vehicle is sold */
445 ChangeVehicleViewports(old_head->index, new_head->index);
446 ChangeVehicleViewWindow(old_head->index, new_head->index);
447 ChangeVehicleNews(old_head->index, new_head->index);
448 }
449
450 return cost;
451}
452
460static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlags flags, bool *nothing_to_do)
461{
462 Train *old_v = Train::From(*single_unit);
463 assert(!old_v->IsArticulatedPart() && !old_v->IsRearDualheaded());
464
466
467 /* Build and refit replacement vehicle */
468 Vehicle *new_v = nullptr;
469 cost.AddCost(BuildReplacementVehicle(old_v, &new_v, false, flags));
470
471 /* Was a new vehicle constructed? */
472 if (cost.Succeeded() && new_v != nullptr) {
473 *nothing_to_do = false;
474
475 if (flags.Test(DoCommandFlag::Execute)) {
476 /* Move the new vehicle behind the old */
477 CmdMoveVehicle(new_v, old_v, DoCommandFlag::Execute, false);
478
479 /* Take over cargo
480 * Note: We do only transfer cargo from the old to the new vehicle.
481 * I.e. we do not transfer remaining cargo to other vehicles.
482 * Else you would also need to consider moving cargo to other free chains,
483 * or doing the same in ReplaceChain(), which would be quite troublesome.
484 */
485 TransferCargo(old_v, new_v, false);
486
487 *single_unit = new_v;
488
489 AI::NewEvent(old_v->owner, new ScriptEventVehicleAutoReplaced(old_v->index, new_v->index));
490 }
491
492 /* Sell the old vehicle */
493 cost.AddCost(Command<Commands::SellVehicle>::Do(flags, old_v->index, false, false, ClientID::Invalid));
494
495 /* If we are not in DoCommandFlag::Execute undo everything */
496 if (!flags.Test(DoCommandFlag::Execute)) {
497 Command<Commands::SellVehicle>::Do(DoCommandFlag::Execute, new_v->index, false, false, ClientID::Invalid);
498 }
499 }
500
501 return cost;
502}
503
524
533static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlags flags, bool wagon_removal, bool *nothing_to_do)
534{
535 Vehicle *old_head = *chain;
536 assert(old_head->IsPrimaryVehicle());
537
539
540 if (old_head->type == VehicleType::Train) {
541 /* Store the length of the old vehicle chain, rounded up to whole tiles */
542 uint16_t old_total_length = CeilDiv(Train::From(old_head)->gcache.cached_total_length, TILE_SIZE) * TILE_SIZE;
543
544 std::vector<ReplaceChainItem> replacements;
545
546 /* Collect vehicles and build replacements
547 * Note: The replacement vehicles can only successfully build as long as the old vehicles are still in their chain */
548 for (Train *w = Train::From(old_head); w != nullptr; w = w->GetNextUnit()) {
549 ReplaceChainItem &replacement = replacements.emplace_back(w, nullptr, 0);
550
551 CommandCost ret = BuildReplacementVehicle(replacement.old_veh, &replacement.new_veh, true, flags);
552 replacement.cost = ret.GetCost();
553 cost.AddCost(std::move(ret));
554 if (cost.Failed()) break;
555
556 if (replacement.new_veh != nullptr) *nothing_to_do = false;
557 }
558 Vehicle *new_head = replacements.front().GetVehicle();
559
560 /* Note: When autoreplace has already failed here, old_vehs[] is not completely initialized. But it is also not needed. */
561 if (cost.Succeeded()) {
562 /* Separate the head, so we can start constructing the new chain */
563 Train *second = Train::From(old_head)->GetNextUnit();
564 if (second != nullptr) cost.AddCost(CmdMoveVehicle(second, nullptr, {DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, true));
565
566 assert(Train::From(new_head)->GetNextUnit() == nullptr);
567
568 /* Append engines to the new chain
569 * We do this from back to front, so that the head of the temporary vehicle chain does not change all the time.
570 * That way we also have less trouble when exceeding the unitnumber limit.
571 * OTOH the vehicle attach callback is more expensive this way :s */
572 Vehicle *last_engine = nullptr;
573 if (cost.Succeeded()) {
574 for (auto it = std::rbegin(replacements); it != std::rend(replacements); ++it) {
575 Vehicle *append = it->GetVehicle();
576
577 if (RailVehInfo(append->engine_type)->railveh_type == RailVehicleType::Wagon) continue;
578
579 if (it->new_veh != nullptr) {
580 /* Move the old engine to a separate row with DoCommandFlag::AutoReplace. Else
581 * moving the wagon in front may fail later due to unitnumber limit.
582 * (We have to attach wagons without DoCommandFlag::AutoReplace.) */
583 CmdMoveVehicle(it->old_veh, nullptr, {DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, false);
584 }
585
586 if (last_engine == nullptr) last_engine = append;
587 cost.AddCost(CmdMoveVehicle(append, new_head, DoCommandFlag::Execute, false));
588 if (cost.Failed()) break;
589 }
590 if (last_engine == nullptr) last_engine = new_head;
591 }
592
593 /* When wagon removal is enabled and the new engines without any wagons are already longer than the old, we have to fail */
594 if (cost.Succeeded() && wagon_removal && Train::From(new_head)->gcache.cached_total_length > old_total_length) cost = CommandCost(STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT);
595
596 /* Append/insert wagons into the new vehicle chain
597 * We do this from back to front, so we can stop when wagon removal or maximum train length (i.e. from mammoth-train setting) is triggered.
598 */
599 if (cost.Succeeded()) {
600 for (auto it = std::rbegin(replacements); it != std::rend(replacements); ++it) {
601 assert(last_engine != nullptr);
602 Vehicle *append = it->GetVehicle();
603
604 if (RailVehInfo(append->engine_type)->railveh_type == RailVehicleType::Wagon) {
605 /* Insert wagon after 'last_engine' */
606 CommandCost res = CmdMoveVehicle(append, last_engine, DoCommandFlag::Execute, false);
607
608 /* When we allow removal of wagons, either the move failing due
609 * to the train becoming too long, or the train becoming longer
610 * would move the vehicle to the empty vehicle chain. */
611 if (wagon_removal && (res.Failed() ? res.GetErrorMessage() == STR_ERROR_TRAIN_TOO_LONG : Train::From(new_head)->gcache.cached_total_length > old_total_length)) {
613 break;
614 }
615
616 cost.AddCost(std::move(res));
617 if (cost.Failed()) break;
618 } else {
619 /* We have reached 'last_engine', continue with the next engine towards the front */
620 assert(append == last_engine);
621 last_engine = Train::From(last_engine)->GetPrevUnit();
622 }
623 }
624 }
625
626 /* Sell superfluous new vehicles that could not be inserted. */
627 if (cost.Succeeded() && wagon_removal) {
628 assert(Train::From(new_head)->gcache.cached_total_length <= _settings_game.vehicle.max_train_length * TILE_SIZE);
629 for (auto it = std::next(std::begin(replacements)); it != std::end(replacements); ++it) {
630 Vehicle *wagon = it->new_veh;
631 if (wagon == nullptr) continue;
632 if (wagon->First() == new_head) break;
633
634 assert(RailVehInfo(wagon->engine_type)->railveh_type == RailVehicleType::Wagon);
635
636 /* Sell wagon */
637 [[maybe_unused]] CommandCost ret = Command<Commands::SellVehicle>::Do(DoCommandFlag::Execute, wagon->index, false, false, ClientID::Invalid);
638 assert(ret.Succeeded());
639 it->new_veh = nullptr;
640
641 /* Revert the money subtraction when the vehicle was built.
642 * This value is different from the sell value, esp. because of refitting */
643 cost.AddCost(-it->cost);
644 }
645 }
646
647 /* The new vehicle chain is constructed, now take over orders and everything... */
648 if (cost.Succeeded()) cost.AddCost(CopyHeadSpecificThings(old_head, new_head, flags));
649
650 if (cost.Succeeded()) {
651 /* Success ! */
652 if (flags.Test(DoCommandFlag::Execute) && new_head != old_head) {
653 *chain = new_head;
654 AI::NewEvent(old_head->owner, new ScriptEventVehicleAutoReplaced(old_head->index, new_head->index));
655 }
656
657 /* Transfer cargo of old vehicles and sell them */
658 for (auto it = std::begin(replacements); it != std::end(replacements); ++it) {
659 Vehicle *w = it->old_veh;
660 /* Is the vehicle again part of the new chain?
661 * Note: We cannot test 'new_vehs[i] != nullptr' as wagon removal might cause to remove both */
662 if (w->First() == new_head) continue;
663
664 if (flags.Test(DoCommandFlag::Execute)) TransferCargo(w, new_head, true);
665
666 /* Sell the vehicle.
667 * Note: This might temporarily construct new trains, so use DoCommandFlag::AutoReplace to prevent
668 * it from failing due to engine limits. */
669 cost.AddCost(Command<Commands::SellVehicle>::Do(DoCommandFlags{flags}.Set(DoCommandFlag::AutoReplace), w->index, false, false, ClientID::Invalid));
670 if (flags.Test(DoCommandFlag::Execute)) {
671 it->old_veh = nullptr;
672 if (it == std::begin(replacements)) old_head = nullptr;
673 }
674 }
675
676 if (flags.Test(DoCommandFlag::Execute)) CheckCargoCapacity(new_head);
677 }
678
679 /* If we are not in DoCommandFlag::Execute undo everything, i.e. rearrange old vehicles.
680 * We do this from back to front, so that the head of the temporary vehicle chain does not change all the time.
681 * Note: The vehicle attach callback is disabled here :) */
682 if (!flags.Test(DoCommandFlag::Execute)) {
683 /* Separate the head, so we can reattach the old vehicles */
684 second = Train::From(old_head)->GetNextUnit();
685 if (second != nullptr) CmdMoveVehicle(second, nullptr, {DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, true);
686
687 assert(Train::From(old_head)->GetNextUnit() == nullptr);
688
689 for (auto it = std::rbegin(replacements); it != std::rend(replacements); ++it) {
690 [[maybe_unused]] CommandCost ret = CmdMoveVehicle(it->old_veh, old_head, {DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, false);
691 assert(ret.Succeeded());
692 }
693 }
694 }
695
696 /* Finally undo buying of new vehicles */
697 if (!flags.Test(DoCommandFlag::Execute)) {
698 for (auto it = std::rbegin(replacements); it != std::rend(replacements); ++it) {
699 if (it->new_veh != nullptr) {
700 Command<Commands::SellVehicle>::Do(DoCommandFlag::Execute, it->new_veh->index, false, false, ClientID::Invalid);
701 it->new_veh = nullptr;
702 }
703 }
704 }
705 } else {
706 /* Build and refit replacement vehicle */
707 Vehicle *new_head = nullptr;
708 cost.AddCost(BuildReplacementVehicle(old_head, &new_head, true, flags));
709
710 /* Was a new vehicle constructed? */
711 if (cost.Succeeded() && new_head != nullptr) {
712 *nothing_to_do = false;
713
714 /* The new vehicle is constructed, now take over orders and everything... */
715 cost.AddCost(CopyHeadSpecificThings(old_head, new_head, flags));
716
717 if (cost.Succeeded()) {
718 /* The new vehicle is constructed, now take over cargo */
719 if (flags.Test(DoCommandFlag::Execute)) {
720 TransferCargo(old_head, new_head, true);
721 *chain = new_head;
722
723 AI::NewEvent(old_head->owner, new ScriptEventVehicleAutoReplaced(old_head->index, new_head->index));
724 }
725
726 /* Sell the old vehicle */
727 cost.AddCost(Command<Commands::SellVehicle>::Do(flags, old_head->index, false, false, ClientID::Invalid));
728 }
729
730 /* If we are not in DoCommandFlag::Execute undo everything */
731 if (!flags.Test(DoCommandFlag::Execute)) {
732 Command<Commands::SellVehicle>::Do(DoCommandFlag::Execute, new_head->index, false, false, ClientID::Invalid);
733 }
734 }
735 }
736
737 return cost;
738}
739
748{
749 Vehicle *v = Vehicle::GetIfValid(veh_id);
750 if (v == nullptr || !IsCompanyBuildableVehicleType(v)) return CMD_ERROR;
751
753 if (ret.Failed()) return ret;
754
756
757 bool free_wagon = false;
758 if (v->type == VehicleType::Train) {
759 Train *t = Train::From(v);
760 if (t->IsArticulatedPart() || t->IsRearDualheaded()) return CMD_ERROR;
761 free_wagon = !t->IsFrontEngine();
762 if (free_wagon && t->First()->IsFrontEngine()) return CMD_ERROR;
763 } else {
764 if (!v->IsPrimaryVehicle()) return CMD_ERROR;
765 }
766 if (!v->IsChainInDepot()) return CMD_ERROR;
767
769 bool wagon_removal = c->settings.renew_keep_length;
770
771 const Group *g = Group::GetIfValid(v->group_id);
772 if (g != nullptr) wagon_removal = g->flags.Test(GroupFlag::ReplaceWagonRemoval);
773
774 /* Test whether any replacement is set, before issuing a whole lot of commands that would end in nothing changed */
775 Vehicle *w = v;
776 bool any_replacements = false;
777 while (w != nullptr) {
778 EngineID e;
779 CommandCost cost = GetNewEngineType(w, c, false, e);
780 if (cost.Failed()) return cost;
781 any_replacements |= (e != EngineID::Invalid());
782 w = (!free_wagon && w->type == VehicleType::Train ? Train::From(w)->GetNextUnit() : nullptr);
783 }
784
786 bool nothing_to_do = true;
787
788 if (any_replacements) {
789 bool was_stopped = free_wagon || v->vehstatus.Test(VehState::Stopped);
790
791 /* Stop the vehicle */
792 if (!was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, true));
793 if (cost.Failed()) return cost;
794
795 assert(free_wagon || v->IsStoppedInDepot());
796
797 /* We have to construct the new vehicle chain to test whether it is valid.
798 * Vehicle construction needs random bits, so we have to save the random seeds
799 * to prevent desyncs and to replay newgrf callbacks during DoCommandFlag::Execute */
800 SavedRandomSeeds saved_seeds;
801 SaveRandomSeeds(&saved_seeds);
802 if (free_wagon) {
803 cost.AddCost(ReplaceFreeUnit(&v, DoCommandFlags{flags}.Reset(DoCommandFlag::Execute), &nothing_to_do));
804 } else {
805 cost.AddCost(ReplaceChain(&v, DoCommandFlags{flags}.Reset(DoCommandFlag::Execute), wagon_removal, &nothing_to_do));
806 }
807 RestoreRandomSeeds(saved_seeds);
808
809 if (cost.Succeeded() && flags.Test(DoCommandFlag::Execute)) {
810 if (free_wagon) {
811 ret = ReplaceFreeUnit(&v, flags, &nothing_to_do);
812 } else {
813 ret = ReplaceChain(&v, flags, wagon_removal, &nothing_to_do);
814 }
815 assert(ret.Succeeded() && ret.GetCost() == cost.GetCost());
816 }
817
818 /* Restart the vehicle */
819 if (!was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, false));
820 }
821
822 if (cost.Succeeded() && nothing_to_do) cost = CommandCost(STR_ERROR_AUTOREPLACE_NOTHING_TO_DO);
823 return cost;
824}
825
835CommandCost CmdSetAutoReplace(DoCommandFlags flags, GroupID id_g, EngineID old_engine_type, EngineID new_engine_type, bool when_old)
836{
838 if (c == nullptr) return CMD_ERROR;
839
840 CommandCost cost;
841
842 if (Group::IsValidID(id_g) ? Group::Get(id_g)->owner != _current_company : !IsAllGroupID(id_g) && !IsDefaultGroupID(id_g)) return CMD_ERROR;
843 if (!Engine::IsValidID(old_engine_type)) return CMD_ERROR;
844 if (Group::IsValidID(id_g) && Group::Get(id_g)->vehicle_type != Engine::Get(old_engine_type)->type) return CMD_ERROR;
845
846 if (new_engine_type != EngineID::Invalid()) {
847 if (!Engine::IsValidID(new_engine_type)) return CMD_ERROR;
848 if (!CheckAutoreplaceValidity(old_engine_type, new_engine_type, _current_company)) return CMD_ERROR;
849
850 cost = AddEngineReplacementForCompany(c, old_engine_type, new_engine_type, id_g, when_old, flags);
851 } else {
852 cost = RemoveEngineReplacementForCompany(c, old_engine_type, id_g, flags);
853 }
854
855 if (flags.Test(DoCommandFlag::Execute)) {
857 if (IsLocalCompany()) SetWindowDirty(WindowClass::ReplaceVehicle, Engine::Get(old_engine_type)->type);
858
859 const VehicleType vt = Engine::Get(old_engine_type)->type;
861 }
862 if (flags.Test(DoCommandFlag::Execute) && IsLocalCompany()) InvalidateAutoreplaceWindow(old_engine_type, id_g);
863
864 return cost;
865}
866
Base functions for all AIs.
CargoTypes GetUnionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type)
Ors the refit_masks of all articulated parts.
CargoTypes GetCargoTypesOfArticulatedVehicle(const Vehicle *v, CargoType *cargo_type)
Get cargo mask of all cargoes carried by an articulated vehicle.
void GetArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type, CargoTypes *union_mask, CargoTypes *intersection_mask)
Merges the refit_masks of all articulated parts.
CargoTypes GetCargoTypesOfArticulatedParts(EngineID engine)
Get the cargo mask of the parts of a given engine.
Functions related to articulated vehicles.
static int GetIncompatibleRefitOrderIdForAutoreplace(const Vehicle *v, EngineID engine_type)
Gets the index of the first refit order that is incompatible with the requested engine type.
static CommandCost DoCmdStartStopVehicle(const Vehicle *v, bool evaluate_callback)
Issue a start/stop command.
void CheckCargoCapacity(Vehicle *v)
Check the capacity of all vehicles in a chain and spread cargo if needed.
static CargoType GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type, bool part_of_chain)
Function to find what type of cargo to refit to when autoreplacing.
void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
Switches viewports following vehicles, which get autoreplaced.
Definition window.cpp:3535
static CommandCost CmdMoveVehicle(const Vehicle *v, const Vehicle *after, DoCommandFlags flags, bool whole_chain)
Issue a train vehicle move command.
CommandCost CmdSetAutoReplace(DoCommandFlags flags, GroupID id_g, EngineID old_engine_type, EngineID new_engine_type, bool when_old)
Change engine renewal parameters.
void ChangeVehicleNews(VehicleID from_index, VehicleID to_index)
Report a change in vehicle IDs (due to autoreplace) to affected vehicle news.
void ChangeVehicleViewWindow(VehicleID from_index, VehicleID to_index)
Report a change in vehicle IDs (due to autoreplace) to affected vehicle windows.
static bool EnginesHaveCargoInCommon(EngineID engine_a, EngineID engine_b)
Figure out if two engines got at least one type of cargo in common (refitting if needed).
static bool VerifyAutoreplaceRefitForOrders(const Vehicle *v, EngineID engine_type)
Tests whether refit orders that applied to v will also apply to the new vehicle type.
bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company)
Checks some basic properties whether autoreplace is allowed.
static CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head, DoCommandFlags flags)
Copy head specific things to the new vehicle chain after it was successfully constructed.
static CommandCost GetNewEngineType(const Vehicle *v, const Company *c, bool always_replace, EngineID &e)
Get the EngineID of the replacement for a vehicle.
static void TransferCargo(Vehicle *old_veh, Vehicle *new_head, bool part_of_chain)
Transfer cargo from a single (articulated )old vehicle to the new vehicle chain.
static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlags flags, bool *nothing_to_do)
Replace a single unit in a free wagon chain.
static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehicle, bool part_of_chain, DoCommandFlags flags)
Builds and refits a replacement vehicle Important: The old vehicle is still in the original vehicle c...
static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlags flags, bool wagon_removal, bool *nothing_to_do)
Replace a whole vehicle chain.
CommandCost CmdAutoreplaceVehicle(DoCommandFlags flags, VehicleID veh_id)
Autoreplaces a vehicle Trains are replaced as a whole chain, free wagons in depot are replaced on the...
Command definitions related to autoreplace.
Functions related to autoreplacing.
CommandCost AddEngineReplacementForCompany(Company *c, EngineID old_engine, EngineID new_engine, GroupID group, bool replace_when_old, DoCommandFlags flags)
Add an engine replacement for the company.
EngineID EngineReplacementForCompany(const Company *c, EngineID engine, GroupID group, bool *replace_when_old=nullptr)
Retrieve the engine replacement for the given company and original engine type.
CommandCost RemoveEngineReplacementForCompany(Company *c, EngineID engine, GroupID group, DoCommandFlags flags)
Remove an engine replacement for the company.
void InvalidateAutoreplaceWindow(EngineID e, GroupID id_g)
Rebuild the left autoreplace list if an engine is removed or added.
Functions related to the autoreplace GUIs.
Functions related to bit mathematics.
constexpr bool HasAtMostOneBit(T value)
Test whether value has at most 1 bit set.
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:110
EnumBitSet< CargoType, uint64_t > CargoTypes
Bitset of CargoType elements.
Definition cargo_type.h:113
static constexpr CargoType NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:75
static constexpr CargoType CARGO_NO_REFIT
Do not refit cargo of a vehicle (used in vehicle orders and auto-replace/auto-renew).
Definition cargo_type.h:79
CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:22
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition ai_core.cpp:231
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Tstorage base() const noexcept
Retrieve the raw value behind this bit set.
constexpr bool None() const
Test if none of the values are set.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
Common return value for all commands.
bool Succeeded() const
Did this command succeed?
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?
StringID GetErrorMessage() const
Returns the error message of a command.
Container for an encoded string, created by GetEncodedString.
VehicleType type
Vehicle type, ie VehicleType::Road, VehicleType::Train, etc.
Definition engine_base.h:64
bool CanCarryCargo() const
Determines whether an engine can carry something.
Definition engine.cpp:194
RoadTypes powered_roadtypes
bitmask to the OTHER roadtypes on which a vehicle of THIS roadtype generates power
Definition road.h:98
uint TotalCount() const
Returns sum of cargo, including reserved cargo.
Functions related to commands.
CommandCost & ExtractCommandCost(Tret &ret)
Extract the CommandCost from a command proc result.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Execute
execute the given command
@ NoCargoCapacityCheck
when autoreplace/autorenew is in progress, this shall prevent truncating the amount of cargo in the v...
@ AutoReplace
autoreplace/autorenew is in progress, this shall disable vehicle limits when building,...
EnumBitSet< DoCommandFlag, uint16_t > DoCommandFlags
Bitset of DoCommandFlag elements.
CommandCost CheckOwnership(Owner owner, TileIndex tile)
Check whether the current owner owns something.
CompanyID _current_company
Company currently doing an action.
Functions related to companies.
bool IsLocalCompany()
Is the current company the local company?
@ NewVehicles
New vehicles.
bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
Check if an engine is buildable.
Definition engine.cpp:1284
Functions related to engines.
@ AIR_CTOL
Conventional Take Off and Landing, i.e. planes.
@ RoadIsTram
Road vehicle is a tram/light rail vehicle.
PoolID< uint16_t, struct EngineIDTag, 64000, 0xFFFF > EngineID
Unique identification number of an engine.
Definition engine_type.h:26
@ Wagon
simple wagon, not motorized
Definition engine_type.h:34
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
@ ReplaceWagonRemoval
If set, autoreplace will perform wagon removal on vehicles in this group.
Definition group.h:69
bool IsAllGroupID(GroupID id_g)
Checks if a GroupID stands for all vehicles of a company.
Definition group.h:112
Command definitions related to engine groups.
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
@ Invalid
Client is not part of anything.
std::optional< bool > TestVehicleBuildProbability(Vehicle *v, BuildProbabilityType type)
Test for vehicle build probability type.
@ Reversed
Change the rail vehicle should be reversed when purchased.
Functions related to news.
void AddVehicleAdviceNewsItem(AdviceType advice_type, EncodedString &&headline, VehicleID vehicle)
Adds a vehicle-advice news item.
Definition news_func.h:43
@ AutorenewFailed
Autorenew or autoreplace failed.
Definition news_type.h:53
Command definitions related to orders.
uint8_t VehicleOrderID
The index of an order within its current vehicle (not pool related).
Definition order_type.h:18
RailTypes GetAllCompatibleRailTypes(RailTypes railtypes)
Returns all compatible railtypes for a set of railtypes.
Definition rail.h:315
Pseudo random number generator.
void SaveRandomSeeds(SavedRandomSeeds *storage)
Saves the current seeds.
void RestoreRandomSeeds(const SavedRandomSeeds &storage)
Restores previously saved seeds.
Road specific functions.
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition road.h:217
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
Definition of base types and functions in a cross-platform compatible way.
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
Functions related to OTTD's strings.
Information about a aircraft vehicle.
uint8_t subtype
Type of aircraft.
VehicleType type
Type of vehicle.
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:141
CompanySettings settings
settings specific for each company
bool renew_keep_length
sell some wagons if after autoreplace the train is longer than before
EngineMiscFlags misc_flags
Miscellaneous flags.
bool IsRearDualheaded() const
Tell if we are dealing with the rear end of a multiheaded engine.
static void AddProfitLastYear(const Vehicle *v)
Add a vehicle's last year profit to the profit sum of its group.
static void UpdateAutoreplace(CompanyID company)
Update autoreplace_defined and autoreplace_finished of all statistics of a company.
Group data.
Definition group.h:76
GroupFlags flags
Group flags.
Definition group.h:81
Shared order list linking together the linked list of orders and the list of vehicles sharing this or...
Definition order_base.h:384
VehicleOrderID GetNumOrders() const
Get number of orders in the order list.
Definition order_base.h:486
const Order * GetOrderAt(VehicleOrderID index) const
Get a certain order of the order chain.
Definition order_base.h:452
If you change this, keep in mind that it is also saved in 2 other places:
Definition order_base.h:34
CargoType GetRefitCargo() const
Get the cargo to to refit to.
Definition order_base.h:128
bool IsRefit() const
Is this order a refit order.
Definition order_base.h:114
static Engine * Get(auto index)
static bool IsValidID(auto index)
static Vehicle * GetIfValid(auto index)
Information about a rail vehicle.
Definition engine_type.h:74
RailTypes railtypes
Railtypes, mangled if elrail is disabled.
Definition engine_type.h:78
RailVehicleType railveh_type
Type of rail vehicle.
Definition engine_type.h:76
Struct for recording vehicle chain replacement information.
Vehicle * GetVehicle() const
Get vehicle to use for this position.
Vehicle * new_veh
Replacement vehicle, or nullptr if no replacement.
ReplaceChainItem(Vehicle *old_veh, Vehicle *new_veh, Money cost)
Create a new item.
Money cost
Cost of buying and refitting replacement.
Vehicle * old_veh
Old vehicle to replace.
Information about a road vehicle.
RoadType roadtype
Road type.
Stores the state of all random number generators.
static Train * From(Vehicle *v)
T * First() const
Get the first vehicle in the chain.
'Train' is either a loco or a wagon.
Definition train.h:97
Train * GetNextUnit() const
Get the next real (non-articulated part and non rear part of dualheaded engine) vehicle in the consis...
Definition train.h:156
Train * GetPrevUnit()
Get the previous real (non-articulated part and non rear part of dualheaded engine) vehicle in the co...
Definition train.h:168
void ConsistChanged(ConsistChangeFlags allowed_changes)
Recalculates the cached stuff of a train.
The information about a vehicle list.
Definition vehiclelist.h:32
Vehicle data structure.
EngineID engine_type
The type of engine used for this vehicle.
const Engine * GetEngine() const
Retrieves the engine of the vehicle.
Definition vehicle.cpp:749
bool IsStoppedInDepot() const
Check whether the vehicle is in the depot and stopped.
virtual bool IsChainInDepot() const
Check whether the whole vehicle chain is in the depot.
VehicleCargoList cargo
The cargo this vehicle is carrying.
GroupID group_id
Index of group Pool array.
VehStates vehstatus
Status.
bool IsArticulatedPart() const
Check if the vehicle is an articulated part of an engine.
bool NeedsAutorenewing(const Company *c, bool use_renew_setting=true) const
Function to tell if a vehicle needs to be autorenewed.
Definition vehicle.cpp:166
CargoType cargo_type
type of cargo this vehicle is carrying
Vehicle * First() const
Get the first vehicle of this vehicle chain.
Vehicle * Next() const
Get the next vehicle of this vehicle.
OrderList * orders
Pointer to the order list for this vehicle.
virtual bool IsPrimaryVehicle() const
Whether this is the primary vehicle in the chain.
bool IsFrontEngine() const
Check if the vehicle is a front engine.
TileIndex tile
Current tile index.
void CopyVehicleConfigAndStatistics(Vehicle *src)
Copy certain configurations and statistics of a vehicle after successful autoreplace/renew The functi...
Owner owner
Which company owns the vehicle?
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
Base for the train class.
static constexpr ConsistChangeFlags CCF_LOADUNLOAD
Valid changes while vehicle is loading/unloading.
Definition train.h:54
@ Flipped
Reverse the visible direction of the vehicle.
Definition train.h:28
Command definitions related to trains.
@ Crashed
Vehicle is crashed.
@ Stopped
Vehicle is stopped by the player.
Command definitions for vehicles.
Functions related to vehicles.
uint8_t GetBestFittingSubType(Vehicle *v_from, Vehicle *v_for, CargoType dest_cargo_type)
Get the best fitting subtype when 'cloning'/'replacing' v_from with v_for.
bool IsCompanyBuildableVehicleType(VehicleType type)
Is the given vehicle type buildable by a company?
WindowClass GetWindowClassForVehicleType(VehicleType vt)
Get WindowClass for vehicle list of given vehicle type.
Definition vehicle_gui.h:97
PoolID< uint32_t, struct VehicleIDTag, 0xFF000, 0xFFFFF > VehicleID
The type all our vehicle IDs have.
VehicleType
Available vehicle types.
@ Aircraft
Aircraft vehicle type.
@ Road
Road vehicle type.
@ Train
Train vehicle type.
Functions and type for generating vehicle lists.
@ Group
Index is the group.
Definition vehiclelist.h:27
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3196