diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp
index 888306b260..df64561cf3 100644
--- a/src/autoreplace_cmd.cpp
+++ b/src/autoreplace_cmd.cpp
@@ -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;
 	}
diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp
index 62dfb9f333..617bb91847 100644
--- a/src/industry_cmd.cpp
+++ b/src/industry_cmd.cpp
@@ -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
diff --git a/src/linkgraph/refresh.cpp b/src/linkgraph/refresh.cpp
index 1ffb1dda50..25e5f47fb1 100644
--- a/src/linkgraph/refresh.cpp
+++ b/src/linkgraph/refresh.cpp
@@ -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 {
diff --git a/src/linkgraph/refresh.h b/src/linkgraph/refresh.h
index 5473cbf92c..449bf2bd0d 100644
--- a/src/linkgraph/refresh.h
+++ b/src/linkgraph/refresh.h
@@ -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 */
diff --git a/src/order_backup.cpp b/src/order_backup.cpp
index f71b46d29d..ae52f635c9 100644
--- a/src/order_backup.cpp
+++ b/src/order_backup.cpp
@@ -27,18 +27,7 @@
 OrderBackupPool _order_backup_pool("BackupOrder");
 INSTANTIATE_POOL_METHODS(OrderBackup)
 
-/** Free everything that is allocated. */
-OrderBackup::~OrderBackup()
-{
-	if (CleaningPool()) return;
-
-	Order *o = this->orders;
-	while (o != nullptr) {
-		Order *next = o->next;
-		delete o;
-		o = next;
-	}
-}
+OrderBackup::~OrderBackup() = default;
 
 /**
  * Create an order backup for the given vehicle.
@@ -54,15 +43,7 @@ OrderBackup::OrderBackup(const Vehicle *v, uint32_t user) : user(user), tile(v->
 		this->clone = (v->FirstShared() == v) ? v->NextShared() : v->FirstShared();
 	} else {
 		/* Else copy the orders */
-		Order **tail = &this->orders;
-
-		/* Count the number of orders */
-		for (const Order *order : v->Orders()) {
-			Order *copy = new Order();
-			copy->AssignOrder(*order);
-			*tail = copy;
-			tail = &copy->next;
-		}
+		this->orders.assign(std::begin(v->Orders()), std::end(v->Orders()));
 	}
 }
 
@@ -75,9 +56,8 @@ void OrderBackup::DoRestore(Vehicle *v)
 	/* If we had shared orders, recover that */
 	if (this->clone != nullptr) {
 		Command<CMD_CLONE_ORDER>::Do(DoCommandFlag::Execute, CO_SHARE, v->index, this->clone->index);
-	} else if (this->orders != nullptr && OrderList::CanAllocateItem()) {
-		v->orders = new OrderList(this->orders, v);
-		this->orders = nullptr;
+	} else if (!this->orders.empty() && OrderList::CanAllocateItem()) {
+		v->orders = new OrderList(std::move(this->orders), v);
 		/* Make sure buoys/oil rigs are updated in the station list. */
 		InvalidateWindowClassesData(WC_STATION_LIST, 0);
 	}
