OpenTTD Source 20260218-master-g2123fca5ea
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
32#include "table/strings.h"
33
34#include "safeguards.h"
35
36extern void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index);
37extern void ChangeVehicleNews(VehicleID from_index, VehicleID to_index);
38extern void ChangeVehicleViewWindow(VehicleID from_index, VehicleID to_index);
39
46static bool EnginesHaveCargoInCommon(EngineID engine_a, EngineID engine_b)
47{
48 CargoTypes available_cargoes_a = GetUnionOfArticulatedRefitMasks(engine_a, true);
49 CargoTypes available_cargoes_b = GetUnionOfArticulatedRefitMasks(engine_b, true);
50 return (available_cargoes_a == 0 || available_cargoes_b == 0 || (available_cargoes_a & available_cargoes_b) != 0);
51}
52
60bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company)
61{
62 assert(Engine::IsValidID(from) && Engine::IsValidID(to));
63
64 const Engine *e_from = Engine::Get(from);
65 const Engine *e_to = Engine::Get(to);
66 VehicleType type = e_from->type;
67
68 /* check that the new vehicle type is available to the company and its type is the same as the original one */
69 if (!IsEngineBuildable(to, type, company)) return false;
70
71 switch (type) {
72 case VEH_TRAIN: {
73 /* make sure the railtypes are compatible */
74 if (!GetAllCompatibleRailTypes(e_from->VehInfo<RailVehicleInfo>().railtypes).Any(GetAllCompatibleRailTypes(e_to->VehInfo<RailVehicleInfo>().railtypes))) return false;
75
76 /* make sure we do not replace wagons with engines or vice versa */
77 if ((e_from->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) != (e_to->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON)) return false;
78 break;
79 }
80
81 case VEH_ROAD:
82 /* make sure the roadtypes are compatible */
83 if (!GetRoadTypeInfo(e_from->VehInfo<RoadVehicleInfo>().roadtype)->powered_roadtypes.Any(GetRoadTypeInfo(e_to->VehInfo<RoadVehicleInfo>().roadtype)->powered_roadtypes)) return false;
84
85 /* make sure that we do not replace a tram with a normal road vehicles or vice versa */
86 if (e_from->info.misc_flags.Test(EngineMiscFlag::RoadIsTram) != e_to->info.misc_flags.Test(EngineMiscFlag::RoadIsTram)) return false;
87 break;
88
89 case VEH_AIRCRAFT:
90 /* make sure that we do not replace a plane with a helicopter or vice versa */
91 if ((e_from->VehInfo<AircraftVehicleInfo>().subtype & AIR_CTOL) != (e_to->VehInfo<AircraftVehicleInfo>().subtype & AIR_CTOL)) return false;
92 break;
93
94 default: break;
95 }
96
97 /* the engines needs to be able to carry the same cargo */
98 return EnginesHaveCargoInCommon(from, to);
99}
100
108{
109 assert(v == nullptr || v->First() == v);
110
111 for (Vehicle *src = v; src != nullptr; src = src->Next()) {
112 assert(src->cargo.TotalCount() == src->cargo.ActionCount(VehicleCargoList::MTA_KEEP));
113
114 /* Do we need to more cargo away? */
115 if (src->cargo.TotalCount() <= src->cargo_cap) continue;
116
117 /* We need to move a particular amount. Try that on the other vehicles. */
118 uint to_spread = src->cargo.TotalCount() - src->cargo_cap;
119 for (Vehicle *dest = v; dest != nullptr && to_spread != 0; dest = dest->Next()) {
120 assert(dest->cargo.TotalCount() == dest->cargo.ActionCount(VehicleCargoList::MTA_KEEP));
121 if (dest->cargo.TotalCount() >= dest->cargo_cap || dest->cargo_type != src->cargo_type) continue;
122
123 uint amount = std::min(to_spread, dest->cargo_cap - dest->cargo.TotalCount());
124 src->cargo.Shift(amount, &dest->cargo);
125 to_spread -= amount;
126 }
127
128 /* Any left-overs will be thrown away, but not their feeder share. */
129 if (src->cargo_cap < src->cargo.TotalCount()) src->cargo.Truncate(src->cargo.TotalCount() - src->cargo_cap);
130 }
131}
132
142static void TransferCargo(Vehicle *old_veh, Vehicle *new_head, bool part_of_chain)
143{
144 assert(!part_of_chain || new_head->IsPrimaryVehicle());
145 /* Loop through source parts */
146 for (Vehicle *src = old_veh; src != nullptr; src = src->Next()) {
147 assert(src->cargo.TotalCount() == src->cargo.ActionCount(VehicleCargoList::MTA_KEEP));
148 if (!part_of_chain && src->type == VEH_TRAIN && src != old_veh && src != Train::From(old_veh)->other_multiheaded_part && !src->IsArticulatedPart()) {
149 /* Skip vehicles, which do not belong to old_veh */
150 src = src->GetLastEnginePart();
151 continue;
152 }
153 if (src->cargo_type >= NUM_CARGO || src->cargo.TotalCount() == 0) continue;
154
155 /* Find free space in the new chain */
156 for (Vehicle *dest = new_head; dest != nullptr && src->cargo.TotalCount() > 0; dest = dest->Next()) {
157 assert(dest->cargo.TotalCount() == dest->cargo.ActionCount(VehicleCargoList::MTA_KEEP));
158 if (!part_of_chain && dest->type == VEH_TRAIN && dest != new_head && dest != Train::From(new_head)->other_multiheaded_part && !dest->IsArticulatedPart()) {
159 /* Skip vehicles, which do not belong to new_head */
160 dest = dest->GetLastEnginePart();
161 continue;
162 }
163 if (dest->cargo_type != src->cargo_type) continue;
164
165 uint amount = std::min(src->cargo.TotalCount(), dest->cargo_cap - dest->cargo.TotalCount());
166 if (amount <= 0) continue;
167
168 src->cargo.Shift(amount, &dest->cargo);
169 }
170 }
171
172 /* Update train weight etc., the old vehicle will be sold anyway */
173 if (part_of_chain && new_head->type == VEH_TRAIN) Train::From(new_head)->ConsistChanged(CCF_LOADUNLOAD);
174}
175
182static bool VerifyAutoreplaceRefitForOrders(const Vehicle *v, EngineID engine_type)
183{
184 CargoTypes union_refit_mask_a = GetUnionOfArticulatedRefitMasks(v->engine_type, false);
185 CargoTypes union_refit_mask_b = GetUnionOfArticulatedRefitMasks(engine_type, false);
186
187 const Vehicle *u = (v->type == VEH_TRAIN) ? v->First() : v;
188 for (const Order &o : u->Orders()) {
189 if (!o.IsRefit() || o.IsAutoRefit()) continue;
190 CargoType cargo_type = o.GetRefitCargo();
191
192 if (!HasBit(union_refit_mask_a, cargo_type)) continue;
193 if (!HasBit(union_refit_mask_b, cargo_type)) return false;
194 }
195
196 return true;
197}
198
206{
207 CargoTypes union_refit_mask = GetUnionOfArticulatedRefitMasks(engine_type, false);
208
209 const Vehicle *u = (v->type == VEH_TRAIN) ? v->First() : v;
210
211 const OrderList *orders = u->orders;
212 if (orders == nullptr) return -1;
213 for (VehicleOrderID i = 0; i < orders->GetNumOrders(); i++) {
214 const Order *o = orders->GetOrderAt(i);
215 if (!o->IsRefit()) continue;
216 if (!HasBit(union_refit_mask, o->GetRefitCargo())) return i;
217 }
218
219 return -1;
220}
221
231static CargoType GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type, bool part_of_chain)
232{
233 CargoTypes available_cargo_types, union_mask;
234 GetArticulatedRefitMasks(engine_type, true, &union_mask, &available_cargo_types);
235
236 if (union_mask == 0) return CARGO_NO_REFIT; // Don't try to refit an engine with no cargo capacity
237
238 CargoType cargo_type;
239 CargoTypes cargo_mask = GetCargoTypesOfArticulatedVehicle(v, &cargo_type);
240 if (!HasAtMostOneBit(cargo_mask)) {
241 CargoTypes new_engine_default_cargoes = GetCargoTypesOfArticulatedParts(engine_type);
242 if ((cargo_mask & new_engine_default_cargoes) == cargo_mask) {
243 return CARGO_NO_REFIT; // engine_type is already a mixed cargo type which matches the incoming vehicle by default, no refit required
244 }
245
246 return INVALID_CARGO; // We cannot refit to mixed cargoes in an automated way
247 }
248
249 if (!IsValidCargoType(cargo_type)) {
250 if (v->type != VEH_TRAIN) return CARGO_NO_REFIT; // If the vehicle does not carry anything at all, every replacement is fine.
251
252 if (!part_of_chain) return CARGO_NO_REFIT;
253
254 /* the old engine didn't have cargo capacity, but the new one does
255 * now we will figure out what cargo the train is carrying and refit to fit this */
256
257 for (v = v->First(); v != nullptr; v = v->Next()) {
258 if (!v->GetEngine()->CanCarryCargo()) continue;
259 /* Now we found a cargo type being carried on the train and we will see if it is possible to carry to this one */
260 if (HasBit(available_cargo_types, v->cargo_type)) return v->cargo_type;
261 }
262
263 return CARGO_NO_REFIT; // We failed to find a cargo type on the old vehicle and we will not refit the new one
264 } else {
265 if (!HasBit(available_cargo_types, cargo_type)) return INVALID_CARGO; // We can't refit the vehicle to carry the cargo we want
266
267 if (part_of_chain && !VerifyAutoreplaceRefitForOrders(v, engine_type)) return INVALID_CARGO; // Some refit orders lose their effect
268
269 return cargo_type;
270 }
271}
272
281static CommandCost GetNewEngineType(const Vehicle *v, const Company *c, bool always_replace, EngineID &e)
282{
283 assert(v->type != VEH_TRAIN || !v->IsArticulatedPart());
284
285 e = EngineID::Invalid();
286
287 if (v->type == VEH_TRAIN && Train::From(v)->IsRearDualheaded()) {
288 /* we build the rear ends of multiheaded trains with the front ones */
289 return CommandCost();
290 }
291
292 bool replace_when_old;
293 e = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
294 if (!always_replace && replace_when_old && !v->NeedsAutorenewing(c, false)) e = EngineID::Invalid();
295
296 /* Autoreplace, if engine is available */
297 if (e != EngineID::Invalid() && IsEngineBuildable(e, v->type, _current_company)) {
298 return CommandCost();
299 }
300
301 /* Autorenew if needed */
302 if (v->NeedsAutorenewing(c)) e = v->engine_type;
303
304 /* Nothing to do or all is fine? */
305 if (e == EngineID::Invalid() || IsEngineBuildable(e, v->type, _current_company)) return CommandCost();
306
307 /* The engine we need is not available. Report error to user */
308 return CommandCost(STR_ERROR_RAIL_VEHICLE_NOT_AVAILABLE + v->type);
309}
310
320static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehicle, bool part_of_chain, DoCommandFlags flags)
321{
322 *new_vehicle = nullptr;
323
324 /* Shall the vehicle be replaced? */
326 EngineID e;
327 CommandCost cost = GetNewEngineType(old_veh, c, true, e);
328 if (cost.Failed()) return cost;
329 if (e == EngineID::Invalid()) return CommandCost(); // neither autoreplace is set, nor autorenew is triggered
330
331 /* Does it need to be refitted */
332 CargoType refit_cargo = GetNewCargoTypeForReplace(old_veh, e, part_of_chain);
333 if (!IsValidCargoType(refit_cargo)) {
334 if (!IsLocalCompany() || !flags.Test(DoCommandFlag::Execute)) return CommandCost();
335
336 VehicleID old_veh_id = (old_veh->type == VEH_TRAIN) ? Train::From(old_veh)->First()->index : old_veh->index;
337 EncodedString headline;
338
339 int order_id = GetIncompatibleRefitOrderIdForAutoreplace(old_veh, e);
340 if (order_id != -1) {
341 /* Orders contained a refit order that is incompatible with the new vehicle. */
342 headline = GetEncodedString(STR_NEWS_VEHICLE_AUTORENEW_FAILED,
343 old_veh_id,
344 STR_ERROR_AUTOREPLACE_INCOMPATIBLE_REFIT,
345 order_id + 1); // 1-based indexing for display
346 } else {
347 /* Current cargo is incompatible with the new vehicle. */
348 headline = GetEncodedString(STR_NEWS_VEHICLE_AUTORENEW_FAILED,
349 old_veh_id,
350 STR_ERROR_AUTOREPLACE_INCOMPATIBLE_CARGO,
351 CargoSpec::Get(old_veh->cargo_type)->name);
352 }
353
354 AddVehicleAdviceNewsItem(AdviceType::AutorenewFailed, std::move(headline), old_veh_id);
355 return CommandCost();
356 }
357
358 /* Build the new vehicle */
359 VehicleID new_veh_id;
360 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, INVALID_CLIENT_ID);
361 if (cost.Failed()) return cost;
362
363 Vehicle *new_veh = Vehicle::Get(new_veh_id);
364 *new_vehicle = new_veh;
365
366 /* Refit the vehicle if needed */
367 if (refit_cargo != CARGO_NO_REFIT) {
368 uint8_t subtype = GetBestFittingSubType(old_veh, new_veh, refit_cargo);
369
370 cost.AddCost(std::get<0>(Command<Commands::RefitVehicle>::Do(DoCommandFlag::Execute, new_veh->index, refit_cargo, subtype, false, false, 0)));
371 assert(cost.Succeeded()); // This should be ensured by GetNewCargoTypeForReplace()
372 }
373
374 /* Try to reverse the vehicle, but do not care if it fails as the new type might not be reversible */
375 if (new_veh->type == VEH_TRAIN && Train::From(old_veh)->flags.Test(VehicleRailFlag::Flipped)) {
376 /* Only copy the reverse state if neither old or new vehicle implements reverse-on-build probability callback. */
379 Command<Commands::ReverseTrainDirection>::Do(DoCommandFlag::Execute, new_veh->index, true);
380 }
381 }
382
383 return cost;
384}
385
392static inline CommandCost DoCmdStartStopVehicle(const Vehicle *v, bool evaluate_callback)
393{
394 return Command<Commands::StartStopVehicle>::Do({DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, v->index, evaluate_callback);
395}
396
405static inline CommandCost CmdMoveVehicle(const Vehicle *v, const Vehicle *after, DoCommandFlags flags, bool whole_chain)
406{
407 return Command<Commands::MoveRailVehicle>::Do(flags.Set(DoCommandFlag::NoCargoCapacityCheck), v->index, after != nullptr ? after->index : VehicleID::Invalid(), whole_chain);
408}
409
417static CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head, DoCommandFlags flags)
418{
419 CommandCost cost = CommandCost();
420
421 /* Share orders */
422 if (cost.Succeeded() && old_head != new_head) cost.AddCost(Command<Commands::CloneOrder>::Do(DoCommandFlag::Execute, CO_SHARE, new_head->index, old_head->index));
423
424 /* Copy group membership */
425 if (cost.Succeeded() && old_head != new_head) cost.AddCost(std::get<0>(Command<Commands::AddVehicleToGroup>::Do(DoCommandFlag::Execute, old_head->group_id, new_head->index, false, VehicleListIdentifier{})));
426
427 /* Perform start/stop check whether the new vehicle suits newgrf restrictions etc. */
428 if (cost.Succeeded()) {
429 /* Start the vehicle, might be denied by certain things */
430 assert(new_head->vehstatus.Test(VehState::Stopped));
431 cost.AddCost(DoCmdStartStopVehicle(new_head, true));
432
433 /* Stop the vehicle again, but do not care about evil newgrfs allowing starting but not stopping :p */
434 if (cost.Succeeded()) cost.AddCost(DoCmdStartStopVehicle(new_head, false));
435 }
436
437 /* Last do those things which do never fail (resp. we do not care about), but which are not undo-able */
438 if (cost.Succeeded() && old_head != new_head && flags.Test(DoCommandFlag::Execute)) {
439 /* Copy other things which cannot be copied by a command and which shall not stay reset from the build vehicle command */
440 new_head->CopyVehicleConfigAndStatistics(old_head);
442
443 /* Switch vehicle windows/news to the new vehicle, so they are not closed/deleted when the old vehicle is sold */
444 ChangeVehicleViewports(old_head->index, new_head->index);
445 ChangeVehicleViewWindow(old_head->index, new_head->index);
446 ChangeVehicleNews(old_head->index, new_head->index);
447 }
448
449 return cost;
450}
451
459static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlags flags, bool *nothing_to_do)
460{
461 Train *old_v = Train::From(*single_unit);
462 assert(!old_v->IsArticulatedPart() && !old_v->IsRearDualheaded());
463
465
466 /* Build and refit replacement vehicle */
467 Vehicle *new_v = nullptr;
468 cost.AddCost(BuildReplacementVehicle(old_v, &new_v, false, flags));
469
470 /* Was a new vehicle constructed? */
471 if (cost.Succeeded() && new_v != nullptr) {
472 *nothing_to_do = false;
473
474 if (flags.Test(DoCommandFlag::Execute)) {
475 /* Move the new vehicle behind the old */
476 CmdMoveVehicle(new_v, old_v, DoCommandFlag::Execute, false);
477
478 /* Take over cargo
479 * Note: We do only transfer cargo from the old to the new vehicle.
480 * I.e. we do not transfer remaining cargo to other vehicles.
481 * Else you would also need to consider moving cargo to other free chains,
482 * or doing the same in ReplaceChain(), which would be quite troublesome.
483 */
484 TransferCargo(old_v, new_v, false);
485
486 *single_unit = new_v;
487
488 AI::NewEvent(old_v->owner, new ScriptEventVehicleAutoReplaced(old_v->index, new_v->index));
489 }
490
491 /* Sell the old vehicle */
492 cost.AddCost(Command<Commands::SellVehicle>::Do(flags, old_v->index, false, false, INVALID_CLIENT_ID));
493
494 /* If we are not in DoCommandFlag::Execute undo everything */
495 if (!flags.Test(DoCommandFlag::Execute)) {
496 Command<Commands::SellVehicle>::Do(DoCommandFlag::Execute, new_v->index, false, false, INVALID_CLIENT_ID);
497 }
498 }
499
500 return cost;
501}
502
507 Money cost;
508
516
521 Vehicle *GetVehicle() const { return new_veh == nullptr ? old_veh : new_veh; }
522};
523
532static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlags flags, bool wagon_removal, bool *nothing_to_do)
533{
534 Vehicle *old_head = *chain;
535 assert(old_head->IsPrimaryVehicle());
536
538
539 if (old_head->type == VEH_TRAIN) {
540 /* Store the length of the old vehicle chain, rounded up to whole tiles */
541 uint16_t old_total_length = CeilDiv(Train::From(old_head)->gcache.cached_total_length, TILE_SIZE) * TILE_SIZE;
542
543 std::vector<ReplaceChainItem> replacements;
544
545 /* Collect vehicles and build replacements
546 * Note: The replacement vehicles can only successfully build as long as the old vehicles are still in their chain */
547 for (Train *w = Train::From(old_head); w != nullptr; w = w->GetNextUnit()) {
548 ReplaceChainItem &replacement = replacements.emplace_back(w, nullptr, 0);
549
550 CommandCost ret = BuildReplacementVehicle(replacement.old_veh, &replacement.new_veh, true, flags);
551 replacement.cost = ret.GetCost();
552 cost.AddCost(std::move(ret));
553 if (cost.Failed()) break;
554
555 if (replacement.new_veh != nullptr) *nothing_to_do = false;
556 }
557 Vehicle *new_head = replacements.front().GetVehicle();
558
559 /* Note: When autoreplace has already failed here, old_vehs[] is not completely initialized. But it is also not needed. */
560 if (cost.Succeeded()) {
561 /* Separate the head, so we can start constructing the new chain */
562 Train *second = Train::From(old_head)->GetNextUnit();
563 if (second != nullptr) cost.AddCost(CmdMoveVehicle(second, nullptr, {DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, true));
564
565 assert(Train::From(new_head)->GetNextUnit() == nullptr);
566
567 /* Append engines to the new chain
568 * We do this from back to front, so that the head of the temporary vehicle chain does not change all the time.
569 * That way we also have less trouble when exceeding the unitnumber limit.
570 * OTOH the vehicle attach callback is more expensive this way :s */
571 Vehicle *last_engine = nullptr;
572 if (cost.Succeeded()) {
573 for (auto it = std::rbegin(replacements); it != std::rend(replacements); ++it) {
574 Vehicle *append = it->GetVehicle();
575
576 if (RailVehInfo(append->engine_type)->railveh_type == RAILVEH_WAGON) continue;
577
578 if (it->new_veh != nullptr) {
579 /* Move the old engine to a separate row with DoCommandFlag::AutoReplace. Else
580 * moving the wagon in front may fail later due to unitnumber limit.
581 * (We have to attach wagons without DoCommandFlag::AutoReplace.) */
582 CmdMoveVehicle(it->old_veh, nullptr, {DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, false);
583 }
584
585 if (last_engine == nullptr) last_engine = append;
586 cost.AddCost(CmdMoveVehicle(append, new_head, DoCommandFlag::Execute, false));
587 if (cost.Failed()) break;
588 }
589 if (last_engine == nullptr) last_engine = new_head;
590 }
591
592 /* When wagon removal is enabled and the new engines without any wagons are already longer than the old, we have to fail */
593 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);
594
595 /* Append/insert wagons into the new vehicle chain
596 * 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.
597 */
598 if (cost.Succeeded()) {
599 for (auto it = std::rbegin(replacements); it != std::rend(replacements); ++it) {
600 assert(last_engine != nullptr);
601 Vehicle *append = it->GetVehicle();
602
603 if (RailVehInfo(append->engine_type)->railveh_type == RAILVEH_WAGON) {
604 /* Insert wagon after 'last_engine' */
605 CommandCost res = CmdMoveVehicle(append, last_engine, DoCommandFlag::Execute, false);
606
607 /* When we allow removal of wagons, either the move failing due
608 * to the train becoming too long, or the train becoming longer
609 * would move the vehicle to the empty vehicle chain. */
610 if (wagon_removal && (res.Failed() ? res.GetErrorMessage() == STR_ERROR_TRAIN_TOO_LONG : Train::From(new_head)->gcache.cached_total_length > old_total_length)) {
612 break;
613 }
614
615 cost.AddCost(std::move(res));
616 if (cost.Failed()) break;
617 } else {
618 /* We have reached 'last_engine', continue with the next engine towards the front */
619 assert(append == last_engine);
620 last_engine = Train::From(last_engine)->GetPrevUnit();
621 }
622 }
623 }
624
625 /* Sell superfluous new vehicles that could not be inserted. */
626 if (cost.Succeeded() && wagon_removal) {
627 assert(Train::From(new_head)->gcache.cached_total_length <= _settings_game.vehicle.max_train_length * TILE_SIZE);
628 for (auto it = std::next(std::begin(replacements)); it != std::end(replacements); ++it) {
629 Vehicle *wagon = it->new_veh;
630 if (wagon == nullptr) continue;
631 if (wagon->First() == new_head) break;
632
633 assert(RailVehInfo(wagon->engine_type)->railveh_type == RAILVEH_WAGON);
634
635 /* Sell wagon */
636 [[maybe_unused]] CommandCost ret = Command<Commands::SellVehicle>::Do(DoCommandFlag::Execute, wagon->index, false, false, INVALID_CLIENT_ID);
637 assert(ret.Succeeded());
638 it->new_veh = nullptr;
639
640 /* Revert the money subtraction when the vehicle was built.
641 * This value is different from the sell value, esp. because of refitting */
642 cost.AddCost(-it->cost);
643 }
644 }
645
646 /* The new vehicle chain is constructed, now take over orders and everything... */
647 if (cost.Succeeded()) cost.AddCost(CopyHeadSpecificThings(old_head, new_head, flags));
648
649 if (cost.Succeeded()) {
650 /* Success ! */
651 if (flags.Test(DoCommandFlag::Execute) && new_head != old_head) {
652 *chain = new_head;
653 AI::NewEvent(old_head->owner, new ScriptEventVehicleAutoReplaced(old_head->index, new_head->index));
654 }
655
656 /* Transfer cargo of old vehicles and sell them */
657 for (auto it = std::begin(replacements); it != std::end(replacements); ++it) {
658 Vehicle *w = it->old_veh;
659 /* Is the vehicle again part of the new chain?
660 * Note: We cannot test 'new_vehs[i] != nullptr' as wagon removal might cause to remove both */
661 if (w->First() == new_head) continue;
662
663 if (flags.Test(DoCommandFlag::Execute)) TransferCargo(w, new_head, true);
664
665 /* Sell the vehicle.
666 * Note: This might temporarily construct new trains, so use DoCommandFlag::AutoReplace to prevent
667 * it from failing due to engine limits. */
668 cost.AddCost(Command<Commands::SellVehicle>::Do(DoCommandFlags{flags}.Set(DoCommandFlag::AutoReplace), w->index, false, false, INVALID_CLIENT_ID));
669 if (flags.Test(DoCommandFlag::Execute)) {
670 it->old_veh = nullptr;
671 if (it == std::begin(replacements)) old_head = nullptr;
672 }
673 }
674
675 if (flags.Test(DoCommandFlag::Execute)) CheckCargoCapacity(new_head);
676 }
677
678 /* If we are not in DoCommandFlag::Execute undo everything, i.e. rearrange old vehicles.
679 * We do this from back to front, so that the head of the temporary vehicle chain does not change all the time.
680 * Note: The vehicle attach callback is disabled here :) */
681 if (!flags.Test(DoCommandFlag::Execute)) {
682 /* Separate the head, so we can reattach the old vehicles */
683 second = Train::From(old_head)->GetNextUnit();
684 if (second != nullptr) CmdMoveVehicle(second, nullptr, {DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, true);
685
686 assert(Train::From(old_head)->GetNextUnit() == nullptr);
687
688 for (auto it = std::rbegin(replacements); it != std::rend(replacements); ++it) {
689 [[maybe_unused]] CommandCost ret = CmdMoveVehicle(it->old_veh, old_head, {DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, false);
690 assert(ret.Succeeded());
691 }
692 }
693 }
694
695 /* Finally undo buying of new vehicles */
696 if (!flags.Test(DoCommandFlag::Execute)) {
697 for (auto it = std::rbegin(replacements); it != std::rend(replacements); ++it) {
698 if (it->new_veh != nullptr) {
699 Command<Commands::SellVehicle>::Do(DoCommandFlag::Execute, it->new_veh->index, false, false, INVALID_CLIENT_ID);
700 it->new_veh = nullptr;
701 }
702 }
703 }
704 } else {
705 /* Build and refit replacement vehicle */
706 Vehicle *new_head = nullptr;
707 cost.AddCost(BuildReplacementVehicle(old_head, &new_head, true, flags));
708
709 /* Was a new vehicle constructed? */
710 if (cost.Succeeded() && new_head != nullptr) {
711 *nothing_to_do = false;
712
713 /* The new vehicle is constructed, now take over orders and everything... */
714 cost.AddCost(CopyHeadSpecificThings(old_head, new_head, flags));
715
716 if (cost.Succeeded()) {
717 /* The new vehicle is constructed, now take over cargo */
718 if (flags.Test(DoCommandFlag::Execute)) {
719 TransferCargo(old_head, new_head, true);
720 *chain = new_head;
721
722 AI::NewEvent(old_head->owner, new ScriptEventVehicleAutoReplaced(old_head->index, new_head->index));
723 }
724
725 /* Sell the old vehicle */
726 cost.AddCost(Command<Commands::SellVehicle>::Do(flags, old_head->index, false, false, INVALID_CLIENT_ID));
727 }
728
729 /* If we are not in DoCommandFlag::Execute undo everything */
730 if (!flags.Test(DoCommandFlag::Execute)) {
731 Command<Commands::SellVehicle>::Do(DoCommandFlag::Execute, new_head->index, false, false, INVALID_CLIENT_ID);
732 }
733 }
734 }
735
736 return cost;
737}
738
746CommandCost CmdAutoreplaceVehicle(DoCommandFlags flags, VehicleID veh_id)
747{
748 Vehicle *v = Vehicle::GetIfValid(veh_id);
749 if (v == nullptr) return CMD_ERROR;
750
752 if (ret.Failed()) return ret;
753
755
756 bool free_wagon = false;
757 if (v->type == VEH_TRAIN) {
758 Train *t = Train::From(v);
759 if (t->IsArticulatedPart() || t->IsRearDualheaded()) return CMD_ERROR;
760 free_wagon = !t->IsFrontEngine();
761 if (free_wagon && t->First()->IsFrontEngine()) return CMD_ERROR;
762 } else {
763 if (!v->IsPrimaryVehicle()) return CMD_ERROR;
764 }
765 if (!v->IsChainInDepot()) return CMD_ERROR;
766
768 bool wagon_removal = c->settings.renew_keep_length;
769
770 const Group *g = Group::GetIfValid(v->group_id);
771 if (g != nullptr) wagon_removal = g->flags.Test(GroupFlag::ReplaceWagonRemoval);
772
773 /* Test whether any replacement is set, before issuing a whole lot of commands that would end in nothing changed */
774 Vehicle *w = v;
775 bool any_replacements = false;
776 while (w != nullptr) {
777 EngineID e;
778 CommandCost cost = GetNewEngineType(w, c, false, e);
779 if (cost.Failed()) return cost;
780 any_replacements |= (e != EngineID::Invalid());
781 w = (!free_wagon && w->type == VEH_TRAIN ? Train::From(w)->GetNextUnit() : nullptr);
782 }
783
785 bool nothing_to_do = true;
786
787 if (any_replacements) {
788 bool was_stopped = free_wagon || v->vehstatus.Test(VehState::Stopped);
789
790 /* Stop the vehicle */
791 if (!was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, true));
792 if (cost.Failed()) return cost;
793
794 assert(free_wagon || v->IsStoppedInDepot());
795
796 /* We have to construct the new vehicle chain to test whether it is valid.
797 * Vehicle construction needs random bits, so we have to save the random seeds
798 * to prevent desyncs and to replay newgrf callbacks during DoCommandFlag::Execute */
799 SavedRandomSeeds saved_seeds;
800 SaveRandomSeeds(&saved_seeds);
801 if (free_wagon) {
802 cost.AddCost(ReplaceFreeUnit(&v, DoCommandFlags{flags}.Reset(DoCommandFlag::Execute), &nothing_to_do));
803 } else {
804 cost.AddCost(ReplaceChain(&v, DoCommandFlags{flags}.Reset(DoCommandFlag::Execute), wagon_removal, &nothing_to_do));
805 }
806 RestoreRandomSeeds(saved_seeds);
807
808 if (cost.Succeeded() && flags.Test(DoCommandFlag::Execute)) {
809 if (free_wagon) {
810 ret = ReplaceFreeUnit(&v, flags, &nothing_to_do);
811 } else {
812 ret = ReplaceChain(&v, flags, wagon_removal, &nothing_to_do);
813 }
814 assert(ret.Succeeded() && ret.GetCost() == cost.GetCost());
815 }
816
817 /* Restart the vehicle */
818 if (!was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, false));
819 }
820
821 if (cost.Succeeded() && nothing_to_do) cost = CommandCost(STR_ERROR_AUTOREPLACE_NOTHING_TO_DO);
822 return cost;
823}
824
834CommandCost CmdSetAutoReplace(DoCommandFlags flags, GroupID id_g, EngineID old_engine_type, EngineID new_engine_type, bool when_old)
835{
837 if (c == nullptr) return CMD_ERROR;
838
839 CommandCost cost;
840
841 if (Group::IsValidID(id_g) ? Group::Get(id_g)->owner != _current_company : !IsAllGroupID(id_g) && !IsDefaultGroupID(id_g)) return CMD_ERROR;
842 if (!Engine::IsValidID(old_engine_type)) return CMD_ERROR;
843 if (Group::IsValidID(id_g) && Group::Get(id_g)->vehicle_type != Engine::Get(old_engine_type)->type) return CMD_ERROR;
844
845 if (new_engine_type != EngineID::Invalid()) {
846 if (!Engine::IsValidID(new_engine_type)) return CMD_ERROR;
847 if (!CheckAutoreplaceValidity(old_engine_type, new_engine_type, _current_company)) return CMD_ERROR;
848
849 cost = AddEngineReplacementForCompany(c, old_engine_type, new_engine_type, id_g, when_old, flags);
850 } else {
851 cost = RemoveEngineReplacementForCompany(c, old_engine_type, id_g, flags);
852 }
853
854 if (flags.Test(DoCommandFlag::Execute)) {
856 if (IsLocalCompany()) SetWindowDirty(WC_REPLACE_VEHICLE, Engine::Get(old_engine_type)->type);
857
858 const VehicleType vt = Engine::Get(old_engine_type)->type;
860 }
861 if (flags.Test(DoCommandFlag::Execute) && IsLocalCompany()) InvalidateAutoreplaceWindow(old_engine_type, id_g);
862
863 return cost;
864}
865
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:3524
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 HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr bool HasAtMostOneBit(T value)
Test whether value has at most 1 bit set.
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:21
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:108
static const CargoType NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:73
static const CargoType CARGO_NO_REFIT
Do not refit cargo of a vehicle (used in vehicle orders and auto-replace/auto-renew).
Definition cargo_type.h:77
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition ai_core.cpp:235
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & 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 VEH_ROAD, VEH_TRAIN, etc.
Definition engine_base.h:62
bool CanCarryCargo() const
Determines whether an engine can carry something.
Definition engine.cpp:171
RoadTypes powered_roadtypes
bitmask to the OTHER roadtypes on which a vehicle of THIS roadtype generates power
Definition road.h:96
uint TotalCount() const
Returns sum of cargo, including reserved cargo.
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Execute
execute the given command
@ NoCargoCapacityCheck
when autoreplace/autorenew is in progress, this shall prevent truncating the amount of cargo in the v...
@ AutoReplace
autoreplace/autorenew is in progress, this shall disable vehicle limits when building,...
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?
@ EXPENSES_NEW_VEHICLES
New vehicles.
bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
Check if an engine is buildable.
Definition engine.cpp:1251
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
@ RAILVEH_WAGON
simple wagon, not motorized
Definition engine_type.h:34
@ 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:104
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_ID
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:312
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:215
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:138
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:74
GroupFlags flags
Group flags.
Definition group.h:79
Shared order list linking together the linked list of orders and the list of vehicles sharing this or...
Definition order_base.h:383
VehicleOrderID GetNumOrders() const
Get number of orders in the order list.
Definition order_base.h:485
const Order * GetOrderAt(VehicleOrderID index) const
Get a certain order of the order chain.
Definition order_base.h:451
CargoType GetRefitCargo() const
Get the cargo to to refit to.
Definition order_base.h:127
bool IsRefit() const
Is this order a refit order.
Definition order_base.h:113
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
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)
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:91
Train * GetNextUnit() const
Get the next real (non-articulated part and non rear part of dualheaded engine) vehicle in the consis...
Definition train.h:149
Train * GetPrevUnit()
Get the previous real (non-articulated part and non rear part of dualheaded engine) vehicle in the co...
Definition train.h:161
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:719
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:156
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:53
@ 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.
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.
@ VEH_ROAD
Road vehicle type.
@ VEH_AIRCRAFT
Aircraft vehicle type.
@ VEH_TRAIN
Train vehicle type.
Functions and type for generating vehicle lists.
@ VL_GROUP_LIST
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:3185
@ WC_REPLACE_VEHICLE
Replace vehicle window; Window numbers: