diff --git a/src/group_cmd.cpp b/src/group_cmd.cpp index 44bfd12c1e..c830d2f13e 100644 --- a/src/group_cmd.cpp +++ b/src/group_cmd.cpp @@ -263,7 +263,7 @@ CommandCost CmdAddSharedVehicleGroup(TileIndex tile, uint32 flags, uint32 p1, ui if (v->group_id != id_g) continue; /* For each shared vehicles add it to the group */ - for (Vehicle *v2 = GetFirstVehicleFromSharedList(v); v2 != NULL; v2 = v2->next_shared) { + for (Vehicle *v2 = v->FirstShared(); v2 != NULL; v2 = v2->NextShared()) { if (v2->group_id != id_g) CmdAddVehicleGroup(tile, flags, id_g, v2->index); } } diff --git a/src/oldloader.cpp b/src/oldloader.cpp index 2cea5c8420..5af1de8c27 100644 --- a/src/oldloader.cpp +++ b/src/oldloader.cpp @@ -372,8 +372,7 @@ static void FixOldVehicles() /* If a vehicle has the same orders, add the link to eachother * in both vehicles */ if (v->orders == u->orders) { - v->next_shared = u; - u->prev_shared = v; + u->AddToShared(v); break; } } diff --git a/src/openttd.cpp b/src/openttd.cpp index 31f175fcb9..fb67df2d17 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -2272,7 +2272,7 @@ bool AfterLoadGame() if (v->orders != NULL && !v->orders->IsValid()) v->orders = NULL; v->current_order.ConvertFromOldSavegame(); - if (v->type == VEH_ROAD && v->IsPrimaryVehicle() && v->prev_shared == NULL) { + if (v->type == VEH_ROAD && v->IsPrimaryVehicle() && v->FirstShared() == v) { FOR_VEHICLE_ORDERS(v, order) order->SetNonStopType(ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS); } } diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 6cf6cfeea4..ed759c79ab 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -506,7 +506,6 @@ CommandCost CmdInsertOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) } if (flags & DC_EXEC) { - Vehicle *u; Order *new_o = new Order(); new_o->AssignOrder(new_order); @@ -537,9 +536,9 @@ CommandCost CmdInsertOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) } } - u = GetFirstVehicleFromSharedList(v); + Vehicle *u = v->FirstShared(); DeleteOrderWarnings(u); - for (; u != NULL; u = u->next_shared) { + for (; u != NULL; u = u->NextShared()) { /* Increase amount of orders */ u->num_orders++; @@ -601,7 +600,7 @@ static CommandCost DecloneOrder(Vehicle *dst, uint32 flags) * Remove the VehicleList that shows all the vehicles with the same shared * orders. */ -static void RemoveSharedOrderVehicleList(Vehicle *v) +void RemoveSharedOrderVehicleList(Vehicle *v) { assert(v->orders != NULL); WindowClass window_class = WC_NONE; @@ -624,7 +623,7 @@ static void RemoveSharedOrderVehicleList(Vehicle *v) */ CommandCost CmdDeleteOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) { - Vehicle *v, *u; + Vehicle *v; VehicleID veh_id = p1; VehicleOrderID sel_ord = p2; Order *order; @@ -665,9 +664,9 @@ CommandCost CmdDeleteOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) /* Give the item free */ order->Free(); - u = GetFirstVehicleFromSharedList(v); + Vehicle *u = v->FirstShared(); DeleteOrderWarnings(u); - for (; u != NULL; u = u->next_shared) { + for (; u != NULL; u = u->NextShared()) { u->num_orders--; if (sel_ord < u->cur_order_index) @@ -803,11 +802,11 @@ CommandCost CmdMoveOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) } /* Update shared list */ - Vehicle *u = GetFirstVehicleFromSharedList(v); + Vehicle *u = v->FirstShared(); DeleteOrderWarnings(u); - for (; u != NULL; u = u->next_shared) { + for (; u != NULL; u = u->NextShared()) { /* Update the first order */ if (u->orders != v->orders) u->orders = v->orders; @@ -1027,9 +1026,9 @@ CommandCost CmdModifyOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) } /* Update the windows and full load flags, also for vehicles that share the same order list */ - Vehicle *u = GetFirstVehicleFromSharedList(v); + Vehicle *u = v->FirstShared(); DeleteOrderWarnings(u); - for (; u != NULL; u = u->next_shared) { + for (; u != NULL; u = u->NextShared()) { /* Toggle u->current_order "Full load" flag if it changed. * However, as the same flag is used for depot orders, check * whether we are not going to a depot as there are three @@ -1090,13 +1089,7 @@ CommandCost CmdCloneOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) } /* Is the vehicle already in the shared list? */ - { - const Vehicle* u; - - for (u = GetFirstVehicleFromSharedList(src); u != NULL; u = u->next_shared) { - if (u == dst) return CMD_ERROR; - } - } + if (src->FirstShared() == dst->FirstShared()) return CMD_ERROR; if (flags & DC_EXEC) { /* If the destination vehicle had a OrderList, destroy it */ @@ -1106,10 +1099,7 @@ CommandCost CmdCloneOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) dst->num_orders = src->num_orders; /* Link this vehicle in the shared-list */ - dst->next_shared = src->next_shared; - dst->prev_shared = src; - if (src->next_shared != NULL) src->next_shared->prev_shared = dst; - src->next_shared = dst; + dst->AddToShared(src); InvalidateVehicleOrder(dst); InvalidateVehicleOrder(src); @@ -1212,12 +1202,9 @@ CommandCost CmdOrderRefit(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) if (order == NULL) return CMD_ERROR; if (flags & DC_EXEC) { - Vehicle *u; - order->SetRefit(cargo, subtype); - u = GetFirstVehicleFromSharedList(v); - for (; u != NULL; u = u->next_shared) { + for (Vehicle *u = v->FirstShared(); u != NULL; u = u->NextShared()) { /* Update any possible open window of the vehicle */ InvalidateVehicleOrder(u); @@ -1253,7 +1240,7 @@ void BackupVehicleOrders(const Vehicle *v, BackuppedOrders *bak) /* If we have shared orders, store it on a special way */ if (v->IsOrderListShared()) { - const Vehicle *u = (v->next_shared) ? v->next_shared : v->prev_shared; + const Vehicle *u = (v->FirstShared() == v) ? v->NextShared() : v->FirstShared(); bak->clone = u->index; } else { @@ -1413,7 +1400,7 @@ void CheckOrders(const Vehicle* v) return; /* do nothing we we're not the first vehicle in a share-chain */ - if (v->next_shared != NULL) return; + if (v->FirstShared() != v) return; /* Only check every 20 days, so that we don't flood the message log */ if (v->owner == _local_player && v->day_counter % 20 == 0) { @@ -1541,49 +1528,16 @@ void DeleteVehicleOrders(Vehicle *v) { DeleteOrderWarnings(v); - /* If we have a shared order-list, don't delete the list, but just - remove our pointer */ if (v->IsOrderListShared()) { - Vehicle *u = v; - - v->orders = NULL; - v->num_orders = 0; - - /* Unlink ourself */ - if (v->prev_shared != NULL) { - v->prev_shared->next_shared = v->next_shared; - u = v->prev_shared; - } - if (v->next_shared != NULL) { - v->next_shared->prev_shared = v->prev_shared; - u = v->next_shared; - } - v->prev_shared = NULL; - v->next_shared = NULL; - - /* If we are the only one left in the Shared Order Vehicle List, - * remove it, as we are no longer a Shared Order Vehicle */ - if (u->prev_shared == NULL && u->next_shared == NULL && u->orders != NULL) RemoveSharedOrderVehicleList(u); - - /* We only need to update this-one, because if there is a third - * vehicle which shares the same order-list, nothing will change. If - * this is the last vehicle, the last line of the order-window - * will change from Shared order list, to Order list, so it needs - * an update */ - InvalidateVehicleOrder(u); - return; + /* Remove ourself from the shared order list. */ + v->RemoveFromShared(); + } else if (v->orders != NULL) { + /* Remove the orders */ + v->orders->FreeChain(); } - /* Remove the orders */ - Order *cur = v->orders; - /* Delete the vehicle list of shared orders, if any */ - if (cur != NULL) RemoveSharedOrderVehicleList(v); v->orders = NULL; v->num_orders = 0; - - if (cur != NULL) { - cur->FreeChain(); // Free the orders. - } } Date GetServiceIntervalClamped(uint index) diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp index 74f7c5cdbe..f0fea8530d 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -32,7 +32,7 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 time } } - for (v = GetFirstVehicleFromSharedList(v); v != NULL; v = v->next_shared) { + for (v = v->FirstShared(); v != NULL; v = v->NextShared()) { InvalidateWindow(WC_VEHICLE_TIMETABLE, v->index); } } @@ -165,7 +165,7 @@ CommandCost CmdAutofillTimetable(TileIndex tile, uint32 flags, uint32 p1, uint32 } } - for (v = GetFirstVehicleFromSharedList(v); v != NULL; v = v->next_shared) { + for (v = v->FirstShared(); v != NULL; v = v->NextShared()) { InvalidateWindow(WC_VEHICLE_TIMETABLE, v->index); } @@ -215,7 +215,7 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling) v->lateness_counter -= (timetabled - time_taken); - for (v = GetFirstVehicleFromSharedList(v); v != NULL; v = v->next_shared) { + for (v = v->FirstShared(); v != NULL; v = v->NextShared()) { InvalidateWindow(WC_VEHICLE_TIMETABLE, v->index); } } diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index c7423f3ee7..950cdd5a6e 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1427,27 +1427,13 @@ CommandCost CmdSellRailWagon(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) new_f->cur_order_index = first->cur_order_index; new_f->orders = first->orders; new_f->num_orders = first->num_orders; + + /* Make sure the group counts stay correct. */ new_f->group_id = first->group_id; + first->group_id = DEFAULT_GROUP; - if (first->prev_shared != NULL) { - first->prev_shared->next_shared = new_f; - new_f->prev_shared = first->prev_shared; - } - - if (first->next_shared != NULL) { - first->next_shared->prev_shared = new_f; - new_f->next_shared = first->next_shared; - } - - /* - * Remove all order information from the front train, to - * prevent the order and the shared order list to be - * destroyed by Destroy/DeleteVehicle. - */ - first->orders = NULL; - first->prev_shared = NULL; - first->next_shared = NULL; - first->group_id = DEFAULT_GROUP; + new_f->AddToShared(first); + first->RemoveFromShared(); /* If we deleted a window then open a new one for the 'new' train */ if (IsLocalPlayer() && w != NULL) ShowVehicleViewWindow(new_f); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index dd4743fff0..ae77872a91 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -255,6 +255,7 @@ void AfterLoadVehicles(bool clear_te_id) FOR_ALL_VEHICLES(v) { /* Reinstate the previous pointer */ if (v->Next() != NULL) v->Next()->previous = v; + if (v->NextShared() != NULL) v->NextShared()->previous_shared = v; v->UpdateDeltaXY(v->direction); @@ -272,6 +273,13 @@ void AfterLoadVehicles(bool clear_te_id) 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; + } + } } } @@ -329,6 +337,7 @@ 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; } @@ -2155,7 +2164,8 @@ static const SaveLoad _common_veh_desc[] = { SLE_CONDVAR(Vehicle, waiting_triggers, SLE_UINT8, 2, SL_MAX_VERSION), SLE_CONDREF(Vehicle, next_shared, REF_VEHICLE, 2, SL_MAX_VERSION), - SLE_CONDREF(Vehicle, prev_shared, REF_VEHICLE, 2, SL_MAX_VERSION), + SLE_CONDNULL(2, 2, 68), + SLE_CONDNULL(4, 69, 100), SLE_CONDVAR(Vehicle, group_id, SLE_UINT16, 60, SL_MAX_VERSION), @@ -2390,8 +2400,7 @@ void Load_VEHS() /* If a vehicle has the same orders, add the link to eachother * in both vehicles */ if (v->orders == u->orders) { - v->next_shared = u; - u->prev_shared = v; + u->AddToShared(v); break; } } @@ -2574,6 +2583,49 @@ void Vehicle::SetNext(Vehicle *next) } } +void Vehicle::AddToShared(Vehicle *shared_chain) +{ + assert(!this->IsOrderListShared()); + + 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; +} + +void Vehicle::RemoveFromShared() +{ + Vehicle *new_first; + + 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 { + /* 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; + + if (new_first->NextShared() == NULL) { + /* When there is only one vehicle, remove the shared order list window. */ + extern void RemoveSharedOrderVehicleList(Vehicle *v); + if (new_first->orders != NULL) RemoveSharedOrderVehicleList(new_first); + InvalidateVehicleOrder(new_first); + } + + this->first_shared = this; + this->next_shared = NULL; + this->previous_shared = NULL; +} + void StopAllVehicles() { Vehicle *v; diff --git a/src/vehicle_base.h b/src/vehicle_base.h index c2e9161f73..d0bc4188cb 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -202,6 +202,10 @@ private: Vehicle *next; ///< pointer to the next vehicle in the chain Vehicle *previous; ///< NOSAVE: pointer to the previous vehicle in the chain Vehicle *first; ///< NOSAVE: pointer to the first vehicle in the chain + + 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 @@ -214,9 +218,6 @@ public: TileIndex tile; ///< Current tile index TileIndex dest_tile; ///< Heading for this tile - Vehicle *next_shared; ///< If not NULL, this points to the next vehicle that shared the order - Vehicle *prev_shared; ///< If not NULL, this points to the prev vehicle that shared the order - Money profit_this_year; ///< Profit this year << 8, low 8 bits are fract Money profit_last_year; ///< Profit last year << 8, low 8 bits are fract Money value; @@ -476,12 +477,37 @@ public: */ inline Vehicle *First() const { return this->first; } + + /** + * Adds this vehicle to a shared vehicle chain. + * @param shared_chain a vehicle of the chain with shared vehicles. + * @pre !this->IsOrderListShared() + */ + void AddToShared(Vehicle *shared_chain); + + /** + * Removes the vehicle from the shared order list. + */ + 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. + */ + inline Vehicle *NextShared() const { return this->next_shared; } + + /** + * Get the first vehicle of this vehicle chain. + * @return the first vehicle of the chain. + */ + inline Vehicle *FirstShared() const { return this->first_shared; } + /** * Check if we share our orders with another vehicle. - * This is done by checking the previous and next pointers in the shared chain. * @return true if there are other vehicles sharing the same order */ - inline bool IsOrderListShared() const { return this->next_shared != NULL || this->prev_shared != NULL; }; + inline bool IsOrderListShared() const { return this->previous_shared != NULL || this->next_shared != NULL; }; /** * Copy certain configurations and statistics of a vehicle after successful autoreplace/renew @@ -648,18 +674,6 @@ static inline Order *GetLastVehicleOrder(const Vehicle *v) return order; } -/** Get the first vehicle of a shared-list, so we only have to walk forwards - * @param v Vehicle to query - * @return first vehicle of a shared-list - */ -static inline Vehicle *GetFirstVehicleFromSharedList(const Vehicle *v) -{ - Vehicle *u = (Vehicle *)v; - while (u->prev_shared != NULL) u = u->prev_shared; - - return u; -} - /** * Returns the Trackdir on which the vehicle is currently located. * Works for trains and ships. diff --git a/src/vehiclelist.cpp b/src/vehiclelist.cpp index a76a34538d..9f2d09c35c 100644 --- a/src/vehiclelist.cpp +++ b/src/vehiclelist.cpp @@ -97,7 +97,7 @@ void GenerateVehicleSortList(VehicleList *list, VehicleType type, PlayerID owner /* Find a vehicle with the order in question */ if (v->orders != NULL && v->orders->index == index) { /* Add all vehicles from this vehicle's shared order list */ - for (v = GetFirstVehicleFromSharedList(v); v != NULL; v = v->next_shared) { + for (v = v->FirstShared(); v != NULL; v = v->NextShared()) { *list->Append() = v; } break;