@@ -251,12 +231,12 @@ CommandCost CmdClearOrderBackup(DoCommandFlags flags, TileIndex tile, ClientID u
 /* static */ void OrderBackup::RemoveOrder(OrderType type, DestinationID destination, bool hangar)
 {
 	for (OrderBackup *ob : OrderBackup::Iterate()) {
-		for (Order *order = ob->orders; order != nullptr; order = order->next) {
-			OrderType ot = order->GetType();
-			if (ot == OT_GOTO_DEPOT && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue;
+		for (Order &order : ob->orders) {
+			OrderType ot = order.GetType();
+			if (ot == OT_GOTO_DEPOT && (order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue;
 			if (ot == OT_GOTO_DEPOT && hangar && !IsHangarTile(ob->tile)) continue; // Not an aircraft? Can't have a hangar order.
 			if (ot == OT_IMPLICIT || (IsHangarTile(ob->tile) && ot == OT_GOTO_DEPOT && !hangar)) ot = OT_GOTO_STATION;
-			if (ot == type && order->GetDestination() == destination) {
+			if (ot == type && order.GetDestination() == destination) {
 				/* Remove the order backup! If a station/depot gets removed, we can't/shouldn't restore those broken orders. */
 				delete ob;
 				break;
diff --git a/src/order_backup.h b/src/order_backup.h
index 20eea9ee57..edeaf85e30 100644
--- a/src/order_backup.h
+++ b/src/order_backup.h
@@ -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);
diff --git a/src/order_base.h b/src/order_base.h
index dd1cffcf17..1b0f6b1752 100644
--- a/src/order_base.h
+++ b/src/order_base.h
@@ -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?
diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp
index 5ac05757b1..0e464f0206 100644
--- a/src/order_cmd.cpp
+++ b/src/order_cmd.cpp
@@ -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(); });
 }
 
 /**
diff --git a/src/order_func.h b/src/order_func.h
index 12f7d4684a..ff7d865f1a 100644
--- a/src/order_func.h
+++ b/src/order_func.h
@@ -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;
diff --git a/src/order_gui.cpp b/src/order_gui.cpp
index 9a44e263c8..965d3e90d8 100644
--- a/src/order_gui.cpp
+++ b/src/order_gui.cpp
@@ -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);
diff --git a/src/order_type.h b/src/order_type.h
index aa3bbbfccd..d52b33599b 100644
--- a/src/order_type.h
+++ b/src/order_type.h
@@ -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 {
diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
index 50934f9814..4e6899ea36 100644
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -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();
 		}
 	}
diff --git a/src/saveload/oldloader_sl.cpp b/src/saveload/oldloader_sl.cpp
index bd08924953..46585f0eab 100644
--- a/src/saveload/oldloader_sl.cpp
+++ b/src/saveload/oldloader_sl.cpp
@@ -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));
 
diff --git a/src/saveload/order_sl.cpp b/src/saveload/order_sl.cpp
index 4d84ba9c7a..6e3c20e1a3 100644
--- a/src/saveload/order_sl.cpp
+++ b/src/saveload/order_sl.cpp
@@ -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);
+			}
 		}
 	}
 };
diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp
index 761f2c4db4..f602bd1524 100644
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -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();
diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h
index 1c896392de..ec78153db1 100644
--- a/src/saveload/saveload.h
+++ b/src/saveload/saveload.h
@@ -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.
diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp
index 2ab144394e..7d3690d112 100644
--- a/src/saveload/station_sl.cpp
+++ b/src/saveload/station_sl.cpp
@@ -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 */
diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp
index 300aa8233a..e9e6033911 100644
--- a/src/saveload/vehicle_sl.cpp
+++ b/src/saveload/vehicle_sl.cpp
@@ -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),
diff --git a/src/saveload/waypoint_sl.cpp b/src/saveload/waypoint_sl.cpp
index ef8b401598..74c51befcb 100644
--- a/src/saveload/waypoint_sl.cpp
+++ b/src/saveload/waypoint_sl.cpp
@@ -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();
diff --git a/src/script/api/script_order.cpp b/src/script/api/script_order.cpp
index 6f9a018459..255bb8790a 100644
--- a/src/script/api/script_order.cpp
+++ b/src/script/api/script_order.cpp
@@ -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);
 }
diff --git a/src/script/api/script_stationlist.cpp b/src/script/api/script_stationlist.cpp
index 564fbe5766..459c9100f5 100644
--- a/src/script/api/script_stationlist.cpp
+++ b/src/script/api/script_stationlist.cpp
@@ -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());
 	}
 }
 
diff --git a/src/script/api/script_waypointlist.cpp b/src/script/api/script_waypointlist.cpp
index 1fb0f4bf8c..ad6ed72513 100644
--- a/src/script/api/script_waypointlist.cpp
+++ b/src/script/api/script_waypointlist.cpp
@@ -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());
 	}
 }
diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp
index be16b3698a..d997e37080 100644
--- a/src/station_cmd.cpp
+++ b/src/station_cmd.cpp
@@ -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;
 							}
diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp
index 48e11988b9..8022137ba6 100644
--- a/src/timetable_cmd.cpp
+++ b/src/timetable_cmd.cpp
@@ -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;
 	}
 
diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp
index b22369bc5d..4eafeb4fb4 100644
--- a/src/timetable_gui.cpp
+++ b/src/timetable_gui.cpp
@@ -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);
 
diff --git a/src/vehicle.cpp b/src/vehicle.cpp
index 99fc6bedf7..e688e19c6e 100644
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -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); });
 }
diff --git a/src/vehicle_base.h b/src/vehicle_base.h
index 6595f03d99..58c311e804 100644
--- a/src/vehicle_base.h
+++ b/src/vehicle_base.h
@@ -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;
 };
diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp
index 2352e8ec62..ba13b43cdf 100644
--- a/src/vehicle_cmd.cpp
+++ b/src/vehicle_cmd.cpp
@@ -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;
diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp
index ba389608c5..d0aff8f8ea 100644
--- a/src/vehicle_gui.cpp
+++ b/src/vehicle_gui.cpp
@@ -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;
diff --git a/src/vehiclelist_func.h b/src/vehiclelist_func.h
index 3e44610164..2a78fb0470 100644
--- a/src/vehiclelist_func.h
+++ b/src/vehiclelist_func.h
@@ -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()) {