OpenTTD Source 20251213-master-g1091fa6071
order_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
10#include "stdafx.h"
11#include "debug.h"
12#include "command_func.h"
13#include "company_func.h"
14#include "news_func.h"
15#include "strings_func.h"
16#include "timetable.h"
17#include "vehicle_func.h"
18#include "depot_base.h"
19#include "core/pool_func.hpp"
20#include "aircraft.h"
21#include "roadveh.h"
22#include "station_base.h"
23#include "waypoint_base.h"
24#include "company_base.h"
25#include "order_backup.h"
26#include "cheat_type.h"
27#include "order_cmd.h"
28#include "train_cmd.h"
29
30#include "table/strings.h"
31
32#include "safeguards.h"
33
34/* DestinationID must be at least as large as every these below, because it can
35 * be any of them
36 */
37static_assert(sizeof(DestinationID) >= sizeof(DepotID));
38static_assert(sizeof(DestinationID) >= sizeof(StationID));
39
40OrderListPool _orderlist_pool("OrderList");
42
43
47void Order::Free()
48{
49 this->type = OT_NOTHING;
50 this->flags = 0;
51 this->dest = 0;
52}
53
59{
60 this->type = OT_GOTO_STATION;
61 this->flags = 0;
62 this->dest = destination;
63}
64
74{
75 this->type = OT_GOTO_DEPOT;
76 this->SetDepotOrderType(order);
77 this->SetDepotActionType(action);
78 this->SetNonStopType(non_stop_type);
79 this->dest = destination;
80 this->SetRefit(cargo);
81}
82
88{
89 this->type = OT_GOTO_WAYPOINT;
90 this->flags = 0;
91 this->dest = destination;
92}
93
98void Order::MakeLoading(bool ordered)
99{
100 this->type = OT_LOADING;
101 if (!ordered) this->flags = 0;
102}
103
108{
109 this->type = OT_LEAVESTATION;
110 this->flags = 0;
111}
112
117{
118 this->type = OT_DUMMY;
119 this->flags = 0;
120}
121
127{
128 this->type = OT_CONDITIONAL;
129 this->flags = order;
130 this->dest = 0;
131}
132
138{
139 this->type = OT_IMPLICIT;
140 this->dest = destination;
141}
142
149{
150 this->refit_cargo = cargo;
151}
152
158bool Order::Equals(const Order &other) const
159{
160 /* In case of go to nearest depot orders we need "only" compare the flags
161 * with the other and not the nearest depot order bit or the actual
162 * destination because those get clear/filled in during the order
163 * evaluation. If we do not do this the order will continuously be seen as
164 * a different order and it will try to find a "nearest depot" every tick. */
165 if ((this->IsType(OT_GOTO_DEPOT) && this->type == other.type) &&
166 (this->GetDepotActionType().Test(OrderDepotActionFlag::NearestDepot) ||
168 return this->GetDepotOrderType() == other.GetDepotOrderType() &&
170 }
171
172 return this->type == other.type && this->flags == other.flags && this->dest == other.dest;
173}
174
180uint16_t Order::MapOldOrder() const
181{
182 uint16_t order = this->GetType();
183 switch (this->GetType()) {
184 case OT_GOTO_STATION:
185 if (this->GetUnloadType() == OrderUnloadType::Unload) SetBit(order, 5);
186 if (this->IsFullLoadOrder()) SetBit(order, 6);
187 if (this->GetNonStopType().Test(OrderNonStopFlag::NoIntermediate)) SetBit(order, 7);
188 order |= GB(this->GetDestination().value, 0, 8) << 8;
189 break;
190 case OT_GOTO_DEPOT:
191 if (!this->GetDepotOrderType().Test(OrderDepotTypeFlag::PartOfOrders)) SetBit(order, 6);
192 SetBit(order, 7);
193 order |= GB(this->GetDestination().value, 0, 8) << 8;
194 break;
195 case OT_LOADING:
196 if (this->IsFullLoadOrder()) SetBit(order, 6);
197 /* If both "no load" and "no unload" are set, return nothing order instead */
199 order = OT_NOTHING;
200 }
201 break;
202 default:
203 break;
204 }
205 return order;
206}
207
213void InvalidateVehicleOrder(const Vehicle *v, int data)
214{
216
217 if (data != 0) {
218 /* Calls SetDirty() too */
221 return;
222 }
223
226}
227
235void Order::AssignOrder(const Order &other)
236{
237 this->type = other.type;
238 this->flags = other.flags;
239 this->dest = other.dest;
240
241 this->refit_cargo = other.refit_cargo;
242
243 this->wait_time = other.wait_time;
244 this->travel_time = other.travel_time;
245 this->max_speed = other.max_speed;
246}
247
254{
255 this->first_shared = v;
256
257 this->num_manual_orders = 0;
258 this->num_vehicles = 1;
259 this->timetable_duration = 0;
260
261 for (const Order &o : this->orders) {
262 if (!o.IsType(OT_IMPLICIT)) ++this->num_manual_orders;
263 this->total_duration += o.GetWaitTime() + o.GetTravelTime();
264 }
265
267
268 for (Vehicle *u = this->first_shared->PreviousShared(); u != nullptr; u = u->PreviousShared()) {
269 ++this->num_vehicles;
270 this->first_shared = u;
271 }
272
273 for (const Vehicle *u = v->NextShared(); u != nullptr; u = u->NextShared()) ++this->num_vehicles;
274}
275
281{
282 this->timetable_duration = 0;
283 for (const Order &o : this->orders) {
284 this->timetable_duration += o.GetTimetabledWait() + o.GetTimetabledTravel();
285 }
286}
287
293void OrderList::FreeChain(bool keep_orderlist)
294{
295 /* We can visit oil rigs and buoys that are not our own. They will be shown in
296 * the list of stations. So, we need to invalidate that window if needed. */
297 for (Order &order: this->orders) {
298 if (order.IsType(OT_GOTO_STATION) || order.IsType(OT_GOTO_WAYPOINT)) {
299 BaseStation *bs = BaseStation::GetIfValid(order.GetDestination().ToStationID());
300 if (bs != nullptr && bs->owner == OWNER_NONE) {
302 break;
303 }
304 }
305 }
306
307 if (keep_orderlist) {
308 this->orders.clear();
309 this->num_manual_orders = 0;
310 this->timetable_duration = 0;
311 } else {
312 delete this;
313 }
314}
315
328{
329 if (hops > this->GetNumOrders() || next >= this->GetNumOrders()) return INVALID_VEH_ORDER_ID;
330
331 const Order &order_next = this->orders[next];
332 if (order_next.IsType(OT_CONDITIONAL)) {
333 if (order_next.GetConditionVariable() != OrderConditionVariable::Unconditionally) return next;
334
335 /* We can evaluate trivial conditions right away. They're conceptually
336 * the same as regular order progression. */
337 return this->GetNextDecisionNode(
338 order_next.GetConditionSkipToOrder(),
339 hops + 1);
340 }
341
342 if (order_next.IsType(OT_GOTO_DEPOT)) {
344 if (order_next.IsRefit()) return next;
345 }
346
347 if (!order_next.CanLoadOrUnload()) {
348 return this->GetNextDecisionNode(this->GetNext(next), hops + 1);
349 }
350
351 return next;
352}
353
363void OrderList::GetNextStoppingStation(std::vector<StationID> &next_station, const Vehicle *v, VehicleOrderID first, uint hops) const
364{
365 VehicleOrderID next = first;
366 if (first == INVALID_VEH_ORDER_ID) {
367 next = v->cur_implicit_order_index;
368 if (next >= this->GetNumOrders()) {
369 next = this->GetFirstOrder();
370 if (next == INVALID_VEH_ORDER_ID) return;
371 } else {
372 /* GetNext never returns INVALID_VEH_ORDER_ID if there is a valid station in the list.
373 * As the given "next" is already valid and a station in the list, we
374 * don't have to check for INVALID_VEH_ORDER_ID here. */
375 next = this->GetNext(next);
376 assert(next != INVALID_VEH_ORDER_ID);
377 }
378 }
379
380 auto orders = v->Orders();
381 do {
382 next = this->GetNextDecisionNode(next, ++hops);
383
384 /* Resolve possibly nested conditionals by estimation. */
385 while (next != INVALID_VEH_ORDER_ID && orders[next].IsType(OT_CONDITIONAL)) {
386 /* We return both options of conditional orders. */
387 VehicleOrderID skip_to = this->GetNextDecisionNode(orders[next].GetConditionSkipToOrder(), hops);
388 VehicleOrderID advance = this->GetNextDecisionNode(this->GetNext(next), hops);
389 if (advance == INVALID_VEH_ORDER_ID || advance == first || skip_to == advance) {
390 next = (skip_to == first) ? INVALID_VEH_ORDER_ID : skip_to;
391 } else if (skip_to == INVALID_VEH_ORDER_ID || skip_to == first) {
392 next = (advance == first) ? INVALID_VEH_ORDER_ID : advance;
393 } else {
394 this->GetNextStoppingStation(next_station, v, skip_to, hops);
395 this->GetNextStoppingStation(next_station, v, advance, hops);
396 return;
397 }
398 ++hops;
399 }
400
401 /* Don't return a next stop if the vehicle has to unload everything. */
402 if (next == INVALID_VEH_ORDER_ID || ((orders[next].IsType(OT_GOTO_STATION) || orders[next].IsType(OT_IMPLICIT)) &&
403 orders[next].GetDestination() == v->last_station_visited &&
404 (orders[next].GetUnloadType() == OrderUnloadType::Transfer || orders[next].GetUnloadType() == OrderUnloadType::Unload))) {
405 return;
406 }
407 } while (orders[next].IsType(OT_GOTO_DEPOT) || orders[next].GetDestination() == v->last_station_visited);
408
409 next_station.push_back(orders[next].GetDestination().ToStationID());
410}
411
418{
419 auto it = std::ranges::next(std::begin(this->orders), index, std::end(this->orders));
420 auto new_order = this->orders.emplace(it, std::move(order));
421
422 if (!new_order->IsType(OT_IMPLICIT)) ++this->num_manual_orders;
423 this->timetable_duration += new_order->GetTimetabledWait() + new_order->GetTimetabledTravel();
424 this->total_duration += new_order->GetWaitTime() + new_order->GetTravelTime();
425
426 /* We can visit oil rigs and buoys that are not our own. They will be shown in
427 * the list of stations. So, we need to invalidate that window if needed. */
428 if (new_order->IsType(OT_GOTO_STATION) || new_order->IsType(OT_GOTO_WAYPOINT)) {
429 BaseStation *bs = BaseStation::Get(new_order->GetDestination().ToStationID());
431 }
432}
433
434
440{
441 auto to_remove = std::ranges::next(std::begin(this->orders), index, std::end(this->orders));
442 if (to_remove == std::end(this->orders)) return;
443
444 if (!to_remove->IsType(OT_IMPLICIT)) --this->num_manual_orders;
445
446 this->timetable_duration -= (to_remove->GetTimetabledWait() + to_remove->GetTimetabledTravel());
447 this->total_duration -= (to_remove->GetWaitTime() + to_remove->GetTravelTime());
448
449 this->orders.erase(to_remove);
450}
451
458{
459 if (from == to) return;
460 if (from >= this->GetNumOrders()) return;
461 if (to >= this->GetNumOrders()) return;
462
463 auto it = std::begin(this->orders);
464 if (from < to) {
465 std::rotate(it + from, it + from + 1, it + to + 1);
466 } else {
467 std::rotate(it + to, it + from, it + from + 1);
468 }
469}
470
477{
478 --this->num_vehicles;
479 if (v == this->first_shared) this->first_shared = v->NextShared();
480}
481
487{
488 for (const Order &o : this->orders) {
489 /* Implicit orders are, by definition, not timetabled. */
490 if (o.IsType(OT_IMPLICIT)) continue;
491 if (!o.IsCompletelyTimetabled()) return false;
492 }
493 return true;
494}
495
496#ifdef WITH_ASSERT
500void OrderList::DebugCheckSanity() const
501{
502 VehicleOrderID check_num_orders = 0;
503 VehicleOrderID check_num_manual_orders = 0;
504 uint check_num_vehicles = 0;
505 TimerGameTick::Ticks check_timetable_duration = 0;
506 TimerGameTick::Ticks check_total_duration = 0;
507
508 Debug(misc, 6, "Checking OrderList {} for sanity...", this->index);
509
510 for (const Order &o : this->orders) {
511 ++check_num_orders;
512 if (!o.IsType(OT_IMPLICIT)) ++check_num_manual_orders;
513 check_timetable_duration += o.GetTimetabledWait() + o.GetTimetabledTravel();
514 check_total_duration += o.GetWaitTime() + o.GetTravelTime();
515 }
516 assert(this->GetNumOrders() == check_num_orders);
517 assert(this->num_manual_orders == check_num_manual_orders);
518 assert(this->timetable_duration == check_timetable_duration);
519 assert(this->total_duration == check_total_duration);
520
521 for (const Vehicle *v = this->first_shared; v != nullptr; v = v->NextShared()) {
522 ++check_num_vehicles;
523 assert(v->orders == this);
524 }
525 assert(this->num_vehicles == check_num_vehicles);
526 Debug(misc, 6, "... detected {} orders ({} manual), {} vehicles, {} timetabled, {} total",
527 (uint)this->GetNumOrders(), (uint)this->num_manual_orders,
529}
530#endif
531
539static inline bool OrderGoesToStation(const Vehicle *v, const Order &o)
540{
541 return o.IsType(OT_GOTO_STATION) ||
542 (v->type == VEH_AIRCRAFT && o.IsType(OT_GOTO_DEPOT) && o.GetDestination() != StationID::Invalid());
543}
544
551static void DeleteOrderWarnings(const Vehicle *v)
552{
554}
555
562TileIndex Order::GetLocation(const Vehicle *v, bool airport) const
563{
564 switch (this->GetType()) {
565 case OT_GOTO_WAYPOINT:
566 case OT_GOTO_STATION:
567 case OT_IMPLICIT:
568 if (airport && v->type == VEH_AIRCRAFT) return Station::Get(this->GetDestination().ToStationID())->airport.tile;
569 return BaseStation::Get(this->GetDestination().ToStationID())->xy;
570
571 case OT_GOTO_DEPOT:
572 if (this->GetDestination() == DepotID::Invalid()) return INVALID_TILE;
573 return (v->type == VEH_AIRCRAFT) ? Station::Get(this->GetDestination().ToStationID())->xy : Depot::Get(this->GetDestination().ToDepotID())->xy;
574
575 default:
576 return INVALID_TILE;
577 }
578}
579
589uint GetOrderDistance(VehicleOrderID prev, VehicleOrderID cur, const Vehicle *v, int conditional_depth)
590{
591 assert(v->orders != nullptr);
592 const OrderList &orderlist = *v->orders;
593 auto orders = orderlist.GetOrders();
594
595 if (orders[cur].IsType(OT_CONDITIONAL)) {
596 if (conditional_depth > v->GetNumOrders()) return 0;
597
598 conditional_depth++;
599
600 int dist1 = GetOrderDistance(prev, orders[cur].GetConditionSkipToOrder(), v, conditional_depth);
601 int dist2 = GetOrderDistance(prev, orderlist.GetNext(cur), v, conditional_depth);
602 return std::max(dist1, dist2);
603 }
604
605 TileIndex prev_tile = orders[prev].GetLocation(v, true);
606 TileIndex cur_tile = orders[cur].GetLocation(v, true);
607 if (prev_tile == INVALID_TILE || cur_tile == INVALID_TILE) return 0;
608 return v->type == VEH_AIRCRAFT ? DistanceSquare(prev_tile, cur_tile) : DistanceManhattan(prev_tile, cur_tile);
609}
610
622{
624 if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
625
627 if (ret.Failed()) return ret;
628
629 /* Validate properties we don't want to have different from default as they are set by other commands. */
630 if (new_order.GetRefitCargo() != CARGO_NO_REFIT || new_order.GetWaitTime() != 0 || new_order.GetTravelTime() != 0 || new_order.GetMaxSpeed() != UINT16_MAX) return CMD_ERROR;
631
632 /* Check if the inserted order is to the correct destination (owner, type),
633 * and has the correct flags if any */
634 switch (new_order.GetType()) {
635 case OT_GOTO_STATION: {
636 const Station *st = Station::GetIfValid(new_order.GetDestination().ToStationID());
637 if (st == nullptr) return CMD_ERROR;
638
639 if (st->owner != OWNER_NONE) {
640 ret = CheckOwnership(st->owner);
641 if (ret.Failed()) return ret;
642 }
643
644 if (!CanVehicleUseStation(v, st)) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER, GetVehicleCannotUseStationReason(v, st));
645 for (Vehicle *u = v->FirstShared(); u != nullptr; u = u->NextShared()) {
646 if (!CanVehicleUseStation(u, st)) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER_SHARED, GetVehicleCannotUseStationReason(u, st));
647 }
648
649 /* Non stop only allowed for ground vehicles. */
650 if (new_order.GetNonStopType().Any() && !v->IsGroundVehicle()) return CMD_ERROR;
651
652 /* Filter invalid load/unload types. */
653 switch (new_order.GetLoadType()) {
656 break;
657
660 if (v->HasUnbunchingOrder()) return CommandCost(STR_ERROR_UNBUNCHING_NO_FULL_LOAD);
661 break;
662
663 default:
664 return CMD_ERROR;
665 }
666 switch (new_order.GetUnloadType()) {
671 break;
672
673 default:
674 return CMD_ERROR;
675 }
676
677 /* Filter invalid stop locations */
678 switch (new_order.GetStopLocation()) {
681 if (v->type != VEH_TRAIN) return CMD_ERROR;
682 [[fallthrough]];
683
685 break;
686
687 default:
688 return CMD_ERROR;
689 }
690
691 break;
692 }
693
694 case OT_GOTO_DEPOT: {
696 if (v->type == VEH_AIRCRAFT) {
697 const Station *st = Station::GetIfValid(new_order.GetDestination().ToStationID());
698
699 if (st == nullptr) return CMD_ERROR;
700
701 ret = CheckOwnership(st->owner);
702 if (ret.Failed()) return ret;
703
704 if (!CanVehicleUseStation(v, st) || !st->airport.HasHangar()) {
705 return CMD_ERROR;
706 }
707 } else {
708 const Depot *dp = Depot::GetIfValid(new_order.GetDestination().ToDepotID());
709
710 if (dp == nullptr) return CMD_ERROR;
711
712 ret = CheckOwnership(GetTileOwner(dp->xy));
713 if (ret.Failed()) return ret;
714
715 switch (v->type) {
716 case VEH_TRAIN:
717 if (!IsRailDepotTile(dp->xy)) return CMD_ERROR;
718 break;
719
720 case VEH_ROAD:
721 if (!IsRoadDepotTile(dp->xy)) return CMD_ERROR;
722 break;
723
724 case VEH_SHIP:
725 if (!IsShipDepotTile(dp->xy)) return CMD_ERROR;
726 break;
727
728 default: return CMD_ERROR;
729 }
730 }
731 }
732
733 if (new_order.GetNonStopType().Any() && !v->IsGroundVehicle()) return CMD_ERROR;
734
735 /* Check depot order type is valid. */
736 OrderDepotTypeFlags depot_order_type = new_order.GetDepotOrderType();
737 if (depot_order_type.Test(OrderDepotTypeFlag::PartOfOrders)) depot_order_type.Reset(OrderDepotTypeFlag::Service);
738 depot_order_type.Reset(OrderDepotTypeFlag::PartOfOrders);
739 if (depot_order_type.Any()) return CMD_ERROR;
740
741 /* Check depot action type is valid. */
742 if (new_order.GetDepotActionType().Reset({OrderDepotActionFlag::Halt, OrderDepotActionFlag::NearestDepot, OrderDepotActionFlag::Unbunch}).Any()) return CMD_ERROR;
743
744 /* Vehicles cannot have a "service if needed" order that also has a depot action. */
745 if (new_order.GetDepotOrderType().Test(OrderDepotTypeFlag::Service) && new_order.GetDepotActionType().Any({OrderDepotActionFlag::Halt, OrderDepotActionFlag::Unbunch})) return CMD_ERROR;
746
747 /* Check if we're allowed to have a new unbunching order. */
749 if (v->HasFullLoadOrder()) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER, STR_ERROR_UNBUNCHING_NO_UNBUNCHING_FULL_LOAD);
750 if (v->HasUnbunchingOrder()) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER, STR_ERROR_UNBUNCHING_ONLY_ONE_ALLOWED);
751 if (v->HasConditionalOrder()) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER, STR_ERROR_UNBUNCHING_NO_UNBUNCHING_CONDITIONAL);
752 }
753 break;
754 }
755
756 case OT_GOTO_WAYPOINT: {
757 const Waypoint *wp = Waypoint::GetIfValid(new_order.GetDestination().ToStationID());
758 if (wp == nullptr) return CMD_ERROR;
759
760 switch (v->type) {
761 default: return CMD_ERROR;
762
763 case VEH_TRAIN: {
764 if (!wp->facilities.Test(StationFacility::Train)) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER, STR_ERROR_NO_RAIL_WAYPOINT);
765
766 ret = CheckOwnership(wp->owner);
767 if (ret.Failed()) return ret;
768 break;
769 }
770
771 case VEH_ROAD: {
772 if (!wp->facilities.Test(StationFacility::BusStop) && !wp->facilities.Test(StationFacility::TruckStop)) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER, STR_ERROR_NO_ROAD_WAYPOINT);
773
774 ret = CheckOwnership(wp->owner);
775 if (ret.Failed()) return ret;
776 break;
777 }
778
779 case VEH_SHIP:
780 if (!wp->facilities.Test(StationFacility::Dock)) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER, STR_ERROR_NO_BUOY);
781 if (wp->owner != OWNER_NONE) {
782 ret = CheckOwnership(wp->owner);
783 if (ret.Failed()) return ret;
784 }
785 break;
786 }
787
788 /* Order flags can be any of the following for waypoints:
789 * [non-stop]
790 * non-stop orders (if any) are only valid for trains and road vehicles */
791 if (new_order.GetNonStopType().Any() && !v->IsGroundVehicle()) return CMD_ERROR;
792 break;
793 }
794
795 case OT_CONDITIONAL: {
796 VehicleOrderID skip_to = new_order.GetConditionSkipToOrder();
797 if (skip_to != 0 && skip_to >= v->GetNumOrders()) return CMD_ERROR; // Always allow jumping to the first (even when there is no order).
798 if (new_order.GetConditionVariable() >= OrderConditionVariable::End) return CMD_ERROR;
799 if (v->HasUnbunchingOrder()) return CommandCost(STR_ERROR_UNBUNCHING_NO_CONDITIONAL);
800
802 if (occ >= OrderConditionComparator::End) return CMD_ERROR;
803 switch (new_order.GetConditionVariable()) {
806 break;
807
810 if (new_order.GetConditionValue() != 0) return CMD_ERROR;
811 break;
812
815 if (new_order.GetConditionValue() > 100) return CMD_ERROR;
816 [[fallthrough]];
817
818 default:
820 break;
821 }
822 break;
823 }
824
825 default: return CMD_ERROR;
826 }
827
828 if (sel_ord > v->GetNumOrders()) return CMD_ERROR;
829
830 if (v->GetNumOrders() >= MAX_VEH_ORDER_ID) return CommandCost(STR_ERROR_TOO_MANY_ORDERS);
831 if (v->orders == nullptr && !OrderList::CanAllocateItem()) return CommandCost(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS);
832
833 if (flags.Test(DoCommandFlag::Execute)) {
834 InsertOrder(v, Order(new_order), sel_ord);
835 }
836
837 return CommandCost();
838}
839
846void InsertOrder(Vehicle *v, Order &&new_o, VehicleOrderID sel_ord)
847{
848 /* Create new order and link in list */
849 if (v->orders == nullptr) {
850 v->orders = new OrderList(std::move(new_o), v);
851 } else {
852 v->orders->InsertOrderAt(std::move(new_o), sel_ord);
853 }
854
855 Vehicle *u = v->FirstShared();
857 for (; u != nullptr; u = u->NextShared()) {
858 assert(v->orders == u->orders);
859
860 /* If there is added an order before the current one, we need
861 * to update the selected order. We do not change implicit/real order indices though.
862 * If the new order is between the current implicit order and real order, the implicit order will
863 * later skip the inserted order. */
864 if (sel_ord <= u->cur_real_order_index) {
865 uint cur = u->cur_real_order_index + 1;
866 /* Check if we don't go out of bound */
867 if (cur < u->GetNumOrders()) {
868 u->cur_real_order_index = cur;
869 }
870 }
871 if (sel_ord == u->cur_implicit_order_index && u->IsGroundVehicle()) {
872 /* We are inserting an order just before the current implicit order.
873 * We do not know whether we will reach current implicit or the newly inserted order first.
874 * So, disable creation of implicit orders until we are on track again. */
875 uint16_t &gv_flags = u->GetGroundVehicleFlags();
877 }
878 if (sel_ord <= u->cur_implicit_order_index) {
879 uint cur = u->cur_implicit_order_index + 1;
880 /* Check if we don't go out of bound */
881 if (cur < u->GetNumOrders()) {
883 }
884 }
885 /* Unbunching data is no longer valid. */
887
888 /* Update any possible open window of the vehicle */
889 InvalidateVehicleOrder(u, INVALID_VEH_ORDER_ID | (sel_ord << 8));
890 }
891
892 /* As we insert an order, the order to skip to will be 'wrong'. */
893 VehicleOrderID cur_order_id = 0;
894 for (Order &order : v->Orders()) {
895 if (order.IsType(OT_CONDITIONAL)) {
896 VehicleOrderID order_id = order.GetConditionSkipToOrder();
897 if (order_id >= sel_ord) {
898 order.SetConditionSkipToOrder(order_id + 1);
899 }
900 if (order_id == cur_order_id) {
901 order.SetConditionSkipToOrder((order_id + 1) % v->GetNumOrders());
902 }
903 }
904 cur_order_id++;
905 }
906
907 /* Make sure to rebuild the whole list */
909}
910
925
934{
935 Vehicle *v = Vehicle::GetIfValid(veh_id);
936
937 if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
938
940 if (ret.Failed()) return ret;
941
942 /* If we did not select an order, we maybe want to de-clone the orders */
943 if (sel_ord >= v->GetNumOrders()) return DecloneOrder(v, flags);
944
945 if (v->GetOrder(sel_ord) == nullptr) return CMD_ERROR;
946
947 if (flags.Test(DoCommandFlag::Execute)) DeleteOrder(v, sel_ord);
948 return CommandCost();
949}
950
956{
957 assert(v->current_order.IsType(OT_LOADING));
958 /* NON-stop flag is misused to see if a train is in a station that is
959 * on its order list or not */
961 /* When full loading, "cancel" that order so the vehicle doesn't
962 * stay indefinitely at this station anymore. */
964}
965
972{
973 v->orders->DeleteOrderAt(sel_ord);
974
975 Vehicle *u = v->FirstShared();
977 for (; u != nullptr; u = u->NextShared()) {
978 assert(v->orders == u->orders);
979
980 if (sel_ord == u->cur_real_order_index && u->current_order.IsType(OT_LOADING)) {
982 }
983
984 if (sel_ord < u->cur_real_order_index) {
986 } else if (sel_ord == u->cur_real_order_index) {
988 }
989
990 if (sel_ord < u->cur_implicit_order_index) {
992 } else if (sel_ord == u->cur_implicit_order_index) {
993 /* Make sure the index is valid */
995
996 /* Skip non-implicit orders for the implicit-order-index (e.g. if the current implicit order was deleted */
1000 }
1001 }
1002 /* Unbunching data is no longer valid. */
1004
1005 /* Update any possible open window of the vehicle */
1006 InvalidateVehicleOrder(u, sel_ord | (INVALID_VEH_ORDER_ID << 8));
1007 }
1008
1009 /* As we delete an order, the order to skip to will be 'wrong'. */
1010 VehicleOrderID cur_order_id = 0;
1011 for (Order &order : v->Orders()) {
1012 if (order.IsType(OT_CONDITIONAL)) {
1013 VehicleOrderID order_id = order.GetConditionSkipToOrder();
1014 if (order_id >= sel_ord) {
1015 order_id = std::max(order_id - 1, 0);
1016 }
1017 if (order_id == cur_order_id) {
1018 order_id = (order_id + 1) % v->GetNumOrders();
1019 }
1020 order.SetConditionSkipToOrder(order_id);
1021 }
1022 cur_order_id++;
1023 }
1024
1026}
1027
1036{
1037 Vehicle *v = Vehicle::GetIfValid(veh_id);
1038
1039 if (v == nullptr || !v->IsPrimaryVehicle() || sel_ord == v->cur_implicit_order_index || sel_ord >= v->GetNumOrders() || v->GetNumOrders() < 2) return CMD_ERROR;
1040
1042 if (ret.Failed()) return ret;
1043
1044 if (flags.Test(DoCommandFlag::Execute)) {
1045 if (v->current_order.IsType(OT_LOADING)) v->LeaveStation();
1046
1049
1050 /* Unbunching data is no longer valid. */
1052
1054
1055 /* We have an aircraft/ship, they have a mini-schedule, so update them all */
1058 }
1059
1060 return CommandCost();
1061}
1062
1074{
1075 Vehicle *v = Vehicle::GetIfValid(veh);
1076 if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
1077
1079 if (ret.Failed()) return ret;
1080
1081 /* Don't make senseless movements */
1082 if (moving_order >= v->GetNumOrders() || target_order >= v->GetNumOrders() ||
1083 moving_order == target_order || v->GetNumOrders() <= 1) return CMD_ERROR;
1084
1085 Order *moving_one = v->GetOrder(moving_order);
1086 /* Don't move an empty order */
1087 if (moving_one == nullptr) return CMD_ERROR;
1088
1089 if (flags.Test(DoCommandFlag::Execute)) {
1090 v->orders->MoveOrder(moving_order, target_order);
1091
1092 /* Update shared list */
1093 Vehicle *u = v->FirstShared();
1094
1096
1097 for (; u != nullptr; u = u->NextShared()) {
1098 /* Update the current order.
1099 * There are multiple ways to move orders, which result in cur_implicit_order_index
1100 * and cur_real_order_index to not longer make any sense. E.g. moving another
1101 * real order between them.
1102 *
1103 * Basically one could choose to preserve either of them, but not both.
1104 * While both ways are suitable in this or that case from a human point of view, neither
1105 * of them makes really sense.
1106 * However, from an AI point of view, preserving cur_real_order_index is the most
1107 * predictable and transparent behaviour.
1108 *
1109 * With that decision it basically does not matter what we do to cur_implicit_order_index.
1110 * If we change orders between the implicit- and real-index, the implicit orders are mostly likely
1111 * completely out-dated anyway. So, keep it simple and just keep cur_implicit_order_index as well.
1112 * The worst which can happen is that a lot of implicit orders are removed when reaching current_order.
1113 */
1114 if (u->cur_real_order_index == moving_order) {
1115 u->cur_real_order_index = target_order;
1116 } else if (u->cur_real_order_index > moving_order && u->cur_real_order_index <= target_order) {
1118 } else if (u->cur_real_order_index < moving_order && u->cur_real_order_index >= target_order) {
1120 }
1121
1122 if (u->cur_implicit_order_index == moving_order) {
1123 u->cur_implicit_order_index = target_order;
1124 } else if (u->cur_implicit_order_index > moving_order && u->cur_implicit_order_index <= target_order) {
1126 } else if (u->cur_implicit_order_index < moving_order && u->cur_implicit_order_index >= target_order) {
1128 }
1129 /* Unbunching data is no longer valid. */
1131
1132
1133 assert(v->orders == u->orders);
1134 /* Update any possible open window of the vehicle */
1135 InvalidateVehicleOrder(u, moving_order | (target_order << 8));
1136 }
1137
1138 /* As we move an order, the order to skip to will be 'wrong'. */
1139 for (Order &order : v->Orders()) {
1140 if (order.IsType(OT_CONDITIONAL)) {
1141 VehicleOrderID order_id = order.GetConditionSkipToOrder();
1142 if (order_id == moving_order) {
1143 order_id = target_order;
1144 } else if (order_id > moving_order && order_id <= target_order) {
1145 order_id--;
1146 } else if (order_id < moving_order && order_id >= target_order) {
1147 order_id++;
1148 }
1149 order.SetConditionSkipToOrder(order_id);
1150 }
1151 }
1152
1153 /* Make sure to rebuild the whole list */
1155 }
1156
1157 return CommandCost();
1158}
1159
1172{
1173 if (mof >= MOF_END) return CMD_ERROR;
1174
1175 Vehicle *v = Vehicle::GetIfValid(veh);
1176 if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
1177
1179 if (ret.Failed()) return ret;
1180
1181 /* Is it a valid order? */
1182 if (sel_ord >= v->GetNumOrders()) return CMD_ERROR;
1183
1184 Order *order = v->GetOrder(sel_ord);
1185 assert(order != nullptr);
1186 switch (order->GetType()) {
1187 case OT_GOTO_STATION:
1188 if (mof != MOF_NON_STOP && mof != MOF_STOP_LOCATION && mof != MOF_UNLOAD && mof != MOF_LOAD) return CMD_ERROR;
1189 break;
1190
1191 case OT_GOTO_DEPOT:
1192 if (mof != MOF_NON_STOP && mof != MOF_DEPOT_ACTION) return CMD_ERROR;
1193 break;
1194
1195 case OT_GOTO_WAYPOINT:
1196 if (mof != MOF_NON_STOP) return CMD_ERROR;
1197 break;
1198
1199 case OT_CONDITIONAL:
1200 if (mof != MOF_COND_VARIABLE && mof != MOF_COND_COMPARATOR && mof != MOF_COND_VALUE && mof != MOF_COND_DESTINATION) return CMD_ERROR;
1201 break;
1202
1203 default:
1204 return CMD_ERROR;
1205 }
1206
1207 switch (mof) {
1208 default: NOT_REACHED();
1209
1210 case MOF_NON_STOP: {
1211 if (!v->IsGroundVehicle()) return CMD_ERROR;
1212
1213 OrderNonStopFlags nonstop_flags = static_cast<OrderNonStopFlags>(data);
1214 if (nonstop_flags == order->GetNonStopType()) return CMD_ERROR;
1215
1216 /* Test for invalid flags. */
1218 if (nonstop_flags.Any()) return CMD_ERROR;
1219 break;
1220 }
1221
1222 case MOF_STOP_LOCATION:
1223 if (v->type != VEH_TRAIN) return CMD_ERROR;
1224 if (data >= to_underlying(OrderStopLocation::End)) return CMD_ERROR;
1225 break;
1226
1227 case MOF_UNLOAD: {
1229
1230 OrderUnloadType unload_type = static_cast<OrderUnloadType>(data);
1231 if (unload_type == order->GetUnloadType()) return CMD_ERROR;
1232
1233 /* Test for invalid types. */
1234 switch (unload_type) {
1239 break;
1240
1241 default: return CMD_ERROR;
1242 }
1243 break;
1244 }
1245
1246 case MOF_LOAD: {
1248
1249 OrderLoadType load_type = static_cast<OrderLoadType>(data);
1250 if (load_type == order->GetLoadType()) return CMD_ERROR;
1251
1252 /* Test for invalid types. */
1253 switch (load_type) {
1256 break;
1257
1260 if (v->HasUnbunchingOrder()) return CommandCost(STR_ERROR_UNBUNCHING_NO_FULL_LOAD);
1261 break;
1262
1263 default: return CMD_ERROR;
1264 }
1265 break;
1266 }
1267
1268 case MOF_DEPOT_ACTION: {
1269 OrderDepotAction depot_action = static_cast<OrderDepotAction>(data);
1270 if (depot_action >= OrderDepotAction::End) return CMD_ERROR;
1271 /* Check if we are allowed to add unbunching. We are always allowed to remove it. */
1272 if (depot_action == OrderDepotAction::Unbunch) {
1273 /* Only one unbunching order is allowed in a vehicle's orders. If this order already has an unbunching action, no error is needed. */
1274 if (v->HasUnbunchingOrder() && !order->GetDepotActionType().Test(OrderDepotActionFlag::Unbunch)) return CommandCost(STR_ERROR_UNBUNCHING_ONLY_ONE_ALLOWED);
1275 /* We don't allow unbunching if the vehicle has a conditional order. */
1276 if (v->HasConditionalOrder()) return CommandCost(STR_ERROR_UNBUNCHING_NO_UNBUNCHING_CONDITIONAL);
1277 /* We don't allow unbunching if the vehicle has a full load order. */
1278 if (v->HasFullLoadOrder()) return CommandCost(STR_ERROR_UNBUNCHING_NO_UNBUNCHING_FULL_LOAD);
1279 }
1280 break;
1281 }
1282
1283 case MOF_COND_VARIABLE: {
1284 OrderConditionVariable cond_variable = static_cast<OrderConditionVariable>(data);
1285 if (cond_variable >= OrderConditionVariable::End) return CMD_ERROR;
1286 break;
1287 }
1288
1289 case MOF_COND_COMPARATOR: {
1290 OrderConditionComparator cond_comparator = static_cast<OrderConditionComparator>(data);
1291 if (cond_comparator >= OrderConditionComparator::End) return CMD_ERROR;
1292 switch (order->GetConditionVariable()) {
1294
1296 if (cond_comparator != OrderConditionComparator::IsTrue && cond_comparator != OrderConditionComparator::IsFalse) return CMD_ERROR;
1297 break;
1298
1299 default:
1300 if (cond_comparator == OrderConditionComparator::IsTrue || cond_comparator == OrderConditionComparator::IsFalse) return CMD_ERROR;
1301 break;
1302 }
1303 break;
1304 }
1305
1306 case MOF_COND_VALUE:
1307 switch (order->GetConditionVariable()) {
1310 return CMD_ERROR;
1311
1314 if (data > 100) return CMD_ERROR;
1315 break;
1316
1317 default:
1318 if (data > 2047) return CMD_ERROR;
1319 break;
1320 }
1321 break;
1322
1324 if (data >= v->GetNumOrders()) return CMD_ERROR;
1325 break;
1326 }
1327
1328 if (flags.Test(DoCommandFlag::Execute)) {
1329 switch (mof) {
1330 case MOF_NON_STOP:
1331 order->SetNonStopType(static_cast<OrderNonStopFlags>(data));
1333 order->SetRefit(CARGO_NO_REFIT);
1336 }
1337 break;
1338
1339 case MOF_STOP_LOCATION:
1340 order->SetStopLocation(static_cast<OrderStopLocation>(data));
1341 break;
1342
1343 case MOF_UNLOAD:
1344 order->SetUnloadType(static_cast<OrderUnloadType>(data));
1345 break;
1346
1347 case MOF_LOAD:
1348 order->SetLoadType(static_cast<OrderLoadType>(data));
1350 break;
1351
1352 case MOF_DEPOT_ACTION: {
1353 switch (static_cast<OrderDepotAction>(data)) {
1357 break;
1358
1362 order->SetRefit(CARGO_NO_REFIT);
1363 break;
1364
1368 order->SetRefit(CARGO_NO_REFIT);
1369 break;
1370
1374 break;
1375
1376 default:
1377 NOT_REACHED();
1378 }
1379 break;
1380 }
1381
1382 case MOF_COND_VARIABLE: {
1384
1386 switch (order->GetConditionVariable()) {
1389 order->SetConditionValue(0);
1390 break;
1391
1394 order->SetConditionValue(0);
1395 break;
1396
1399 if (order->GetConditionValue() > 100) order->SetConditionValue(100);
1400 [[fallthrough]];
1401
1402 default:
1404 break;
1405 }
1406 break;
1407 }
1408
1411 break;
1412
1413 case MOF_COND_VALUE:
1414 order->SetConditionValue(data);
1415 break;
1416
1418 order->SetConditionSkipToOrder(data);
1419 break;
1420
1421 default: NOT_REACHED();
1422 }
1423
1424 /* Update the windows and full load flags, also for vehicles that share the same order list */
1425 Vehicle *u = v->FirstShared();
1427 for (; u != nullptr; u = u->NextShared()) {
1428 /* Toggle u->current_order "Full load" flag if it changed.
1429 * However, as the same flag is used for depot orders, check
1430 * whether we are not going to a depot as there are three
1431 * cases where the full load flag can be active and only
1432 * one case where the flag is used for depot orders. In the
1433 * other cases for the OrderType the flags are not used,
1434 * so do not care and those orders should not be active
1435 * when this function is called.
1436 */
1437 if (sel_ord == u->cur_real_order_index &&
1438 (u->current_order.IsType(OT_GOTO_STATION) || u->current_order.IsType(OT_LOADING)) &&
1439 u->current_order.GetLoadType() != order->GetLoadType()) {
1441 }
1442
1443 /* Unbunching data is no longer valid. */
1445
1447 }
1448 }
1449
1450 return CommandCost();
1451}
1452
1460static bool CheckAircraftOrderDistance(const Aircraft *v_new, const Vehicle *v_order)
1461{
1462 if (v_new->acache.cached_max_range == 0) return true;
1463 if (v_order->GetNumOrders() == 0) return true;
1464
1465 const OrderList &orderlist = *v_order->orders;
1466 auto orders = orderlist.GetOrders();
1467
1468 /* Iterate over all orders to check the distance between all
1469 * 'goto' orders and their respective next order (of any type). */
1470 for (VehicleOrderID cur = 0; cur < orderlist.GetNumOrders(); ++cur) {
1471 switch (orders[cur].GetType()) {
1472 case OT_GOTO_STATION:
1473 case OT_GOTO_DEPOT:
1474 case OT_GOTO_WAYPOINT:
1475 /* If we don't have a next order, we've reached the end and must check the first order instead. */
1476 if (GetOrderDistance(cur, orderlist.GetNext(cur), v_order) > v_new->acache.cached_max_range_sqr) return false;
1477 break;
1478
1479 default: break;
1480 }
1481 }
1482
1483 return true;
1484}
1485
1495{
1496 Vehicle *dst = Vehicle::GetIfValid(veh_dst);
1497 if (dst == nullptr || !dst->IsPrimaryVehicle()) return CMD_ERROR;
1498
1499 CommandCost ret = CheckOwnership(dst->owner);
1500 if (ret.Failed()) return ret;
1501
1502 switch (action) {
1503 case CO_SHARE: {
1504 Vehicle *src = Vehicle::GetIfValid(veh_src);
1505
1506 /* Sanity checks */
1507 if (src == nullptr || !src->IsPrimaryVehicle() || dst->type != src->type || dst == src) return CMD_ERROR;
1508
1509 ret = CheckOwnership(src->owner);
1510 if (ret.Failed()) return ret;
1511
1512 /* Trucks can't share orders with busses (and visa versa) */
1513 if (src->type == VEH_ROAD && RoadVehicle::From(src)->IsBus() != RoadVehicle::From(dst)->IsBus()) {
1514 return CMD_ERROR;
1515 }
1516
1517 /* Is the vehicle already in the shared list? */
1518 if (src->FirstShared() == dst->FirstShared()) return CMD_ERROR;
1519
1520 for (const Order &order : src->Orders()) {
1521 if (!OrderGoesToStation(dst, order)) continue;
1522
1523 /* Allow copying unreachable destinations if they were already unreachable for the source.
1524 * This is basically to allow cloning / autorenewing / autoreplacing vehicles, while the stations
1525 * are temporarily invalid due to reconstruction. */
1526 const Station *st = Station::Get(order.GetDestination().ToStationID());
1527 if (CanVehicleUseStation(src, st) && !CanVehicleUseStation(dst, st)) {
1528 return CommandCost(STR_ERROR_CAN_T_COPY_SHARE_ORDER, GetVehicleCannotUseStationReason(dst, st));
1529 }
1530 }
1531
1532 /* Check for aircraft range limits. */
1533 if (dst->type == VEH_AIRCRAFT && !CheckAircraftOrderDistance(Aircraft::From(dst), src)) {
1534 return CommandCost(STR_ERROR_AIRCRAFT_NOT_ENOUGH_RANGE);
1535 }
1536
1537 if (src->orders == nullptr && !OrderList::CanAllocateItem()) {
1538 return CommandCost(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS);
1539 }
1540
1541 if (flags.Test(DoCommandFlag::Execute)) {
1542 /* If the destination vehicle had a OrderList, destroy it.
1543 * We only reset the order indices, if the new orders are obviously different.
1544 * (We mainly do this to keep the order indices valid and in range.) */
1545 DeleteVehicleOrders(dst, false, dst->GetNumOrders() != src->GetNumOrders());
1546
1547 dst->orders = src->orders;
1548
1549 /* Link this vehicle in the shared-list */
1550 dst->AddToShared(src);
1551
1554
1556 }
1557 break;
1558 }
1559
1560 case CO_COPY: {
1561 Vehicle *src = Vehicle::GetIfValid(veh_src);
1562
1563 /* Sanity checks */
1564 if (src == nullptr || !src->IsPrimaryVehicle() || dst->type != src->type || dst == src) return CMD_ERROR;
1565
1566 ret = CheckOwnership(src->owner);
1567 if (ret.Failed()) return ret;
1568
1569 /* Trucks can't copy all the orders from busses (and visa versa),
1570 * and neither can helicopters and aircraft. */
1571 for (const Order &order : src->Orders()) {
1572 if (!OrderGoesToStation(dst, order)) continue;
1573 Station *st = Station::Get(order.GetDestination().ToStationID());
1574 if (!CanVehicleUseStation(dst, st)) {
1575 return CommandCost(STR_ERROR_CAN_T_COPY_SHARE_ORDER, GetVehicleCannotUseStationReason(dst, st));
1576 }
1577 }
1578
1579 /* Check for aircraft range limits. */
1580 if (dst->type == VEH_AIRCRAFT && !CheckAircraftOrderDistance(Aircraft::From(dst), src)) {
1581 return CommandCost(STR_ERROR_AIRCRAFT_NOT_ENOUGH_RANGE);
1582 }
1583
1584 /* make sure there are orders available */
1586 return CommandCost(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS);
1587 }
1588
1589 if (flags.Test(DoCommandFlag::Execute)) {
1590 /* If the destination vehicle had an order list, destroy the chain but keep the OrderList.
1591 * We only reset the order indices, if the new orders are obviously different.
1592 * (We mainly do this to keep the order indices valid and in range.) */
1593 DeleteVehicleOrders(dst, true, dst->GetNumOrders() != src->GetNumOrders());
1594
1595 std::vector<Order> dst_orders;
1596 for (const Order &order : src->Orders()) {
1597 dst_orders.emplace_back(order);
1598 }
1599
1600 if (dst->orders != nullptr) {
1601 assert(dst->orders->GetNumOrders() == 0);
1602 assert(!dst->orders->IsShared());
1603 delete dst->orders;
1604 }
1605
1607 dst->orders = new OrderList(std::move(dst_orders), dst);
1608
1610
1612 }
1613 break;
1614 }
1615
1616 case CO_UNSHARE: return DecloneOrder(dst, flags);
1617 default: return CMD_ERROR;
1618 }
1619
1620 return CommandCost();
1621}
1622
1632{
1633 if (cargo >= NUM_CARGO && cargo != CARGO_NO_REFIT && cargo != CARGO_AUTO_REFIT) return CMD_ERROR;
1634
1635 const Vehicle *v = Vehicle::GetIfValid(veh);
1636 if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
1637
1639 if (ret.Failed()) return ret;
1640
1641 Order *order = v->GetOrder(order_number);
1642 if (order == nullptr) return CMD_ERROR;
1643
1644 /* Automatic refit cargo is only supported for goto station orders. */
1645 if (cargo == CARGO_AUTO_REFIT && !order->IsType(OT_GOTO_STATION)) return CMD_ERROR;
1646
1647 if (order->GetLoadType() == OrderLoadType::NoLoad) return CMD_ERROR;
1648
1649 if (flags.Test(DoCommandFlag::Execute)) {
1650 order->SetRefit(cargo);
1651
1652 /* Make the depot order an 'always go' order. */
1653 if (cargo != CARGO_NO_REFIT && order->IsType(OT_GOTO_DEPOT)) {
1656 }
1657
1658 for (Vehicle *u = v->FirstShared(); u != nullptr; u = u->NextShared()) {
1659 /* Update any possible open window of the vehicle */
1661
1662 /* If the vehicle already got the current depot set as current order, then update current order as well */
1663 if (u->cur_real_order_index == order_number && u->current_order.GetDepotOrderType().Test(OrderDepotTypeFlag::PartOfOrders)) {
1664 u->current_order.SetRefit(cargo);
1665 }
1666 }
1667 }
1668
1669 return CommandCost();
1670}
1671
1672
1678void CheckOrders(const Vehicle *v)
1679{
1680 /* Does the user wants us to check things? */
1681 if (_settings_client.gui.order_review_system == 0) return;
1682
1683 /* Do nothing for crashed vehicles */
1684 if (v->vehstatus.Test(VehState::Crashed)) return;
1685
1686 /* Do nothing for stopped vehicles if setting is '1' */
1688
1689 /* do nothing we we're not the first vehicle in a share-chain */
1690 if (v->FirstShared() != v) return;
1691
1692 /* Only check every 20 days, so that we don't flood the message log */
1693 if (v->owner == _local_company && v->day_counter % 20 == 0) {
1694 StringID message = INVALID_STRING_ID;
1695
1696 /* Check the order list */
1697 int n_st = 0;
1698
1699 for (const Order &order : v->Orders()) {
1700 /* Dummy order? */
1701 if (order.IsType(OT_DUMMY)) {
1702 message = STR_NEWS_VEHICLE_HAS_VOID_ORDER;
1703 break;
1704 }
1705 /* Does station have a load-bay for this vehicle? */
1706 if (order.IsType(OT_GOTO_STATION)) {
1707 const Station *st = Station::Get(order.GetDestination().ToStationID());
1708
1709 n_st++;
1710 if (!CanVehicleUseStation(v, st)) {
1711 message = STR_NEWS_VEHICLE_HAS_INVALID_ENTRY;
1712 } else if (v->type == VEH_AIRCRAFT &&
1713 (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
1716 message == INVALID_STRING_ID) {
1717 message = STR_NEWS_PLANE_USES_TOO_SHORT_RUNWAY;
1718 }
1719 }
1720 }
1721
1722 /* Check if the last and the first order are the same */
1723 if (v->GetNumOrders() > 1) {
1724 auto orders = v->Orders();
1725
1726 if (orders.front().Equals(orders.back())) {
1727 message = STR_NEWS_VEHICLE_HAS_DUPLICATE_ENTRY;
1728 }
1729 }
1730
1731 /* Do we only have 1 station in our order list? */
1732 if (n_st < 2 && message == INVALID_STRING_ID) message = STR_NEWS_VEHICLE_HAS_TOO_FEW_ORDERS;
1733
1734#ifdef WITH_ASSERT
1735 if (v->orders != nullptr) v->orders->DebugCheckSanity();
1736#endif
1737
1738 /* We don't have a problem */
1739 if (message == INVALID_STRING_ID) return;
1740
1742 }
1743}
1744
1753void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination, bool hangar)
1754{
1755 /* Aircraft have StationIDs for depot orders and never use DepotIDs
1756 * This fact is handled specially below
1757 */
1758
1759 /* Go through all vehicles */
1760 for (Vehicle *v : Vehicle::Iterate()) {
1761 if ((v->type == VEH_AIRCRAFT && v->current_order.IsType(OT_GOTO_DEPOT) && !hangar ? OT_GOTO_STATION : v->current_order.GetType()) == type &&
1762 (!hangar || v->type == VEH_AIRCRAFT) && v->current_order.GetDestination() == destination) {
1763 v->current_order.MakeDummy();
1765 }
1766
1767 if (v->orders == nullptr) continue;
1768
1769 /* Clear the order from the order-list */
1770 for (VehicleOrderID id = 0, next_id = 0; id < v->GetNumOrders(); id = next_id) {
1771 next_id = id + 1;
1772 Order *order = v->orders->GetOrderAt(id);
1773 OrderType ot = order->GetType();
1774 if (ot == OT_GOTO_DEPOT && order->GetDepotActionType().Test(OrderDepotActionFlag::NearestDepot)) continue;
1775 if (ot == OT_GOTO_DEPOT && hangar && v->type != VEH_AIRCRAFT) continue; // Not an aircraft? Can't have a hangar order.
1776 if (ot == OT_IMPLICIT || (v->type == VEH_AIRCRAFT && ot == OT_GOTO_DEPOT && !hangar)) ot = OT_GOTO_STATION;
1777 if (ot == type && order->GetDestination() == destination) {
1778 /* We want to clear implicit orders, but we don't want to make them
1779 * dummy orders. They should just vanish. Also check the actual order
1780 * type as ot is currently OT_GOTO_STATION. */
1781 if (order->IsType(OT_IMPLICIT)) {
1782 DeleteOrder(v, id);
1783 next_id = id;
1784 continue;
1785 }
1786
1787 /* Clear wait time */
1788 v->orders->UpdateTotalDuration(-order->GetWaitTime());
1789 if (order->IsWaitTimetabled()) {
1790 v->orders->UpdateTimetableDuration(-order->GetTimetabledWait());
1791 order->SetWaitTimetabled(false);
1792 }
1793 order->SetWaitTime(0);
1794
1795 /* Clear order, preserving travel time */
1796 bool travel_timetabled = order->IsTravelTimetabled();
1797 order->MakeDummy();
1798 order->SetTravelTimetabled(travel_timetabled);
1799
1800 for (const Vehicle *w = v->FirstShared(); w != nullptr; w = w->NextShared()) {
1801 /* In GUI, simulate by removing the order and adding it back */
1804 }
1805 }
1806 }
1807 }
1808
1809 OrderBackup::RemoveOrder(type, destination, hangar);
1810}
1811
1817{
1818 return std::ranges::any_of(this->Orders(), [](const Order &order) { return order.IsType(OT_GOTO_DEPOT); });
1819}
1820
1830void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist, bool reset_order_indices)
1831{
1833
1834 if (v->IsOrderListShared()) {
1835 /* Remove ourself from the shared order list. */
1836 v->RemoveFromShared();
1837 v->orders = nullptr;
1838 } else if (v->orders != nullptr) {
1839 /* Remove the orders */
1840 v->orders->FreeChain(keep_orderlist);
1841 if (!keep_orderlist) v->orders = nullptr;
1842 }
1843
1844 /* Unbunching data is no longer valid. */
1846
1847 if (reset_order_indices) {
1849 if (v->current_order.IsType(OT_LOADING)) {
1851 }
1852 }
1853}
1854
1862uint16_t GetServiceIntervalClamped(int interval, bool ispercent)
1863{
1864 /* Service intervals are in percents. */
1865 if (ispercent) return Clamp(interval, MIN_SERVINT_PERCENT, MAX_SERVINT_PERCENT);
1866
1867 /* Service intervals are in minutes. */
1868 if (TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU)) return Clamp(interval, MIN_SERVINT_MINUTES, MAX_SERVINT_MINUTES);
1869
1870 /* Service intervals are in days. */
1871 return Clamp(interval, MIN_SERVINT_DAYS, MAX_SERVINT_DAYS);
1872}
1873
1882static bool CheckForValidOrders(const Vehicle *v)
1883{
1884 return std::ranges::any_of(v->Orders(), [](const Order &order) { return order.IsGotoOrder(); });
1885}
1886
1890static bool OrderConditionCompare(OrderConditionComparator occ, int variable, int value)
1891{
1892 switch (occ) {
1893 case OrderConditionComparator::Equal: return variable == value;
1894 case OrderConditionComparator::NotEqual: return variable != value;
1895 case OrderConditionComparator::LessThan: return variable < value;
1896 case OrderConditionComparator::LessThanOrEqual: return variable <= value;
1897 case OrderConditionComparator::MoreThan: return variable > value;
1898 case OrderConditionComparator::MoreThanOrEqual: return variable >= value;
1899 case OrderConditionComparator::IsTrue: return variable != 0;
1900 case OrderConditionComparator::IsFalse: return variable == 0;
1901 default: NOT_REACHED();
1902 }
1903}
1904
1905static bool OrderConditionCompare(OrderConditionComparator occ, ConvertibleThroughBase auto variable, int value)
1906{
1907 return OrderConditionCompare(occ, variable.base(), value);
1908}
1909
1917{
1918 if (order->GetType() != OT_CONDITIONAL) return INVALID_VEH_ORDER_ID;
1919
1920 bool skip_order = false;
1922 uint16_t value = order->GetConditionValue();
1923
1924 switch (order->GetConditionVariable()) {
1925 case OrderConditionVariable::LoadPercentage: skip_order = OrderConditionCompare(occ, CalcPercentVehicleFilled(v, nullptr), value); break;
1926 case OrderConditionVariable::Reliability: skip_order = OrderConditionCompare(occ, ToPercent16(v->reliability), value); break;
1928 case OrderConditionVariable::MaxSpeed: skip_order = OrderConditionCompare(occ, v->GetDisplayMaxSpeed() * 10 / 16, value); break;
1930 case OrderConditionVariable::RequiresService: skip_order = OrderConditionCompare(occ, v->NeedsServicing(), value); break;
1931 case OrderConditionVariable::Unconditionally: skip_order = true; break;
1933 default: NOT_REACHED();
1934 }
1935
1936 return skip_order ? order->GetConditionSkipToOrder() : (VehicleOrderID)INVALID_VEH_ORDER_ID;
1937}
1938
1946bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool pbs_look_ahead)
1947{
1948 if (conditional_depth > v->GetNumOrders()) {
1949 v->current_order.Free();
1950 v->SetDestTile(TileIndex{});
1951 return false;
1952 }
1953
1954 switch (order->GetType()) {
1955 case OT_GOTO_STATION:
1956 v->SetDestTile(v->GetOrderStationLocation(order->GetDestination().ToStationID()));
1957 return true;
1958
1959 case OT_GOTO_DEPOT:
1961 assert(!pbs_look_ahead);
1962 UpdateVehicleTimetable(v, true);
1964 break;
1965 }
1966
1968 /* If the vehicle can't find its destination, delay its next search.
1969 * In case many vehicles are in this state, use the vehicle index to spread out pathfinder calls. */
1970 if (v->dest_tile == 0 && TimerGameEconomy::date_fract != (v->index % Ticks::DAY_TICKS)) break;
1971
1972 /* We need to search for the nearest depot (hangar). */
1973 ClosestDepot closest_depot = v->FindClosestDepot();
1974
1975 if (closest_depot.found) {
1976 /* PBS reservations cannot reverse */
1977 if (pbs_look_ahead && closest_depot.reverse) return false;
1978
1979 v->SetDestTile(closest_depot.location);
1980 v->current_order.SetDestination(closest_depot.destination);
1981
1982 /* If there is no depot in front, reverse automatically (trains only) */
1983 if (v->type == VEH_TRAIN && closest_depot.reverse) Command<CMD_REVERSE_TRAIN_DIRECTION>::Do(DoCommandFlag::Execute, v->index, false);
1984
1985 if (v->type == VEH_AIRCRAFT) {
1986 Aircraft *a = Aircraft::From(v);
1987 if (a->state == FLYING && a->targetairport != closest_depot.destination) {
1988 /* The aircraft is now heading for a different hangar than the next in the orders */
1990 }
1991 }
1992 return true;
1993 }
1994
1995 /* If there is no depot, we cannot help PBS either. */
1996 if (pbs_look_ahead) return false;
1997
1998 UpdateVehicleTimetable(v, true);
2000 } else {
2001 if (v->type != VEH_AIRCRAFT) {
2002 v->SetDestTile(Depot::Get(order->GetDestination().ToStationID())->xy);
2003 } else {
2004 Aircraft *a = Aircraft::From(v);
2005 DestinationID destination = a->current_order.GetDestination();
2006 if (a->targetairport != destination) {
2007 /* The aircraft is now heading for a different hangar than the next in the orders */
2008 a->SetDestTile(a->GetOrderStationLocation(destination.ToStationID()));
2009 }
2010 }
2011 return true;
2012 }
2013 break;
2014
2015 case OT_GOTO_WAYPOINT:
2016 v->SetDestTile(Waypoint::Get(order->GetDestination().ToStationID())->xy);
2017 return true;
2018
2019 case OT_CONDITIONAL: {
2020 assert(!pbs_look_ahead);
2021 VehicleOrderID next_order = ProcessConditionalOrder(order, v);
2022 if (next_order != INVALID_VEH_ORDER_ID) {
2023 /* Jump to next_order. cur_implicit_order_index becomes exactly that order,
2024 * cur_real_order_index might come after next_order. */
2025 UpdateVehicleTimetable(v, false);
2029
2030 /* Disable creation of implicit orders.
2031 * When inserting them we do not know that we would have to make the conditional orders point to them. */
2032 if (v->IsGroundVehicle()) {
2033 uint16_t &gv_flags = v->GetGroundVehicleFlags();
2035 }
2036 } else {
2037 UpdateVehicleTimetable(v, true);
2039 }
2040 break;
2041 }
2042
2043 default:
2044 v->SetDestTile(TileIndex{});
2045 return false;
2046 }
2047
2048 assert(v->cur_implicit_order_index < v->GetNumOrders());
2049 assert(v->cur_real_order_index < v->GetNumOrders());
2050
2051 /* Get the current order */
2052 order = v->GetOrder(v->cur_real_order_index);
2053 if (order != nullptr && order->IsType(OT_IMPLICIT)) {
2054 assert(v->GetNumManualOrders() == 0);
2055 order = nullptr;
2056 }
2057
2058 if (order == nullptr) {
2059 v->current_order.Free();
2060 v->SetDestTile(TileIndex{});
2061 return false;
2062 }
2063
2064 v->current_order = *order;
2065 return UpdateOrderDest(v, order, conditional_depth + 1, pbs_look_ahead);
2066}
2067
2076{
2077 switch (v->current_order.GetType()) {
2078 case OT_GOTO_DEPOT:
2079 /* Let a depot order in the orderlist interrupt. */
2081 break;
2082
2083 case OT_LOADING:
2084 return false;
2085
2086 case OT_LEAVESTATION:
2087 if (v->type != VEH_AIRCRAFT) return false;
2088 break;
2089
2090 default: break;
2091 }
2092
2100 bool may_reverse = v->current_order.IsType(OT_NOTHING);
2101
2102 /* Check if we've reached a 'via' destination. */
2103 if (((v->current_order.IsType(OT_GOTO_STATION) && v->current_order.GetNonStopType().Test(OrderNonStopFlag::NoDestination)) || v->current_order.IsType(OT_GOTO_WAYPOINT)) &&
2104 IsTileType(v->tile, MP_STATION) &&
2107 /* We set the last visited station here because we do not want
2108 * the train to stop at this 'via' station if the next order
2109 * is a no-non-stop order; in that case not setting the last
2110 * visited station will cause the vehicle to still stop. */
2111 v->last_station_visited = v->current_order.GetDestination().ToStationID();
2112 UpdateVehicleTimetable(v, true);
2114 }
2115
2116 /* Get the current order */
2119
2120 const Order *order = v->GetOrder(v->cur_real_order_index);
2121 if (order != nullptr && order->IsType(OT_IMPLICIT)) {
2122 assert(v->GetNumManualOrders() == 0);
2123 order = nullptr;
2124 }
2125
2126 /* If no order, do nothing. */
2127 if (order == nullptr || (v->type == VEH_AIRCRAFT && !CheckForValidOrders(v))) {
2128 if (v->type == VEH_AIRCRAFT) {
2129 /* Aircraft do something vastly different here, so handle separately */
2130 HandleMissingAircraftOrders(Aircraft::From(v));
2131 return false;
2132 }
2133
2134 v->current_order.Free();
2135 v->SetDestTile(TileIndex{});
2136 return false;
2137 }
2138
2139 /* If it is unchanged, keep it. */
2140 if (order->Equals(v->current_order) && (v->type == VEH_AIRCRAFT || v->dest_tile != 0) &&
2141 (v->type != VEH_SHIP || !order->IsType(OT_GOTO_STATION) || Station::Get(order->GetDestination().ToStationID())->ship_station.tile != INVALID_TILE)) {
2142 return false;
2143 }
2144
2145 /* Otherwise set it, and determine the destination tile. */
2146 v->current_order = *order;
2147
2149 switch (v->type) {
2150 default:
2151 NOT_REACHED();
2152
2153 case VEH_ROAD:
2154 case VEH_TRAIN:
2155 break;
2156
2157 case VEH_AIRCRAFT:
2158 case VEH_SHIP:
2160 break;
2161 }
2162
2163 return UpdateOrderDest(v, order) && may_reverse;
2164}
2165
2173bool Order::ShouldStopAtStation(const Vehicle *v, StationID station) const
2174{
2175 bool is_dest_station = this->IsType(OT_GOTO_STATION) && this->dest == station;
2176
2177 return (!this->IsType(OT_GOTO_DEPOT) || this->GetDepotOrderType().Test(OrderDepotTypeFlag::PartOfOrders)) &&
2178 v->last_station_visited != station && // Do stop only when we've not just been there
2179 /* Finally do stop when there is no non-stop flag set for this type of station. */
2181}
2182
2183bool Order::CanLoadOrUnload() const
2184{
2185 return (this->IsType(OT_GOTO_STATION) || this->IsType(OT_IMPLICIT)) &&
2187 (this->GetLoadType() != OrderLoadType::NoLoad ||
2189}
2190
2197bool Order::CanLeaveWithCargo(bool has_cargo) const
2198{
2199 return this->GetLoadType() != OrderLoadType::NoLoad || (has_cargo &&
2202}
Base for aircraft.
void AircraftNextAirportPos_and_Order(Aircraft *v)
set the right pos when heading to other airports after takeoff
@ FLYING
Vehicle is flying in the air.
Definition airport.h:77
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:21
static const CargoType CARGO_AUTO_REFIT
Automatically choose cargo type when doing auto refitting.
Definition cargo_type.h:76
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
Cheats _cheats
All the cheats.
Definition cheat.cpp:16
Types related to cheating.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Reset()
Reset all bits.
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 Failed() const
Did this command fail?
uint16_t reliability
Current reliability of the engine.
Definition engine_base.h:49
Enum-as-bit-set wrapper.
static constexpr TimerGameTick::Ticks DAY_TICKS
1 day is 74 ticks; TimerGameCalendar::date_fract used to be uint16_t and incremented by 885.
static constexpr int DAYS_IN_LEAP_YEAR
sometimes, you need one day more...
static bool UsingWallclockUnits(bool newgame=false)
Check if we are using wallclock units.
static DateFract date_fract
Fractional part of the day.
int32_t Ticks
The type to store ticks in.
static constexpr Year DateToYear(Date date)
Calculate the year of a given date.
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Execute
execute the given command
Definition of stuff that is very close to a company, like the company struct itself.
CommandCost CheckOwnership(Owner owner, TileIndex tile)
Check whether the current owner owns something.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
Functions related to companies.
static constexpr Owner OWNER_NONE
The tile has no ownership.
A type is considered 'convertible through base()' when it has a 'base()' function that returns someth...
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
Base for all depots (except hangars)
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23)
Definition enum_type.hpp:17
@ GVF_SUPPRESS_IMPLICIT_ORDERS
Disable insertion and removal of automatic orders until the vehicle completes the real order.
uint DistanceSquare(TileIndex t0, TileIndex t1)
Gets the 'Square' distance between the two given tiles.
Definition map.cpp:175
uint DistanceManhattan(TileIndex t0, TileIndex t1)
Gets the Manhattan distance between the two given tiles.
Definition map.cpp:158
constexpr uint ToPercent16(uint i)
Converts a "fract" value 0..65535 to "percent" value 0..100.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
Functions related to news.
void AddVehicleAdviceNewsItem(AdviceType advice_type, EncodedString &&headline, VehicleID vehicle)
Adds a vehicle-advice news item.
Definition news_func.h:40
void DeleteVehicleNews(VehicleID vid, AdviceType advice_type=AdviceType::Invalid)
Delete news with a given advice type about a vehicle.
@ Order
Something wrong with the order, e.g. invalid or duplicate entries, too few entries.
Functions related to order backups.
CommandCost CmdSkipToOrder(DoCommandFlags flags, VehicleID veh_id, VehicleOrderID sel_ord)
Goto order of order-list.
uint16_t GetServiceIntervalClamped(int interval, bool ispercent)
Clamp the service interval to the correct min/max.
static bool CheckForValidOrders(const Vehicle *v)
Check if a vehicle has any valid orders.
bool ProcessOrders(Vehicle *v)
Handle the orders of a vehicle and determine the next place to go to if needed.
static bool OrderConditionCompare(OrderConditionComparator occ, int variable, int value)
Compare the variable and value based on the given comparator.
static void CancelLoadingDueToDeletedOrder(Vehicle *v)
Cancel the current loading order of the vehicle as the order was deleted.
bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool pbs_look_ahead)
Update the vehicle's destination tile from an order.
CommandCost CmdOrderRefit(DoCommandFlags flags, VehicleID veh, VehicleOrderID order_number, CargoType cargo)
Add/remove refit orders from an order.
void InsertOrder(Vehicle *v, Order &&new_o, VehicleOrderID sel_ord)
Insert a new order but skip the validation.
void InvalidateVehicleOrder(const Vehicle *v, int data)
Updates the widgets of a vehicle which contains the order-data.
void CheckOrders(const Vehicle *v)
Check the orders of a vehicle, to see if there are invalid orders and stuff.
CommandCost CmdInsertOrder(DoCommandFlags flags, VehicleID veh, VehicleOrderID sel_ord, const Order &new_order)
Add an order to the orderlist of a vehicle.
CommandCost CmdModifyOrder(DoCommandFlags flags, VehicleID veh, VehicleOrderID sel_ord, ModifyOrderFlags mof, uint16_t data)
Modify an order in the orderlist of a vehicle.
CommandCost CmdCloneOrder(DoCommandFlags flags, CloneOptions action, VehicleID veh_dst, VehicleID veh_src)
Clone/share/copy an order-list of another vehicle.
static CommandCost DecloneOrder(Vehicle *dst, DoCommandFlags flags)
Declone an order-list.
void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist, bool reset_order_indices)
Delete all orders from a vehicle.
CommandCost CmdDeleteOrder(DoCommandFlags flags, VehicleID veh_id, VehicleOrderID sel_ord)
Delete an order from the orderlist of a vehicle.
uint GetOrderDistance(VehicleOrderID prev, VehicleOrderID cur, const Vehicle *v, int conditional_depth)
Get the distance between two orders of a vehicle.
static void DeleteOrderWarnings(const Vehicle *v)
Delete all news items regarding defective orders about a vehicle This could kill still valid warnings...
void DeleteOrder(Vehicle *v, VehicleOrderID sel_ord)
Delete an order but skip the parameter validation.
VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v)
Process a conditional order and determine the next order.
CommandCost CmdMoveOrder(DoCommandFlags flags, VehicleID veh, VehicleOrderID moving_order, VehicleOrderID target_order)
Move an order inside the orderlist.
void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination, bool hangar)
Removes an order from all vehicles.
static bool CheckAircraftOrderDistance(const Aircraft *v_new, const Vehicle *v_order)
Check if an aircraft has enough range for an order list.
static bool OrderGoesToStation(const Vehicle *v, const Order &o)
Checks whether the order goes to a station or not, i.e.
Command definitions related to orders.
OrderUnloadType
Unloading order types.
Definition order_type.h:67
@ Transfer
Transfer all cargo onto the platform.
@ UnloadIfPossible
Unload all cargo that the station accepts.
@ NoUnload
Totally no unloading will be done.
@ Unload
Force unloading all cargo onto the platform, possibly not getting paid.
OrderConditionVariable
Variables (of a vehicle) to 'cause' skipping on.
Definition order_type.h:128
@ Unconditionally
Always skip.
@ MaxSpeed
Skip based on the maximum speed.
@ Reliability
Skip based on the reliability.
@ MaxReliability
Skip based on the maximum reliability.
@ RequiresService
Skip when the vehicle requires service.
@ LoadPercentage
Skip based on the amount of load.
@ Age
Skip based on the age.
@ RemainingLifetime
Skip based on the remaining lifetime.
OrderStopLocation
Where to stop the trains.
Definition order_type.h:97
@ NearEnd
Stop at the near end of the platform.
@ FarEnd
Stop at the far end of the platform.
@ Middle
Stop at the middle of the platform.
ModifyOrderFlags
Enumeration for the data to set in CmdModifyOrder.
Definition order_type.h:159
@ MOF_COND_VARIABLE
A conditional variable changes.
Definition order_type.h:165
@ MOF_LOAD
Passes an OrderLoadType.
Definition order_type.h:163
@ MOF_UNLOAD
Passes an OrderUnloadType.
Definition order_type.h:162
@ MOF_STOP_LOCATION
Passes an OrderStopLocation.
Definition order_type.h:161
@ MOF_COND_DESTINATION
Change the destination of a conditional order.
Definition order_type.h:168
@ MOF_COND_COMPARATOR
A comparator changes.
Definition order_type.h:166
@ MOF_COND_VALUE
The value to set the condition to.
Definition order_type.h:167
@ MOF_DEPOT_ACTION
Selects the OrderDepotAction.
Definition order_type.h:164
@ MOF_NON_STOP
Passes an OrderNonStopFlags.
Definition order_type.h:160
OrderDepotAction
Depot action to switch to when doing a MOF_DEPOT_ACTION.
Definition order_type.h:175
@ Stop
Go to the depot and stop there.
@ AlwaysGo
Always go to the depot.
@ Service
Service only if needed.
@ Unbunch
Go to the depot and unbunch.
@ NoDestination
The vehicle will stop at any station it passes except the destination, aka via.
@ NoIntermediate
The vehicle will not stop at any stations it passes except the destination, aka non-stop.
uint8_t VehicleOrderID
The index of an order within its current vehicle (not pool related)
Definition order_type.h:18
OrderLoadType
Loading order types.
Definition order_type.h:77
@ FullLoad
Full load all cargoes of the consist.
@ NoLoad
Do not load anything.
@ FullLoadAny
Full load a single cargo of the consist.
@ LoadIfPossible
Load as long as there is cargo that fits in the train.
static const VehicleOrderID MAX_VEH_ORDER_ID
Last valid VehicleOrderID.
Definition order_type.h:41
@ Halt
Service the vehicle and then halt it.
@ NearestDepot
Send the vehicle to the nearest depot.
@ Unbunch
Service the vehicle and then unbunch it.
@ PartOfOrders
This depot order is because of a regular order.
@ Service
This depot order is because of the servicing limit.
static const VehicleOrderID INVALID_VEH_ORDER_ID
Invalid vehicle order index (sentinel)
Definition order_type.h:39
OrderConditionComparator
Comparator for the skip reasoning.
Definition order_type.h:143
@ IsTrue
Skip if the variable is true.
@ NotEqual
Skip if both values are not equal.
@ IsFalse
Skip if the variable is false.
@ LessThanOrEqual
Skip if the value is less or equal to the limit.
@ MoreThan
Skip if the value is more than the limit.
@ MoreThanOrEqual
Skip if the value is more or equal to the limit.
@ LessThan
Skip if the value is less than the limit.
@ Equal
Skip if both values are equal.
CloneOptions
Clone actions.
Definition order_type.h:194
OrderType
Order types.
Definition order_type.h:50
Some methods of Pool are placed here in order to reduce compilation time and binary size.
#define INSTANTIATE_POOL_METHODS(name)
Force instantiation of pool methods so we don't get linker errors.
static bool IsRailDepotTile(Tile t)
Is this tile rail tile and a rail depot?
Definition rail_map.h:105
static bool IsRoadDepotTile(Tile t)
Return whether a tile is a road depot tile.
Definition road_map.h:100
Road vehicle states.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
Base classes/functions for stations.
StationID GetStationIndex(Tile t)
Get StationID from a tile.
Definition station_map.h:28
@ Dock
Station with a dock.
@ TruckStop
Station with truck stops.
@ Train
Station with train station.
@ BusStop
Station with bus stops.
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.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames)
uint32_t cached_max_range_sqr
Cached squared maximum range.
Definition aircraft.h:65
uint16_t cached_max_range
Cached maximum range.
Definition aircraft.h:66
uint8_t subtype
Type of aircraft.
Aircraft, helicopters, rotors and their shadows belong to this class.
Definition aircraft.h:72
uint8_t state
State of the airport.
Definition aircraft.h:77
TileIndex GetOrderStationLocation(StationID station) override
Determine the location for the station where the vehicle goes to next.
StationID targetairport
Airport to go to next.
Definition aircraft.h:76
@ ShortStrip
This airport has a short landing strip, dangerous for fast aircraft.
Flags flags
Flags for this airport type.
Definition airport.h:194
bool HasHangar() const
Check if this airport has at least one hangar.
const AirportFTAClass * GetFTA() const
Get the finite-state machine for this airport or the finite-state machine for the dummy airport in ca...
TimerGameTick::Ticks current_order_time
How many ticks have passed since this order started.
VehicleOrderID cur_real_order_index
The index to the current real (non-implicit) order.
VehicleOrderID cur_implicit_order_index
The index to the current implicit order.
void ResetDepotUnbunching()
Resets all the data used for depot unbunching.
Base class for all station-ish types.
TileIndex xy
Base tile of the station.
StationFacilities facilities
The facilities that this station has.
Owner owner
The owner of this station.
VehicleType type
Type of vehicle.
bool value
tells if the bool cheat is active or not
Definition cheat_type.h:18
Cheat no_jetcrash
no jet will crash on small airports anymore
Definition cheat_type.h:31
GUISettings gui
settings related to the GUI
Structure to return information about the closest depot location, and whether it could be found.
DestinationID destination
The DestinationID as used for orders.
uint8_t order_review_system
perform order reviews on vehicles
static void RemoveOrder(OrderType type, DestinationID destination, bool hangar)
Removes an order from all vehicles.
Shared order list linking together the linked list of orders and the list of vehicles sharing this or...
Definition order_base.h:274
void DeleteOrderAt(VehicleOrderID index)
Remove an order from the order list and delete it.
std::vector< Order > orders
Orders of the order list.
Definition order_base.h:285
bool IsCompleteTimetable() const
Checks whether all orders of the list have a filled timetable.
void InsertOrderAt(Order &&order, VehicleOrderID index)
Insert a new order into the order chain.
void RemoveVehicle(Vehicle *v)
Removes the vehicle from the shared order list.
uint num_vehicles
NOSAVE: Number of vehicles that share this order list.
Definition order_base.h:283
void Initialize(Vehicle *v)
Recomputes everything.
TimerGameTick::Ticks timetable_duration
NOSAVE: Total timetabled duration of the order list.
Definition order_base.h:288
Vehicle * first_shared
NOSAVE: pointer to the first vehicle in the shared order chain.
Definition order_base.h:284
VehicleOrderID GetFirstOrder() const
Get the first order of the order chain.
Definition order_base.h:328
VehicleOrderID num_manual_orders
NOSAVE: How many manually added orders are there in the list.
Definition order_base.h:282
void MoveOrder(VehicleOrderID from, VehicleOrderID to)
Move an order to another position within the order list.
void GetNextStoppingStation(std::vector< StationID > &next_station, const Vehicle *v, VehicleOrderID first=INVALID_VEH_ORDER_ID, uint hops=0) const
Recursively determine the next deterministic station to stop at.
VehicleOrderID GetNext(VehicleOrderID cur) const
Get the order after the given one or the first one, if the given one is the last one.
Definition order_base.h:362
VehicleOrderID GetNumOrders() const
Get number of orders in the order list.
Definition order_base.h:372
void RecalculateTimetableDuration()
Recomputes Timetable duration.
void FreeChain(bool keep_orderlist=false)
Free a complete order chain.
VehicleOrderID GetNextDecisionNode(VehicleOrderID next, uint hops) const
Get the next order which will make the given vehicle stop at a station or refit at a depot or evaluat...
bool IsShared() const
Is this a shared order list?
Definition order_base.h:391
TimerGameTick::Ticks total_duration
NOSAVE: Total (timetabled or not) duration of the order list.
Definition order_base.h:289
TileIndex GetLocation(const Vehicle *v, bool airport=false) const
Returns a tile somewhat representing the order destination (not suitable for pathfinding).
OrderDepotTypeFlags GetDepotOrderType() const
What caused us going to the depot?
Definition order_base.h:150
uint16_t GetTimetabledTravel() const
Get the time in ticks a vehicle should take to reach the destination or 0 if it's not timetabled.
Definition order_base.h:196
OrderConditionVariable GetConditionVariable() const
What variable do we have to compare?
Definition order_base.h:154
bool Equals(const Order &other) const
Does this order have the same type, flags and destination?
uint16_t MapOldOrder() const
Pack this order into a 16 bits integer as close to the TTD representation as possible.
uint16_t GetMaxSpeed() const
Get the maxmimum speed in km-ish/h a vehicle is allowed to reach on the way to the destination.
Definition order_base.h:207
uint16_t max_speed
How fast the vehicle may go on the way to the destination.
Definition order_base.h:55
void SetTravelTimetabled(bool timetabled)
Set if the travel time is explicitly timetabled (unless the order is conditional).
Definition order_base.h:212
DestinationID GetDestination() const
Gets the destination of this order.
Definition order_base.h:99
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:66
void SetNonStopType(OrderNonStopFlags non_stop_type)
Set whether we must stop at stations or not.
Definition order_base.h:167
VehicleOrderID GetConditionSkipToOrder() const
Get the order to skip to.
Definition order_base.h:158
OrderStopLocation GetStopLocation() const
Where must we stop at the platform?
Definition order_base.h:148
uint16_t GetWaitTime() const
Get the time in ticks a vehicle will probably wait at the destination (timetabled or not).
Definition order_base.h:198
CargoType GetRefitCargo() const
Get the cargo to to refit to.
Definition order_base.h:127
OrderType GetType() const
Get the type of order of this order.
Definition order_base.h:72
bool IsFullLoadOrder() const
Is this order a OrderLoadType::FullLoad or OrderLoadType::FullLoadAny?
Definition order_base.h:135
void MakeGoToStation(StationID destination)
Makes this order a Go To Station order.
Definition order_cmd.cpp:58
uint8_t type
The type of order + non-stop flags.
Definition order_base.h:47
uint16_t wait_time
How long in ticks to wait at the destination.
Definition order_base.h:53
void SetRefit(CargoType cargo)
Make this depot/station order also a refit order.
void SetDepotOrderType(OrderDepotTypeFlags depot_order_type)
Set the cause to go to the depot.
Definition order_base.h:171
void SetWaitTime(uint16_t time)
Set the time in ticks to wait at the destination.
Definition order_base.h:218
void SetStopLocation(OrderStopLocation stop_location)
Set where we must stop at the platform.
Definition order_base.h:169
void MakeDummy()
Makes this order a Dummy order.
void MakeGoToWaypoint(StationID destination)
Makes this order a Go To Waypoint order.
Definition order_cmd.cpp:87
void SetConditionVariable(OrderConditionVariable condition_variable)
Set variable we have to compare.
Definition order_base.h:175
OrderUnloadType GetUnloadType() const
How must the consist be unloaded?
Definition order_base.h:144
uint8_t flags
Load/unload types, depot order/action types.
Definition order_base.h:48
void MakeGoToDepot(DestinationID destination, OrderDepotTypeFlags order, OrderNonStopFlags non_stop_type=OrderNonStopFlag::NoIntermediate, OrderDepotActionFlags action={}, CargoType cargo=CARGO_NO_REFIT)
Makes this order a Go To Depot order.
Definition order_cmd.cpp:73
bool IsWaitTimetabled() const
Does this order have an explicit wait time set?
Definition order_base.h:189
DestinationID dest
The destination of the order.
Definition order_base.h:49
void SetDestination(DestinationID destination)
Sets the destination of this order.
Definition order_base.h:106
void SetWaitTimetabled(bool timetabled)
Set if the wait time is explicitly timetabled (unless the order is conditional).
Definition order_base.h:210
bool IsTravelTimetabled() const
Does this order have an explicit travel time set?
Definition order_base.h:191
void SetConditionComparator(OrderConditionComparator condition_comparator)
Set the comparator to use.
Definition order_base.h:177
void MakeConditional(VehicleOrderID order)
Makes this order an conditional order.
void SetDepotActionType(OrderDepotActionFlags depot_service_type)
Set what we are going to do in the depot.
Definition order_base.h:173
void SetConditionSkipToOrder(VehicleOrderID order_id)
Get the order to skip to.
Definition order_base.h:179
OrderLoadType GetLoadType() const
How must the consist be loaded?
Definition order_base.h:142
OrderDepotActionFlags GetDepotActionType() const
What are we going to do when in the depot.
Definition order_base.h:152
void MakeLeaveStation()
Makes this order a Leave Station order.
bool CanLeaveWithCargo(bool has_cargo) const
A vehicle can leave the current station with cargo if:
void SetConditionValue(uint16_t value)
Set the value to base the skip on.
Definition order_base.h:181
void SetUnloadType(OrderUnloadType unload_type)
Set how the consist must be unloaded.
Definition order_base.h:165
void Free()
'Free' the order
Definition order_cmd.cpp:47
uint16_t GetTimetabledWait() const
Get the time in ticks a vehicle should wait at the destination or 0 if it's not timetabled.
Definition order_base.h:194
bool ShouldStopAtStation(const Vehicle *v, StationID station) const
Check whether the given vehicle should stop at the given station based on this order and the non-stop...
CargoType refit_cargo
Refit CargoType.
Definition order_base.h:51
uint16_t GetTravelTime() const
Get the time in ticks a vehicle will probably take to reach the destination (timetabled or not).
Definition order_base.h:200
void MakeImplicit(StationID destination)
Makes this order an implicit order.
void SetLoadType(OrderLoadType load_type)
Set how the consist must be loaded.
Definition order_base.h:163
void AssignOrder(const Order &other)
Assign data to an order (from another order) This function makes sure that the index is maintained co...
OrderConditionComparator GetConditionComparator() const
What is the comparator to use?
Definition order_base.h:156
OrderNonStopFlags GetNonStopType() const
At which stations must we stop?
Definition order_base.h:146
bool IsRefit() const
Is this order a refit order.
Definition order_base.h:113
void MakeLoading(bool ordered)
Makes this order a Loading order.
Definition order_cmd.cpp:98
uint16_t GetConditionValue() const
Get the value to base the skip on.
Definition order_base.h:160
uint16_t travel_time
How long in ticks the journey to this destination should take.
Definition order_base.h:54
TileIndex tile
The base tile of the area.
Templated helper to make a PoolID a single POD value.
Definition pool_type.hpp:43
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
static Titem * Get(auto index)
Returns Titem with given index.
Tindex index
Index of this pool item.
static bool CanAllocateItem(size_t n=1)
Helper functions so we can use PoolItem::Function() instead of _poolitem_pool.Function()
static Titem * GetIfValid(auto index)
Returns Titem with given index.
Base class for all pools.
static Station * Get(auto index)
Gets station with given index.
static Station * GetIfValid(auto index)
Returns station if the index is a valid index for this station type.
static T * From(Vehicle *v)
Converts a Vehicle to SpecializedVehicle with type checking.
Station data structure.
TileArea ship_station
Tile area the ship 'station' part covers.
Airport airport
Tile area the airport covers.
Vehicle data structure.
EngineID engine_type
The type of engine used for this vehicle.
uint16_t & GetGroundVehicleFlags()
Access the ground vehicle flags of the vehicle.
Definition vehicle.cpp:3173
bool IsOrderListShared() const
Check if we share our orders with another vehicle.
const Engine * GetEngine() const
Retrieves the engine of the vehicle.
Definition vehicle.cpp:718
void IncrementRealOrderIndex()
Advanced cur_real_order_index to the next real order, keeps care of the wrap-around and invalidates t...
Order * GetOrder(int index) const
Returns order 'index' of a vehicle or nullptr when it doesn't exists.
bool HasDepotOrder() const
Checks if a vehicle has a depot in its order list.
void LeaveStation()
Perform all actions when leaving a station.
Definition vehicle.cpp:2327
void AddToShared(Vehicle *shared_chain)
Adds this vehicle to a shared vehicle chain.
Definition vehicle.cpp:2956
bool HasUnbunchingOrder() const
Check if the current vehicle has an unbunching order.
Definition vehicle.cpp:2462
VehicleOrderID GetNumOrders() const
Get the number of orders this vehicle has.
uint8_t day_counter
Increased by one for each day.
virtual int GetDisplayMaxSpeed() const
Gets the maximum speed in km-ish/h that can be sent into string parameters for string processing.
void IncrementImplicitOrderIndex()
Increments cur_implicit_order_index, keeps care of the wrap-around and invalidates the GUI.
bool IsGroundVehicle() const
Check if the vehicle is a ground vehicle.
VehStates vehstatus
Status.
VehicleOrderID GetNumManualOrders() const
Get the number of manually added orders this vehicle has.
virtual TileIndex GetOrderStationLocation(StationID station)
Determine the location for the station where the vehicle goes to next.
Order current_order
The current order (+ status, like: loading)
OrderList * orders
Pointer to the order list for this vehicle.
virtual ClosestDepot FindClosestDepot()
Find the closest depot for this vehicle and tell us the location, DestinationID and whether we should...
Vehicle * NextShared() const
Get the next vehicle of the shared vehicle chain.
bool HasFullLoadOrder() const
Check if the current vehicle has a full load order.
Definition vehicle.cpp:2442
virtual bool IsPrimaryVehicle() const
Whether this is the primary vehicle in the chain.
TimerGameCalendar::Date age
Age in calendar days.
TimerGameCalendar::Date max_age
Maximum age.
uint16_t reliability
Reliability.
Vehicle * PreviousShared() const
Get the previous vehicle of the shared vehicle chain.
Vehicle * FirstShared() const
Get the first vehicle of this vehicle chain.
void RemoveFromShared()
Removes the vehicle from the shared order list.
Definition vehicle.cpp:2979
TileIndex tile
Current tile index.
TileIndex dest_tile
Heading for this tile.
bool NeedsServicing() const
Check if the vehicle needs to go to a depot in near future (if a opportunity presents itself) for ser...
Definition vehicle.cpp:201
bool HasConditionalOrder() const
Check if the current vehicle has a conditional order.
Definition vehicle.cpp:2453
StationID last_station_visited
The last station we stopped at.
Owner owner
Which company owns the vehicle?
void DeleteUnreachedImplicitOrders()
Delete all implicit orders which were not reached.
Definition vehicle.cpp:2145
void UpdateRealOrderIndex()
Skip implicit orders until cur_real_order_index is a non-implicit order.
Representation of a waypoint.
static bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition tile_map.h:178
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
@ MP_STATION
A tile of a station.
Definition tile_type.h:53
Functions related to time tabling.
void UpdateVehicleTimetable(Vehicle *v, bool travelling)
Update the timetable for the vehicle.
Command definitions related to trains.
StringID GetVehicleCannotUseStationReason(const Vehicle *v, const Station *st)
Get reason string why this station can't be used by the given vehicle.
Definition vehicle.cpp:3092
uint8_t CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
Calculates how full a vehicle is.
Definition vehicle.cpp:1461
bool CanVehicleUseStation(EngineID engine_type, const Station *st)
Can this station be used by the given engine type?
Definition vehicle.cpp:3046
@ Crashed
Vehicle is crashed.
@ Stopped
Vehicle is stopped by the player.
Functions related to vehicles.
@ VIWD_MODIFY_ORDERS
Other order modifications.
Definition vehicle_gui.h:36
@ VIWD_REMOVE_ALL_ORDERS
Removed / replaced all orders (after deleting / sharing).
Definition vehicle_gui.h:35
WindowClass GetWindowClassForVehicleType(VehicleType vt)
Get WindowClass for vehicle list of given vehicle type.
Definition vehicle_gui.h:97
@ VEH_ROAD
Road vehicle type.
@ VEH_AIRCRAFT
Aircraft vehicle type.
@ VEH_SHIP
Ship vehicle type.
@ VEH_TRAIN
Train vehicle type.
bool IsShipDepotTile(Tile t)
Is it a ship depot tile?
Definition water_map.h:234
Base of waypoints.
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3202
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3294
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3176
void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
Mark window data of all windows of a given class as invalid (in need of re-computing) Note that by de...
Definition window.cpp:3311
@ WC_STATION_LIST
Station list; Window numbers:
@ WC_VEHICLE_ORDERS
Vehicle orders; Window numbers:
@ WC_SHIPS_LIST
Ships list; Window numbers:
@ WC_VEHICLE_VIEW
Vehicle view; Window numbers:
@ WC_VEHICLE_TIMETABLE
Vehicle timetable; Window numbers:
@ WC_AIRCRAFT_LIST
Aircraft list; Window numbers: