OpenTTD Source 20241224-master-gf74b0cf984
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 <http://www.gnu.org/licenses/>.
6 */
7
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
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 ((GetRailTypeInfo(e_from->u.rail.railtype)->compatible_railtypes & GetRailTypeInfo(e_to->u.rail.railtype)->compatible_railtypes) == 0) return false;
75
76 /* make sure we do not replace wagons with engines or vice versa */
77 if ((e_from->u.rail.railveh_type == RAILVEH_WAGON) != (e_to->u.rail.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->u.road.roadtype)->powered_roadtypes & GetRoadTypeInfo(e_to->u.road.roadtype)->powered_roadtypes) == ROADTYPES_NONE) return false;
84
85 /* make sure that we do not replace a tram with a normal road vehicles or vice versa */
86 if (HasBit(e_from->info.misc_flags, EF_ROAD_TRAM) != HasBit(e_to->info.misc_flags, EF_ROAD_TRAM)) 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->u.air.subtype & AIR_CTOL) != (e_to->u.air.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 CargoID 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 Order *o;
210 const Vehicle *u = (v->type == VEH_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 o = orders->GetOrderAt(i);
216 if (!o->IsRefit()) continue;
217 if (!HasBit(union_refit_mask, o->GetRefitCargo())) return i;
218 }
219
220 return -1;
221}
222
232static CargoID 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 == 0) return CARGO_NO_REFIT; // Don't try to refit an engine with no cargo capacity
238
239 CargoID cargo_type;
240 CargoTypes cargo_mask = GetCargoTypesOfArticulatedVehicle(v, &cargo_type);
241 if (!HasAtMostOneBit(cargo_mask)) {
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 (!IsValidCargoID(cargo_type)) {
251 if (v->type != VEH_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 (HasBit(available_cargo_types, 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 (!HasBit(available_cargo_types, 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 != VEH_TRAIN || !v->IsArticulatedPart());
285
286 e = INVALID_ENGINE;
287
288 if (v->type == VEH_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 = INVALID_ENGINE;
296
297 /* Autoreplace, if engine is available */
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? */
307
308 /* The engine we need is not available. Report error to user */
309 return CommandCost(STR_ERROR_RAIL_VEHICLE_NOT_AVAILABLE + v->type);
310}
311
321static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehicle, bool part_of_chain, DoCommandFlag 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 == INVALID_ENGINE) return CommandCost(); // neither autoreplace is set, nor autorenew is triggered
331
332 /* Does it need to be refitted */
333 CargoID refit_cargo = GetNewCargoTypeForReplace(old_veh, e, part_of_chain);
334 if (!IsValidCargoID(refit_cargo)) {
335 if (!IsLocalCompany() || (flags & DC_EXEC) == 0) return CommandCost();
336
337 VehicleID old_veh_id = (old_veh->type == VEH_TRAIN) ? Train::From(old_veh)->First()->index : old_veh->index;
338 SetDParam(0, old_veh_id);
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 SetDParam(1, STR_ERROR_AUTOREPLACE_INCOMPATIBLE_REFIT);
344 SetDParam(2, order_id + 1); // 1-based indexing for display
345 } else {
346 /* Current cargo is incompatible with the new vehicle. */
347 SetDParam(1, STR_ERROR_AUTOREPLACE_INCOMPATIBLE_CARGO);
349 }
350
351 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_AUTORENEW_FAILED, old_veh_id);
352 return CommandCost();
353 }
354
355 /* Build the new vehicle */
356 VehicleID new_veh_id;
357 std::tie(cost, new_veh_id, std::ignore, std::ignore, std::ignore) = Command<CMD_BUILD_VEHICLE>::Do(DC_EXEC | DC_AUTOREPLACE, old_veh->tile, e, true, INVALID_CARGO, INVALID_CLIENT_ID);
358 if (cost.Failed()) return cost;
359
360 Vehicle *new_veh = Vehicle::Get(new_veh_id);
361 *new_vehicle = new_veh;
362
363 /* Refit the vehicle if needed */
364 if (refit_cargo != CARGO_NO_REFIT) {
365 uint8_t subtype = GetBestFittingSubType(old_veh, new_veh, refit_cargo);
366
367 cost.AddCost(std::get<0>(Command<CMD_REFIT_VEHICLE>::Do(DC_EXEC, new_veh->index, refit_cargo, subtype, false, false, 0)));
368 assert(cost.Succeeded()); // This should be ensured by GetNewCargoTypeForReplace()
369 }
370
371 /* Try to reverse the vehicle, but do not care if it fails as the new type might not be reversible */
372 if (new_veh->type == VEH_TRAIN && HasBit(Train::From(old_veh)->flags, VRF_REVERSE_DIRECTION)) {
374 }
375
376 return cost;
377}
378
385static inline CommandCost DoCmdStartStopVehicle(const Vehicle *v, bool evaluate_callback)
386{
387 return Command<CMD_START_STOP_VEHICLE>::Do(DC_EXEC | DC_AUTOREPLACE, v->index, evaluate_callback);
388}
389
398static inline CommandCost CmdMoveVehicle(const Vehicle *v, const Vehicle *after, DoCommandFlag flags, bool whole_chain)
399{
400 return Command<CMD_MOVE_RAIL_VEHICLE>::Do(flags | DC_NO_CARGO_CAP_CHECK, v->index, after != nullptr ? after->index : INVALID_VEHICLE, whole_chain);
401}
402
410{
411 CommandCost cost = CommandCost();
412
413 /* Share orders */
414 if (cost.Succeeded() && old_head != new_head) cost.AddCost(Command<CMD_CLONE_ORDER>::Do(DC_EXEC, CO_SHARE, new_head->index, old_head->index));
415
416 /* Copy group membership */
417 if (cost.Succeeded() && old_head != new_head) cost.AddCost(std::get<0>(Command<CMD_ADD_VEHICLE_GROUP>::Do(DC_EXEC, old_head->group_id, new_head->index, false, VehicleListIdentifier{})));
418
419 /* Perform start/stop check whether the new vehicle suits newgrf restrictions etc. */
420 if (cost.Succeeded()) {
421 /* Start the vehicle, might be denied by certain things */
422 assert((new_head->vehstatus & VS_STOPPED) != 0);
423 cost.AddCost(DoCmdStartStopVehicle(new_head, true));
424
425 /* Stop the vehicle again, but do not care about evil newgrfs allowing starting but not stopping :p */
426 if (cost.Succeeded()) cost.AddCost(DoCmdStartStopVehicle(new_head, false));
427 }
428
429 /* Last do those things which do never fail (resp. we do not care about), but which are not undo-able */
430 if (cost.Succeeded() && old_head != new_head && (flags & DC_EXEC) != 0) {
431 /* Copy other things which cannot be copied by a command and which shall not stay resetted from the build vehicle command */
432 new_head->CopyVehicleConfigAndStatistics(old_head);
434
435 /* Switch vehicle windows/news to the new vehicle, so they are not closed/deleted when the old vehicle is sold */
436 ChangeVehicleViewports(old_head->index, new_head->index);
437 ChangeVehicleViewWindow(old_head->index, new_head->index);
438 ChangeVehicleNews(old_head->index, new_head->index);
439 }
440
441 return cost;
442}
443
451static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlag flags, bool *nothing_to_do)
452{
453 Train *old_v = Train::From(*single_unit);
454 assert(!old_v->IsArticulatedPart() && !old_v->IsRearDualheaded());
455
457
458 /* Build and refit replacement vehicle */
459 Vehicle *new_v = nullptr;
460 cost.AddCost(BuildReplacementVehicle(old_v, &new_v, false, flags));
461
462 /* Was a new vehicle constructed? */
463 if (cost.Succeeded() && new_v != nullptr) {
464 *nothing_to_do = false;
465
466 if ((flags & DC_EXEC) != 0) {
467 /* Move the new vehicle behind the old */
468 CmdMoveVehicle(new_v, old_v, DC_EXEC, false);
469
470 /* Take over cargo
471 * Note: We do only transfer cargo from the old to the new vehicle.
472 * I.e. we do not transfer remaining cargo to other vehicles.
473 * Else you would also need to consider moving cargo to other free chains,
474 * or doing the same in ReplaceChain(), which would be quite troublesome.
475 */
476 TransferCargo(old_v, new_v, false);
477
478 *single_unit = new_v;
479
480 AI::NewEvent(old_v->owner, new ScriptEventVehicleAutoReplaced(old_v->index, new_v->index));
481 }
482
483 /* Sell the old vehicle */
484 cost.AddCost(Command<CMD_SELL_VEHICLE>::Do(flags, old_v->index, false, false, INVALID_CLIENT_ID));
485
486 /* If we are not in DC_EXEC undo everything */
487 if ((flags & DC_EXEC) == 0) {
489 }
490 }
491
492 return cost;
493}
494
499 Money cost;
500
502
507 Vehicle *GetVehicle() const { return new_veh == nullptr ? old_veh : new_veh; }
508};
509
518static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon_removal, bool *nothing_to_do)
519{
520 Vehicle *old_head = *chain;
521 assert(old_head->IsPrimaryVehicle());
522
524
525 if (old_head->type == VEH_TRAIN) {
526 /* Store the length of the old vehicle chain, rounded up to whole tiles */
527 uint16_t old_total_length = CeilDiv(Train::From(old_head)->gcache.cached_total_length, TILE_SIZE) * TILE_SIZE;
528
529 std::vector<ReplaceChainItem> replacements;
530
531 /* Collect vehicles and build replacements
532 * Note: The replacement vehicles can only successfully build as long as the old vehicles are still in their chain */
533 for (Train *w = Train::From(old_head); w != nullptr; w = w->GetNextUnit()) {
534 ReplaceChainItem &replacement = replacements.emplace_back(w, nullptr, 0);
535
536 CommandCost ret = BuildReplacementVehicle(replacement.old_veh, &replacement.new_veh, true, flags);
537 cost.AddCost(ret);
538 if (cost.Failed()) break;
539
540 replacement.cost = ret.GetCost();
541 if (replacement.new_veh != nullptr) *nothing_to_do = false;
542 }
543 Vehicle *new_head = replacements.front().GetVehicle();
544
545 /* Note: When autoreplace has already failed here, old_vehs[] is not completely initialized. But it is also not needed. */
546 if (cost.Succeeded()) {
547 /* Separate the head, so we can start constructing the new chain */
548 Train *second = Train::From(old_head)->GetNextUnit();
549 if (second != nullptr) cost.AddCost(CmdMoveVehicle(second, nullptr, DC_EXEC | DC_AUTOREPLACE, true));
550
551 assert(Train::From(new_head)->GetNextUnit() == nullptr);
552
553 /* Append engines to the new chain
554 * We do this from back to front, so that the head of the temporary vehicle chain does not change all the time.
555 * That way we also have less trouble when exceeding the unitnumber limit.
556 * OTOH the vehicle attach callback is more expensive this way :s */
557 Vehicle *last_engine = nullptr;
558 if (cost.Succeeded()) {
559 for (auto it = std::rbegin(replacements); it != std::rend(replacements); ++it) {
560 Vehicle *append = it->GetVehicle();
561
562 if (RailVehInfo(append->engine_type)->railveh_type == RAILVEH_WAGON) continue;
563
564 if (it->new_veh != nullptr) {
565 /* Move the old engine to a separate row with DC_AUTOREPLACE. Else
566 * moving the wagon in front may fail later due to unitnumber limit.
567 * (We have to attach wagons without DC_AUTOREPLACE.) */
568 CmdMoveVehicle(it->old_veh, nullptr, DC_EXEC | DC_AUTOREPLACE, false);
569 }
570
571 if (last_engine == nullptr) last_engine = append;
572 cost.AddCost(CmdMoveVehicle(append, new_head, DC_EXEC, false));
573 if (cost.Failed()) break;
574 }
575 if (last_engine == nullptr) last_engine = new_head;
576 }
577
578 /* When wagon removal is enabled and the new engines without any wagons are already longer than the old, we have to fail */
579 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);
580
581 /* Append/insert wagons into the new vehicle chain
582 * 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.
583 */
584 if (cost.Succeeded()) {
585 for (auto it = std::rbegin(replacements); it != std::rend(replacements); ++it) {
586 assert(last_engine != nullptr);
587 Vehicle *append = it->GetVehicle();
588
589 if (RailVehInfo(append->engine_type)->railveh_type == RAILVEH_WAGON) {
590 /* Insert wagon after 'last_engine' */
591 CommandCost res = CmdMoveVehicle(append, last_engine, DC_EXEC, false);
592
593 /* When we allow removal of wagons, either the move failing due
594 * to the train becoming too long, or the train becoming longer
595 * would move the vehicle to the empty vehicle chain. */
596 if (wagon_removal && (res.Failed() ? res.GetErrorMessage() == STR_ERROR_TRAIN_TOO_LONG : Train::From(new_head)->gcache.cached_total_length > old_total_length)) {
597 CmdMoveVehicle(append, nullptr, DC_EXEC | DC_AUTOREPLACE, false);
598 break;
599 }
600
601 cost.AddCost(res);
602 if (cost.Failed()) break;
603 } else {
604 /* We have reached 'last_engine', continue with the next engine towards the front */
605 assert(append == last_engine);
606 last_engine = Train::From(last_engine)->GetPrevUnit();
607 }
608 }
609 }
610
611 /* Sell superfluous new vehicles that could not be inserted. */
612 if (cost.Succeeded() && wagon_removal) {
613 assert(Train::From(new_head)->gcache.cached_total_length <= _settings_game.vehicle.max_train_length * TILE_SIZE);
614 for (auto it = std::next(std::begin(replacements)); it != std::end(replacements); ++it) {
615 Vehicle *wagon = it->new_veh;
616 if (wagon == nullptr) continue;
617 if (wagon->First() == new_head) break;
618
619 assert(RailVehInfo(wagon->engine_type)->railveh_type == RAILVEH_WAGON);
620
621 /* Sell wagon */
622 [[maybe_unused]] CommandCost ret = Command<CMD_SELL_VEHICLE>::Do(DC_EXEC, wagon->index, false, false, INVALID_CLIENT_ID);
623 assert(ret.Succeeded());
624 it->new_veh = nullptr;
625
626 /* Revert the money subtraction when the vehicle was built.
627 * This value is different from the sell value, esp. because of refitting */
628 cost.AddCost(-it->cost);
629 }
630 }
631
632 /* The new vehicle chain is constructed, now take over orders and everything... */
633 if (cost.Succeeded()) cost.AddCost(CopyHeadSpecificThings(old_head, new_head, flags));
634
635 if (cost.Succeeded()) {
636 /* Success ! */
637 if ((flags & DC_EXEC) != 0 && new_head != old_head) {
638 *chain = new_head;
639 AI::NewEvent(old_head->owner, new ScriptEventVehicleAutoReplaced(old_head->index, new_head->index));
640 }
641
642 /* Transfer cargo of old vehicles and sell them */
643 for (auto it = std::begin(replacements); it != std::end(replacements); ++it) {
644 Vehicle *w = it->old_veh;
645 /* Is the vehicle again part of the new chain?
646 * Note: We cannot test 'new_vehs[i] != nullptr' as wagon removal might cause to remove both */
647 if (w->First() == new_head) continue;
648
649 if ((flags & DC_EXEC) != 0) TransferCargo(w, new_head, true);
650
651 /* Sell the vehicle.
652 * Note: This might temporarily construct new trains, so use DC_AUTOREPLACE to prevent
653 * it from failing due to engine limits. */
655 if ((flags & DC_EXEC) != 0) {
656 it->old_veh = nullptr;
657 if (it == std::begin(replacements)) old_head = nullptr;
658 }
659 }
660
661 if ((flags & DC_EXEC) != 0) CheckCargoCapacity(new_head);
662 }
663
664 /* If we are not in DC_EXEC undo everything, i.e. rearrange old vehicles.
665 * We do this from back to front, so that the head of the temporary vehicle chain does not change all the time.
666 * Note: The vehicle attach callback is disabled here :) */
667 if ((flags & DC_EXEC) == 0) {
668 /* Separate the head, so we can reattach the old vehicles */
669 Train *second = Train::From(old_head)->GetNextUnit();
670 if (second != nullptr) CmdMoveVehicle(second, nullptr, DC_EXEC | DC_AUTOREPLACE, true);
671
672 assert(Train::From(old_head)->GetNextUnit() == nullptr);
673
674 for (auto it = std::rbegin(replacements); it != std::rend(replacements); ++it) {
675 [[maybe_unused]] CommandCost ret = CmdMoveVehicle(it->old_veh, old_head, DC_EXEC | DC_AUTOREPLACE, false);
676 assert(ret.Succeeded());
677 }
678 }
679 }
680
681 /* Finally undo buying of new vehicles */
682 if ((flags & DC_EXEC) == 0) {
683 for (auto it = std::rbegin(replacements); it != std::rend(replacements); ++it) {
684 if (it->new_veh != nullptr) {
685 Command<CMD_SELL_VEHICLE>::Do(DC_EXEC, it->new_veh->index, false, false, INVALID_CLIENT_ID);
686 it->new_veh = nullptr;
687 }
688 }
689 }
690 } else {
691 /* Build and refit replacement vehicle */
692 Vehicle *new_head = nullptr;
693 cost.AddCost(BuildReplacementVehicle(old_head, &new_head, true, flags));
694
695 /* Was a new vehicle constructed? */
696 if (cost.Succeeded() && new_head != nullptr) {
697 *nothing_to_do = false;
698
699 /* The new vehicle is constructed, now take over orders and everything... */
700 cost.AddCost(CopyHeadSpecificThings(old_head, new_head, flags));
701
702 if (cost.Succeeded()) {
703 /* The new vehicle is constructed, now take over cargo */
704 if ((flags & DC_EXEC) != 0) {
705 TransferCargo(old_head, new_head, true);
706 *chain = new_head;
707
708 AI::NewEvent(old_head->owner, new ScriptEventVehicleAutoReplaced(old_head->index, new_head->index));
709 }
710
711 /* Sell the old vehicle */
712 cost.AddCost(Command<CMD_SELL_VEHICLE>::Do(flags, old_head->index, false, false, INVALID_CLIENT_ID));
713 }
714
715 /* If we are not in DC_EXEC undo everything */
716 if ((flags & DC_EXEC) == 0) {
718 }
719 }
720 }
721
722 return cost;
723}
724
733{
734 Vehicle *v = Vehicle::GetIfValid(veh_id);
735 if (v == nullptr) return CMD_ERROR;
736
738 if (ret.Failed()) return ret;
739
740 if (v->vehstatus & VS_CRASHED) return CMD_ERROR;
741
742 bool free_wagon = false;
743 if (v->type == VEH_TRAIN) {
744 Train *t = Train::From(v);
745 if (t->IsArticulatedPart() || t->IsRearDualheaded()) return CMD_ERROR;
746 free_wagon = !t->IsFrontEngine();
747 if (free_wagon && t->First()->IsFrontEngine()) return CMD_ERROR;
748 } else {
749 if (!v->IsPrimaryVehicle()) return CMD_ERROR;
750 }
751 if (!v->IsChainInDepot()) return CMD_ERROR;
752
754 bool wagon_removal = c->settings.renew_keep_length;
755
756 const Group *g = Group::GetIfValid(v->group_id);
757 if (g != nullptr) wagon_removal = HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL);
758
759 /* Test whether any replacement is set, before issuing a whole lot of commands that would end in nothing changed */
760 Vehicle *w = v;
761 bool any_replacements = false;
762 while (w != nullptr) {
763 EngineID e;
764 CommandCost cost = GetNewEngineType(w, c, false, e);
765 if (cost.Failed()) return cost;
766 any_replacements |= (e != INVALID_ENGINE);
767 w = (!free_wagon && w->type == VEH_TRAIN ? Train::From(w)->GetNextUnit() : nullptr);
768 }
769
771 bool nothing_to_do = true;
772
773 if (any_replacements) {
774 bool was_stopped = free_wagon || ((v->vehstatus & VS_STOPPED) != 0);
775
776 /* Stop the vehicle */
777 if (!was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, true));
778 if (cost.Failed()) return cost;
779
780 assert(free_wagon || v->IsStoppedInDepot());
781
782 /* We have to construct the new vehicle chain to test whether it is valid.
783 * Vehicle construction needs random bits, so we have to save the random seeds
784 * to prevent desyncs and to replay newgrf callbacks during DC_EXEC */
785 SavedRandomSeeds saved_seeds;
786 SaveRandomSeeds(&saved_seeds);
787 if (free_wagon) {
788 cost.AddCost(ReplaceFreeUnit(&v, flags & ~DC_EXEC, &nothing_to_do));
789 } else {
790 cost.AddCost(ReplaceChain(&v, flags & ~DC_EXEC, wagon_removal, &nothing_to_do));
791 }
792 RestoreRandomSeeds(saved_seeds);
793
794 if (cost.Succeeded() && (flags & DC_EXEC) != 0) {
795 if (free_wagon) {
796 ret = ReplaceFreeUnit(&v, flags, &nothing_to_do);
797 } else {
798 ret = ReplaceChain(&v, flags, wagon_removal, &nothing_to_do);
799 }
800 assert(ret.Succeeded() && ret.GetCost() == cost.GetCost());
801 }
802
803 /* Restart the vehicle */
804 if (!was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, false));
805 }
806
807 if (cost.Succeeded() && nothing_to_do) cost = CommandCost(STR_ERROR_AUTOREPLACE_NOTHING_TO_DO);
808 return cost;
809}
810
820CommandCost CmdSetAutoReplace(DoCommandFlag flags, GroupID id_g, EngineID old_engine_type, EngineID new_engine_type, bool when_old)
821{
823 if (c == nullptr) return CMD_ERROR;
824
825 CommandCost cost;
826
827 if (Group::IsValidID(id_g) ? Group::Get(id_g)->owner != _current_company : !IsAllGroupID(id_g) && !IsDefaultGroupID(id_g)) return CMD_ERROR;
828 if (!Engine::IsValidID(old_engine_type)) return CMD_ERROR;
829 if (Group::IsValidID(id_g) && Group::Get(id_g)->vehicle_type != Engine::Get(old_engine_type)->type) return CMD_ERROR;
830
831 if (new_engine_type != INVALID_ENGINE) {
832 if (!Engine::IsValidID(new_engine_type)) return CMD_ERROR;
833 if (!CheckAutoreplaceValidity(old_engine_type, new_engine_type, _current_company)) return CMD_ERROR;
834
835 cost = AddEngineReplacementForCompany(c, old_engine_type, new_engine_type, id_g, when_old, flags);
836 } else {
837 cost = RemoveEngineReplacementForCompany(c, old_engine_type, id_g, flags);
838 }
839
840 if (flags & DC_EXEC) {
842 if (IsLocalCompany()) SetWindowDirty(WC_REPLACE_VEHICLE, Engine::Get(old_engine_type)->type);
843
844 const VehicleType vt = Engine::Get(old_engine_type)->type;
846 }
847 if ((flags & DC_EXEC) && IsLocalCompany()) InvalidateAutoreplaceWindow(old_engine_type, id_g);
848
849 return cost;
850}
851
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, CargoID *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 CargoID GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type, bool part_of_chain)
Function to find what type of cargo to refit to when autoreplacing.
static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon_removal, bool *nothing_to_do)
Replace a whole vehicle chain.
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.
CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id)
Autoreplaces a vehicle Trains are replaced as a whole chain, free wagons in depot are replaced on the...
void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
Switches viewports following vehicles, which get autoreplaced.
Definition window.cpp:3435
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 BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehicle, bool part_of_chain, DoCommandFlag flags)
Builds and refits a replacement vehicle Important: The old vehicle is still in the original vehicle c...
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.
CommandCost CmdSetAutoReplace(DoCommandFlag flags, GroupID id_g, EngineID old_engine_type, EngineID new_engine_type, bool when_old)
Change engine renewal parameters.
static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlag flags, bool *nothing_to_do)
Replace a single unit in a free wagon chain.
static CommandCost CmdMoveVehicle(const Vehicle *v, const Vehicle *after, DoCommandFlag flags, bool whole_chain)
Issue a train vehicle move command.
static CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head, DoCommandFlag flags)
Copy head specific things to the new vehicle chain after it was successfully constructed.
Command definitions related to autoreplace.
Functions related to autoreplacing.
CommandCost RemoveEngineReplacementForCompany(Company *c, EngineID engine, GroupID group, DoCommandFlag flags)
Remove 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 AddEngineReplacementForCompany(Company *c, EngineID old_engine, EngineID new_engine, GroupID group, bool replace_when_old, DoCommandFlag flags)
Add 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.
debug_inline 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.
static const CargoID CARGO_NO_REFIT
Do not refit cargo of a vehicle (used in vehicle orders and auto-replace/auto-renew).
Definition cargo_type.h:78
uint8_t CargoID
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:22
bool IsValidCargoID(CargoID t)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:107
static const CargoID NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:74
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition ai_core.cpp:243
@ MTA_KEEP
Keep the cargo in the vehicle.
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.
RailTypes compatible_railtypes
bitmask to the OTHER railtypes on which an engine of THIS railtype can physically travel
Definition rail.h:191
RoadTypes powered_roadtypes
bitmask to the OTHER roadtypes on which a vehicle of THIS roadtype generates power
Definition road.h:122
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.
DoCommandFlag
List of flags for a command.
@ DC_AUTOREPLACE
autoreplace/autorenew is in progress, this shall disable vehicle limits when building,...
@ DC_NO_CARGO_CAP_CHECK
when autoreplace/autorenew is in progress, this shall prevent truncating the amount of cargo in the v...
@ DC_EXEC
execute the given command
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?
Owner
Enum for all companies/owners.
@ EXPENSES_NEW_VEHICLES
New vehicles.
bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
Check if an engine is buildable.
Definition engine.cpp:1254
Functions related to engines.
static const EngineID INVALID_ENGINE
Constant denoting an invalid engine.
uint16_t EngineID
Unique identification number of an engine.
Definition engine_type.h:21
@ AIR_CTOL
Conventional Take Off and Landing, i.e. planes.
Definition engine_type.h:95
@ EF_ROAD_TRAM
Road vehicle is a tram/light rail vehicle.
@ RAILVEH_WAGON
simple wagon, not motorized
Definition engine_type.h:29
@ GF_REPLACE_WAGON_REMOVAL
If set, autoreplace will perform wagon removal on vehicles in this group.
Definition group.h:67
bool IsAllGroupID(GroupID id_g)
Checks if a GroupID stands for all vehicles of a company.
Definition group.h:100
Command definitions related to engine groups.
uint16_t GroupID
Type for all group identifiers.
Definition group_type.h:13
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.
Functions related to news.
void AddVehicleAdviceNewsItem(StringID string, VehicleID vehicle)
Adds a vehicle-advice news item.
Definition news_func.h:40
Command definitions related to orders.
uint8_t VehicleOrderID
The index of an order within its current vehicle (not pool related)
Definition order_type.h:15
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:307
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:227
@ ROADTYPES_NONE
No roadtypes.
Definition road_type.h:39
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:57
Definition of base types and functions in a cross-platform compatible way.
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition strings.cpp:104
Functions related to OTTD's strings.
uint8_t subtype
Type of aircraft.
VehicleType type
Type of vehicle.
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo ID.
Definition cargotype.h:139
StringID name
Name of this type of cargo.
Definition cargotype.h:93
CompanySettings settings
settings specific for each company
bool renew_keep_length
sell some wagons if after autoreplace the train is longer than before
uint8_t misc_flags
Miscellaneous flags.
VehicleType type
Vehicle type, ie VEH_ROAD, VEH_TRAIN, etc.
Definition engine_base.h:60
bool CanCarryCargo() const
Determines whether an engine can carry something.
Definition engine.cpp:168
VehicleSettings vehicle
options for vehicles
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:72
uint8_t flags
Group flags.
Definition group.h:77
Shared order list linking together the linked list of orders and the list of vehicles sharing this or...
Definition order_base.h:259
Order * GetOrderAt(int index) const
Get a certain order of the order chain.
VehicleOrderID GetNumOrders() const
Get number of orders in the order list.
Definition order_base.h:319
CargoID GetRefitCargo() const
Get the cargo to to refit to.
Definition order_base.h:131
bool IsRefit() const
Is this order a refit order.
Definition order_base.h:117
Tindex index
Index of this pool item.
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
static Titem * GetIfValid(size_t index)
Returns Titem with given index.
static Titem * Get(size_t index)
Returns Titem with given index.
RailType railtype
Railtype, mangled if elrail is disabled.
Definition engine_type.h:46
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.
RoadType roadtype
Road type.
Stores the state of all random number generators.
static T * From(Vehicle *v)
Converts a Vehicle to SpecializedVehicle with type checking.
T * First() const
Get the first vehicle in the chain.
'Train' is either a loco or a wagon.
Definition train.h:89
Train * GetNextUnit() const
Get the next real (non-articulated part and non rear part of dualheaded engine) vehicle in the consis...
Definition train.h:148
The information about a vehicle list.
Definition vehiclelist.h:28
uint8_t max_train_length
maximum length for trains
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:747
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.
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:145
debug_inline bool IsFrontEngine() const
Check if the vehicle is a front engine.
Vehicle * First() const
Get the first vehicle of this vehicle chain.
CargoID cargo_type
type of cargo this vehicle is carrying
Vehicle * Next() const
Get the next vehicle of this vehicle.
OrderList * orders
Pointer to the order list for this vehicle.
uint8_t vehstatus
Status.
virtual bool IsPrimaryVehicle() const
Whether this is the primary vehicle in the chain.
IterateWrapper Orders() const
Returns an iterable ensemble of orders of a vehicle.
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 const uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
Base for the train class.
@ VRF_REVERSE_DIRECTION
Reverse the visible direction of the vehicle.
Definition train.h:28
@ CCF_LOADUNLOAD
Valid changes while vehicle is loading/unloading.
Definition train.h:49
Command definitions related to trains.
@ VS_STOPPED
Vehicle is stopped by the player.
@ VS_CRASHED
Vehicle is crashed.
Command definitions for vehicles.
Functions related to vehicles.
uint8_t GetBestFittingSubType(Vehicle *v_from, Vehicle *v_for, CargoID 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
VehicleType
Available vehicle types.
@ VEH_ROAD
Road vehicle type.
@ VEH_AIRCRAFT
Aircraft vehicle type.
@ VEH_TRAIN
Train vehicle type.
uint32_t VehicleID
The type all our vehicle IDs have.
static const VehicleID INVALID_VEHICLE
Constant representing a non-existing vehicle.
Functions and type for generating vehicle lists.
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3101
@ WC_REPLACE_VEHICLE
Replace vehicle window; Window numbers: