1
0
Fork 0

Codechange: Move ownership of Orders to OrderList. (#13948)

Removes the orders pool, and orders are now stored directly in each OrderList.

Iterating orders now no longer needs to traverse a linked-list, all orders in an OrderList are sequential.
pull/14293/head
Peter Nelson 2025-05-23 10:36:28 +01:00 committed by GitHub
parent 7344dfe651
commit 0455627d16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 602 additions and 652 deletions

View File

@ -185,9 +185,9 @@ static bool VerifyAutoreplaceRefitForOrders(const Vehicle *v, EngineID engine_ty
CargoTypes union_refit_mask_b = GetUnionOfArticulatedRefitMasks(engine_type, false);
const Vehicle *u = (v->type == VEH_TRAIN) ? v->First() : v;
for (const Order *o : u->Orders()) {
if (!o->IsRefit() || o->IsAutoRefit()) continue;
CargoType cargo_type = o->GetRefitCargo();
for (const Order &o : u->Orders()) {
if (!o.IsRefit() || o.IsAutoRefit()) continue;
CargoType cargo_type = o.GetRefitCargo();
if (!HasBit(union_refit_mask_a, cargo_type)) continue;
if (!HasBit(union_refit_mask_b, cargo_type)) return false;
@ -206,13 +206,12 @@ static int GetIncompatibleRefitOrderIdForAutoreplace(const Vehicle *v, EngineID
{
CargoTypes union_refit_mask = GetUnionOfArticulatedRefitMasks(engine_type, false);
const Order *o;
const Vehicle *u = (v->type == VEH_TRAIN) ? v->First() : v;
const OrderList *orders = u->orders;
if (orders == nullptr) return -1;
for (VehicleOrderID i = 0; i < orders->GetNumOrders(); i++) {
o = orders->GetOrderAt(i);
const Order *o = orders->GetOrderAt(i);
if (!o->IsRefit()) continue;
if (!HasBit(union_refit_mask, o->GetRefitCargo())) return i;
}

View File

@ -2735,14 +2735,14 @@ int WhoCanServiceIndustry(Industry *ind)
* We cannot check the first of shared orders only, since the first vehicle in such a chain
* may have a different cargo type.
*/
for (const Order *o : v->Orders()) {
if (o->IsType(OT_GOTO_STATION) && !(o->GetUnloadType() & OUFB_TRANSFER)) {
for (const Order &o : v->Orders()) {
if (o.IsType(OT_GOTO_STATION) && !(o.GetUnloadType() & OUFB_TRANSFER)) {
/* Vehicle visits a station to load or unload */
Station *st = Station::Get(o->GetDestination().ToStationID());
Station *st = Station::Get(o.GetDestination().ToStationID());
assert(st != nullptr);
/* Same cargo produced by industry is dropped here => not serviced by vehicle v */
if ((o->GetUnloadType() & OUFB_UNLOAD) && !c_accepts) break;
if ((o.GetUnloadType() & OUFB_UNLOAD) && !c_accepts) break;
if (ind->stations_near.find(st) != ind->stations_near.end()) {
if (v->owner == _local_company) return 2; // Company services industry

View File

@ -29,8 +29,8 @@
if (v->orders == nullptr) return;
/* Make sure the first order is a useful order. */
const Order *first = v->orders->GetNextDecisionNode(v->GetOrder(v->cur_implicit_order_index), 0);
if (first == nullptr) return;
VehicleOrderID first = v->orders->GetNextDecisionNode(v->cur_implicit_order_index, 0);
if (first == INVALID_VEH_ORDER_ID) return;
HopSet seen_hops;
LinkRefresher refresher(v, &seen_hops, allow_merge, is_full_loading);
@ -138,22 +138,25 @@ void LinkRefresher::ResetRefit()
* @param num_hops Number of hops already taken by recursive calls to this method.
* @return new next Order.
*/
const Order *LinkRefresher::PredictNextOrder(const Order *cur, const Order *next, RefreshFlags flags, uint num_hops)
VehicleOrderID LinkRefresher::PredictNextOrder(VehicleOrderID cur, VehicleOrderID next, RefreshFlags flags, uint num_hops)
{
assert(this->vehicle->orders != nullptr);
const OrderList &orderlist = *this->vehicle->orders;
auto orders = orderlist.GetOrders();
/* next is good if it's either nullptr (then the caller will stop the
* evaluation) or if it's not conditional and the caller allows it to be
* chosen (by setting RefreshFlag::UseNext). */
while (next != nullptr && (!flags.Test(RefreshFlag::UseNext) || next->IsType(OT_CONDITIONAL))) {
while (next < orderlist.GetNumOrders() && (!flags.Test(RefreshFlag::UseNext) || orders[next].IsType(OT_CONDITIONAL))) {
/* After the first step any further non-conditional order is good,
* regardless of previous RefreshFlag::UseNext settings. The case of cur and next or
* their respective stations being equal is handled elsewhere. */
flags.Set(RefreshFlag::UseNext);
if (next->IsType(OT_CONDITIONAL)) {
const Order *skip_to = this->vehicle->orders->GetNextDecisionNode(
this->vehicle->orders->GetOrderAt(next->GetConditionSkipToOrder()), num_hops);
if (skip_to != nullptr && num_hops < this->vehicle->orders->GetNumOrders()) {
if (orders[next].IsType(OT_CONDITIONAL)) {
VehicleOrderID skip_to = orderlist.GetNextDecisionNode(orders[next].GetConditionSkipToOrder(), num_hops);
if (skip_to != INVALID_VEH_ORDER_ID && num_hops < orderlist.GetNumOrders()) {
/* Make copies of capacity tracking lists. There is potential
* for optimization here: If the vehicle never refits we don't
* need to copy anything. Also, if we've seen the branched link
@ -165,8 +168,7 @@ const Order *LinkRefresher::PredictNextOrder(const Order *cur, const Order *next
/* Reassign next with the following stop. This can be a station or a
* depot.*/
next = this->vehicle->orders->GetNextDecisionNode(
this->vehicle->orders->GetNext(next), num_hops++);
next = orderlist.GetNextDecisionNode(orderlist.GetNext(next), num_hops++);
}
return next;
}
@ -176,10 +178,14 @@ const Order *LinkRefresher::PredictNextOrder(const Order *cur, const Order *next
* @param cur Last stop where the consist could interact with cargo.
* @param next Next order to be processed.
*/
void LinkRefresher::RefreshStats(const Order *cur, const Order *next)
void LinkRefresher::RefreshStats(VehicleOrderID cur, VehicleOrderID next)
{
StationID next_station = next->GetDestination().ToStationID();
Station *st = Station::GetIfValid(cur->GetDestination().ToStationID());
assert(this->vehicle->orders != nullptr);
const OrderList &orderlist = *this->vehicle->orders;
auto orders = orderlist.GetOrders();
StationID next_station = orders[next].GetDestination().ToStationID();
Station *st = Station::GetIfValid(orders[cur].GetDestination().ToStationID());
if (st != nullptr && next_station != StationID::Invalid() && next_station != st->index) {
Station *st_to = Station::Get(next_station);
for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) {
@ -197,7 +203,7 @@ void LinkRefresher::RefreshStats(const Order *cur, const Order *next)
}
/* A link is at least partly restricted if a vehicle can't load at its source. */
EdgeUpdateMode restricted_mode = (cur->GetLoadType() & OLFB_NO_LOAD) == 0 ?
EdgeUpdateMode restricted_mode = (orders[cur].GetLoadType() & OLFB_NO_LOAD) == 0 ?
EdgeUpdateMode::Unrestricted : EdgeUpdateMode::Restricted;
/* This estimates the travel time of the link as the time needed
* to travel between the stations at half the max speed of the consist.
@ -240,14 +246,17 @@ void LinkRefresher::RefreshStats(const Order *cur, const Order *next)
* @param flags RefreshFlags to give hints about the previous link and state carried over from that.
* @param num_hops Number of hops already taken by recursive calls to this method.
*/
void LinkRefresher::RefreshLinks(const Order *cur, const Order *next, RefreshFlags flags, uint num_hops)
void LinkRefresher::RefreshLinks(VehicleOrderID cur, VehicleOrderID next, RefreshFlags flags, uint num_hops)
{
while (next != nullptr) {
assert(this->vehicle->orders != nullptr);
const OrderList &orderlist = *this->vehicle->orders;
while (next < orderlist.GetNumOrders()) {
const Order *next_order = orderlist.GetOrderAt(next);
if ((next->IsType(OT_GOTO_DEPOT) || next->IsType(OT_GOTO_STATION)) && next->IsRefit()) {
if ((next_order->IsType(OT_GOTO_DEPOT) || next_order->IsType(OT_GOTO_STATION)) && next_order->IsRefit()) {
flags.Set(RefreshFlag::WasRefit);
if (!next->IsAutoRefit()) {
this->HandleRefit(next->GetRefitCargo());
if (!next_order->IsAutoRefit()) {
this->HandleRefit(next_order->GetRefitCargo());
} else if (!flags.Test(RefreshFlag::InAutorefit)) {
flags.Set(RefreshFlag::InAutorefit);
LinkRefresher backup(*this);
@ -263,34 +272,38 @@ void LinkRefresher::RefreshLinks(const Order *cur, const Order *next, RefreshFla
/* Only reset the refit capacities if the "previous" next is a station,
* meaning that either the vehicle was refit at the previous station or
* it wasn't at all refit during the current hop. */
if (flags.Test(RefreshFlag::WasRefit) && (next->IsType(OT_GOTO_STATION) || next->IsType(OT_IMPLICIT))) {
if (flags.Test(RefreshFlag::WasRefit) && (next_order->IsType(OT_GOTO_STATION) || next_order->IsType(OT_IMPLICIT))) {
flags.Set(RefreshFlag::ResetRefit);
} else {
flags.Reset(RefreshFlag::ResetRefit);
}
next = this->PredictNextOrder(cur, next, flags, num_hops);
if (next == nullptr) break;
Hop hop(cur->index, next->index, this->cargo);
if (next == INVALID_VEH_ORDER_ID) break;
Hop hop(cur, next, this->cargo);
if (this->seen_hops->find(hop) != this->seen_hops->end()) {
break;
} else {
this->seen_hops->insert(hop);
}
next_order = orderlist.GetOrderAt(next);
/* Don't use the same order again, but choose a new one in the next round. */
flags.Reset(RefreshFlag::UseNext);
/* Skip resetting and link refreshing if next order won't do anything with cargo. */
if (!next->IsType(OT_GOTO_STATION) && !next->IsType(OT_IMPLICIT)) continue;
if (!next_order->IsType(OT_GOTO_STATION) && !next_order->IsType(OT_IMPLICIT)) continue;
if (flags.Test(RefreshFlag::ResetRefit)) {
this->ResetRefit();
flags.Reset({RefreshFlag::ResetRefit, RefreshFlag::WasRefit});
}
if (cur->IsType(OT_GOTO_STATION) || cur->IsType(OT_IMPLICIT)) {
if (cur->CanLeaveWithCargo(flags.Test(RefreshFlag::HasCargo))) {
const Order *cur_order = orderlist.GetOrderAt(cur);
if (cur_order->IsType(OT_GOTO_STATION) || cur_order->IsType(OT_IMPLICIT)) {
if (cur_order->CanLeaveWithCargo(flags.Test(RefreshFlag::HasCargo))) {
flags.Set(RefreshFlag::HasCargo);
this->RefreshStats(cur, next);
} else {

View File

@ -56,8 +56,8 @@ protected:
* line.
*/
struct Hop {
OrderID from; ///< Last order where vehicle could interact with cargo or absolute first order.
OrderID to; ///< Next order to be processed.
VehicleOrderID from; ///< Last order where vehicle could interact with cargo or absolute first order.
VehicleOrderID to; ///< Next order to be processed.
CargoType cargo; ///< Cargo the consist is probably carrying or INVALID_CARGO if unknown.
/**
@ -72,7 +72,7 @@ protected:
* @param to Second order of the hop.
* @param cargo Cargo the consist is probably carrying when passing the hop.
*/
Hop(OrderID from, OrderID to, CargoType cargo) : from(from), to(to), cargo(cargo) {}
Hop(VehicleOrderID from, VehicleOrderID to, CargoType cargo) : from(from), to(to), cargo(cargo) {}
constexpr auto operator<=>(const Hop &) const noexcept = default;
};
@ -92,10 +92,10 @@ protected:
bool HandleRefit(CargoType refit_cargo);
void ResetRefit();
void RefreshStats(const Order *cur, const Order *next);
const Order *PredictNextOrder(const Order *cur, const Order *next, RefreshFlags flags, uint num_hops = 0);
void RefreshStats(VehicleOrderID cur, VehicleOrderID next);
VehicleOrderID PredictNextOrder(VehicleOrderID cur, VehicleOrderID next, RefreshFlags flags, uint num_hops = 0);
void RefreshLinks(const Order *cur, const Order *next, RefreshFlags flags, uint num_hops = 0);
void RefreshLinks(VehicleOrderID cur, VehicleOrderID next, RefreshFlags flags, uint num_hops = 0);
};
#endif /* REFRESH_H */

View File

@ -27,18 +27,7 @@
OrderBackupPool _order_backup_pool("BackupOrder");
INSTANTIATE_POOL_METHODS(OrderBackup)
/** Free everything that is allocated. */
OrderBackup::~OrderBackup()
{
if (CleaningPool()) return;
Order *o = this->orders;
while (o != nullptr) {
Order *next = o->next;
delete o;
o = next;
}
}
OrderBackup::~OrderBackup() = default;
/**
* Create an order backup for the given vehicle.
@ -54,15 +43,7 @@ OrderBackup::OrderBackup(const Vehicle *v, uint32_t user) : user(user), tile(v->
this->clone = (v->FirstShared() == v) ? v->NextShared() : v->FirstShared();
} else {
/* Else copy the orders */
Order **tail = &this->orders;
/* Count the number of orders */
for (const Order *order : v->Orders()) {
Order *copy = new Order();
copy->AssignOrder(*order);
*tail = copy;
tail = &copy->next;
}
this->orders.assign(std::begin(v->Orders()), std::end(v->Orders()));
}
}
@ -75,9 +56,8 @@ void OrderBackup::DoRestore(Vehicle *v)
/* If we had shared orders, recover that */
if (this->clone != nullptr) {
Command<CMD_CLONE_ORDER>::Do(DoCommandFlag::Execute, CO_SHARE, v->index, this->clone->index);
} else if (this->orders != nullptr && OrderList::CanAllocateItem()) {
v->orders = new OrderList(this->orders, v);
this->orders = nullptr;
} else if (!this->orders.empty() && OrderList::CanAllocateItem()) {
v->orders = new OrderList(std::move(this->orders), v);
/* Make sure buoys/oil rigs are updated in the station list. */
InvalidateWindowClassesData(WC_STATION_LIST, 0);
}
@ -251,12 +231,12 @@ CommandCost CmdClearOrderBackup(DoCommandFlags flags, TileIndex tile, ClientID u
/* static */ void OrderBackup::RemoveOrder(OrderType type, DestinationID destination, bool hangar)
{
for (OrderBackup *ob : OrderBackup::Iterate()) {
for (Order *order = ob->orders; order != nullptr; order = order->next) {
OrderType ot = order->GetType();
if (ot == OT_GOTO_DEPOT && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue;
for (Order &order : ob->orders) {
OrderType ot = order.GetType();
if (ot == OT_GOTO_DEPOT && (order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue;
if (ot == OT_GOTO_DEPOT && hangar && !IsHangarTile(ob->tile)) continue; // Not an aircraft? Can't have a hangar order.
if (ot == OT_IMPLICIT || (IsHangarTile(ob->tile) && ot == OT_GOTO_DEPOT && !hangar)) ot = OT_GOTO_STATION;
if (ot == type && order->GetDestination() == destination) {
if (ot == type && order.GetDestination() == destination) {
/* Remove the order backup! If a station/depot gets removed, we can't/shouldn't restore those broken orders. */
delete ob;
break;

View File

@ -34,15 +34,19 @@ struct OrderBackup : OrderBackupPool::PoolItem<&_order_backup_pool>, BaseConsist
private:
friend SaveLoadTable GetOrderBackupDescription(); ///< Saving and loading of order backups.
friend struct BKORChunkHandler; ///< Creating empty orders upon savegame loading.
template <typename T>
friend class SlOrders;
uint32_t user = 0; ///< The user that requested the backup.
TileIndex tile = INVALID_TILE; ///< Tile of the depot where the order was changed.
GroupID group = GroupID::Invalid(); ///< The group the vehicle was part of.
const Vehicle *clone = nullptr; ///< Vehicle this vehicle was a clone of.
Order *orders = nullptr; ///< The actual orders if the vehicle was not a clone.
std::vector<Order> orders; ///< The actual orders if the vehicle was not a clone.
uint32_t old_order_index = 0;
/** Creation for savegame restoration. */
OrderBackup() {}
OrderBackup() = default;
OrderBackup(const Vehicle *v, uint32_t user);
void DoRestore(Vehicle *v);

View File

@ -20,9 +20,7 @@
#include "timer/timer_game_tick.h"
#include "saveload/saveload.h"
using OrderPool = Pool<Order, OrderID, 256>;
using OrderListPool = Pool<OrderList, OrderListID, 128>;
extern OrderPool _order_pool;
extern OrderListPool _orderlist_pool;
template <typename, typename>
@ -31,15 +29,16 @@ class EndianBufferWriter;
/* If you change this, keep in mind that it is saved on 3 places:
* - Load_ORDR, all the global orders
* - Vehicle -> current_order
* - REF_ORDER (all REFs are currently limited to 16 bits!!)
*/
struct Order : OrderPool::PoolItem<&_order_pool> {
struct Order {
private:
friend struct VEHSChunkHandler; ///< Loading of ancient vehicles.
friend SaveLoadTable GetOrderDescription(); ///< Saving and loading of orders.
/* So we can use private/protected variables in the saveload code */
friend class SlVehicleCommon;
friend class SlVehicleDisaster;
template <typename T>
friend class SlOrders;
template <typename Tcont, typename Titer>
friend EndianBufferWriter<Tcont, Titer> &operator <<(EndianBufferWriter<Tcont, Titer> &buffer, const Order &data);
@ -56,11 +55,8 @@ private:
uint16_t max_speed = UINT16_MAX; ///< How fast the vehicle may go on the way to the destination.
public:
Order *next = nullptr; ///< Pointer to next order. If nullptr, end of list
Order() {}
Order(uint8_t type, uint8_t flags, DestinationID dest) : type(type), flags(flags), dest(dest) {}
~Order();
/**
* Check whether this order is of the given type.
@ -248,7 +244,17 @@ public:
void ConvertFromOldSavegame();
};
void InsertOrder(Vehicle *v, Order *new_o, VehicleOrderID sel_ord);
/** Compatibility struct to allow saveload of pool-based orders. */
struct OldOrderSaveLoadItem {
uint32_t index = 0; ///< This order's index (1-based).
uint32_t next = 0; ///< The next order index (1-based).
Order order{}; ///< The order data.
};
OldOrderSaveLoadItem *GetOldOrder(size_t pool_index);
OldOrderSaveLoadItem &AllocateOldOrder(size_t pool_index);
void InsertOrder(Vehicle *v, Order &&new_o, VehicleOrderID sel_ord);
void DeleteOrder(Vehicle *v, VehicleOrderID sel_ord);
/**
@ -259,31 +265,49 @@ struct OrderList : OrderListPool::PoolItem<&_orderlist_pool> {
private:
friend void AfterLoadVehiclesPhase1(bool part_of_load); ///< For instantiating the shared vehicle chain
friend SaveLoadTable GetOrderListDescription(); ///< Saving and loading of order lists.
friend struct ORDLChunkHandler;
template <typename T>
friend class SlOrders;
VehicleOrderID num_orders = INVALID_VEH_ORDER_ID; ///< NOSAVE: How many orders there are in the list.
VehicleOrderID num_manual_orders = 0; ///< NOSAVE: How many manually added orders are there in the list.
uint num_vehicles = 0; ///< NOSAVE: Number of vehicles that share this order list.
Vehicle *first_shared = nullptr; ///< NOSAVE: pointer to the first vehicle in the shared order chain.
Order *first = nullptr; ///< First order of the order list.
std::vector<Order> orders; ///< Orders of the order list.
uint32_t old_order_index = 0;
TimerGameTick::Ticks timetable_duration{}; ///< NOSAVE: Total timetabled duration of the order list.
TimerGameTick::Ticks total_duration{}; ///< NOSAVE: Total (timetabled or not) duration of the order list.
public:
/** Default constructor producing an invalid order list. */
OrderList(VehicleOrderID num_orders = INVALID_VEH_ORDER_ID) : num_orders(num_orders) { }
OrderList() {}
/**
* Create an order list with the given order chain for the given vehicle.
* @param chain pointer to the first order of the order chain
* @param v any vehicle using this orderlist
*/
OrderList(Order *chain, Vehicle *v) { this->Initialize(chain, v); }
OrderList(Order &&order, Vehicle *v)
{
this->orders.emplace_back(std::move(order));
this->Initialize(v);
}
OrderList(std::vector<Order> &&orders, Vehicle *v)
{
this->orders = std::move(orders);
this->Initialize(v);
}
OrderList(Vehicle *v)
{
this->Initialize(v);
}
/** Destructor. Invalidates OrderList for re-usage by the pool. */
~OrderList() {}
void Initialize(Order *chain, Vehicle *v);
void Initialize(Vehicle *v);
void RecalculateTimetableDuration();
@ -291,15 +315,33 @@ public:
* Get the first order of the order chain.
* @return the first order of the chain.
*/
inline Order *GetFirstOrder() const { return this->first; }
inline VehicleOrderID GetFirstOrder() const { return this->orders.empty() ? INVALID_VEH_ORDER_ID : 0; }
Order *GetOrderAt(int index) const;
inline std::span<const Order> GetOrders() const { return this->orders; }
inline std::span<Order> GetOrders() { return this->orders; }
/**
* Get a certain order of the order chain.
* @param index zero-based index of the order within the chain.
* @return the order at position index.
*/
const Order *GetOrderAt(VehicleOrderID index) const
{
if (index >= this->GetNumOrders()) return nullptr;
return &this->orders[index];
}
Order *GetOrderAt(VehicleOrderID index)
{
if (index >= this->GetNumOrders()) return nullptr;
return &this->orders[index];
}
/**
* Get the last order of the order chain.
* @return the last order of the chain.
*/
inline Order *GetLastOrder() const { return this->GetOrderAt(this->num_orders - 1); }
inline VehicleOrderID GetLastOrder() const { return this->orders.empty() ? INVALID_VEH_ORDER_ID : (this->GetNumOrders() - 1); }
/**
* Get the order after the given one or the first one, if the given one is the
@ -307,13 +349,17 @@ public:
* @param curr Order to find the next one for.
* @return Next order.
*/
inline const Order *GetNext(const Order *curr) const { return (curr->next == nullptr) ? this->GetFirstOrder() : curr->next; }
inline VehicleOrderID GetNext(VehicleOrderID cur) const
{
if (this->orders.empty()) return INVALID_VEH_ORDER_ID;
return static_cast<VehicleOrderID>((cur + 1) % this->GetNumOrders());
}
/**
* Get number of orders in the order list.
* @return number of orders in the chain.
*/
inline VehicleOrderID GetNumOrders() const { return this->num_orders; }
inline VehicleOrderID GetNumOrders() const { return static_cast<VehicleOrderID>(std::size(this->orders)); }
/**
* Get number of manually added orders in the order list.
@ -321,12 +367,12 @@ public:
*/
inline VehicleOrderID GetNumManualOrders() const { return this->num_manual_orders; }
StationIDStack GetNextStoppingStation(const Vehicle *v, const Order *first = nullptr, uint hops = 0) const;
const Order *GetNextDecisionNode(const Order *next, uint hops) const;
StationIDStack GetNextStoppingStation(const Vehicle *v, VehicleOrderID first = INVALID_VEH_ORDER_ID, uint hops = 0) const;
VehicleOrderID GetNextDecisionNode(VehicleOrderID next, uint hops) const;
void InsertOrderAt(Order *new_order, int index);
void DeleteOrderAt(int index);
void MoveOrder(int from, int to);
void InsertOrderAt(Order &&order, VehicleOrderID index);
void DeleteOrderAt(VehicleOrderID index);
void MoveOrder(VehicleOrderID from, VehicleOrderID to);
/**
* Is this a shared order list?

View File

@ -38,24 +38,9 @@
static_assert(sizeof(DestinationID) >= sizeof(DepotID));
static_assert(sizeof(DestinationID) >= sizeof(StationID));
OrderPool _order_pool("Order");
INSTANTIATE_POOL_METHODS(Order)
OrderListPool _orderlist_pool("OrderList");
INSTANTIATE_POOL_METHODS(OrderList)
/** Clean everything up. */
Order::~Order()
{
if (CleaningPool()) return;
/* We can visit oil rigs and buoys that are not our own. They will be shown in
* the list of stations. So, we need to invalidate that window if needed. */
if (this->IsType(OT_GOTO_STATION) || this->IsType(OT_GOTO_WAYPOINT)) {
BaseStation *bs = BaseStation::GetIfValid(this->GetDestination().ToStationID());
if (bs != nullptr && bs->owner == OWNER_NONE) InvalidateWindowClassesData(WC_STATION_LIST, 0);
}
}
/**
* 'Free' the order
* @note ONLY use on "current_order" vehicle orders!
@ -65,7 +50,6 @@ void Order::Free()
this->type = OT_NOTHING;
this->flags = 0;
this->dest = 0;
this->next = nullptr;
}
/**
@ -267,20 +251,17 @@ void Order::AssignOrder(const Order &other)
* @param chain first order in the chain
* @param v one of vehicle that is using this orderlist
*/
void OrderList::Initialize(Order *chain, Vehicle *v)
void OrderList::Initialize(Vehicle *v)
{
this->first = chain;
this->first_shared = v;
this->num_orders = 0;
this->num_manual_orders = 0;
this->num_vehicles = 1;
this->timetable_duration = 0;
for (Order *o = this->first; o != nullptr; o = o->next) {
++this->num_orders;
if (!o->IsType(OT_IMPLICIT)) ++this->num_manual_orders;
this->total_duration += o->GetWaitTime() + o->GetTravelTime();
for (const Order &o : this->orders) {
if (!o.IsType(OT_IMPLICIT)) ++this->num_manual_orders;
this->total_duration += o.GetWaitTime() + o.GetTravelTime();
}
this->RecalculateTimetableDuration();
@ -300,8 +281,8 @@ void OrderList::Initialize(Order *chain, Vehicle *v)
void OrderList::RecalculateTimetableDuration()
{
this->timetable_duration = 0;
for (Order *o = this->first; o != nullptr; o = o->next) {
this->timetable_duration += o->GetTimetabledWait() + o->GetTimetabledTravel();
for (const Order &o : this->orders) {
this->timetable_duration += o.GetTimetabledWait() + o.GetTimetabledTravel();
}
}
@ -312,15 +293,20 @@ void OrderList::RecalculateTimetableDuration()
*/
void OrderList::FreeChain(bool keep_orderlist)
{
Order *next;
for (Order *o = this->first; o != nullptr; o = next) {
next = o->next;
delete o;
/* We can visit oil rigs and buoys that are not our own. They will be shown in
* the list of stations. So, we need to invalidate that window if needed. */
for (Order &order: this->orders) {
if (order.IsType(OT_GOTO_STATION) || order.IsType(OT_GOTO_WAYPOINT)) {
BaseStation *bs = BaseStation::GetIfValid(order.GetDestination().ToStationID());
if (bs != nullptr && bs->owner == OWNER_NONE) {
InvalidateWindowClassesData(WC_STATION_LIST, 0);
break;
}
}
}
if (keep_orderlist) {
this->first = nullptr;
this->num_orders = 0;
this->orders.clear();
this->num_manual_orders = 0;
this->timetable_duration = 0;
} else {
@ -328,23 +314,6 @@ void OrderList::FreeChain(bool keep_orderlist)
}
}
/**
* Get a certain order of the order chain.
* @param index zero-based index of the order within the chain.
* @return the order at position index.
*/
Order *OrderList::GetOrderAt(int index) const
{
if (index < 0) return nullptr;
Order *order = this->first;
while (order != nullptr && index-- > 0) {
order = order->next;
}
return order;
}
/**
* Get the next order which will make the given vehicle stop at a station
* or refit at a depot or evaluate a non-trivial condition.
@ -354,28 +323,29 @@ Order *OrderList::GetOrderAt(int index) const
* \li a station order
* \li a refitting depot order
* \li a non-trivial conditional order
* \li nullptr if the vehicle won't stop anymore.
* \li INVALID_VEH_ORDER_ID if the vehicle won't stop anymore.
*/
const Order *OrderList::GetNextDecisionNode(const Order *next, uint hops) const
VehicleOrderID OrderList::GetNextDecisionNode(VehicleOrderID next, uint hops) const
{
if (hops > this->GetNumOrders() || next == nullptr) return nullptr;
if (hops > this->GetNumOrders() || next >= this->GetNumOrders()) return INVALID_VEH_ORDER_ID;
if (next->IsType(OT_CONDITIONAL)) {
if (next->GetConditionVariable() != OCV_UNCONDITIONALLY) return next;
const Order &order_next = this->orders[next];
if (order_next.IsType(OT_CONDITIONAL)) {
if (order_next.GetConditionVariable() != OCV_UNCONDITIONALLY) return next;
/* We can evaluate trivial conditions right away. They're conceptually
* the same as regular order progression. */
return this->GetNextDecisionNode(
this->GetOrderAt(next->GetConditionSkipToOrder()),
order_next.GetConditionSkipToOrder(),
hops + 1);
}
if (next->IsType(OT_GOTO_DEPOT)) {
if ((next->GetDepotActionType() & ODATFB_HALT) != 0) return nullptr;
if (next->IsRefit()) return next;
if (order_next.IsType(OT_GOTO_DEPOT)) {
if ((order_next.GetDepotActionType() & ODATFB_HALT) != 0) return INVALID_VEH_ORDER_ID;
if (order_next.IsRefit()) return next;
}
if (!next->CanLoadOrUnload()) {
if (!order_next.CanLoadOrUnload()) {
return this->GetNextDecisionNode(this->GetNext(next), hops + 1);
}
@ -385,44 +355,42 @@ const Order *OrderList::GetNextDecisionNode(const Order *next, uint hops) const
/**
* Recursively determine the next deterministic station to stop at.
* @param v The vehicle we're looking at.
* @param first Order to start searching at or nullptr to start at cur_implicit_order_index + 1.
* @param first Order to start searching at or INVALID_VEH_ORDER_ID to start at cur_implicit_order_index + 1.
* @param hops Number of orders we have already looked at.
* @return Next stopping station or StationID::Invalid().
* @pre The vehicle is currently loading and v->last_station_visited is meaningful.
* @note This function may draw a random number. Don't use it from the GUI.
*/
StationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, const Order *first, uint hops) const
StationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, VehicleOrderID first, uint hops) const
{
const Order *next = first;
if (first == nullptr) {
next = this->GetOrderAt(v->cur_implicit_order_index);
if (next == nullptr) {
next = this->GetFirstOrder();
if (next == nullptr) return StationID::Invalid().base();
VehicleOrderID next = first;
if (first == INVALID_VEH_ORDER_ID) {
next = v->cur_implicit_order_index;
if (next == INVALID_VEH_ORDER_ID) {
next = v->orders->GetFirstOrder();
if (next == INVALID_VEH_ORDER_ID) return StationID::Invalid().base();
} else {
/* GetNext never returns nullptr if there is a valid station in the list.
/* GetNext never returns INVALID_VEH_ORDER_ID if there is a valid station in the list.
* As the given "next" is already valid and a station in the list, we
* don't have to check for nullptr here. */
* don't have to check for INVALID_VEH_ORDER_ID here. */
next = this->GetNext(next);
assert(next != nullptr);
assert(next != INVALID_VEH_ORDER_ID);
}
}
auto orders = v->Orders();
do {
next = this->GetNextDecisionNode(next, ++hops);
/* Resolve possibly nested conditionals by estimation. */
while (next != nullptr && next->IsType(OT_CONDITIONAL)) {
while (next != INVALID_VEH_ORDER_ID && orders[next].IsType(OT_CONDITIONAL)) {
/* We return both options of conditional orders. */
const Order *skip_to = this->GetNextDecisionNode(
this->GetOrderAt(next->GetConditionSkipToOrder()), hops);
const Order *advance = this->GetNextDecisionNode(
this->GetNext(next), hops);
if (advance == nullptr || advance == first || skip_to == advance) {
next = (skip_to == first) ? nullptr : skip_to;
} else if (skip_to == nullptr || skip_to == first) {
next = (advance == first) ? nullptr : advance;
VehicleOrderID skip_to = this->GetNextDecisionNode(orders[next].GetConditionSkipToOrder(), hops);
VehicleOrderID advance = this->GetNextDecisionNode(this->GetNext(next), hops);
if (advance == INVALID_VEH_ORDER_ID || advance == first || skip_to == advance) {
next = (skip_to == first) ? INVALID_VEH_ORDER_ID : skip_to;
} else if (skip_to == INVALID_VEH_ORDER_ID || skip_to == first) {
next = (advance == first) ? INVALID_VEH_ORDER_ID : advance;
} else {
StationIDStack st1 = this->GetNextStoppingStation(v, skip_to, hops);
StationIDStack st2 = this->GetNextStoppingStation(v, advance, hops);
@ -433,14 +401,14 @@ StationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, const Order *
}
/* Don't return a next stop if the vehicle has to unload everything. */
if (next == nullptr || ((next->IsType(OT_GOTO_STATION) || next->IsType(OT_IMPLICIT)) &&
next->GetDestination() == v->last_station_visited &&
(next->GetUnloadType() & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0)) {
if (next == INVALID_VEH_ORDER_ID || ((orders[next].IsType(OT_GOTO_STATION) || orders[next].IsType(OT_IMPLICIT)) &&
orders[next].GetDestination() == v->last_station_visited &&
(orders[next].GetUnloadType() & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0)) {
return StationID::Invalid().base();
}
} while (next->IsType(OT_GOTO_DEPOT) || next->GetDestination() == v->last_station_visited);
} while (orders[next].IsType(OT_GOTO_DEPOT) || orders[next].GetDestination() == v->last_station_visited);
return next->GetDestination().ToStationID().base();
return orders[next].GetDestination().ToStationID().base();
}
/**
@ -448,26 +416,11 @@ StationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, const Order *
* @param new_order is the order to insert into the chain.
* @param index is the position where the order is supposed to be inserted.
*/
void OrderList::InsertOrderAt(Order *new_order, int index)
void OrderList::InsertOrderAt(Order &&order, VehicleOrderID index)
{
if (this->first == nullptr) {
this->first = new_order;
} else {
if (index == 0) {
/* Insert as first or only order */
new_order->next = this->first;
this->first = new_order;
} else if (index >= this->num_orders) {
/* index is after the last order, add it to the end */
this->GetLastOrder()->next = new_order;
} else {
/* Put the new order in between */
Order *order = this->GetOrderAt(index - 1);
new_order->next = order->next;
order->next = new_order;
}
}
++this->num_orders;
auto it = std::ranges::next(std::begin(this->orders), index, std::end(this->orders));
auto new_order = this->orders.emplace(it, std::move(order));
if (!new_order->IsType(OT_IMPLICIT)) ++this->num_manual_orders;
this->timetable_duration += new_order->GetTimetabledWait() + new_order->GetTimetabledTravel();
this->total_duration += new_order->GetWaitTime() + new_order->GetTravelTime();
@ -478,7 +431,6 @@ void OrderList::InsertOrderAt(Order *new_order, int index)
BaseStation *bs = BaseStation::Get(new_order->GetDestination().ToStationID());
if (bs->owner == OWNER_NONE) InvalidateWindowClassesData(WC_STATION_LIST, 0);
}
}
@ -486,25 +438,17 @@ void OrderList::InsertOrderAt(Order *new_order, int index)
* Remove an order from the order list and delete it.
* @param index is the position of the order which is to be deleted.
*/
void OrderList::DeleteOrderAt(int index)
void OrderList::DeleteOrderAt(VehicleOrderID index)
{
if (index >= this->num_orders) return;
auto to_remove = std::ranges::next(std::begin(this->orders), index, std::end(this->orders));
if (to_remove == std::end(this->orders)) return;
Order *to_remove;
if (index == 0) {
to_remove = this->first;
this->first = to_remove->next;
} else {
Order *prev = GetOrderAt(index - 1);
to_remove = prev->next;
prev->next = to_remove->next;
}
--this->num_orders;
if (!to_remove->IsType(OT_IMPLICIT)) --this->num_manual_orders;
this->timetable_duration -= (to_remove->GetTimetabledWait() + to_remove->GetTimetabledTravel());
this->total_duration -= (to_remove->GetWaitTime() + to_remove->GetTravelTime());
delete to_remove;
this->orders.erase(to_remove);
}
/**
@ -512,30 +456,17 @@ void OrderList::DeleteOrderAt(int index)
* @param from is the zero-based position of the order to move.
* @param to is the zero-based position where the order is moved to.
*/
void OrderList::MoveOrder(int from, int to)
void OrderList::MoveOrder(VehicleOrderID from, VehicleOrderID to)
{
if (from >= this->num_orders || to >= this->num_orders || from == to) return;
if (from == to) return;
if (from >= this->GetNumOrders()) return;
if (to >= this->GetNumOrders()) return;
Order *moving_one;
/* Take the moving order out of the pointer-chain */
if (from == 0) {
moving_one = this->first;
this->first = moving_one->next;
auto it = std::begin(this->orders);
if (from < to) {
std::rotate(it + from, it + from + 1, it + to + 1);
} else {
Order *one_before = GetOrderAt(from - 1);
moving_one = one_before->next;
one_before->next = moving_one->next;
}
/* Insert the moving_order again in the pointer-chain */
if (to == 0) {
moving_one->next = this->first;
this->first = moving_one;
} else {
Order *one_before = GetOrderAt(to - 1);
moving_one->next = one_before->next;
one_before->next = moving_one;
std::rotate(it + to, it + from, it + from + 1);
}
}
@ -556,10 +487,10 @@ void OrderList::RemoveVehicle(Vehicle *v)
*/
bool OrderList::IsCompleteTimetable() const
{
for (Order *o = this->first; o != nullptr; o = o->next) {
for (const Order &o : this->orders) {
/* Implicit orders are, by definition, not timetabled. */
if (o->IsType(OT_IMPLICIT)) continue;
if (!o->IsCompletelyTimetabled()) return false;
if (o.IsType(OT_IMPLICIT)) continue;
if (!o.IsCompletelyTimetabled()) return false;
}
return true;
}
@ -578,13 +509,13 @@ void OrderList::DebugCheckSanity() const
Debug(misc, 6, "Checking OrderList {} for sanity...", this->index);
for (const Order *o = this->first; o != nullptr; o = o->next) {
for (const Order &o : this->orders) {
++check_num_orders;
if (!o->IsType(OT_IMPLICIT)) ++check_num_manual_orders;
check_timetable_duration += o->GetTimetabledWait() + o->GetTimetabledTravel();
check_total_duration += o->GetWaitTime() + o->GetTravelTime();
if (!o.IsType(OT_IMPLICIT)) ++check_num_manual_orders;
check_timetable_duration += o.GetTimetabledWait() + o.GetTimetabledTravel();
check_total_duration += o.GetWaitTime() + o.GetTravelTime();
}
assert(this->num_orders == check_num_orders);
assert(this->GetNumOrders() == check_num_orders);
assert(this->num_manual_orders == check_num_manual_orders);
assert(this->timetable_duration == check_timetable_duration);
assert(this->total_duration == check_total_duration);
@ -595,7 +526,7 @@ void OrderList::DebugCheckSanity() const
}
assert(this->num_vehicles == check_num_vehicles);
Debug(misc, 6, "... detected {} orders ({} manual), {} vehicles, {} timetabled, {} total",
(uint)this->num_orders, (uint)this->num_manual_orders,
(uint)this->GetNumOrders(), (uint)this->num_manual_orders,
this->num_vehicles, this->timetable_duration, this->total_duration);
}
#endif
@ -607,10 +538,10 @@ void OrderList::DebugCheckSanity() const
* @param o the order to check
* @return true if the destination is a station
*/
static inline bool OrderGoesToStation(const Vehicle *v, const Order *o)
static inline bool OrderGoesToStation(const Vehicle *v, const Order &o)
{
return o->IsType(OT_GOTO_STATION) ||
(v->type == VEH_AIRCRAFT && o->IsType(OT_GOTO_DEPOT) && o->GetDestination() != StationID::Invalid());
return o.IsType(OT_GOTO_STATION) ||
(v->type == VEH_AIRCRAFT && o.IsType(OT_GOTO_DEPOT) && o.GetDestination() != StationID::Invalid());
}
/**
@ -657,20 +588,24 @@ TileIndex Order::GetLocation(const Vehicle *v, bool airport) const
* @param conditional_depth Internal param for resolving conditional orders.
* @return Maximum distance between the two orders.
*/
uint GetOrderDistance(const Order *prev, const Order *cur, const Vehicle *v, int conditional_depth)
uint GetOrderDistance(VehicleOrderID prev, VehicleOrderID cur, const Vehicle *v, int conditional_depth)
{
if (cur->IsType(OT_CONDITIONAL)) {
assert(v->orders != nullptr);
const OrderList &orderlist = *v->orders;
auto orders = orderlist.GetOrders();
if (orders[cur].IsType(OT_CONDITIONAL)) {
if (conditional_depth > v->GetNumOrders()) return 0;
conditional_depth++;
int dist1 = GetOrderDistance(prev, v->GetOrder(cur->GetConditionSkipToOrder()), v, conditional_depth);
int dist2 = GetOrderDistance(prev, cur->next == nullptr ? v->orders->GetFirstOrder() : cur->next, v, conditional_depth);
int dist1 = GetOrderDistance(prev, orders[cur].GetConditionSkipToOrder(), v, conditional_depth);
int dist2 = GetOrderDistance(prev, orderlist.GetNext(cur), v, conditional_depth);
return std::max(dist1, dist2);
}
TileIndex prev_tile = prev->GetLocation(v, true);
TileIndex cur_tile = cur->GetLocation(v, true);
TileIndex prev_tile = orders[prev].GetLocation(v, true);
TileIndex cur_tile = orders[cur].GetLocation(v, true);
if (prev_tile == INVALID_TILE || cur_tile == INVALID_TILE) return 0;
return v->type == VEH_AIRCRAFT ? DistanceSquare(prev_tile, cur_tile) : DistanceManhattan(prev_tile, cur_tile);
}
@ -882,13 +817,10 @@ CommandCost CmdInsertOrder(DoCommandFlags flags, VehicleID veh, VehicleOrderID s
if (sel_ord > v->GetNumOrders()) return CMD_ERROR;
if (v->GetNumOrders() >= MAX_VEH_ORDER_ID) return CommandCost(STR_ERROR_TOO_MANY_ORDERS);
if (!Order::CanAllocateItem()) return CommandCost(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS);
if (v->orders == nullptr && !OrderList::CanAllocateItem()) return CommandCost(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS);
if (flags.Test(DoCommandFlag::Execute)) {
Order *new_o = new Order();
new_o->AssignOrder(new_order);
InsertOrder(v, new_o, sel_ord);
InsertOrder(v, Order(new_order), sel_ord);
}
return CommandCost();
@ -900,13 +832,13 @@ CommandCost CmdInsertOrder(DoCommandFlags flags, VehicleID veh, VehicleOrderID s
* @param new_o The new order.
* @param sel_ord The position the order should be inserted at.
*/
void InsertOrder(Vehicle *v, Order *new_o, VehicleOrderID sel_ord)
void InsertOrder(Vehicle *v, Order &&new_o, VehicleOrderID sel_ord)
{
/* Create new order and link in list */
if (v->orders == nullptr) {
v->orders = new OrderList(new_o, v);
v->orders = new OrderList(std::move(new_o), v);
} else {
v->orders->InsertOrderAt(new_o, sel_ord);
v->orders->InsertOrderAt(std::move(new_o), sel_ord);
}
Vehicle *u = v->FirstShared();
@ -948,14 +880,14 @@ void InsertOrder(Vehicle *v, Order *new_o, VehicleOrderID sel_ord)
/* As we insert an order, the order to skip to will be 'wrong'. */
VehicleOrderID cur_order_id = 0;
for (Order *order : v->Orders()) {
if (order->IsType(OT_CONDITIONAL)) {
VehicleOrderID order_id = order->GetConditionSkipToOrder();
for (Order &order : v->Orders()) {
if (order.IsType(OT_CONDITIONAL)) {
VehicleOrderID order_id = order.GetConditionSkipToOrder();
if (order_id >= sel_ord) {
order->SetConditionSkipToOrder(order_id + 1);
order.SetConditionSkipToOrder(order_id + 1);
}
if (order_id == cur_order_id) {
order->SetConditionSkipToOrder((order_id + 1) % v->GetNumOrders());
order.SetConditionSkipToOrder((order_id + 1) % v->GetNumOrders());
}
}
cur_order_id++;
@ -1065,16 +997,16 @@ void DeleteOrder(Vehicle *v, VehicleOrderID sel_ord)
/* As we delete an order, the order to skip to will be 'wrong'. */
VehicleOrderID cur_order_id = 0;
for (Order *order : v->Orders()) {
if (order->IsType(OT_CONDITIONAL)) {
VehicleOrderID order_id = order->GetConditionSkipToOrder();
for (Order &order : v->Orders()) {
if (order.IsType(OT_CONDITIONAL)) {
VehicleOrderID order_id = order.GetConditionSkipToOrder();
if (order_id >= sel_ord) {
order_id = std::max(order_id - 1, 0);
}
if (order_id == cur_order_id) {
order_id = (order_id + 1) % v->GetNumOrders();
}
order->SetConditionSkipToOrder(order_id);
order.SetConditionSkipToOrder(order_id);
}
cur_order_id++;
}
@ -1193,9 +1125,9 @@ CommandCost CmdMoveOrder(DoCommandFlags flags, VehicleID veh, VehicleOrderID mov
}
/* As we move an order, the order to skip to will be 'wrong'. */
for (Order *order : v->Orders()) {
if (order->IsType(OT_CONDITIONAL)) {
VehicleOrderID order_id = order->GetConditionSkipToOrder();
for (Order &order : v->Orders()) {
if (order.IsType(OT_CONDITIONAL)) {
VehicleOrderID order_id = order.GetConditionSkipToOrder();
if (order_id == moving_order) {
order_id = target_order;
} else if (order_id > moving_order && order_id <= target_order) {
@ -1203,7 +1135,7 @@ CommandCost CmdMoveOrder(DoCommandFlags flags, VehicleID veh, VehicleOrderID mov
} else if (order_id < moving_order && order_id >= target_order) {
order_id++;
}
order->SetConditionSkipToOrder(order_id);
order.SetConditionSkipToOrder(order_id);
}
}
@ -1480,19 +1412,24 @@ CommandCost CmdModifyOrder(DoCommandFlags flags, VehicleID veh, VehicleOrderID s
* @param first First order in the source order list.
* @return True if the aircraft has enough range for the orders, false otherwise.
*/
static bool CheckAircraftOrderDistance(const Aircraft *v_new, const Vehicle *v_order, const Order *first)
static bool CheckAircraftOrderDistance(const Aircraft *v_new, const Vehicle *v_order)
{
if (first == nullptr || v_new->acache.cached_max_range == 0) return true;
assert(v_order->orders != nullptr);
const OrderList &orderlist = *v_order->orders;
if (v_new->acache.cached_max_range == 0) return true;
if (orderlist.GetNumOrders() == 0) return true;
auto orders = orderlist.GetOrders();
/* Iterate over all orders to check the distance between all
* 'goto' orders and their respective next order (of any type). */
for (const Order *o = first; o != nullptr; o = o->next) {
switch (o->GetType()) {
for (VehicleOrderID cur = 0; cur < orderlist.GetNumOrders(); ++cur) {
switch (orders[cur].GetType()) {
case OT_GOTO_STATION:
case OT_GOTO_DEPOT:
case OT_GOTO_WAYPOINT:
/* If we don't have a next order, we've reached the end and must check the first order instead. */
if (GetOrderDistance(o, o->next != nullptr ? o->next : first, v_order) > v_new->acache.cached_max_range_sqr) return false;
if (GetOrderDistance(cur, orderlist.GetNext(cur), v_order) > v_new->acache.cached_max_range_sqr) return false;
break;
default: break;
@ -1536,20 +1473,20 @@ CommandCost CmdCloneOrder(DoCommandFlags flags, CloneOptions action, VehicleID v
/* Is the vehicle already in the shared list? */
if (src->FirstShared() == dst->FirstShared()) return CMD_ERROR;
for (const Order *order : src->Orders()) {
for (const Order &order : src->Orders()) {
if (!OrderGoesToStation(dst, order)) continue;
/* Allow copying unreachable destinations if they were already unreachable for the source.
* This is basically to allow cloning / autorenewing / autoreplacing vehicles, while the stations
* are temporarily invalid due to reconstruction. */
const Station *st = Station::Get(order->GetDestination().ToStationID());
const Station *st = Station::Get(order.GetDestination().ToStationID());
if (CanVehicleUseStation(src, st) && !CanVehicleUseStation(dst, st)) {
return CommandCost(STR_ERROR_CAN_T_COPY_SHARE_ORDER, GetVehicleCannotUseStationReason(dst, st));
}
}
/* Check for aircraft range limits. */
if (dst->type == VEH_AIRCRAFT && !CheckAircraftOrderDistance(Aircraft::From(dst), src, src->GetFirstOrder())) {
if (dst->type == VEH_AIRCRAFT && !CheckAircraftOrderDistance(Aircraft::From(dst), src)) {
return CommandCost(STR_ERROR_AIRCRAFT_NOT_ENOUGH_RANGE);
}
@ -1587,49 +1524,44 @@ CommandCost CmdCloneOrder(DoCommandFlags flags, CloneOptions action, VehicleID v
/* Trucks can't copy all the orders from busses (and visa versa),
* and neither can helicopters and aircraft. */
for (const Order *order : src->Orders()) {
for (const Order &order : src->Orders()) {
if (!OrderGoesToStation(dst, order)) continue;
Station *st = Station::Get(order->GetDestination().ToStationID());
Station *st = Station::Get(order.GetDestination().ToStationID());
if (!CanVehicleUseStation(dst, st)) {
return CommandCost(STR_ERROR_CAN_T_COPY_SHARE_ORDER, GetVehicleCannotUseStationReason(dst, st));
}
}
/* Check for aircraft range limits. */
if (dst->type == VEH_AIRCRAFT && !CheckAircraftOrderDistance(Aircraft::From(dst), src, src->GetFirstOrder())) {
if (dst->type == VEH_AIRCRAFT && !CheckAircraftOrderDistance(Aircraft::From(dst), src)) {
return CommandCost(STR_ERROR_AIRCRAFT_NOT_ENOUGH_RANGE);
}
/* make sure there are orders available */
if (!Order::CanAllocateItem(src->GetNumOrders()) || !OrderList::CanAllocateItem()) {
if (!OrderList::CanAllocateItem()) {
return CommandCost(STR_ERROR_NO_MORE_SPACE_FOR_ORDERS);
}
if (flags.Test(DoCommandFlag::Execute)) {
Order *first = nullptr;
Order **order_dst;
/* If the destination vehicle had an order list, destroy the chain but keep the OrderList.
* We only reset the order indices, if the new orders are obviously different.
* (We mainly do this to keep the order indices valid and in range.) */
DeleteVehicleOrders(dst, true, dst->GetNumOrders() != src->GetNumOrders());
order_dst = &first;
for (const Order *order : src->Orders()) {
*order_dst = new Order();
(*order_dst)->AssignOrder(*order);
order_dst = &(*order_dst)->next;
std::vector<Order> dst_orders;
for (const Order &order : src->Orders()) {
dst_orders.emplace_back(order);
}
if (dst->orders == nullptr) {
dst->orders = new OrderList(first, dst);
} else {
assert(dst->orders->GetFirstOrder() == nullptr);
if (dst->orders != nullptr) {
assert(dst->orders->GetNumOrders() == 0);
assert(!dst->orders->IsShared());
delete dst->orders;
assert(OrderList::CanAllocateItem());
dst->orders = new OrderList(first, dst);
}
assert(OrderList::CanAllocateItem());
dst->orders = new OrderList(std::move(dst_orders), dst);
InvalidateVehicleOrder(dst, VIWD_REMOVE_ALL_ORDERS);
InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0);
@ -1720,15 +1652,15 @@ void CheckOrders(const Vehicle *v)
/* Check the order list */
int n_st = 0;
for (const Order *order : v->Orders()) {
for (const Order &order : v->Orders()) {
/* Dummy order? */
if (order->IsType(OT_DUMMY)) {
if (order.IsType(OT_DUMMY)) {
message = STR_NEWS_VEHICLE_HAS_VOID_ORDER;
break;
}
/* Does station have a load-bay for this vehicle? */
if (order->IsType(OT_GOTO_STATION)) {
const Station *st = Station::Get(order->GetDestination().ToStationID());
if (order.IsType(OT_GOTO_STATION)) {
const Station *st = Station::Get(order.GetDestination().ToStationID());
n_st++;
if (!CanVehicleUseStation(v, st)) {
@ -1745,9 +1677,9 @@ void CheckOrders(const Vehicle *v)
/* Check if the last and the first order are the same */
if (v->GetNumOrders() > 1) {
const Order *last = v->GetLastOrder();
auto orders = v->Orders();
if (v->orders->GetFirstOrder()->Equals(*last)) {
if (orders.front().Equals(orders.back())) {
message = STR_NEWS_VEHICLE_HAS_DUPLICATE_ENTRY;
}
}
@ -1788,12 +1720,12 @@ void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination, bool
SetWindowDirty(WC_VEHICLE_VIEW, v->index);
}
/* Clear the order from the order-list */
int id = -1;
for (Order *order : v->Orders()) {
id++;
restart:
if (v->orders == nullptr) continue;
/* Clear the order from the order-list */
for (VehicleOrderID id = 0, next_id = 0; id < v->GetNumOrders(); id = next_id) {
next_id = id + 1;
Order *order = v->orders->GetOrderAt(id);
OrderType ot = order->GetType();
if (ot == OT_GOTO_DEPOT && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue;
if (ot == OT_GOTO_DEPOT && hangar && v->type != VEH_AIRCRAFT) continue; // Not an aircraft? Can't have a hangar order.
@ -1803,10 +1735,9 @@ restart:
* dummy orders. They should just vanish. Also check the actual order
* type as ot is currently OT_GOTO_STATION. */
if (order->IsType(OT_IMPLICIT)) {
order = order->next; // DeleteOrder() invalidates current order
DeleteOrder(v, id);
if (order != nullptr) goto restart;
break;
next_id = id;
continue;
}
/* Clear wait time */
@ -1840,11 +1771,7 @@ restart:
*/
bool Vehicle::HasDepotOrder() const
{
for (const Order *order : this->Orders()) {
if (order->IsType(OT_GOTO_DEPOT)) return true;
}
return false;
return std::ranges::any_of(this->Orders(), [](const Order &order) { return order.IsType(OT_GOTO_DEPOT); });
}
/**
@ -1910,19 +1837,7 @@ uint16_t GetServiceIntervalClamped(int interval, bool ispercent)
*/
static bool CheckForValidOrders(const Vehicle *v)
{
for (const Order *order : v->Orders()) {
switch (order->GetType()) {
case OT_GOTO_STATION:
case OT_GOTO_DEPOT:
case OT_GOTO_WAYPOINT:
return true;
default:
break;
}
}
return false;
return std::ranges::any_of(v->Orders(), [](const Order &order) { return order.IsGotoOrder(); });
}
/**

View File

@ -22,9 +22,9 @@ void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist = false, bool reset_ord
bool ProcessOrders(Vehicle *v);
bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth = 0, bool pbs_look_ahead = false);
VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v);
uint GetOrderDistance(const Order *prev, const Order *cur, const Vehicle *v, int conditional_depth = 0);
uint GetOrderDistance(VehicleOrderID prev, VehicleOrderID cur, const Vehicle *v, int conditional_depth = 0);
void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int y, bool selected, bool timetable, int left, int middle, int right);
void DrawOrderString(const Vehicle *v, const Order *order, VehicleOrderID order_index, int y, bool selected, bool timetable, int left, int middle, int right);
static const uint DEF_SERVINT_DAYS_TRAINS = 150;
static const uint DEF_SERVINT_DAYS_ROADVEH = 150;

View File

@ -225,7 +225,7 @@ static StringID GetOrderGoToString(const Order &order)
* @param middle X position between order index and order text
* @param right Right border for text drawing
*/
void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int y, bool selected, bool timetable, int left, int middle, int right)
void DrawOrderString(const Vehicle *v, const Order *order, VehicleOrderID order_index, int y, bool selected, bool timetable, int left, int middle, int right)
{
bool rtl = _current_text_dir == TD_RTL;
@ -355,8 +355,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int
/* Check range for aircraft. */
if (v->type == VEH_AIRCRAFT && Aircraft::From(v)->GetRange() > 0 && order->IsGotoOrder()) {
const Order *next = order->next != nullptr ? order->next : v->GetFirstOrder();
if (GetOrderDistance(order, next, v) > Aircraft::From(v)->acache.cached_max_range_sqr) {
if (GetOrderDistance(order_index, v->orders->GetNext(order_index), v) > Aircraft::From(v)->acache.cached_max_range_sqr) {
line += GetString(STR_ORDER_OUT_OF_RANGE);
}
}
@ -372,9 +371,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int
*/
static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
{
/* Override the index as it is not coming from a pool, so would not be initialised correctly. */
Order order;
order.index = OrderID::Begin();
Order order{};
/* check depot first */
if (IsDepotTypeTile(tile, (TransportType)(uint)v->type) && IsTileOwner(tile, _local_company)) {
@ -657,9 +654,7 @@ private:
*/
void OrderClick_NearestDepot()
{
Order order;
order.next = nullptr;
order.index = OrderID::Begin();
Order order{};
order.MakeGoToDepot(DepotID::Invalid(), ODTFB_PART_OF_ORDERS,
_settings_client.gui.new_nonstop && this->vehicle->IsGroundVehicle() ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
order.SetDepotActionType(ODATFB_NEAREST_DEPOT);
@ -816,10 +811,7 @@ public:
if (_settings_client.gui.quick_goto && v->owner == _local_company) {
/* If there are less than 2 station, make Go To active. */
int station_orders = 0;
for (const Order *order : v->Orders()) {
if (order->IsType(OT_GOTO_STATION)) station_orders++;
}
int station_orders = std::ranges::count_if(v->Orders(), [](const Order &order) { return order.IsType(OT_GOTO_STATION); });
if (station_orders < 2) this->OrderClick_Goto(OPOS_GOTO);
}
@ -1114,11 +1106,12 @@ public:
int y = ir.top;
int line_height = this->GetWidget<NWidgetBase>(WID_O_ORDER_LIST)->resize_y;
int i = this->vscroll->GetPosition();
const Order *order = this->vehicle->GetOrder(i);
VehicleOrderID i = this->vscroll->GetPosition();
VehicleOrderID num_orders = this->vehicle->GetNumOrders();
/* First draw the highlighting underground if it exists. */
if (this->order_over != INVALID_VEH_ORDER_ID) {
while (order != nullptr) {
while (i < num_orders) {
/* Don't draw anything if it extends past the end of the window. */
if (!this->vscroll->IsVisible(i)) break;
@ -1133,25 +1126,22 @@ public:
y += line_height;
i++;
order = order->next;
}
/* Reset counters for drawing the orders. */
y = ir.top;
i = this->vscroll->GetPosition();
order = this->vehicle->GetOrder(i);
}
/* Draw the orders. */
while (order != nullptr) {
while (i < num_orders) {
/* Don't draw anything if it extends past the end of the window. */
if (!this->vscroll->IsVisible(i)) break;
DrawOrderString(this->vehicle, order, i, y, i == this->selected_order, false, ir.left, middle, ir.right);
DrawOrderString(this->vehicle, this->vehicle->GetOrder(i), i, y, i == this->selected_order, false, ir.left, middle, ir.right);
y += line_height;
i++;
order = order->next;
}
if (this->vscroll->IsVisible(i)) {
@ -1203,9 +1193,7 @@ public:
if (this->goto_type == OPOS_CONDITIONAL) {
VehicleOrderID order_id = this->GetOrderFromPt(_cursor.pos.y - this->top);
if (order_id != INVALID_VEH_ORDER_ID) {
Order order;
order.next = nullptr;
order.index = OrderID::Begin();
Order order{};
order.MakeConditional(order_id);
Command<CMD_INSERT_ORDER>::Post(STR_ERROR_CAN_T_INSERT_NEW_ORDER, this->vehicle->tile, this->vehicle->index, this->OrderGetSel(), order);

View File

@ -16,7 +16,6 @@
#include "station_type.h"
typedef uint8_t VehicleOrderID; ///< The index of an order within its current vehicle (not pool related)
using OrderID = PoolID<uint32_t, struct OrderIDTag, 0xFF0000, 0xFFFFFF>;
using OrderListID = PoolID<uint16_t, struct OrderListIDTag, 64000, 0xFFFF>;
struct DestinationID {

View File

@ -76,6 +76,7 @@
#include "../safeguards.h"
extern Company *DoStartupNewCompany(bool is_ai, CompanyID company = CompanyID::Invalid());
extern void ClearOldOrders();
/**
* Makes a tile canal or water depending on the surroundings.
@ -818,6 +819,9 @@ bool AfterLoadGame()
/* Update all vehicles: Phase 1 */
AfterLoadVehiclesPhase1(true);
/* Old orders are no longer needed. */
ClearOldOrders();
/* make sure there is a town in the game */
if (_game_mode == GM_NORMAL && Town::GetNumItems() == 0) {
SetSaveLoadError(STR_ERROR_NO_TOWN_IN_SCENARIO);
@ -1485,8 +1489,10 @@ bool AfterLoadGame()
/* Setting no refit flags to all orders in savegames from before refit in orders were added */
if (IsSavegameVersionBefore(SLV_36)) {
for (Order *order : Order::Iterate()) {
order->SetRefit(CARGO_NO_REFIT);
for (OrderList *orderlist : OrderList::Iterate()) {
for (Order &order : orderlist->GetOrders()) {
order.SetRefit(CARGO_NO_REFIT);
}
}
for (Vehicle *v : Vehicle::Iterate()) {
@ -1781,25 +1787,31 @@ bool AfterLoadGame()
if (IsSavegameVersionBefore(SLV_93)) {
/* Rework of orders. */
for (Order *order : Order::Iterate()) order->ConvertFromOldSavegame();
for (OrderList *orderlist : OrderList::Iterate()) {
for (Order &o : orderlist->GetOrders()) {
o.ConvertFromOldSavegame();
}
}
for (Vehicle *v : Vehicle::Iterate()) {
if (v->orders != nullptr && v->orders->GetFirstOrder() != nullptr && v->orders->GetFirstOrder()->IsType(OT_NOTHING)) {
if (v->orders != nullptr && v->GetFirstOrder() != nullptr && v->GetFirstOrder()->IsType(OT_NOTHING)) {
v->orders->FreeChain();
v->orders = nullptr;
}
v->current_order.ConvertFromOldSavegame();
if (v->type == VEH_ROAD && v->IsPrimaryVehicle() && v->FirstShared() == v) {
for (Order *order : v->Orders()) order->SetNonStopType(ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS);
for (Order &order : v->Orders()) order.SetNonStopType(ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS);
}
}
} else if (IsSavegameVersionBefore(SLV_94)) {
/* Unload and transfer are now mutual exclusive. */
for (Order *order : Order::Iterate()) {
if ((order->GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) == (OUFB_UNLOAD | OUFB_TRANSFER)) {
order->SetUnloadType(OUFB_TRANSFER);
order->SetLoadType(OLFB_NO_LOAD);
for (OrderList *orderlist : OrderList::Iterate()) {
for (Order &order : orderlist->GetOrders()) {
if ((order.GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) == (OUFB_UNLOAD | OUFB_TRANSFER)) {
order.SetUnloadType(OUFB_TRANSFER);
order.SetLoadType(OLFB_NO_LOAD);
}
}
}
@ -1811,9 +1823,11 @@ bool AfterLoadGame()
}
} else if (IsSavegameVersionBefore(SLV_DEPOT_UNBUNCHING)) {
/* OrderDepotActionFlags were moved, instead of starting at bit 4 they now start at bit 3. */
for (Order *order : Order::Iterate()) {
if (!order->IsType(OT_GOTO_DEPOT)) continue;
order->SetDepotActionType((OrderDepotActionFlags)(order->GetDepotActionType() >> 1));
for (OrderList *orderlist : OrderList::Iterate()) {
for (Order &order : orderlist->GetOrders()) {
if (!order.IsType(OT_GOTO_DEPOT)) continue;
order.SetDepotActionType((OrderDepotActionFlags)(order.GetDepotActionType() >> 1));
}
}
for (Vehicle *v : Vehicle::Iterate()) {
@ -2175,8 +2189,10 @@ bool AfterLoadGame()
/* Trains could now stop in a specific location. */
if (IsSavegameVersionBefore(SLV_117)) {
for (Order *o : Order::Iterate()) {
if (o->IsType(OT_GOTO_STATION)) o->SetStopLocation(OSL_PLATFORM_FAR_END);
for (OrderList *orderlist : OrderList::Iterate()) {
for (Order &o : orderlist->GetOrders()) {
if (o.IsType(OT_GOTO_STATION)) o.SetStopLocation(OSL_PLATFORM_FAR_END);
}
}
}
@ -3054,11 +3070,11 @@ bool AfterLoadGame()
}
if (IsSavegameVersionBefore(SLV_190)) {
for (Order *order : Order::Iterate()) {
order->SetTravelTimetabled(order->GetTravelTime() > 0);
order->SetWaitTimetabled(order->GetWaitTime() > 0);
}
for (OrderList *orderlist : OrderList::Iterate()) {
for (Order &order : orderlist->GetOrders()) {
order.SetTravelTimetabled(order.GetTravelTime() > 0);
order.SetWaitTimetabled(order.GetWaitTime() > 0);
}
orderlist->RecalculateTimetableDuration();
}
}

View File

@ -643,16 +643,14 @@ static bool LoadOldOrder(LoadgameState &ls, int num)
{
if (!LoadChunk(ls, nullptr, order_chunk)) return false;
Order *o = new (OrderID(num)) Order();
o->AssignOrder(UnpackOldOrder(_old_order));
OldOrderSaveLoadItem &o = AllocateOldOrder(num);
o.order.AssignOrder(UnpackOldOrder(_old_order));
if (o->IsType(OT_NOTHING)) {
delete o;
} else {
if (!o.order.IsType(OT_NOTHING) && num > 0) {
/* Relink the orders to each other (in the orders for one vehicle are behind each other,
* with an invalid order (OT_NOTHING) as indication that it is the last order */
Order *prev = Order::GetIfValid(num - 1);
if (prev != nullptr) prev->next = o;
OldOrderSaveLoadItem *prev = GetOldOrder(num + 1 - 1);
if (prev != nullptr) prev->next = num + 1; // next is 1-based.
}
return true;
@ -1364,7 +1362,7 @@ bool LoadOldVehicle(LoadgameState &ls, int num)
if (_old_order_ptr != 0 && _old_order_ptr != 0xFFFFFFFF) {
uint max = _savegame_type == SGT_TTO ? 3000 : 5000;
uint old_id = RemapOrderIndex(_old_order_ptr);
if (old_id < max) v->old_orders = Order::Get(old_id); // don't accept orders > max number of orders
if (old_id < max) v->old_orders = old_id + 1;
}
v->current_order.AssignOrder(UnpackOldOrder(_old_order));

View File

@ -102,35 +102,61 @@ Order UnpackOldOrder(uint16_t packed)
return order;
}
/** Temporary storage for conversion from old order pool. */
static std::vector<OldOrderSaveLoadItem> _old_order_saveload_pool;
/**
* Clear all old orders.
*/
void ClearOldOrders()
{
_old_order_saveload_pool.clear();
_old_order_saveload_pool.shrink_to_fit();
}
/**
* Get a pointer to an old order with the given reference index.
* @param ref_index Reference index (one-based) to get.
* @return Pointer to old order, or nullptr if not present.
*/
OldOrderSaveLoadItem *GetOldOrder(size_t ref_index)
{
if (ref_index == 0) return nullptr;
assert(ref_index <= _old_order_saveload_pool.size());
return &_old_order_saveload_pool[ref_index - 1];
}
/**
* Allocate an old order with the given pool index.
* @param pool_index Pool index (zero-based) to allocate.
* @return Reference to allocated old order.
*/
OldOrderSaveLoadItem &AllocateOldOrder(size_t pool_index)
{
assert(pool_index < UINT32_MAX);
if (pool_index >= _old_order_saveload_pool.size()) _old_order_saveload_pool.resize(pool_index + 1);
return _old_order_saveload_pool[pool_index];
}
SaveLoadTable GetOrderDescription()
{
static const SaveLoad _order_desc[] = {
SLE_VAR(Order, type, SLE_UINT8),
SLE_VAR(Order, flags, SLE_UINT8),
SLE_VAR(Order, dest, SLE_UINT16),
SLE_REF(Order, next, REF_ORDER),
SLE_CONDVAR(Order, refit_cargo, SLE_UINT8, SLV_36, SL_MAX_VERSION),
SLE_CONDVAR(Order, wait_time, SLE_UINT16, SLV_67, SL_MAX_VERSION),
SLE_CONDVAR(Order, travel_time, SLE_UINT16, SLV_67, SL_MAX_VERSION),
SLE_CONDVAR(Order, max_speed, SLE_UINT16, SLV_172, SL_MAX_VERSION),
SLE_VARNAME(OldOrderSaveLoadItem, order.type, "type", SLE_UINT8),
SLE_VARNAME(OldOrderSaveLoadItem, order.flags, "flags", SLE_UINT8),
SLE_VARNAME(OldOrderSaveLoadItem, order.dest, "dest", SLE_UINT16),
SLE_CONDVARNAME(OldOrderSaveLoadItem, next, "next", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_69),
SLE_CONDVARNAME(OldOrderSaveLoadItem, next, "next", SLE_UINT32, SLV_69, SL_MAX_VERSION),
SLE_CONDVARNAME(OldOrderSaveLoadItem, order.refit_cargo, "refit_cargo", SLE_UINT8, SLV_36, SL_MAX_VERSION),
SLE_CONDVARNAME(OldOrderSaveLoadItem, order.wait_time, "wait_time", SLE_UINT16, SLV_67, SL_MAX_VERSION),
SLE_CONDVARNAME(OldOrderSaveLoadItem, order.travel_time, "travel_time", SLE_UINT16, SLV_67, SL_MAX_VERSION),
SLE_CONDVARNAME(OldOrderSaveLoadItem, order.max_speed, "max_speed", SLE_UINT16, SLV_172, SL_MAX_VERSION),
};
return _order_desc;
}
struct ORDRChunkHandler : ChunkHandler {
ORDRChunkHandler() : ChunkHandler('ORDR', CH_TABLE) {}
void Save() const override
{
const SaveLoadTable slt = GetOrderDescription();
SlTableHeader(slt);
for (Order *order : Order::Iterate()) {
SlSetArrayIndex(order->index);
SlObject(order, slt);
}
}
ORDRChunkHandler() : ChunkHandler('ORDR', CH_READONLY) {}
void Load() const override
{
@ -148,8 +174,8 @@ struct ORDRChunkHandler : ChunkHandler {
SlCopy(&orders[0], len, SLE_UINT16);
for (size_t i = 0; i < len; ++i) {
Order *o = new (OrderID(static_cast<uint32_t>(i))) Order();
o->AssignOrder(UnpackVersion4Order(orders[i]));
auto &item = AllocateOldOrder(i);
item.order.AssignOrder(UnpackVersion4Order(orders[i]));
}
} else if (IsSavegameVersionBefore(SLV_5, 2)) {
len /= sizeof(uint32_t);
@ -158,22 +184,19 @@ struct ORDRChunkHandler : ChunkHandler {
SlCopy(&orders[0], len, SLE_UINT32);
for (size_t i = 0; i < len; ++i) {
new (OrderID(static_cast<uint32_t>(i))) Order(GB(orders[i], 0, 8), GB(orders[i], 8, 8), GB(orders[i], 16, 16));
auto &item = AllocateOldOrder(i);
item.order = Order(GB(orders[i], 0, 8), GB(orders[i], 8, 8), GB(orders[i], 16, 16));
}
}
/* Update all the next pointer */
for (Order *o : Order::Iterate()) {
size_t order_index = o->index.base();
/* Delete invalid orders */
if (o->IsType(OT_NOTHING)) {
delete o;
continue;
/* Update all the next pointer. The orders were built like this:
* While the order is valid, the previous order will get its next pointer set */
for (uint32_t num = 1; OldOrderSaveLoadItem item : _old_order_saveload_pool) {
if (!item.order.IsType(OT_NOTHING) && num > 1) {
OldOrderSaveLoadItem *prev = GetOldOrder(num - 1);
if (prev != nullptr) prev->next = num;
}
/* The orders were built like this:
* While the order is valid, set the previous will get its next pointer set */
Order *prev = Order::GetIfValid(order_index - 1);
if (prev != nullptr) prev->next = o;
++num;
}
} else {
const std::vector<SaveLoad> slt = SlCompatTableHeader(GetOrderDescription(), _order_sl_compat);
@ -181,27 +204,42 @@ struct ORDRChunkHandler : ChunkHandler {
int index;
while ((index = SlIterateArray()) != -1) {
Order *order = new (OrderID(index)) Order();
SlObject(order, slt);
auto &item = AllocateOldOrder(index);
SlObject(&item, slt);
}
}
}
void FixPointers() const override
{
/* Orders from old savegames have pointers corrected in Load_ORDR */
if (IsSavegameVersionBefore(SLV_5, 2)) return;
for (Order *o : Order::Iterate()) {
SlObject(o, GetOrderDescription());
}
}
};
template <typename T>
class SlOrders : public VectorSaveLoadHandler<SlOrders<T>, T, Order> {
public:
inline static const SaveLoad description[] = {
SLE_VAR(Order, type, SLE_UINT8),
SLE_VAR(Order, flags, SLE_UINT8),
SLE_VAR(Order, dest, SLE_UINT16),
SLE_VAR(Order, refit_cargo, SLE_UINT8),
SLE_VAR(Order, wait_time, SLE_UINT16),
SLE_VAR(Order, travel_time, SLE_UINT16),
SLE_VAR(Order, max_speed, SLE_UINT16),
};
inline const static SaveLoadCompatTable compat_description = {};
std::vector<Order> &GetVector(T *container) const override { return container->orders; }
void LoadCheck(T *container) const override { this->Load(container); }
};
/* Instantiate SlOrders classes. */
template class SlOrders<OrderList>;
template class SlOrders<OrderBackup>;
SaveLoadTable GetOrderListDescription()
{
static const SaveLoad _orderlist_desc[] = {
SLE_REF(OrderList, first, REF_ORDER),
SLE_CONDVARNAME(OrderList, old_order_index, "first", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_69),
SLE_CONDVARNAME(OrderList, old_order_index, "first", SLE_UINT32, SLV_69, SLV_ORDERS_OWNED_BY_ORDERLIST),
SLEG_CONDSTRUCTLIST("orders", SlOrders<OrderList>, SLV_ORDERS_OWNED_BY_ORDERLIST, SL_MAX_VERSION),
};
return _orderlist_desc;
@ -228,8 +266,7 @@ struct ORDLChunkHandler : ChunkHandler {
int index;
while ((index = SlIterateArray()) != -1) {
/* set num_orders to 0 so it's a valid OrderList */
OrderList *list = new (OrderListID(index)) OrderList(0);
OrderList *list = new (OrderListID(index)) OrderList();
SlObject(list, slt);
}
@ -237,8 +274,18 @@ struct ORDLChunkHandler : ChunkHandler {
void FixPointers() const override
{
bool migrate_orders = IsSavegameVersionBefore(SLV_ORDERS_OWNED_BY_ORDERLIST);
for (OrderList *list : OrderList::Iterate()) {
SlObject(list, GetOrderListDescription());
if (migrate_orders) {
std::vector<Order> orders;
for (OldOrderSaveLoadItem *old_order = GetOldOrder(list->old_order_index); old_order != nullptr; old_order = GetOldOrder(old_order->next)) {
orders.push_back(std::move(old_order->order));
}
list->orders = std::move(orders);
}
}
}
};
@ -261,7 +308,9 @@ SaveLoadTable GetOrderBackupDescription()
SLE_CONDVAR(OrderBackup, timetable_start, SLE_UINT64, SLV_TIMETABLE_START_TICKS_FIX, SL_MAX_VERSION),
SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_FILE_U8 | SLE_VAR_U16, SLV_176, SLV_180),
SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_UINT16, SLV_180, SL_MAX_VERSION),
SLE_REF(OrderBackup, orders, REF_ORDER),
SLE_CONDVARNAME(OrderBackup, old_order_index, "orders", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_69),
SLE_CONDVARNAME(OrderBackup, old_order_index, "orders", SLE_UINT32, SLV_69, SLV_ORDERS_OWNED_BY_ORDERLIST),
SLEG_CONDSTRUCTLIST("orders", SlOrders<OrderBackup>, SLV_ORDERS_OWNED_BY_ORDERLIST, SL_MAX_VERSION),
};
return _order_backup_desc;
@ -301,8 +350,18 @@ struct BKORChunkHandler : ChunkHandler {
void FixPointers() const override
{
bool migrate_orders = IsSavegameVersionBefore(SLV_ORDERS_OWNED_BY_ORDERLIST);
for (OrderBackup *ob : OrderBackup::Iterate()) {
SlObject(ob, GetOrderBackupDescription());
if (migrate_orders) {
std::vector<Order> orders;
for (OldOrderSaveLoadItem *old_order = GetOldOrder(ob->old_order_index); old_order != nullptr; old_order = GetOldOrder(old_order->next)) {
orders.push_back(std::move(old_order->order));
}
ob->orders = std::move(orders);
}
}
}
};

View File

@ -1222,7 +1222,6 @@ static size_t ReferenceToInt(const void *obj, SLRefType rt)
case REF_VEHICLE: return ((const Vehicle*)obj)->index + 1;
case REF_STATION: return ((const Station*)obj)->index + 1;
case REF_TOWN: return ((const Town*)obj)->index + 1;
case REF_ORDER: return ((const Order*)obj)->index + 1;
case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1;
case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1;
case REF_CARGO_PACKET: return ((const CargoPacket*)obj)->index + 1;
@ -1268,12 +1267,6 @@ static void *IntToReference(size_t index, SLRefType rt)
if (OrderList::IsValidID(index)) return OrderList::Get(index);
SlErrorCorrupt("Referencing invalid OrderList");
case REF_ORDER:
if (Order::IsValidID(index)) return Order::Get(index);
/* in old versions, invalid order was used to mark end of order list */
if (IsSavegameVersionBefore(SLV_5, 2)) return nullptr;
SlErrorCorrupt("Referencing invalid Order");
case REF_VEHICLE_OLD:
case REF_VEHICLE:
if (Vehicle::IsValidID(index)) return Vehicle::Get(index);
@ -2907,11 +2900,14 @@ static void ResetSettings()
}
}
extern void ClearOldOrders();
/**
* Clear temporary data that is passed between various saveload phases.
*/
static void ResetSaveloadData()
{
ClearOldOrders();
ResetTempEngineData();
ClearRailTypeLabelList();
ClearRoadTypeLabelList();

View File

@ -401,6 +401,7 @@ enum SaveLoadVersion : uint16_t {
SLV_PROTECT_PLACED_HOUSES, ///< 351 PR#13270 Houses individually placed by players can be protected from town/AI removal.
SLV_SCRIPT_SAVE_INSTANCES, ///< 352 PR#13556 Scripts are allowed to save instances.
SLV_FIX_SCC_ENCODED_NEGATIVE, ///< 353 PR#14049 Fix encoding of negative parameters.
SLV_ORDERS_OWNED_BY_ORDERLIST, ///< 354 PR#13948 Orders stored in OrderList, pool removed.
SL_MAX_VERSION, ///< Highest possible saveload version
};
@ -602,7 +603,6 @@ public:
/** Type of reference (#SLE_REF, #SLE_CONDREF). */
enum SLRefType : uint8_t {
REF_ORDER = 0, ///< Load/save a reference to an order.
REF_VEHICLE = 1, ///< Load/save a reference to a vehicle.
REF_STATION = 2, ///< Load/save a reference to a station.
REF_TOWN = 3, ///< Load/save a reference to a town.

View File

@ -28,14 +28,14 @@
* Update the buoy orders to be waypoint orders.
* @param o the order 'list' to check.
*/
static void UpdateWaypointOrder(Order *o)
static void UpdateWaypointOrder(Order &o)
{
if (!o->IsType(OT_GOTO_STATION)) return;
if (!o.IsType(OT_GOTO_STATION)) return;
const Station *st = Station::Get(o->GetDestination().ToStationID());
const Station *st = Station::Get(o.GetDestination().ToStationID());
if ((st->had_vehicle_of_type & HVOT_WAYPOINT) == 0) return;
o->MakeGoToWaypoint(o->GetDestination().ToStationID());
o.MakeGoToWaypoint(o.GetDestination().ToStationID());
}
/**
@ -49,14 +49,14 @@ void MoveBuoysToWaypoints()
VehicleType vt = ol->GetFirstSharedVehicle()->type;
if (vt != VEH_SHIP && vt != VEH_TRAIN) continue;
for (Order *o = ol->GetFirstOrder(); o != nullptr; o = o->next) UpdateWaypointOrder(o);
for (Order &o : ol->GetOrders()) UpdateWaypointOrder(o);
}
for (Vehicle *v : Vehicle::Iterate()) {
VehicleType vt = v->type;
if (vt != VEH_SHIP && vt != VEH_TRAIN) continue;
UpdateWaypointOrder(&v->current_order);
UpdateWaypointOrder(v->current_order);
}
/* Now make the stations waypoints */

View File

@ -274,10 +274,10 @@ void AfterLoadVehiclesPhase1(bool part_of_load)
* a) both next_shared and previous_shared are not set for pre 5,2 games
* b) both next_shared and previous_shared are set for later games
*/
std::map<Order*, OrderList*> mapping;
std::map<uint32_t, OrderList *> mapping;
for (Vehicle *v : Vehicle::Iterate()) {
if (v->old_orders != nullptr) {
if (v->orders != nullptr) {
if (IsSavegameVersionBefore(SLV_105)) { // Pre-105 didn't save an OrderList
if (mapping[v->old_orders] == nullptr) {
/* This adds the whole shared vehicle chain for case b */
@ -286,7 +286,11 @@ void AfterLoadVehiclesPhase1(bool part_of_load)
* allowed in these savegames matches the number of OrderLists. As
* such each vehicle can get an OrderList and it will (still) fit. */
assert(OrderList::CanAllocateItem());
v->orders = mapping[v->old_orders] = new OrderList(v->old_orders, v);
std::vector<Order> orders;
for (const OldOrderSaveLoadItem *old_order = GetOldOrder(v->old_orders); old_order != nullptr; old_order = GetOldOrder(old_order->next)) {
orders.push_back(std::move(old_order->order));
}
v->orders = mapping[v->old_orders] = new OrderList(std::move(orders), v);
} else {
v->orders = mapping[v->old_orders];
/* For old games (case a) we must create the shared vehicle chain */
@ -296,7 +300,7 @@ void AfterLoadVehiclesPhase1(bool part_of_load)
}
} else { // OrderList was saved as such, only recalculate not saved values
if (v->PreviousShared() == nullptr) {
v->orders->Initialize(v->orders->first, v);
v->orders->Initialize(v);
}
}
}
@ -320,7 +324,8 @@ void AfterLoadVehiclesPhase1(bool part_of_load)
/* As above, allocating OrderList here is safe. */
assert(OrderList::CanAllocateItem());
v->orders = new OrderList(nullptr, v);
v->orders = new OrderList();
v->orders->first_shared = v;
for (Vehicle *u = v; u != nullptr; u = u->next_shared) {
u->orders = v->orders;
}
@ -715,7 +720,8 @@ public:
SLE_CONDVAR(Vehicle, timetable_start, SLE_FILE_I32 | SLE_VAR_U64, SLV_129, SLV_TIMETABLE_START_TICKS),
SLE_CONDVAR(Vehicle, timetable_start, SLE_UINT64, SLV_TIMETABLE_START_TICKS, SL_MAX_VERSION),
SLE_CONDREF(Vehicle, orders, REF_ORDER, SL_MIN_VERSION, SLV_105),
SLE_CONDVARNAME(Vehicle, old_orders, "orders", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_69),
SLE_CONDVARNAME(Vehicle, old_orders, "orders", SLE_UINT32, SLV_69, SLV_105),
SLE_CONDREF(Vehicle, orders, REF_ORDERLIST, SLV_105, SL_MAX_VERSION),
SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31),

View File

@ -49,14 +49,14 @@ static std::vector<OldWaypoint> _old_waypoints;
* Update the waypoint orders to get the new waypoint ID.
* @param o the order 'list' to check.
*/
static void UpdateWaypointOrder(Order *o)
static void UpdateWaypointOrder(Order &o)
{
if (!o->IsType(OT_GOTO_WAYPOINT)) return;
if (!o.IsType(OT_GOTO_WAYPOINT)) return;
for (OldWaypoint &wp : _old_waypoints) {
if (wp.index != o->GetDestination()) continue;
if (wp.index != o.GetDestination()) continue;
o->SetDestination(wp.new_index);
o.SetDestination(wp.new_index);
return;
}
}
@ -147,13 +147,13 @@ void MoveWaypointsToBaseStations()
for (OrderList *ol : OrderList::Iterate()) {
if (ol->GetFirstSharedVehicle()->type != VEH_TRAIN) continue;
for (Order *o = ol->GetFirstOrder(); o != nullptr; o = o->next) UpdateWaypointOrder(o);
for (Order &o : ol->GetOrders()) UpdateWaypointOrder(o);
}
for (Vehicle *v : Vehicle::Iterate()) {
if (v->type != VEH_TRAIN) continue;
UpdateWaypointOrder(&v->current_order);
UpdateWaypointOrder(v->current_order);
}
ResetOldWaypoints();

View File

@ -8,6 +8,7 @@
/** @file script_order.cpp Implementation of ScriptOrder. */
#include "../../stdafx.h"
#include <ranges>
#include "script_order.hpp"
#include "script_cargo.hpp"
#include "script_map.hpp"
@ -67,15 +68,12 @@ static const Order *ResolveOrder(VehicleID vehicle_id, ScriptOrder::OrderPositio
order_position = ScriptOrder::ResolveOrderPosition(vehicle_id, order_position);
if (order_position == ScriptOrder::ORDER_INVALID) return nullptr;
}
const Order *order = v->GetFirstOrder();
assert(order != nullptr);
while (order->GetType() == OT_IMPLICIT) order = order->next;
while (order_position > 0) {
order_position = (ScriptOrder::OrderPosition)(order_position - 1);
order = order->next;
while (order->GetType() == OT_IMPLICIT) order = order->next;
}
return order;
auto real_orders = v->Orders() | std::views::filter([](const Order &order) { return !order.IsType(OT_IMPLICIT); });
auto it = std::ranges::next(std::begin(real_orders), order_position, std::end(real_orders));
if (it != std::end(real_orders)) return &*it;
return nullptr;
}
/**
@ -91,16 +89,16 @@ static int ScriptOrderPositionToRealOrderPosition(VehicleID vehicle_id, ScriptOr
assert(ScriptOrder::IsValidVehicleOrder(vehicle_id, order_position));
int pos = (int)order_position;
int res = (int)order_position;
const Order *order = v->orders->GetFirstOrder();
assert(order != nullptr);
for (; order->GetType() == OT_IMPLICIT; order = order->next) res++;
while (order_position > 0) {
order_position = (ScriptOrder::OrderPosition)(order_position - 1);
order = order->next;
for (; order->GetType() == OT_IMPLICIT; order = order->next) res++;
for (const Order &order : v->Orders()) {
if (order.IsType(OT_IMPLICIT)) {
++res;
} else {
if (pos == 0) break;
--pos;
}
}
return res;
}
@ -111,12 +109,18 @@ static int ScriptOrderPositionToRealOrderPosition(VehicleID vehicle_id, ScriptOr
*/
static ScriptOrder::OrderPosition RealOrderPositionToScriptOrderPosition(VehicleID vehicle_id, int order_position)
{
const Order *order = ::Vehicle::Get(vehicle_id)->GetFirstOrder();
assert(order != nullptr);
const Vehicle *v = ::Vehicle::Get(vehicle_id);
int num_implicit_orders = 0;
for (int i = 0; i < order_position; i++) {
if (order->GetType() == OT_IMPLICIT) num_implicit_orders++;
order = order->next;
int pos = order_position;
for (const Order &order : v->Orders()) {
if (order.IsType(OT_IMPLICIT)) {
++num_implicit_orders;
} else {
if (pos == 0) break;
--pos;
}
}
return static_cast<ScriptOrder::OrderPosition>(order_position - num_implicit_orders);
}

View File

@ -34,8 +34,8 @@ ScriptStationList_Vehicle::ScriptStationList_Vehicle(VehicleID vehicle_id)
const Vehicle *v = ::Vehicle::Get(vehicle_id);
for (Order *o = v->GetFirstOrder(); o != nullptr; o = o->next) {
if (o->IsType(OT_GOTO_STATION)) this->AddItem(o->GetDestination().ToStationID().base());
for (const Order &o : v->Orders()) {
if (o.IsType(OT_GOTO_STATION)) this->AddItem(o.GetDestination().ToStationID().base());
}
}

View File

@ -34,7 +34,7 @@ ScriptWaypointList_Vehicle::ScriptWaypointList_Vehicle(VehicleID vehicle_id)
const Vehicle *v = ::Vehicle::Get(vehicle_id);
for (const Order *o = v->GetFirstOrder(); o != nullptr; o = o->next) {
if (o->IsType(OT_GOTO_WAYPOINT)) this->AddItem(o->GetDestination().ToStationID().base());
for (const Order &o : v->Orders()) {
if (o.IsType(OT_GOTO_WAYPOINT)) this->AddItem(o.GetDestination().ToStationID().base());
}
}

View File

@ -2752,8 +2752,8 @@ bool HasStationInUse(StationID station, bool include_company, CompanyID company)
assert(v != nullptr);
if ((v->owner == company) != include_company) continue;
for (const Order *order = orderlist->GetFirstOrder(); order != nullptr; order = order->next) {
if (order->GetDestination() == station && (order->IsType(OT_GOTO_STATION) || order->IsType(OT_GOTO_WAYPOINT))) {
for (const Order &order : orderlist->GetOrders()) {
if (order.GetDestination() == station && (order.IsType(OT_GOTO_STATION) || order.IsType(OT_GOTO_WAYPOINT))) {
return true;
}
}
@ -4045,15 +4045,15 @@ void DeleteStaleLinks(Station *from)
/* Have all vehicles refresh their next hops before deciding to
* remove the node. */
std::vector<Vehicle *> vehicles;
for (OrderList *l : OrderList::Iterate()) {
for (const OrderList *l : OrderList::Iterate()) {
bool found_from = false;
bool found_to = false;
for (Order *order = l->GetFirstOrder(); order != nullptr; order = order->next) {
if (!order->IsType(OT_GOTO_STATION) && !order->IsType(OT_IMPLICIT)) continue;
if (order->GetDestination() == from->index) {
for (const Order &order : l->GetOrders()) {
if (!order.IsType(OT_GOTO_STATION) && !order.IsType(OT_IMPLICIT)) continue;
if (order.GetDestination() == from->index) {
found_from = true;
if (found_to) break;
} else if (order->GetDestination() == to->index) {
} else if (order.GetDestination() == to->index) {
found_to = true;
if (found_from) break;
}

View File

@ -479,7 +479,8 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling)
assert(real_current_order != nullptr);
VehicleOrderID first_manual_order = 0;
for (Order *o = v->GetFirstOrder(); o != nullptr && o->IsType(OT_IMPLICIT); o = o->next) {
for (const Order &o : v->Orders()) {
if (!o.IsType(OT_IMPLICIT)) break;
++first_manual_order;
}

View File

@ -102,15 +102,15 @@ bool VehicleIsAboveLatenessThreshold(TimerGameTick::Ticks ticks, bool round_to_d
* @param travelling whether we are interested in the travel or the wait part.
* @return true if the travel/wait time can be used.
*/
static bool CanDetermineTimeTaken(const Order *order, bool travelling)
static bool CanDetermineTimeTaken(const Order &order, bool travelling)
{
/* Current order is conditional */
if (order->IsType(OT_CONDITIONAL) || order->IsType(OT_IMPLICIT)) return false;
if (order.IsType(OT_CONDITIONAL) || order.IsType(OT_IMPLICIT)) return false;
/* No travel time and we have not already finished travelling */
if (travelling && !order->IsTravelTimetabled()) return false;
if (travelling && !order.IsTravelTimetabled()) return false;
/* No wait time but we are loading at this timetabled station */
if (!travelling && !order->IsWaitTimetabled() && order->IsType(OT_GOTO_STATION) &&
!(order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) {
if (!travelling && !order.IsWaitTimetabled() && order.IsType(OT_GOTO_STATION) &&
!(order.GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) {
return false;
}
@ -139,7 +139,7 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID
TimerGameTick::Ticks sum = offset;
VehicleOrderID i = start;
const Order *order = v->GetOrder(i);
auto orders = v->Orders();
/* Cyclically loop over all orders until we reach the current one again.
* As we may start at the current order, do a post-checking loop */
@ -147,32 +147,26 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID
/* Automatic orders don't influence the overall timetable;
* they just add some untimetabled entries, but the time till
* the next non-implicit order can still be known. */
if (!order->IsType(OT_IMPLICIT)) {
if (!orders[i].IsType(OT_IMPLICIT)) {
if (travelling || i != start) {
if (!CanDetermineTimeTaken(order, true)) return;
sum += order->GetTimetabledTravel();
if (!CanDetermineTimeTaken(orders[i], true)) return;
sum += orders[i].GetTimetabledTravel();
table[i].arrival = sum;
}
if (!CanDetermineTimeTaken(order, false)) return;
sum += order->GetTimetabledWait();
if (!CanDetermineTimeTaken(orders[i], false)) return;
sum += orders[i].GetTimetabledWait();
table[i].departure = sum;
}
++i;
order = order->next;
if (i >= v->GetNumOrders()) {
i = 0;
assert(order == nullptr);
order = v->orders->GetFirstOrder();
}
i = v->orders->GetNext(i);
} while (i != start);
/* When loading at a scheduled station we still have to treat the
* travelling part of the first order. */
if (!travelling) {
if (!CanDetermineTimeTaken(order, true)) return;
sum += order->GetTimetabledTravel();
if (!CanDetermineTimeTaken(orders[i], true)) return;
sum += orders[i].GetTimetabledTravel();
table[i].arrival = sum;
}
}
@ -443,25 +437,18 @@ struct TimetableWindow : Window {
int index_column_width = GetStringBoundingBox(GetString(STR_ORDER_INDEX, GetParamMaxValue(v->GetNumOrders(), 2))).width + 2 * GetSpriteSize(rtl ? SPR_ARROW_RIGHT : SPR_ARROW_LEFT).width + WidgetDimensions::scaled.hsep_normal;
int middle = rtl ? tr.right - index_column_width : tr.left + index_column_width;
const Order *order = v->GetOrder(order_id);
while (order != nullptr) {
auto orders = v->Orders();
while (true) {
/* Don't draw anything if it extends past the end of the window. */
if (!this->vscroll->IsVisible(i)) break;
if (i % 2 == 0) {
DrawOrderString(v, order, order_id, tr.top, i == selected, true, tr.left, middle, tr.right);
order_id++;
if (order_id >= v->GetNumOrders()) {
order = v->GetOrder(0);
final_order = true;
} else {
order = order->next;
}
DrawOrderString(v, &orders[order_id], order_id, tr.top, i == selected, true, tr.left, middle, tr.right);
if (order_id > v->orders->GetNext(order_id)) final_order = true;
order_id = v->orders->GetNext(order_id);
} else {
TextColour colour;
std::string string = GetTimetableTravelString(*order, i, colour);
std::string string = GetTimetableTravelString(orders[order_id], i, colour);
DrawString(rtl ? tr.left : middle, rtl ? middle : tr.right, tr.top, string, colour);

View File

@ -2144,24 +2144,23 @@ void Vehicle::DeleteUnreachedImplicitOrders()
}
}
const Order *order = this->GetOrder(this->cur_implicit_order_index);
while (order != nullptr) {
auto orders = this->Orders();
VehicleOrderID cur = this->cur_implicit_order_index;
while (cur != INVALID_VEH_ORDER_ID) {
if (this->cur_implicit_order_index == this->cur_real_order_index) break;
if (order->IsType(OT_IMPLICIT)) {
if (orders[cur].IsType(OT_IMPLICIT)) {
DeleteOrder(this, this->cur_implicit_order_index);
/* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
order = this->GetOrder(this->cur_implicit_order_index);
} else {
/* Skip non-implicit orders, e.g. service-orders */
order = order->next;
this->cur_implicit_order_index++;
}
/* Wrap around */
if (order == nullptr) {
order = this->GetOrder(0);
this->cur_implicit_order_index = 0;
if (cur < this->orders->GetNext(cur)) {
this->cur_implicit_order_index++;
} else {
/* Wrapped around. */
this->cur_implicit_order_index = 0;
}
cur = this->orders->GetNext(cur);
}
}
}
@ -2201,7 +2200,7 @@ void Vehicle::BeginLoading()
in_list->GetDestination() != this->last_station_visited)) {
bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
/* Do not create consecutive duplicates of implicit orders */
Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : nullptr);
const Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : nullptr);
if (prev_order == nullptr ||
(!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
prev_order->GetDestination() != this->last_station_visited) {
@ -2238,33 +2237,30 @@ void Vehicle::BeginLoading()
InvalidateVehicleOrder(this, 0);
} else {
/* Delete all implicit orders up to the station we just reached */
const Order *order = this->GetOrder(this->cur_implicit_order_index);
while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
if (order->IsType(OT_IMPLICIT)) {
VehicleOrderID cur = this->cur_implicit_order_index;
auto orders = this->Orders();
while (!orders[cur].IsType(OT_IMPLICIT) || orders[cur].GetDestination() != this->last_station_visited) {
if (orders[cur].IsType(OT_IMPLICIT)) {
DeleteOrder(this, this->cur_implicit_order_index);
/* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
order = this->GetOrder(this->cur_implicit_order_index);
} else {
/* Skip non-implicit orders, e.g. service-orders */
order = order->next;
this->cur_implicit_order_index++;
if (cur < this->orders->GetNext(cur)) {
this->cur_implicit_order_index++;
} else {
/* Wrapped around. */
this->cur_implicit_order_index = 0;
}
cur = this->orders->GetNext(cur);
}
/* Wrap around */
if (order == nullptr) {
order = this->GetOrder(0);
this->cur_implicit_order_index = 0;
}
assert(order != nullptr);
}
}
} else if (!suppress_implicit_orders &&
((this->orders == nullptr ? OrderList::CanAllocateItem() : this->orders->GetNumOrders() < MAX_VEH_ORDER_ID)) &&
Order::CanAllocateItem()) {
(this->orders == nullptr ? OrderList::CanAllocateItem() : this->orders->GetNumOrders() < MAX_VEH_ORDER_ID)) {
/* Insert new implicit order */
Order *implicit_order = new Order();
implicit_order->MakeImplicit(this->last_station_visited);
InsertOrder(this, implicit_order, this->cur_implicit_order_index);
Order implicit_order{};
implicit_order.MakeImplicit(this->last_station_visited);
InsertOrder(this, std::move(implicit_order), this->cur_implicit_order_index);
if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
/* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
@ -2434,10 +2430,9 @@ void Vehicle::HandleLoading(bool mode)
*/
bool Vehicle::HasFullLoadOrder() const
{
for (Order *o : this->Orders()) {
if (o->IsType(OT_GOTO_STATION) && o->GetLoadType() & (OLFB_FULL_LOAD | OLF_FULL_LOAD_ANY)) return true;
}
return false;
return std::ranges::any_of(this->Orders(), [](const Order &o) {
return o.IsType(OT_GOTO_STATION) && o.GetLoadType() & (OLFB_FULL_LOAD | OLF_FULL_LOAD_ANY);
});
}
/**
@ -2446,10 +2441,7 @@ bool Vehicle::HasFullLoadOrder() const
*/
bool Vehicle::HasConditionalOrder() const
{
for (Order *o : this->Orders()) {
if (o->IsType(OT_CONDITIONAL)) return true;
}
return false;
return std::ranges::any_of(this->Orders(), [](const Order &o) { return o.IsType(OT_CONDITIONAL); });
}
/**
@ -2458,10 +2450,9 @@ bool Vehicle::HasConditionalOrder() const
*/
bool Vehicle::HasUnbunchingOrder() const
{
for (Order *o : this->Orders()) {
if (o->IsType(OT_GOTO_DEPOT) && o->GetDepotActionType() & ODATFB_UNBUNCH) return true;
}
return false;
return std::ranges::any_of(this->Orders(), [](const Order &o) {
return o.IsType(OT_GOTO_DEPOT) && (o.GetDepotActionType() & ODATFB_UNBUNCH);
});
}
/**
@ -2472,7 +2463,7 @@ static bool PreviousOrderIsUnbunching(const Vehicle *v)
{
/* If we are headed for the first order, we must wrap around back to the last order. */
bool is_first_order = (v->GetOrder(v->cur_implicit_order_index) == v->GetFirstOrder());
Order *previous_order = (is_first_order) ? v->GetLastOrder() : v->GetOrder(v->cur_implicit_order_index - 1);
const Order *previous_order = (is_first_order) ? v->GetLastOrder() : v->GetOrder(v->cur_implicit_order_index - 1);
if (previous_order == nullptr || !previous_order->IsType(OT_GOTO_DEPOT)) return false;
return (previous_order->GetDepotActionType() & ODATFB_UNBUNCH) != 0;
@ -2942,7 +2933,7 @@ void Vehicle::AddToShared(Vehicle *shared_chain)
if (shared_chain->orders == nullptr) {
assert(shared_chain->previous_shared == nullptr);
assert(shared_chain->next_shared == nullptr);
this->orders = shared_chain->orders = new OrderList(nullptr, shared_chain);
this->orders = shared_chain->orders = new OrderList(shared_chain);
}
this->next_shared = shared_chain->next_shared;
@ -3258,13 +3249,5 @@ bool VehiclesHaveSameEngineList(const Vehicle *v1, const Vehicle *v2)
*/
bool VehiclesHaveSameOrderList(const Vehicle *v1, const Vehicle *v2)
{
const Order *o1 = v1->GetFirstOrder();
const Order *o2 = v2->GetFirstOrder();
while (true) {
if (o1 == nullptr && o2 == nullptr) return true;
if (o1 == nullptr || o2 == nullptr) return false;
if (!o1->Equals(*o2)) return false;
o1 = o1->next;
o2 = o2->next;
}
return std::ranges::equal(v1->Orders(), v2->Orders(), [](const Order &o1, const Order &o2) { return o1.Equals(o2); });
}

View File

@ -337,7 +337,7 @@ public:
union {
OrderList *orders = nullptr; ///< Pointer to the order list for this vehicle
Order *old_orders; ///< Only used during conversion of old save games
uint32_t old_orders; ///< Only used during conversion of old save games
};
NewGRFCache grf_cache{}; ///< Cache of often used calculated NewGRF values
@ -682,7 +682,19 @@ public:
* Get the first order of the vehicles order list.
* @return first order of order list.
*/
inline Order *GetFirstOrder() const { return (this->orders == nullptr) ? nullptr : this->orders->GetFirstOrder(); }
inline const Order *GetFirstOrder() const { return (this->orders == nullptr) ? nullptr : this->GetOrder(this->orders->GetFirstOrder()); }
inline std::span<const Order> Orders() const
{
if (this->orders == nullptr) return {};
return this->orders->GetOrders();
}
inline std::span<Order> Orders()
{
if (this->orders == nullptr) return {};
return this->orders->GetOrders();
}
void AddToShared(Vehicle *shared_chain);
void RemoveFromShared();
@ -908,9 +920,9 @@ public:
* Returns the last order of a vehicle, or nullptr if it doesn't exists
* @return last order of a vehicle, if available
*/
inline Order *GetLastOrder() const
inline const Order *GetLastOrder() const
{
return (this->orders == nullptr) ? nullptr : this->orders->GetLastOrder();
return (this->orders == nullptr) ? nullptr : this->orders->GetOrderAt(this->orders->GetLastOrder());
}
bool IsEngineCountable() const;
@ -1014,54 +1026,6 @@ public:
return v;
}
/**
* Iterator to iterate orders
* Supports deletion of current order
*/
struct OrderIterator {
typedef Order value_type;
typedef Order *pointer;
typedef Order &reference;
typedef size_t difference_type;
typedef std::forward_iterator_tag iterator_category;
explicit OrderIterator(OrderList *list) : list(list), prev(nullptr)
{
this->order = (this->list == nullptr) ? nullptr : this->list->GetFirstOrder();
}
bool operator==(const OrderIterator &other) const { return this->order == other.order; }
Order * operator*() const { return this->order; }
OrderIterator & operator++()
{
this->prev = (this->prev == nullptr) ? this->list->GetFirstOrder() : this->prev->next;
this->order = (this->prev == nullptr) ? nullptr : this->prev->next;
return *this;
}
private:
OrderList *list;
Order *order;
Order *prev;
};
/**
* Iterable ensemble of orders
*/
struct IterateWrapper {
OrderList *list;
IterateWrapper(OrderList *list = nullptr) : list(list) {}
OrderIterator begin() { return OrderIterator(this->list); }
OrderIterator end() { return OrderIterator(nullptr); }
bool empty() { return this->begin() == this->end(); }
};
/**
* Returns an iterable ensemble of orders of a vehicle
* @return an iterable ensemble of orders of a vehicle
*/
IterateWrapper Orders() const { return IterateWrapper(this->orders); }
uint32_t GetDisplayMaxWeight() const;
uint32_t GetDisplayMinPowerToWeight() const;
};

View File

@ -248,10 +248,7 @@ CommandCost CmdSellVehicle(DoCommandFlags flags, VehicleID v_id, bool sell_chain
if (!front->IsStoppedInDepot()) return CommandCost(STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT + front->type);
/* Can we actually make the order backup, i.e. are there enough orders? */
if (backup_order &&
front->orders != nullptr &&
!front->orders->IsShared() &&
!Order::CanAllocateItem(front->orders->GetNumOrders())) {
if (backup_order && front->orders != nullptr && !front->orders->IsShared()) {
/* Only happens in exceptional cases when there aren't enough orders anyhow.
* Thus it should be safe to just drop the orders in that case. */
backup_order = false;

View File

@ -1658,8 +1658,8 @@ static constexpr NWidgetPart _nested_vehicle_list[] = {
static void DrawSmallOrderList(const Vehicle *v, int left, int right, int y, uint order_arrow_width, VehicleOrderID start)
{
const Order *order = v->GetOrder(start);
if (order == nullptr) return;
auto orders = v->Orders();
if (orders.empty()) return;
bool rtl = _current_text_dir == TD_RTL;
int l_offset = rtl ? 0 : order_arrow_width;
@ -1670,37 +1670,32 @@ static void DrawSmallOrderList(const Vehicle *v, int left, int right, int y, uin
do {
if (oid == v->cur_real_order_index) DrawString(left, right, y, rtl ? STR_JUST_LEFT_ARROW : STR_JUST_RIGHT_ARROW, TC_BLACK, SA_LEFT, false, FS_SMALL);
if (order->IsType(OT_GOTO_STATION)) {
DrawString(left + l_offset, right - r_offset, y, GetString(STR_STATION_NAME, order->GetDestination()), TC_BLACK, SA_LEFT, false, FS_SMALL);
if (orders[oid].IsType(OT_GOTO_STATION)) {
DrawString(left + l_offset, right - r_offset, y, GetString(STR_STATION_NAME, orders[oid].GetDestination()), TC_BLACK, SA_LEFT, false, FS_SMALL);
y += GetCharacterHeight(FS_SMALL);
if (++i == 4) break;
}
oid++;
order = order->next;
if (order == nullptr) {
order = v->orders->GetFirstOrder();
oid = 0;
}
oid = v->orders->GetNext(oid);
} while (oid != start);
}
/** Draw small order list in the vehicle GUI, but without the little black arrow. This is used for shared order groups. */
static void DrawSmallOrderList(const Order *order, int left, int right, int y, uint order_arrow_width)
static void DrawSmallOrderList(const OrderList &orderlist, int left, int right, int y, uint order_arrow_width)
{
bool rtl = _current_text_dir == TD_RTL;
int l_offset = rtl ? 0 : order_arrow_width;
int r_offset = rtl ? order_arrow_width : 0;
int i = 0;
while (order != nullptr) {
if (order->IsType(OT_GOTO_STATION)) {
DrawString(left + l_offset, right - r_offset, y, GetString(STR_STATION_NAME, order->GetDestination()), TC_BLACK, SA_LEFT, false, FS_SMALL);
for (const Order &order : orderlist.GetOrders()) {
if (order.IsType(OT_GOTO_STATION)) {
DrawString(left + l_offset, right - r_offset, y, GetString(STR_STATION_NAME, order.GetDestination()), TC_BLACK, SA_LEFT, false, FS_SMALL);
y += GetCharacterHeight(FS_SMALL);
if (++i == 4) break;
}
order = order->next;
}
}
@ -1849,7 +1844,7 @@ void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int
DrawVehicleImage(vehgroup.vehicles_begin[i], {image_left + WidgetDimensions::scaled.hsep_wide * i, ir.top, image_right, ir.bottom}, selected_vehicle, EIT_IN_LIST, 0);
}
if (show_orderlist) DrawSmallOrderList((vehgroup.vehicles_begin[0])->GetFirstOrder(), olr.left, olr.right, ir.top + GetCharacterHeight(FS_SMALL), this->order_arrow_width);
if (show_orderlist) DrawSmallOrderList(*(vehgroup.vehicles_begin[0])->orders, olr.left, olr.right, ir.top + GetCharacterHeight(FS_SMALL), this->order_arrow_width);
DrawString(ir.left, ir.right, ir.top + WidgetDimensions::scaled.framerect.top, GetString(STR_JUST_COMMA, vehgroup.NumVehicles()), TC_BLACK);
break;

View File

@ -30,9 +30,9 @@ void FindVehiclesWithOrder(VehiclePredicate veh_pred, OrderPredicate ord_pred, V
if (!veh_pred(v)) continue;
/* Vehicle is a candidate, search for a matching order. */
for (const Order *order = orderlist->GetFirstOrder(); order != nullptr; order = order->next) {
for (const Order &order : orderlist->GetOrders()) {
if (!ord_pred(order)) continue;
if (!ord_pred(&order)) continue;
/* An order matches, we can add all shared vehicles to the list. */
for (; v != nullptr; v = v->NextShared()) {