Index: src/autoreplace_cmd.cpp =================================================================== --- src/autoreplace_cmd.cpp (revision 10977) +++ src/autoreplace_cmd.cpp (working copy) @@ -209,7 +209,7 @@ DoCommand(0, (front->index << 16) | new_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); } else { // copy/clone the orders - DoCommand(0, (old_v->index << 16) | new_v->index, IsOrderListShared(old_v) ? CO_SHARE : CO_COPY, DC_EXEC, CMD_CLONE_ORDER); + DoCommand(0, (old_v->index << 16) | new_v->index, old_v->IsOrderListShared() ? CO_SHARE : CO_COPY, DC_EXEC, CMD_CLONE_ORDER); new_v->cur_order_index = old_v->cur_order_index; ChangeVehicleViewWindow(old_v, new_v); new_v->profit_this_year = old_v->profit_this_year; Index: src/order.h =================================================================== --- src/order.h (revision 10977) +++ src/order.h (working copy) @@ -211,7 +211,6 @@ bool VehicleHasDepotOrders(const Vehicle *v); void CheckOrders(const Vehicle*); void DeleteVehicleOrders(Vehicle *v); -bool IsOrderListShared(const Vehicle *v); void AssignOrder(Order *order, Order data); bool CheckForValidOrders(const Vehicle* v); Index: src/order_cmd.cpp =================================================================== --- src/order_cmd.cpp (revision 10977) +++ src/order_cmd.cpp (working copy) @@ -19,6 +19,7 @@ #include "vehicle_gui.h" #include "cargotype.h" #include "strings.h" +#include "station_map.h" DEFINE_OLD_POOL_GENERIC(Order, Order) @@ -830,7 +831,7 @@ } /* make sure there are orders available */ - delta = IsOrderListShared(dst) ? src->num_orders + 1 : src->num_orders - dst->num_orders; + delta = dst->IsOrderListShared() ? src->num_orders + 1 : src->num_orders - dst->num_orders; if (!HasOrderPoolFree(delta)) return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); @@ -933,7 +934,7 @@ } /* If we have shared orders, store it on a special way */ - if (IsOrderListShared(v)) { + if (v->IsOrderListShared()) { const Vehicle *u = (v->next_shared) ? v->next_shared : v->prev_shared; bak->clone = u->index; @@ -1196,7 +1197,7 @@ /* If we have a shared order-list, don't delete the list, but just remove our pointer */ - if (IsOrderListShared(v)) { + if (v->IsOrderListShared()) { Vehicle *u = v; v->orders = NULL; @@ -1241,18 +1242,6 @@ /** * - * Check if we share our orders with an other vehicle - * - * @return Returns the vehicle who has the same order - * - */ -bool IsOrderListShared(const Vehicle *v) -{ - return v->next_shared != NULL || v->prev_shared != NULL; -} - -/** - * * Check if a vehicle has any valid orders * * @return false if there are no valid orders Index: src/order_gui.cpp =================================================================== --- src/order_gui.cpp (revision 10977) +++ src/order_gui.cpp (working copy) @@ -118,7 +118,7 @@ v = GetVehicle(w->window_number); - shared_orders = IsOrderListShared(v); + shared_orders = v->IsOrderListShared(); SetVScrollCount(w, v->num_orders + 1); Index: src/timetable_cmd.cpp =================================================================== --- src/timetable_cmd.cpp (revision 10977) +++ src/timetable_cmd.cpp (working copy) @@ -11,8 +11,8 @@ #include "date.h" #include "player.h" #include "vehicle.h" +#include "station_map.h" - static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 time, bool is_journey) { Order *order = GetVehicleOrder(v, order_number); Index: src/vehicle.cpp =================================================================== --- src/vehicle.cpp (revision 10977) +++ src/vehicle.cpp (working copy) @@ -598,6 +598,139 @@ } } +/** + * Checks if the vehicle is part of the shared orders list + */ +bool Vehicle::IsVehicleInSharedOrdersList(Vehicle *v) +{ + for (Vehicle *vShared = this->prev_shared; vShared != NULL; vShared = vShared->prev_shared) { + if (vShared == v) return true; + } + + for (Vehicle *vShared = this->next_shared; vShared != NULL; vShared = vShared->next_shared) { + if (vShared == v) return true; + } + + return false; +} + +/** + * Counts the vehicles that share it's orders list + */ +int Vehicle::CountSharedOrderdList() +{ + int count = 1; ///< Includes this one first + for (Vehicle *v = this->prev_shared; v != NULL; v = v->prev_shared) count++; + for (Vehicle *v = this->next_shared; v != NULL; v = v->next_shared) count++; + return count; +} + +/** + * Finds this vehicle position in the shared order list chain + */ +int Vehicle::GetPositionInSharedOrderList() +{ + int count = 0; ///< Do not include this one + for (Vehicle *v = this->prev_shared; v != NULL; v = v->prev_shared) count++; + return count; +} + +/** + * Gets the total timetable duration + */ +int Vehicle::GetTimetableTotalDuration() +{ + int total = 0; + for (Order *o = this->orders; o != NULL; o = o->next) { + total += o->wait_time + o->travel_time; + } + return total; +} + +/** + * Get the current theorical separation with the first shared order vehicle. + * This includes its lateness_counter. + * The vehicle must be in the first order station for this function to work. + */ +int Vehicle::GetFirstSharedSeparation() +{ + if (this->prev_shared == NULL) return 0; + + Vehicle *first; + /// Gets the first vehicle in the chain + for (first = this->prev_shared; first->prev_shared != NULL; first = first->prev_shared); + + return this->GetVehicleSeparation(first, false, true) + first->lateness_counter; +} + +/** + * Get the current separation with the given vehicle. + */ +int Vehicle::GetVehicleSeparation(Vehicle *v, bool add_this_current_order_time, bool add_other_current_order_time) +{ + if (this == v) return 0; + + int this_position = this->GetTimetablePosition(add_this_current_order_time); + int other_position = v->GetTimetablePosition(add_other_current_order_time); + + if (this_position <= other_position) { + /// The other vehicle is in front of this one. It's really easy to compute. + return other_position - this_position; + } else { + /// The other vehicle is behind this one. We must add the total timetable duration minus the current order wait_time as we may start immediately. + int total_timetable = this->GetTimetableTotalDuration(); + return total_timetable + other_position - this_position; + } +} + +/** + * Get the theorical current vehicle position in the timetable + * @param bool adds current_order_time + * @return numbers of ticks from the begining of the timetable without lateness_counter. + */ +int Vehicle::GetTimetablePosition(bool add_current_order_time) +{ + int total = 0; + + /// Add current order time + if (add_current_order_time) total += current_order_time; + + Order *o = orders; + for (int i = 0; i < cur_order_index%(this->num_orders); i++) { + total += o->travel_time + o->wait_time; + o = o->next; + } + + if (last_station_visited == o->dest) + { + total += o->travel_time; + } + + return total; +} + +/** + * Returns the vehicle order index where it first stops at a station + */ +VehicleOrderID Vehicle::FirstStopStationOrderID() +{ + VehicleOrderID index = 0; + for (Order *first = this->orders; first != NULL; first = first->next) { + if (first->type == OT_GOTO_STATION && first->wait_time > 0) { + /** + * Take it easy : if wait_time is 0 then it means the train won't wait here + * because user choice or because station type (waypoint, buoy) or because + * order type (non-stop, etc.) + * This test is quite filthy, but it should always work unlikely all my previous + * attempts of cleaner code. + */ + return index; + } + ++index; + } + return INVALID_VEH_ORDER_ID; +} + Vehicle::~Vehicle() { DeleteName(this->string_id); @@ -3104,6 +3237,49 @@ * that arrives at random stations is bad. */ this->current_order.flags |= OF_NON_STOP; UpdateVehicleTimetable(this, true); + + /** + * We ensure the vehicle is well positionned in the shared order list + * This means the last vehicle from this shared order list that visited the + * station must be its ->prev_shared + */ + if (_patches.timetabling && this->prev_shared != NULL && this->IsOrderListShared() && this->FirstStopStationOrderID() == this->cur_order_index) { + /** + * Find the previous vehicle of the shared order that visited the station + */ + int min_separation = -1; + int tmp_separation = 0; + Vehicle *previous = NULL; + /// Check for previous vehicles + for (Vehicle *v = this->prev_shared; v != NULL; v = v->prev_shared) { + tmp_separation = this->GetVehicleSeparation(v, true, false); + if (min_separation > 0 && tmp_separation < min_separation || min_separation == -1) { + min_separation = tmp_separation; + previous = v; + } + } + /// Check for next vehicles + for (Vehicle *v = this->next_shared; v != NULL; v = v->next_shared) { + tmp_separation = this->GetVehicleSeparation(v, false, true); + if (min_separation > 0 && tmp_separation < min_separation || min_separation == -1) { + min_separation = tmp_separation; + previous = v; + } + } + + /// Check if prev_shared vehicle is really the previous one and reorder the chain if needed + if (this->prev_shared != previous && previous != NULL) { + // Remove this vehicle from the list + this->prev_shared->next_shared = this->next_shared; + if (this->next_shared != NULL) this->next_shared->prev_shared = this->prev_shared; + // Insert in the list + this->prev_shared = previous; + this->next_shared = previous->next_shared; + // Update previous and next + if (previous->next_shared != NULL) previous->next_shared->prev_shared = this; + previous->next_shared = this; + } + } } else { /* This is just an unordered intermediate stop */ this->current_order.flags = 0; @@ -3144,12 +3320,29 @@ { switch (this->current_order.type) { case OT_LOADING: { - uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0); + /** + * For shared orders vehicles other than the first in chain arriving to the + * first order station, we have to recompute the separation + */ + if (_patches.timetabling && this->IsOrderListShared() && this->prev_shared != NULL && this->cur_order_index == this->FirstStopStationOrderID()) { + /** + * This vehicle have to ensure separation + */ + /// Compute wait_time for auto-separation system + int wait_time = (GetTimetableTotalDuration() / CountSharedOrderdList()) - GetVehicleSeparation(prev_shared, false, true) + prev_shared->lateness_counter + current_order.wait_time; + /// Check if the vehicle can leave the station now or not + if (mode || !HASBIT(this->vehicle_flags, VF_LOADING_FINISHED) || wait_time > 0) return; + /// The vehicle can leave the station. Fill the lateness counter with separation informations, and override the actual waiting time. + this->lateness_counter = -wait_time; + this->current_order_time = this->current_order.wait_time; + } else { + /// Uses standard timetable system (no timetable, no shared orders, first vehicle, etc.) + uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0); + /* Not the first call for this tick, or still loading */ + if (mode || !HASBIT(this->vehicle_flags, VF_LOADING_FINISHED) || + (_patches.timetabling && this->current_order_time < wait_time)) return; + } - /* Not the first call for this tick, or still loading */ - if (mode || !HASBIT(this->vehicle_flags, VF_LOADING_FINISHED) || - (_patches.timetabling && this->current_order_time < wait_time)) return; - this->PlayLeaveStationSound(); Order b = this->current_order; Index: src/vehicle.h =================================================================== --- src/vehicle.h (revision 10977) +++ src/vehicle.h (working copy) @@ -420,6 +420,21 @@ virtual void Tick() {}; bool IsValid() const { return this->type != VEH_INVALID; } + + /** + * Check if we share our orders with an other vehicle + * + * @return Returns the vehicle who has the same order + */ + bool IsOrderListShared() const { return this->prev_shared != NULL || this->next_shared != NULL; } + bool IsVehicleInSharedOrdersList(Vehicle *v); + int CountSharedOrderdList(); + int GetPositionInSharedOrderList(); + int GetTimetableTotalDuration(); + int GetFirstSharedSeparation(); + int GetVehicleSeparation(Vehicle *v, bool add_this_current_order_time, bool add_other_current_order_time); + int GetTimetablePosition(bool add_current_order_time); + VehicleOrderID FirstStopStationOrderID(); }; /**