From 0d54db5f9fb3a3987f908645ca9693684f34674b Mon Sep 17 00:00:00 2001 From: rubidium Date: Sat, 3 Jan 2009 13:52:06 +0000 Subject: [PATCH] (svn r14803) -Codechange: move order list management into it's own class and remove the order count from the savegame as we can easily count that (PhilSophus) --- src/oldloader.cpp | 16 +- src/openttd.cpp | 5 +- src/order_base.h | 189 +++++++++++++++++++++- src/order_cmd.cpp | 364 ++++++++++++++++++++++++++++-------------- src/order_func.h | 2 +- src/order_type.h | 2 + src/saveload.cpp | 7 +- src/saveload.h | 1 + src/timetable_cmd.cpp | 4 + src/train_cmd.cpp | 6 +- src/vehicle.cpp | 110 +++++++------ src/vehicle_base.h | 60 +++---- 12 files changed, 535 insertions(+), 231 deletions(-) diff --git a/src/oldloader.cpp b/src/oldloader.cpp index 2e072b76d7..a948249789 100644 --- a/src/oldloader.cpp +++ b/src/oldloader.cpp @@ -329,12 +329,9 @@ static StringID *_old_vehicle_names = NULL; static void FixOldVehicles() { - /* Check for shared orders, and link them correctly */ Vehicle* v; FOR_ALL_VEHICLES(v) { - Vehicle *u; - v->name = CopyFromOldName(_old_vehicle_names[v->index]); /* We haven't used this bit for stations for ages */ @@ -356,14 +353,7 @@ static void FixOldVehicles() v->current_order.MakeDummy(); } - FOR_ALL_VEHICLES_FROM(u, v->index + 1) { - /* If a vehicle has the same orders, add the link to eachother - * in both vehicles */ - if (v->orders == u->orders) { - u->AddToShared(v); - break; - } - } + /* Shared orders are fixed in AfterLoadVehicles now */ } } @@ -1144,7 +1134,7 @@ static const OldChunks vehicle_chunk[] = { OCL_VAR ( OC_UINT32, 1, &_old_order_ptr ), OCL_VAR ( OC_UINT16, 1, &_old_order ), - OCL_SVAR( OC_UINT8, Vehicle, num_orders ), + OCL_NULL ( 1 ), ///< num_orders, now calculated OCL_SVAR( OC_UINT8, Vehicle, cur_order_index ), OCL_SVAR( OC_TILE, Vehicle, dest_tile ), OCL_SVAR( OC_UINT16, Vehicle, load_unload_time_rem ), @@ -1250,7 +1240,7 @@ bool LoadOldVehicle(LoadgameState *ls, int num) * we go over that limit something is very wrong. In that case * we just assume there are no orders for the vehicle. */ - if (old_id < 5000) v->orders = GetOrder(old_id); + if (old_id < 5000) v->orders.old = GetOrder(old_id); } v->current_order.AssignOrder(UnpackOldOrder(_old_order)); diff --git a/src/openttd.cpp b/src/openttd.cpp index 488c969bd0..6bb681788f 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -2386,7 +2386,10 @@ bool AfterLoadGame() Vehicle *v; FOR_ALL_VEHICLES(v) { - if (v->orders != NULL && !v->orders->IsValid()) v->orders = NULL; + if (v->orders.list != NULL && v->orders.list->GetFirstOrder() != NULL && !v->orders.list->GetFirstOrder()->IsValid()) { + v->orders.list->FreeChain(); + v->orders.list = NULL; + } v->current_order.ConvertFromOldSavegame(); if (v->type == VEH_ROAD && v->IsPrimaryVehicle() && v->FirstShared() == v) { diff --git a/src/order_base.h b/src/order_base.h index 8462ec8e84..bb02ec103e 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -15,6 +15,7 @@ #include "waypoint_type.h" DECLARE_OLD_POOL(Order, Order, 6, 1000) +DECLARE_OLD_POOL(OrderList, OrderList, 4, 4000) /* If you change this, keep in mind that it is saved on 3 places: * - Load_ORDR, all the global orders @@ -118,12 +119,6 @@ public: */ void MakeConditional(VehicleOrderID order); - /** - * Free a complete order chain. - * @note do not use on "current_order" vehicle orders! - */ - void FreeChain(); - /** * Gets the destination of this order. * @pre IsType(OT_GOTO_WAYPOINT) || IsType(OT_GOTO_DEPOT) || IsType(OT_GOTO_STATION). @@ -207,6 +202,14 @@ public: bool ShouldStopAtStation(const Vehicle *v, StationID station) const; + /** Checks if this order has travel_time and if needed wait_time set. */ + inline bool IsCompletelyTimetabled() const + { + if (this->travel_time == 0 && !this->IsType(OT_CONDITIONAL)) return false; + if (this->wait_time == 0 && this->IsType(OT_GOTO_STATION) && !(this->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) return false; + return true; + } + /** * Assign the given order to this one. * @param other the data to copy (except next pointer). @@ -250,11 +253,183 @@ static inline VehicleOrderID GetNumOrders() return GetOrderPoolSize(); } +/** Shared order list linking together the linked list of orders and the list + * of vehicles sharing this order list. + */ +struct OrderList : PoolItem { +private: + friend void AfterLoadVehicles(bool part_of_load); ///< For instantiating the shared vehicle chain + friend const struct SaveLoad *GetOrderListDescription(); ///< Saving and loading of order lists. + + Order *first; ///< First order of the order list + VehicleOrderID num_orders; ///< NOSAVE: How many orders there are in the list + unsigned num_vehicles; ///< NOSAVE: Number of vehicles that share this order list + Vehicle *first_shared; ///< NOSAVE: pointer to the first vehicle in the shared order chain + + unsigned timetable_duration; ///< NOSAVE: Total duration of the order list + +public: + /** Default constructor producing an invalid order list. */ + OrderList() + : first(NULL), num_orders(INVALID_VEH_ORDER_ID), num_vehicles(0), first_shared(NULL), + timetable_duration(0) { } + + /** Create an order list with the given order chain for the given vehicle. + * @param chain is the pointer to the first order of the order chain + * @param v is any vehicle of the shared order vehicle chain (does not need to be the first) + */ + OrderList(Order *chain, Vehicle *v); + + /** Destructor. Invalidates OrderList for re-usage by the pool. */ + ~OrderList() { this->num_orders = INVALID_VEH_ORDER_ID; } + + /** Checks, if this is a valid order list. */ + inline bool IsValid() const { return this->num_orders != INVALID_VEH_ORDER_ID; } + + /** + * Get the first order of the order chain. + * @return the first order of the chain. + */ + inline Order *GetFirstOrder() const { return this->first; } + + /** + * 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 *GetOrderAt(int index) const; + + /** + * 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); } + + /** + * Get number of orders in the order list. + * @return number of orders in the chain. */ + inline VehicleOrderID GetNumOrders() const { return this->num_orders; } + + /** + * Insert a new order into the order chain. + * @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 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 DeleteOrderAt(int index); + + /** + * Move an order to another position within the order list. + * @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 MoveOrder(int from, int to); + + /** + * Is this a shared order list? + * @return whether this order list is shared among multiple vehicles + */ + inline bool IsShared() const { return this->num_vehicles > 1; }; + + /** + * Get the first vehicle of this vehicle chain. + * @return the first vehicle of the chain. + */ + inline Vehicle *GetFirstSharedVehicle() const { return this->first_shared; } + + /** + * Return the number of vehicles that share this orders list + * @return the count of vehicles that use this shared orders list + */ + inline unsigned GetNumVehicles() const { return this->num_vehicles; } + + /** + * Checks whether a vehicle is part of the shared vehicle chain. + * @param v is the vehicle to search in the shared vehicle chain. + */ + bool IsVehicleInSharedOrdersList(const Vehicle *v) const; + + /** + * Gets the position of the given vehicle within the shared order vehicle list. + * @param v is the vehicle of which to get the position + * @return position of v within the shared vehicle chain. + */ + int GetPositionInSharedOrderList(const Vehicle *v) const; + + /** + * Adds the given vehicle to this shared order list. + * @note This is supposed to be called after the vehicle has been inserted + * into the shared vehicle chain. + * @param v vehicle to add to the list + */ + inline void AddVehicle(Vehicle *v) { ++this->num_vehicles; } + + /** + * Removes the vehicle from the shared order list. + * @note This is supposed to be called when the vehicle is still in the chain + * @param v vehicle to remove from the list + */ + void RemoveVehicle(Vehicle *v); + + /** + * Checks whether all orders of the list have a filled timetable. + * @return whether all orders have a filled timetable. + */ + bool IsCompleteTimetable() const; + + /** + * Gets the total duration of the vehicles timetable or -1 is the timetable is not complete. + * @return total timetable duration or -1 for incomplete timetables + */ + inline int GetTimetableTotalDuration() const { return this->IsCompleteTimetable() ? this->timetable_duration : -1; } + + /** + * Gets the known duration of the vehicles timetable even if the timetable is not complete. + * @return known timetable duration + */ + inline int GetTimetableDurationIncomplete() const { return this->timetable_duration; } + + /** + * Must be called if an order's timetable is changed to update internal book keeping. + * @param delta By how many ticks has the timetable duration changed + */ + void UpdateOrderTimetable(int delta) { this->timetable_duration += delta; } + + /** + * Must be called if the whole timetable is cleared to update internal book keeping. + */ + void ResetOrderTimetable() { this->timetable_duration = 0; } + + /** + * Free a complete order chain. + * @param keep_orderlist If this is true only delete the orders, otherwise also delete the OrderList. + * @note do not use on "current_order" vehicle orders! + */ + void FreeChain(bool keep_orderlist = false); + + /** + * Checks for internal consistency of order list. Triggers assertion if something is wrong. + */ + void DebugCheckSanity() const; +}; + +static inline bool IsValidOrderListID(uint index) +{ + return index < GetOrderListPoolSize() && GetOrderList(index)->IsValid(); +} + #define FOR_ALL_ORDERS_FROM(order, start) for (order = GetOrder(start); order != NULL; order = (order->index + 1U < GetOrderPoolSize()) ? GetOrder(order->index + 1U) : NULL) if (order->IsValid()) #define FOR_ALL_ORDERS(order) FOR_ALL_ORDERS_FROM(order, 0) -#define FOR_VEHICLE_ORDERS(v, order) for (order = v->orders; order != NULL; order = order->next) +#define FOR_VEHICLE_ORDERS(v, order) for (order = (v->orders.list == NULL) ? NULL : v->orders.list->GetFirstOrder(); order != NULL; order = order->next) + + +#define FOR_ALL_ORDER_LISTS_FROM(ol, start) for (ol = GetOrderList(start); ol != NULL; ol = (ol->index + 1U < GetOrderListPoolSize()) ? GetOrderList(ol->index + 1U) : NULL) if (ol->IsValid()) +#define FOR_ALL_ORDER_LISTS(ol) FOR_ALL_ORDER_LISTS_FROM(ol, 0) /* (Un)pack routines */ Order UnpackOldOrder(uint16 packed); diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index d97eeadec9..21bbaf4f75 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -4,6 +4,7 @@ #include "stdafx.h" #include "openttd.h" +#include "debug.h" #include "order_base.h" #include "order_func.h" #include "airport.h" @@ -41,6 +42,7 @@ TileIndex _backup_orders_tile; BackuppedOrders _backup_orders_data; DEFINE_OLD_POOL_GENERIC(Order, Order); +DEFINE_OLD_POOL_GENERIC(OrderList, OrderList); void Order::Free() { @@ -107,12 +109,6 @@ void Order::SetRefit(CargoID cargo, byte subtype) this->refit_subtype = subtype; } -void Order::FreeChain() -{ - if (next != NULL) next->FreeChain(); - delete this; -} - bool Order::Equals(const Order &other) const { /* In case of go to nearest depot orders we need "only" compare the flags @@ -247,22 +243,6 @@ void InvalidateVehicleOrder(const Vehicle *v, int data) InvalidateWindow(WC_VEHICLE_TIMETABLE, v->index); } -/** - * - * Swap two orders - * - */ -static void SwapOrders(Order *order1, Order *order2) -{ - Order temp_order; - - temp_order = *order1; - order1->AssignOrder(*order2); - order1->next = order2->next; - order2->AssignOrder(temp_order); - order2->next = temp_order.next; -} - /** * * Assign data to an order (from an other order) @@ -283,6 +263,176 @@ void Order::AssignOrder(const Order &other) } +OrderList::OrderList(Order *chain, Vehicle *v) : + first(chain), num_orders(0), num_vehicles(1), first_shared(v), + timetable_duration(0) +{ + for (Order *o = this->first; o != NULL; o = o->next) { + ++this->num_orders; + this->timetable_duration += o->wait_time + o->travel_time; + } + + for (Vehicle *u = v->PreviousShared(); u != NULL; u = u->PreviousShared()) { + ++this->num_vehicles; + this->first_shared = u; + } + + for (const Vehicle *u = v->NextShared(); u != NULL; u = u->NextShared()) ++this->num_vehicles; +} + +void OrderList::FreeChain(bool keep_orderlist) +{ + Order *next; + for(Order *o = this->first; o != NULL; o = next) { + next = o->next; + delete o; + } + + if (keep_orderlist) { + this->first = NULL; + this->num_orders = 0; + this->timetable_duration = 0; + } else { + delete this; + } +} + +Order *OrderList::GetOrderAt(int index) const +{ + if (index < 0) return NULL; + + Order *order = this->first; + + while (order != NULL && index-- > 0) + order = order->next; + + return order; +} + +void OrderList::InsertOrderAt(Order *new_order, int index) +{ + if (this->first == NULL) { + 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; + this->timetable_duration += new_order->wait_time + new_order->travel_time; +} + + +void OrderList::DeleteOrderAt(int index) +{ + if (index >= this->num_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; + this->timetable_duration -= (to_remove->wait_time + to_remove->travel_time); + delete to_remove; +} + +void OrderList::MoveOrder(int from, int to) +{ + if (from >= this->num_orders || to >= this->num_orders || from == to) 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; + } 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; + } +} + +void OrderList::RemoveVehicle(Vehicle *v) +{ + --this->num_vehicles; + if (v == this->first_shared) this->first_shared = v->NextShared(); +} + +bool OrderList::IsVehicleInSharedOrdersList(const Vehicle *v) const +{ + for (const Vehicle *v_shared = this->first_shared; v_shared != NULL; v_shared = v_shared->NextShared()) { + if (v_shared == v) return true; + } + + return false; +} + +int OrderList::GetPositionInSharedOrderList(const Vehicle *v) const +{ + int count = 0; + for (const Vehicle *v_shared = v->PreviousShared(); v_shared != NULL; v_shared = v_shared->PreviousShared()) count++; + return count; +} + +bool OrderList::IsCompleteTimetable() const +{ + for (Order *o = this->first; o != NULL; o = o->next) { + if (!o->IsCompletelyTimetabled()) return false; + } + return true; +} + +void OrderList::DebugCheckSanity() const +{ + VehicleOrderID check_num_orders = 0; + unsigned check_num_vehicles = 0; + unsigned check_timetable_duration = 0; + + DEBUG(misc, 6, "Checking OrderList %hu for sanity...", this->index); + + for (const Order *o = this->first; o != NULL; o = o->next) { + ++check_num_orders; + check_timetable_duration += o->wait_time + o->travel_time; + } + assert(this->num_orders == check_num_orders); + assert(this->timetable_duration == check_timetable_duration); + + for (const Vehicle *v = this->first_shared; v != NULL; v = v->NextShared()) { + ++check_num_vehicles; + assert(v->orders.list == this); + } + assert(this->num_vehicles == check_num_vehicles); + DEBUG(misc, 6, "... detected %u orders, %u vehicles, %u ticks", (unsigned)this->num_orders, + this->num_vehicles, this->timetable_duration); +} + /** * Delete all news items regarding defective orders about a vehicle * This could kill still valid warnings (for example about void order when just @@ -315,7 +465,7 @@ static uint GetOrderDistance(const Order *prev, const Order *cur, const Vehicle conditional_depth++; int dist1 = GetOrderDistance(prev, GetVehicleOrder(v, cur->GetConditionSkipToOrder()), v, conditional_depth); - int dist2 = GetOrderDistance(prev, cur->next == NULL ? v->orders : cur->next, v, conditional_depth); + int dist2 = GetOrderDistance(prev, cur->next == NULL ? v->orders.list->GetFirstOrder() : cur->next, v, conditional_depth); return max(dist1, dist2); } @@ -495,6 +645,7 @@ CommandCost CmdInsertOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, c if (sel_ord > v->GetNumOrders()) return CMD_ERROR; if (!Order::CanAllocateItem()) return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); + if (v->orders.list == NULL && !OrderList::CanAllocateItem()) return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); if (v->type == VEH_SHIP && IsHumanCompany(v->owner) && _settings_game.pf.pathfinder_for_ships != VPF_NPF) { /* Make sure the new destination is not too far away from the previous */ @@ -504,7 +655,8 @@ CommandCost CmdInsertOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, c /* Find the last goto station or depot order before the insert location. * If the order is to be inserted at the beginning of the order list this * finds the last order in the list. */ - for (const Order *o = v->orders; o != NULL; o = o->next) { + const Order *o; + FOR_VEHICLE_ORDERS(v, o) { if (o->IsType(OT_GOTO_STATION) || o->IsType(OT_GOTO_DEPOT)) prev = o; if (++n == sel_ord && prev != NULL) break; } @@ -521,42 +673,16 @@ CommandCost CmdInsertOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, c new_o->AssignOrder(new_order); /* Create new order and link in list */ - if (v->orders == NULL) { - v->orders = new_o; + if (v->orders.list == NULL) { + v->orders.list = new OrderList(new_o, v); } else { - /* Try to get the previous item (we are inserting above the - selected) */ - Order *order = GetVehicleOrder(v, sel_ord - 1); - - if (order == NULL && GetVehicleOrder(v, sel_ord) != NULL) { - /* There is no previous item, so we are altering v->orders itself - But because the orders can be shared, we copy the info over - the v->orders, so we don't have to change the pointers of - all vehicles */ - SwapOrders(v->orders, new_o); - /* Now update the next pointers */ - v->orders->next = new_o; - } else if (order == NULL) { - /* 'sel' is a non-existing order, add him to the end */ - order = GetLastVehicleOrder(v); - order->next = new_o; - } else { - /* Put the new order in between */ - new_o->next = order->next; - order->next = new_o; - } + v->orders.list->InsertOrderAt(new_o, sel_ord); } Vehicle *u = v->FirstShared(); DeleteOrderWarnings(u); for (; u != NULL; u = u->NextShared()) { - /* Increase amount of orders */ - u->num_orders++; - - /* If the orderlist was empty, assign it */ - if (u->orders == NULL) u->orders = v->orders; - - assert(v->orders == u->orders); + assert(v->orders.list == u->orders.list); /* If there is added an order before the current one, we need to update the selected order */ @@ -634,37 +760,15 @@ CommandCost CmdDeleteOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, c if (order == NULL) return CMD_ERROR; if (flags & DC_EXEC) { - if (GetVehicleOrder(v, sel_ord - 1) == NULL) { - if (GetVehicleOrder(v, sel_ord + 1) != NULL) { - /* First item, but not the last, so we need to alter v->orders - Because we can have shared order, we copy the data - from the next item over the deleted */ - order = GetVehicleOrder(v, sel_ord + 1); - SwapOrders(v->orders, order); - } else { - /* Last item, so clean the list */ - v->orders = NULL; - } - } else { - GetVehicleOrder(v, sel_ord - 1)->next = order->next; - } - - /* Give the item free */ - delete order; + v->orders.list->DeleteOrderAt(sel_ord); Vehicle *u = v->FirstShared(); DeleteOrderWarnings(u); for (; u != NULL; u = u->NextShared()) { - u->num_orders--; - if (sel_ord < u->cur_order_index) u->cur_order_index--; - /* If we removed the last order, make sure the shared vehicles - * also set their orders to NULL */ - if (v->orders == NULL) u->orders = NULL; - - assert(v->orders == u->orders); + assert(v->orders.list == u->orders.list); /* NON-stop flag is misused to see if a train is in a station that is * on his order list or not */ @@ -764,30 +868,7 @@ CommandCost CmdMoveOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, con if (moving_one == NULL) return CMD_ERROR; if (flags & DC_EXEC) { - /* Take the moving order out of the pointer-chain */ - Order *one_before = GetVehicleOrder(v, moving_order - 1); - Order *one_past = GetVehicleOrder(v, moving_order + 1); - - if (one_before == NULL) { - /* First order edit */ - v->orders = moving_one->next; - } else { - one_before->next = moving_one->next; - } - - /* Insert the moving_order again in the pointer-chain */ - one_before = GetVehicleOrder(v, target_order - 1); - one_past = GetVehicleOrder(v, target_order); - - moving_one->next = one_past; - - if (one_before == NULL) { - /* first order edit */ - SwapOrders(v->orders, moving_one); - v->orders->next = moving_one; - } else { - one_before->next = moving_one; - } + v->orders.list->MoveOrder(moving_order, target_order); /* Update shared list */ Vehicle *u = v->FirstShared(); @@ -795,9 +876,6 @@ CommandCost CmdMoveOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, con DeleteOrderWarnings(u); for (; u != NULL; u = u->NextShared()) { - /* Update the first order */ - if (u->orders != v->orders) u->orders = v->orders; - /* Update the current order */ if (u->cur_order_index == moving_order) { u->cur_order_index = target_order; @@ -807,7 +885,7 @@ CommandCost CmdMoveOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, con u->cur_order_index++; } - assert(v->orders == u->orders); + assert(v->orders.list == u->orders.list); /* Update any possible open window of the vehicle */ InvalidateVehicleOrder(u, moving_order | (target_order << 8)); } @@ -1101,8 +1179,7 @@ CommandCost CmdCloneOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, co /* If the destination vehicle had a OrderList, destroy it */ DeleteVehicleOrders(dst); - dst->orders = src->orders; - dst->num_orders = src->GetNumOrders(); + dst->orders.list = src->orders.list; /* Link this vehicle in the shared-list */ dst->AddToShared(src); @@ -1148,25 +1225,30 @@ CommandCost CmdCloneOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, co /* make sure there are orders available */ delta = dst->IsOrderListShared() ? src->GetNumOrders() + 1 : src->GetNumOrders() - dst->GetNumOrders(); - if (!Order::CanAllocateItem(delta)) { + if (!Order::CanAllocateItem(delta) || + ((dst->orders.list == NULL || dst->IsOrderListShared()) && !OrderList::CanAllocateItem())) { return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); } if (flags & DC_EXEC) { const Order *order; + Order *first = NULL; Order **order_dst; - /* If the destination vehicle had a OrderList, destroy it */ - DeleteVehicleOrders(dst); + /* If the destination vehicle had an order list, destroy the chain but keep the OrderList */ + DeleteVehicleOrders(dst, true); - order_dst = &dst->orders; + order_dst = &first; FOR_VEHICLE_ORDERS(src, order) { *order_dst = new Order(); (*order_dst)->AssignOrder(*order); order_dst = &(*order_dst)->next; } - - dst->num_orders = src->GetNumOrders(); + if (dst->orders.list == NULL) dst->orders.list = new OrderList(first, dst); + else { + assert(dst->orders.list->GetFirstOrder() == NULL); + new (dst->orders.list) OrderList(first, dst); + } InvalidateVehicleOrder(dst, -1); @@ -1435,7 +1517,7 @@ void CheckOrders(const Vehicle* v) if (v->GetNumOrders() > 1) { const Order* last = GetLastVehicleOrder(v); - if (v->orders->Equals(*last)) { + if (v->orders.list->GetFirstOrder()->Equals(*last)) { problem_type = 2; } } @@ -1443,6 +1525,11 @@ void CheckOrders(const Vehicle* v) /* Do we only have 1 station in our order list? */ if (n_st < 2 && problem_type == -1) problem_type = 0; + assert(v->orders.list); // otherwise the check for v->FirstShared() != v would have been true +#ifndef NDEBUG + v->orders.list->DebugCheckSanity(); +#endif + /* We don't have a problem */ if (problem_type < 0) return; @@ -1530,20 +1617,19 @@ bool VehicleHasDepotOrders(const Vehicle *v) * Delete all orders from a vehicle * */ -void DeleteVehicleOrders(Vehicle *v) +void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist) { DeleteOrderWarnings(v); if (v->IsOrderListShared()) { /* Remove ourself from the shared order list. */ v->RemoveFromShared(); - } else if (v->orders != NULL) { + v->orders.list = NULL; + } else if (v->orders.list != NULL) { /* Remove the orders */ - v->orders->FreeChain(); + v->orders.list->FreeChain(keep_orderlist); + if (!keep_orderlist) v->orders.list = NULL; } - - v->orders = NULL; - v->num_orders = 0; } Date GetServiceIntervalClamped(uint index) @@ -1807,6 +1893,9 @@ void InitializeOrders() _Order_pool.CleanPool(); _Order_pool.AddBlockToPool(); + _OrderList_pool.CleanPool(); + _OrderList_pool.AddBlockToPool(); + _backup_orders_tile = 0; } @@ -1892,6 +1981,35 @@ static void Load_ORDR() } } -extern const ChunkHandler _order_chunk_handlers[] = { - { 'ORDR', Save_ORDR, Load_ORDR, CH_ARRAY | CH_LAST}, +const SaveLoad *GetOrderListDescription() { +static const SaveLoad _orderlist_desc[] = { + SLE_REF(OrderList, first, REF_ORDER), + SLE_END() +}; + return _orderlist_desc; +} + +static void Save_ORDL() +{ + OrderList *list; + + FOR_ALL_ORDER_LISTS(list) { + SlSetArrayIndex(list->index); + SlObject(list, GetOrderListDescription()); + } +} + +static void Load_ORDL() +{ + int index; + + while ((index = SlIterateArray()) != -1) { + OrderList *list = new (index) OrderList(); + SlObject(list, GetOrderListDescription()); + } +} + +extern const ChunkHandler _order_chunk_handlers[] = { + { 'ORDR', Save_ORDR, Load_ORDR, CH_ARRAY}, + { 'ORDL', Save_ORDL, Load_ORDL, CH_ARRAY | CH_LAST}, }; diff --git a/src/order_func.h b/src/order_func.h index 578862064f..62a33f089b 100644 --- a/src/order_func.h +++ b/src/order_func.h @@ -34,7 +34,7 @@ void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination); void InvalidateVehicleOrder(const Vehicle *v, int data); bool VehicleHasDepotOrders(const Vehicle *v); void CheckOrders(const Vehicle*); -void DeleteVehicleOrders(Vehicle *v); +void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist = false); bool ProcessOrders(Vehicle *v); bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth = 0); VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v); diff --git a/src/order_type.h b/src/order_type.h index 3ffbdb6f1d..55653b8250 100644 --- a/src/order_type.h +++ b/src/order_type.h @@ -9,6 +9,7 @@ typedef byte VehicleOrderID; ///< The index of an order within its current vehicle (not pool related) typedef uint16 OrderID; +typedef uint16 OrderListID; typedef uint16 DestinationID; enum { @@ -149,5 +150,6 @@ enum { }; struct Order; +struct OrderList; #endif /* ORDER_TYPE_H */ diff --git a/src/saveload.cpp b/src/saveload.cpp index a5de05b06e..ebbba0edfb 100644 --- a/src/saveload.cpp +++ b/src/saveload.cpp @@ -39,7 +39,7 @@ #include "table/strings.h" -extern const uint16 SAVEGAME_VERSION = 104; +extern const uint16 SAVEGAME_VERSION = 105; SavegameType _savegame_type; ///< type of savegame we are loading @@ -1367,6 +1367,7 @@ static uint ReferenceToInt(const void *obj, SLRefType rt) 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; + case REF_ORDERLIST: return ((const OrderList*)obj)->index + 1; default: NOT_REACHED(); } @@ -1399,6 +1400,10 @@ static void *IntToReference(uint index, SLRefType rt) index--; // correct for the NULL index switch (rt) { + case REF_ORDERLIST: + if (_OrderList_pool.AddBlockIfNeeded(index)) return GetOrderList(index); + error("Orders: failed loading savegame: too many order lists"); + case REF_ORDER: if (_Order_pool.AddBlockIfNeeded(index)) return GetOrder(index); error("Orders: failed loading savegame: too many orders"); diff --git a/src/saveload.h b/src/saveload.h index e52045a831..6585230a95 100644 --- a/src/saveload.h +++ b/src/saveload.h @@ -66,6 +66,7 @@ enum SLRefType { REF_ROADSTOPS = 5, REF_ENGINE_RENEWS = 6, REF_CARGO_PACKET = 7, + REF_ORDERLIST = 8, }; #define SL_MAX_VERSION 255 diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp index c45af9d5ee..5364dd9533 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -17,12 +17,16 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 time, bool is_journey) { Order *order = GetVehicleOrder(v, order_number); + int delta; if (is_journey) { + delta = time - order->travel_time; order->travel_time = time; } else { + delta = time - order->wait_time; order->wait_time = time; } + v->orders.list->UpdateOrderTimetable(delta); for (v = v->FirstShared(); v != NULL; v = v->NextShared()) { if (v->cur_order_index == order_number && v->current_order.Equals(*order)) { diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index e5a2e75f12..5cf5947167 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1274,8 +1274,7 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p if (!IsFrontEngine(src)) { /* setting the type to 0 also involves setting up the orders field. */ SetFrontEngine(src); - assert(src->orders == NULL); - src->num_orders = 0; + assert(src->orders.list == NULL); /* Decrease the engines number of the src engine_type */ if (!IsDefaultGroupID(src->group_id) && IsValidGroupID(src->group_id)) { @@ -1453,8 +1452,7 @@ CommandCost CmdSellRailWagon(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, first->group_id = DEFAULT_GROUP; /* Copy orders (by sharing) */ - new_f->orders = first->orders; - new_f->num_orders = first->GetNumOrders(); + new_f->orders.list = first->orders.list; new_f->AddToShared(first); DeleteVehicleOrders(first); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index f267c635ff..4f620e5f0d 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -59,6 +59,8 @@ #include "table/sprites.h" #include "table/strings.h" +#include + #define INVALID_COORD (0x7fffffff) #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6)) @@ -221,7 +223,7 @@ void VehiclePositionChanged(Vehicle *v) } /** Called after load to update coordinates */ -void AfterLoadVehicles(bool clear_te_id) +void AfterLoadVehicles(bool part_of_load) { Vehicle *v; @@ -232,7 +234,7 @@ void AfterLoadVehicles(bool clear_te_id) v->UpdateDeltaXY(v->direction); - if (clear_te_id) v->fill_percent_te_id = INVALID_TE_ID; + if (part_of_load) v->fill_percent_te_id = INVALID_TE_ID; v->first = NULL; if (v->type == VEH_TRAIN) v->u.rail.first_engine = INVALID_ENGINE; if (v->type == VEH_ROAD) v->u.road.first_engine = INVALID_ENGINE; @@ -240,19 +242,45 @@ void AfterLoadVehicles(bool clear_te_id) v->cargo.InvalidateCache(); } + /* AfterLoadVehicles may also be called in case of NewGRF reload, in this + * case we may not convert orders again. */ + if (part_of_load) { + /* Create shared vehicle chain for very old games (pre 5,2) and create + * OrderList from shared vehicle chains. For this to work correctly, the + * following conditions must be fulfilled: + * 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 mapping; + + FOR_ALL_VEHICLES(v) { + if (v->orders.old != NULL) { + if (CheckSavegameVersion(105)) { // Pre-105 didn't save an OrderList + if (mapping[v->orders.old] == NULL) { + /* This adds the whole shared vehicle chain for case b */ + v->orders.list = mapping[v->orders.old] = new OrderList(v->orders.old, v); + } else { + v->orders.list = mapping[v->orders.old]; + /* For old games (case a) we must create the shared vehicle chain */ + if (CheckSavegameVersionOldStyle(5, 2)) { + v->AddToShared(v->orders.list->GetFirstSharedVehicle()); + } + } + } else { // OrderList was saved as such, only recalculate not saved values + if (v->PreviousShared() == NULL) { + new (v->orders.list) OrderList(v->orders.list->GetFirstOrder(), v); + } + } + } + } + } + FOR_ALL_VEHICLES(v) { /* Fill the first pointers */ if (v->Previous() == NULL) { for (Vehicle *u = v; u != NULL; u = u->Next()) { u->first = v; } - - /* Shared orders are only valid for first vehicles in chains. */ - if (v->previous_shared == NULL) { - for (Vehicle *u = v; u != NULL; u = u->NextShared()) { - u->first_shared = v; - } - } } } @@ -315,7 +343,6 @@ Vehicle::Vehicle() this->group_id = DEFAULT_GROUP; this->fill_percent_te_id = INVALID_TE_ID; this->first = this; - this->first_shared = this; this->colormap = PAL_NONE; } @@ -2168,7 +2195,8 @@ static const SaveLoad _common_veh_desc[] = { SLE_CONDVAR(Vehicle, running_ticks, SLE_UINT8, 88, SL_MAX_VERSION), SLE_VAR(Vehicle, cur_order_index, SLE_UINT8), - SLE_VAR(Vehicle, num_orders, SLE_UINT8), + /* num_orders is now part of OrderList and is not saved but counted */ + SLE_CONDNULL(1, 0, 104), /* This next line is for version 4 and prior compatibility.. it temporarily reads type and flags (which were both 4 bits) into type. Later on this is @@ -2189,7 +2217,8 @@ static const SaveLoad _common_veh_desc[] = { SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, wait_time), SLE_UINT16, 67, SL_MAX_VERSION), SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, travel_time), SLE_UINT16, 67, SL_MAX_VERSION), - SLE_REF(Vehicle, orders, REF_ORDER), + SLE_CONDREF(Vehicle, orders, REF_ORDER, 0, 104), + SLE_CONDREF(Vehicle, orders, REF_ORDERLIST, 105, SL_MAX_VERSION), SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30), SLE_CONDVAR(Vehicle, age, SLE_INT32, 31, SL_MAX_VERSION), @@ -2409,7 +2438,6 @@ void Load_VEHS() _vehicles_to_autoreplace.Reset(); int index; - Vehicle *v; _cargo_count = 0; @@ -2456,22 +2484,6 @@ void Load_VEHS() /* Advanced vehicle lists got added */ if (CheckSavegameVersion(60)) v->group_id = DEFAULT_GROUP; } - - /* Check for shared order-lists (we now use pointers for that) */ - if (CheckSavegameVersionOldStyle(5, 2)) { - FOR_ALL_VEHICLES(v) { - Vehicle *u; - - FOR_ALL_VEHICLES_FROM(u, v->index + 1) { - /* If a vehicle has the same orders, add the link to eachother - * in both vehicles */ - if (v->orders == u->orders) { - u->AddToShared(v); - break; - } - } - } - } } extern const ChunkHandler _veh_chunk_handlers[] = { @@ -2651,47 +2663,51 @@ void Vehicle::SetNext(Vehicle *next) void Vehicle::AddToShared(Vehicle *shared_chain) { - assert(!this->IsOrderListShared()); + assert(this->previous_shared == NULL && this->next_shared == NULL); + + if (!shared_chain->orders.list) { + assert(shared_chain->previous_shared == NULL); + assert(shared_chain->next_shared == NULL); + this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain); + } this->next_shared = shared_chain->next_shared; this->previous_shared = shared_chain; - this->first_shared = shared_chain->first_shared; shared_chain->next_shared = this; if (this->next_shared != NULL) this->next_shared->previous_shared = this; + + shared_chain->orders.list->AddVehicle(this); } void Vehicle::RemoveFromShared() { - Vehicle *new_first; + /* Remember if we were first and the old window number before RemoveVehicle() + * as this changes first if needed. */ + bool were_first = (this->FirstShared() == this); + uint32 old_window_number = (this->FirstShared()->index << 16) | (this->type << 11) | VLW_SHARED_ORDERS | this->owner; - if (this->FirstShared() == this) { - /* We are the first shared one, so update all the first pointers of our next shared ones. */ - new_first = this->NextShared(); - for (Vehicle *u = new_first; u != NULL; u = u->NextShared()) { - u->first_shared = new_first; - } - } else { + this->orders.list->RemoveVehicle(this); + + if (!were_first) { /* We are not the first shared one, so only relink our previous one. */ - new_first = this->FirstShared(); this->previous_shared->next_shared = this->NextShared(); } if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared; - uint32 old_window_number = (this->FirstShared()->index << 16) | (this->type << 11) | VLW_SHARED_ORDERS | this->owner; - if (new_first->NextShared() == NULL) { + if (this->orders.list->GetNumVehicles() == 1) { /* When there is only one vehicle, remove the shared order list window. */ DeleteWindowById(GetWindowClassForVehicleType(this->type), old_window_number); - InvalidateVehicleOrder(new_first, 0); - } else if (this->FirstShared() == this) { - /* If we were the first one, update to the new first one. */ - InvalidateWindowData(GetWindowClassForVehicleType(this->type), old_window_number, (new_first->index << 16) | (1 << 15)); + InvalidateVehicleOrder(this->FirstShared(), 0); + } else if (were_first) { + /* If we were the first one, update to the new first one. + * Note: FirstShared() is already the new first */ + InvalidateWindowData(GetWindowClassForVehicleType(this->type), old_window_number, (this->FirstShared()->index << 16) | (1 << 15)); } - this->first_shared = this; this->next_shared = NULL; this->previous_shared = NULL; } diff --git a/src/vehicle_base.h b/src/vehicle_base.h index e49219ddb0..42fd3d44d1 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -194,7 +194,7 @@ DECLARE_OLD_POOL(Vehicle, Vehicle, 9, 125) /* Some declarations of functions, so we can make them friendly */ struct SaveLoad; extern const SaveLoad *GetVehicleDescription(VehicleType vt); -extern void AfterLoadVehicles(bool clear_te_id); +extern void AfterLoadVehicles(bool part_of_load); struct LoadgameState; extern bool LoadOldVehicle(LoadgameState *ls, int num); @@ -206,10 +206,9 @@ private: Vehicle *next_shared; ///< pointer to the next vehicle that shares the order Vehicle *previous_shared; ///< NOSAVE: pointer to the previous vehicle in the shared order chain - Vehicle *first_shared; ///< NOSAVE: pointer to the first vehicle in the shared order chain public: friend const SaveLoad *GetVehicleDescription(VehicleType vt); ///< So we can use private/protected variables in the saveload code - friend void AfterLoadVehicles(bool clear_te_id); ///< So we can set the previous and first pointers while loading + friend void AfterLoadVehicles(bool part_of_load); ///< So we can set the previous and first pointers while loading friend bool LoadOldVehicle(LoadgameState *ls, int num); ///< So we can set the proper next pointer while loading char *name; ///< Name of vehicle @@ -302,10 +301,12 @@ public: byte vehstatus; ///< Status Order current_order; ///< The current order (+ status, like: loading) - VehicleOrderID num_orders; ///< How many orders there are in the list VehicleOrderID cur_order_index; ///< The index to the current order - Order *orders; ///< Pointer to the first order for this vehicle + union { + OrderList *list; ///< Pointer to the order list for this vehicle + Order *old; ///< Only used during conversion of old save games + } orders; byte vehicle_flags; ///< Used for gradual loading and other miscellaneous things (@see VehicleFlags enum) uint16 load_unload_time_rem; @@ -481,6 +482,12 @@ public: inline Vehicle *First() const { return this->first; } + /** + * Get the first order of the vehicles order list. + * @return first order of order list. + */ + inline Order *GetFirstOrder() const { return (this->orders.list == NULL) ? NULL : this->orders.list->GetFirstOrder(); } + /** * Adds this vehicle to a shared vehicle chain. * @param shared_chain a vehicle of the chain with shared vehicles. @@ -494,29 +501,34 @@ public: void RemoveFromShared(); /** - * Get the next vehicle of this vehicle. - * @note articulated parts are also counted as vehicles. - * @return the next vehicle or NULL when there isn't a next vehicle. + * Get the next vehicle of the shared vehicle chain. + * @return the next shared vehicle or NULL when there isn't a next vehicle. */ inline Vehicle *NextShared() const { return this->next_shared; } + /** + * Get the previous vehicle of the shared vehicle chain + * @return the previous shared vehicle or NULL when there isn't a previous vehicle. + */ + inline Vehicle *PreviousShared() const { return this->previous_shared; } + /** * Get the first vehicle of this vehicle chain. * @return the first vehicle of the chain. */ - inline Vehicle *FirstShared() const { return this->first_shared; } + inline Vehicle *FirstShared() const { return (this->orders.list == NULL) ? NULL : this->orders.list->GetFirstSharedVehicle(); } /** * Check if we share our orders with another vehicle. * @return true if there are other vehicles sharing the same order */ - inline bool IsOrderListShared() const { return this->previous_shared != NULL || this->next_shared != NULL; }; + inline bool IsOrderListShared() const { return this->orders.list != NULL && this->orders.list->IsShared(); } - /** + /** * Get the number of orders this vehicle has. * @return the number of orders this vehicle has. */ - inline VehicleOrderID GetNumOrders() const { return this->num_orders; } + inline VehicleOrderID GetNumOrders() const { return (this->orders.list == NULL) ? 0 : this->orders.list->GetNumOrders(); } /** * Copy certain configurations and statistics of a vehicle after successful autoreplace/renew @@ -676,34 +688,14 @@ struct FreeUnitIDGenerator { }; /* Returns order 'index' of a vehicle or NULL when it doesn't exists */ -static inline Order *GetVehicleOrder(const Vehicle *v, int index) -{ - Order *order = v->orders; - - if (index < 0) return NULL; - - while (order != NULL && index-- > 0) - order = order->next; - - return order; -} +static inline Order *GetVehicleOrder(const Vehicle *v, int index) { return (v->orders.list == NULL) ? NULL : v->orders.list->GetOrderAt(index); } /** * Returns the last order of a vehicle, or NULL if it doesn't exists * @param v Vehicle to query * @return last order of a vehicle, if available */ -static inline Order *GetLastVehicleOrder(const Vehicle *v) -{ - Order *order = v->orders; - - if (order == NULL) return NULL; - - while (order->next != NULL) - order = order->next; - - return order; -} +static inline Order *GetLastVehicleOrder(const Vehicle *v) { return (v->orders.list == NULL) ? NULL : v->orders.list->GetLastOrder(); } /** * Returns the Trackdir on which the vehicle is currently located.