mirror of https://github.com/OpenTTD/OpenTTD
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
parent
7344dfe651
commit
0455627d16
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 = ©->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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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(); });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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); });
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()) {
|
||||
|
|
Loading…
Reference in New Issue