mirror of https://github.com/OpenTTD/OpenTTD.git
(svn r21642) -Feature: concept of automatic station orders; add stub orders for intermediate stations and remove them when not visiting them anymore. This allows you to see what trains visit a station without actually having to order a vehicle to stop at all stations. Based on patch by fonsinchen
This commit is contained in:
parent
8a278f7711
commit
64f04c3a74
|
@ -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}
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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))) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ enum OrderType {
|
|||
OT_DUMMY = 5,
|
||||
OT_GOTO_WAYPOINT = 6,
|
||||
OT_CONDITIONAL = 7,
|
||||
OT_AUTOMATIC = 8,
|
||||
OT_END
|
||||
};
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue