Index: src/autoreplace_cmd.cpp =================================================================== --- src/autoreplace_cmd.cpp (revision 10887) +++ 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 10887) +++ 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 10887) +++ 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) @@ -367,6 +368,15 @@ SwapOrders(v->orders, new_o); /* Now update the next pointers */ v->orders->next = new_o; + + /** + * We have to clean up the Station->shared_order_previous_vehicles list + * We should check also for non shared vehicles as it can be the last one from a old shared list + */ + if (IsTileType(v->orders->dest, MP_STATION)) { + Station *st = GetStationByTile(v->orders->next->dest); + st->shared_order_previous_vehicles.remove(v); + } } else if (order == NULL) { /* 'sel' is a non-existing order, add him to the end */ order = GetLastVehicleOrder(v); @@ -467,6 +477,15 @@ order = GetVehicleOrder(v, sel_ord); if (order == NULL) return CMD_ERROR; + /** + * We have to clean up the Station->shared_order_previous_vehicles list + * We should check also for non shared vehicles as it can be the last one from a old shared list + */ + if (IsTileType(order->dest, MP_STATION) && v->orders == order) { + Station *st = GetStationByTile(v->orders->dest); + st->shared_order_previous_vehicles.remove(v); + } + if (flags & DC_EXEC) { if (GetVehicleOrder(v, sel_ord - 1) == NULL) { if (GetVehicleOrder(v, sel_ord + 1) != NULL) { @@ -599,6 +618,16 @@ if (one_before == NULL) { /* First order edit */ + + /** + * We have to clean up the Station->shared_order_previous_vehicles list + * We should check also for non shared vehicles as it can be the last one from a old shared list + */ + if (IsTileType(v->orders->dest, MP_STATION)) { + Station *st = GetStationByTile(v->orders->dest); + st->shared_order_previous_vehicles.remove(v); + } + v->orders = moving_one->next; } else { one_before->next = moving_one->next; @@ -614,6 +643,15 @@ /* first order edit */ SwapOrders(v->orders, moving_one); v->orders->next = moving_one; + + /** + * We have to clean up the Station->shared_order_previous_vehicles list + * We should check also for non shared vehicles as it can be the last one from a old shared list + */ + if (IsTileType(moving_one->dest, MP_STATION)) { + Station *st = GetStationByTile(moving_one->dest); + st->shared_order_previous_vehicles.remove(v); + } } else { one_before->next = moving_one; } @@ -682,6 +720,15 @@ return CMD_ERROR; } + /** + * We have to clean up the Station->shared_order_previous_vehicles list + * We should check also for non shared vehicles as it can be the last one from a old shared list + */ + if (v->orders == order && IsTileType(v->orders->dest, MP_STATION)) { + Station *st = GetStationByTile(order->dest); + st->shared_order_previous_vehicles.remove(v); + } + if (flags & DC_EXEC) { switch (p2) { case OFB_FULL_LOAD: @@ -830,7 +877,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 +980,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; @@ -1194,9 +1241,18 @@ { DeleteOrderWarnings(v); + /** + * We have to clean up the Station->shared_order_previous_vehicles list + * We should check also for non shared vehicles as it can be the last one from a old shared list + */ + if (v->orders != NULL && IsTileType(v->orders->dest, MP_STATION)) { + Station *st = GetStationByTile(v->orders->dest); + st->shared_order_previous_vehicles.remove(v); + } + /* 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 +1297,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 10887) +++ 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/station.h =================================================================== --- src/station.h (revision 10887) +++ src/station.h (working copy) @@ -147,6 +147,7 @@ byte last_vehicle_type; std::list loading_vehicles; + std::list shared_order_previous_vehicles; ///< No need to save this as it should be rebuilt shortly GoodsEntry goods[NUM_CARGO]; uint16 random_bits; Index: src/timetable_cmd.cpp =================================================================== --- src/timetable_cmd.cpp (revision 10887) +++ 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 10887) +++ src/vehicle.cpp (working copy) @@ -598,6 +598,120 @@ } } +/** + * 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. + * The vehicle must not be the first vehicle of the chain. + */ +int Vehicle::GetFirstSharedSeparation() +{ + Vehicle *first; + Order *o; + /// Gets the first vehicle in the chain + for (first = this->prev_shared; first->prev_shared != NULL; first = first->prev_shared); + + /// Adds current_order_time plus lateness_counter + int total = first->current_order_time + first->lateness_counter; + + if (first->cur_order_index == 0 && first->current_order.type != OT_LOADING) { + /// If it's travelling to the first order, we have to add all times but the travel_time for the first order + for (o = first->orders->next; o != NULL; o = o->next) total += o->travel_time + o->wait_time; + total += first->orders->wait_time; + } else if (first->cur_order_index > 0) { + /// If it's not at first order, we add the first order wait_time plus all orders times between first and current + total += first->orders->wait_time; + /// Then all travel_time and wait_time for orders between first and current + byte order_index = 0; + for (o = first->orders->next; ++order_index < first->cur_order_index; o = o->next) total += o->travel_time + o->wait_time; + /// Adds the current order travel time if it's already loading + if (first->current_order.type == OT_LOADING) total += first->current_order.travel_time; + } + + /** + * As now the station where the separation is computed can be different from the first one + * we have to remove the time needed to go order 0 to first stop station order + */ + o = this->orders; + for (int i = 1, cpt = this->FirstStopStationOrderID(); i <= cpt; i++) { + total -= o->wait_time; + o = o->next; + 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 they 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 +3218,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->IsOrderListShared() && this->FirstStopStationOrderID() == this->cur_order_index) { + /// Find the previous vehicle of the shared order that visited the station + Station *st = GetStation(this->current_order.dest); + std::list::iterator iter; + Vehicle *vehicle_to_remove = NULL; + for (iter = st->shared_order_previous_vehicles.begin(); iter != st->shared_order_previous_vehicles.end(); ++iter) { + Vehicle *previous = *iter; + if (this->IsVehicleInSharedOrdersList(previous)) { + /** + * This vehicle is in the shared order list. + * If it's not this previous one, we have to reorder the chain + */ + if (this->prev_shared != previous && this->prev_shared != 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; + } + + /** + * Remove the previous vehicle from the shared_order_previous_vehicles + */ + vehicle_to_remove = previous; + break; + } + } + /** + * Remove the previous vehicle and add this vehicle to the shared_order_previous_vehicles + */ + if (vehicle_to_remove != NULL) st->shared_order_previous_vehicles.remove(vehicle_to_remove); + st->shared_order_previous_vehicles.push_back(this); + } } else { /* This is just an unordered intermediate stop */ this->current_order.flags = 0; @@ -3144,12 +3301,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 = ((this->GetTimetableTotalDuration() * this->GetPositionInSharedOrderList()) / this->CountSharedOrderdList()) - this->GetFirstSharedSeparation(); + /// 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 10887) +++ src/vehicle.h (working copy) @@ -420,6 +420,19 @@ 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(); + VehicleOrderID FirstStopStationOrderID(); }; /**