diff --git a/src/lang/english.txt b/src/lang/english.txt index f2848f0f0b..aa2e449dfd 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3252,6 +3252,8 @@ STR_ORDER_STOP_ORDER :(Stop) STR_ORDER_GO_TO_STATION :{STRING} {STATION} {STRING} +STR_ORDER_AUTOMATIC :(Automatic) + STR_ORDER_FULL_LOAD :(Full load) STR_ORDER_FULL_LOAD_ANY :(Full load any cargo) STR_ORDER_NO_LOAD :(No loading) @@ -3285,6 +3287,7 @@ STR_TIMETABLE_ORDER_VIEW_TOOLTIP :{BLACK}Switch t STR_TIMETABLE_TOOLTIP :{BLACK}Timetable - click on an order to highlight it STR_TIMETABLE_NO_TRAVEL :No travel +STR_TIMETABLE_NOT_TIMETABLEABLE :Travel (automatic; timetabled by next manual order) STR_TIMETABLE_TRAVEL_NOT_TIMETABLED :Travel (not timetabled) STR_TIMETABLE_TRAVEL_FOR :Travel for {STRING1} STR_TIMETABLE_STAY_FOR :and stay for {STRING1} diff --git a/src/order_base.h b/src/order_base.h index 2b605925b1..6503c2999e 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -123,6 +123,12 @@ public: */ void MakeConditional(VehicleOrderID order); + /** + * Makes this order an automatic order. + * @param destination the station to go to. + */ + void MakeAutomatic(StationID destination); + /** * Gets the destination of this order. * @pre IsType(OT_GOTO_WAYPOINT) || IsType(OT_GOTO_DEPOT) || IsType(OT_GOTO_STATION). diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 684ff62c99..57df98a8b7 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -99,6 +99,12 @@ void Order::MakeConditional(VehicleOrderID order) this->dest = 0; } +void Order::MakeAutomatic(StationID destination) +{ + this->type = OT_AUTOMATIC; + this->dest = destination; +} + void Order::SetRefit(CargoID cargo, byte subtype) { this->refit_cargo = cargo; @@ -346,6 +352,8 @@ int OrderList::GetPositionInSharedOrderList(const Vehicle *v) const bool OrderList::IsCompleteTimetable() const { for (Order *o = this->first; o != NULL; o = o->next) { + /* Automatic orders are, by definition, not timetabled. */ + if (o->IsType(OT_AUTOMATIC)) continue; if (!o->IsCompletelyTimetabled()) return false; } return true; @@ -1446,9 +1454,20 @@ void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination) int id = -1; FOR_VEHICLE_ORDERS(v, order) { id++; - if (order->IsType(OT_GOTO_DEPOT) && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue; - if ((v->type == VEH_AIRCRAFT && order->IsType(OT_GOTO_DEPOT) ? OT_GOTO_STATION : order->GetType()) == type && - order->GetDestination() == destination) { + + OrderType ot = order->GetType(); + if (ot == OT_GOTO_DEPOT && (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) continue; + if (ot == OT_AUTOMATIC || (v->type == VEH_AIRCRAFT && ot == OT_GOTO_DEPOT)) ot = OT_GOTO_STATION; + if (ot == type && order->GetDestination() == destination) { + /* We want to clear automatic orders, but we don't want to make them + * dummy orders. They should just vanish. Also check the actual order + * type as ot is currently OT_GOTO_STATION. */ + if (order->IsType(OT_AUTOMATIC)) { + DeleteOrder(v, id); + id--; + continue; + } + order->MakeDummy(); for (const Vehicle *w = v->FirstShared(); w != NULL; w = w->NextShared()) { /* In GUI, simulate by removing the order and adding it back */ @@ -1653,7 +1672,15 @@ bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth) assert(v->cur_order_index < v->GetNumOrders()); /* Get the current order */ - order = v->GetOrder(v->cur_order_index); + order = v->GetNextManualOrder(v->cur_order_index); + if (order == NULL) { + order = v->GetNextManualOrder(0); + if (order == NULL) { + v->current_order.Free(); + v->dest_tile = 0; + return false; + } + } v->current_order = *order; return UpdateOrderDest(v, order, conditional_depth + 1); } @@ -1708,7 +1735,7 @@ bool ProcessOrders(Vehicle *v) /* Get the current order */ if (v->cur_order_index >= v->GetNumOrders()) v->cur_order_index = 0; - const Order *order = v->GetOrder(v->cur_order_index); + const Order *order = v->GetNextManualOrder(v->cur_order_index); /* If no order, do nothing. */ if (order == NULL || (v->type == VEH_AIRCRAFT && !CheckForValidOrders(v))) { diff --git a/src/order_gui.cpp b/src/order_gui.cpp index cc0701f4a0..a63df3fc49 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -200,8 +200,15 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int DrawSprite(sprite, PAL_NONE, rtl ? right - sprite_size.width : left, y + ((int)FONT_HEIGHT_NORMAL - (int)sprite_size.height) / 2); } + TextColour colour = TC_BLACK; + if (order->IsType(OT_AUTOMATIC)) { + colour = (selected ? TC_SILVER : TC_GREY) | TC_NO_SHADE; + } else if (selected) { + colour = TC_WHITE; + } + SetDParam(0, order_index + 1); - DrawString(left, rtl ? right - sprite_size.width - 3 : middle, y, STR_ORDER_INDEX, selected ? TC_WHITE : TC_BLACK, SA_RIGHT | SA_FORCE); + DrawString(left, rtl ? right - sprite_size.width - 3 : middle, y, STR_ORDER_INDEX, colour, SA_RIGHT | SA_FORCE); SetDParam(5, STR_EMPTY); @@ -211,6 +218,13 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int SetDParam(1, order->GetDestination()); break; + case OT_AUTOMATIC: + SetDParam(0, STR_ORDER_GO_TO_STATION); + SetDParam(1, STR_ORDER_GO_TO); + SetDParam(2, order->GetDestination()); + SetDParam(3, timetable ? STR_EMPTY : STR_ORDER_AUTOMATIC); + break; + case OT_GOTO_STATION: { OrderLoadFlags load = order->GetLoadType(); OrderUnloadFlags unload = order->GetUnloadType(); @@ -298,7 +312,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int default: NOT_REACHED(); } - DrawString(rtl ? left : middle, rtl ? middle : right, y, STR_ORDER_TEXT, selected ? TC_WHITE : TC_BLACK); + DrawString(rtl ? left : middle, rtl ? middle : right, y, STR_ORDER_TEXT, colour); } diff --git a/src/order_type.h b/src/order_type.h index 7b252add0f..6d9b0f0b3f 100644 --- a/src/order_type.h +++ b/src/order_type.h @@ -38,6 +38,7 @@ enum OrderType { OT_DUMMY = 5, OT_GOTO_WAYPOINT = 6, OT_CONDITIONAL = 7, + OT_AUTOMATIC = 8, OT_END }; diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp index 0f8fc39e97..40064f4a91 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -72,7 +72,7 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u VehicleOrderID order_number = GB(p1, 20, 8); Order *order = v->GetOrder(order_number); - if (order == NULL) return CMD_ERROR; + if (order == NULL || order->IsType(OT_AUTOMATIC)) return CMD_ERROR; bool is_journey = HasBit(p1, 28); @@ -238,11 +238,17 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling) v->current_order_time = 0; if (!_settings_game.order.timetabling) return; + if (v->current_order.IsType(OT_AUTOMATIC)) return; // no timetabling of auto orders + + VehicleOrderID first_manual_order = 0; + for (Order *o = v->GetFirstOrder(); o != NULL && o->IsType(OT_AUTOMATIC); o = o->next) { + ++first_manual_order; + } bool just_started = false; /* This vehicle is arriving at the first destination in the timetable. */ - if (v->cur_order_index == 0 && travelling) { + if (v->cur_order_index == first_manual_order && travelling) { /* If the start date hasn't been set, or it was set automatically when * the vehicle last arrived at the first destination, update it to the * current time. Otherwise set the late counter appropriately to when @@ -279,7 +285,7 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling) ChangeTimetable(v, v->cur_order_index, time_taken, travelling); } - if (v->cur_order_index == 0 && travelling) { + if (v->cur_order_index == first_manual_order && travelling) { /* If we just started we would have returned earlier and have not reached * this code. So obviously, we have completed our round: So turn autofill * off again. */ diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index 026d20a10e..fdd308bed1 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -90,7 +90,7 @@ static void SetArrivalDepartParams(int param1, int param2, Ticks ticks) static bool CanDetermineTimeTaken(const Order *order, bool travelling) { /* Current order is conditional */ - if (order->IsType(OT_CONDITIONAL)) return false; + if (order->IsType(OT_CONDITIONAL) || order->IsType(OT_AUTOMATIC)) return false; /* No travel time and we have not already finished travelling */ if (travelling && order->travel_time == 0) return false; /* No wait time but we are loading at this timetabled station */ @@ -126,15 +126,20 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID /* 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 */ do { - if (travelling || i != start) { - if (!CanDetermineTimeTaken(order, true)) return; - sum += order->travel_time; - table[i].arrival = sum; - } + /* Automatic orders don't influence the overall timetable; + * they just add some untimetabled entries, but the time till + * the next non-automatic order can still be known. */ + if (!order->IsType(OT_AUTOMATIC)) { + if (travelling || i != start) { + if (!CanDetermineTimeTaken(order, true)) return; + sum += order->travel_time; + table[i].arrival = sum; + } - if (!CanDetermineTimeTaken(order, false)) return; - sum += order->wait_time; - table[i].departure = sum; + if (!CanDetermineTimeTaken(order, false)) return; + sum += order->wait_time; + table[i].departure = sum; + } ++i; order = order->next; @@ -317,7 +322,7 @@ struct TimetableWindow : Window { if (selected != -1) { const Order *order = v->GetOrder(((selected + 1) / 2) % v->GetNumOrders()); if (selected % 2 == 1) { - disable = order != NULL && order->IsType(OT_CONDITIONAL); + disable = order != NULL && (order->IsType(OT_CONDITIONAL) || order->IsType(OT_AUTOMATIC)); } else { disable = order == NULL || ((!order->IsType(OT_GOTO_STATION) || (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) && !order->IsType(OT_CONDITIONAL)); } @@ -387,9 +392,12 @@ struct TimetableWindow : Window { } } else { StringID string; - + TextColour colour = (i == selected) ? TC_WHITE : TC_BLACK; if (order->IsType(OT_CONDITIONAL)) { string = STR_TIMETABLE_NO_TRAVEL; + } else if(order->IsType(OT_AUTOMATIC)) { + string = STR_TIMETABLE_NOT_TIMETABLEABLE; + colour = ((i == selected) ? TC_SILVER : TC_GREY) | TC_NO_SHADE; } else if (order->travel_time == 0) { string = STR_TIMETABLE_TRAVEL_NOT_TIMETABLED; } else { @@ -397,7 +405,7 @@ struct TimetableWindow : Window { string = STR_TIMETABLE_TRAVEL_FOR; } - DrawString(rtl ? r.left + WD_FRAMERECT_LEFT : middle, rtl ? middle : r.right - WD_FRAMERECT_LEFT, y, string, (i == selected) ? TC_WHITE : TC_BLACK); + DrawString(rtl ? r.left + WD_FRAMERECT_LEFT : middle, rtl ? middle : r.right - WD_FRAMERECT_LEFT, y, string, colour); if (final_order) break; } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index bc54690625..9dc25a2dd4 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1231,7 +1231,7 @@ void VehicleEnterDepot(Vehicle *v) if (v->current_order.IsType(OT_GOTO_DEPOT)) { SetWindowDirty(WC_VEHICLE_VIEW, v->index); - const Order *real_order = v->GetOrder(v->cur_order_index); + const Order *real_order = v->GetNextManualOrder(v->cur_order_index); Order t = v->current_order; v->current_order.MakeDummy(); @@ -1747,6 +1747,12 @@ void Vehicle::BeginLoading() current_order.MakeLoading(true); UpdateVehicleTimetable(this, true); + for (Order *order = this->GetOrder(this->cur_order_index); + order != NULL && order->IsType(OT_AUTOMATIC); + order = order->next) { + DeleteOrder(this, this->cur_order_index); + } + /* Furthermore add the Non Stop flag to mark that this station * is the actual destination of the vehicle, which is (for example) * necessary to be known for HandleTrainLoading to determine @@ -1755,6 +1761,18 @@ void Vehicle::BeginLoading() this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION); } else { + /* We weren't scheduled to stop here. Insert an automatic order + * to show that we are stopping here. */ + Order *in_list = this->GetOrder(this->cur_order_index); + if (this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID && + ((in_list == NULL && this->cur_order_index == 0) || + (in_list != NULL && (!in_list->IsType(OT_AUTOMATIC) || + in_list->GetDestination() != this->last_station_visited)))) { + Order *auto_order = new Order(); + auto_order->MakeAutomatic(this->last_station_visited); + InsertOrder(this, auto_order, this->cur_order_index); + if (this->cur_order_index > 0) --this->cur_order_index; + } current_order.MakeLoading(false); } @@ -1808,11 +1826,8 @@ void Vehicle::HandleLoading(bool mode) this->PlayLeaveStationSound(); - bool at_destination_station = this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE; this->LeaveStation(); - /* If this was not the final order, don't remove it from the list. */ - if (!at_destination_station) return; break; } @@ -2143,6 +2158,20 @@ void Vehicle::RemoveFromShared() this->previous_shared = NULL; } +/** + * Get the next manual (not OT_AUTOMATIC) order after the one at the given index. + * @param index The index to start searching at. + * @return The next manual order at or after index or NULL if there is none. + */ +Order *Vehicle::GetNextManualOrder(int index) const +{ + Order *order = this->GetOrder(index); + while(order != NULL && order->IsType(OT_AUTOMATIC)) { + order = order->next; + } + return order; +} + void StopAllVehicles() { Vehicle *v; diff --git a/src/vehicle_base.h b/src/vehicle_base.h index 381a5e3f6c..2d59c3795e 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -662,6 +662,8 @@ public: return (this->orders.list == NULL) ? NULL : this->orders.list->GetOrderAt(index); } + Order *GetNextManualOrder(int index) const; + /** * Returns the last order of a vehicle, or NULL if it doesn't exists * @return last order of a vehicle, if available diff --git a/src/vehiclelist.cpp b/src/vehiclelist.cpp index 5303e19047..1d0f096348 100644 --- a/src/vehiclelist.cpp +++ b/src/vehiclelist.cpp @@ -119,7 +119,7 @@ bool GenerateVehicleSortList(VehicleList *list, const VehicleListIdentifier &vli const Order *order; FOR_VEHICLE_ORDERS(v, order) { - if ((order->IsType(OT_GOTO_STATION) || order->IsType(OT_GOTO_WAYPOINT)) + if ((order->IsType(OT_GOTO_STATION) || order->IsType(OT_GOTO_WAYPOINT) || order->IsType(OT_AUTOMATIC)) && order->GetDestination() == vli.index) { *list->Append() = v; break;