changeset 8900:a12b2ec504da draft

(svn r12667) -Feature: conditional 'skip/jump' orders.
author rubidium <rubidium@openttd.org>
date Sat, 12 Apr 2008 11:58:19 +0000
parents 93068462f33c
children f30afabf2b45
files src/lang/english.txt src/order_base.h src/order_cmd.cpp src/order_gui.cpp src/order_type.h src/timetable_gui.cpp
diffstat 6 files changed, 463 insertions(+), 37 deletions(-) [+]
line wrap: on
line diff
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -2652,8 +2652,8 @@
 STR_8801_CITIZENS_CELEBRATE_FIRST                               :{BLACK}{BIGFONT}Citizens celebrate . . .{}First train arrives at {STATION}!
 STR_8802_DETAILS                                                :{WHITE}{VEHICLE} (Details)
 STR_8803_TRAIN_IN_THE_WAY                                       :{WHITE}Train in the way
-STR_8804                                                        :{SETX 10}{COMMA}: {STRING3} {STRING2}
-STR_8805                                                        :{RIGHTARROW}{SETX 10}{COMMA}: {STRING3} {STRING2}
+STR_8804                                                        :{SETX 10}{COMMA}: {STRING4} {STRING2}
+STR_8805                                                        :{RIGHTARROW}{SETX 10}{COMMA}: {STRING4} {STRING2}
 STR_ORDER_GO_TO                                                 :Go to
 STR_ORDER_GO_NON_STOP_TO                                        :Go non-stop to
 STR_ORDER_GO_VIA                                                :Go via
@@ -2685,7 +2685,7 @@
 STR_ORDER_TOOLTIP_UNLOAD                                        :{BLACK}Change the unloading behaviour of the highlighted order
 STR_GO_TO_STATION                                               :{STRING} {STATION} {STRING}
 
-STR_ORDER_GO_TO_DROPDOWN_TOOLTIP                                :Insert an advanced order
+STR_ORDER_GO_TO_DROPDOWN_TOOLTIP                                :{BLACK}Insert an advanced order
 STR_ORDER_GO_TO_NEAREST_DEPOT                                   :Go to nearest depot
 STR_ORDER_GO_TO_NEAREST_HANGAR                                  :Go to nearest hangar
 STR_ORDER_NEAREST_DEPOT                                         :the nearest
@@ -2699,7 +2699,29 @@
 STR_GO_TO_NEAREST_DEPOT                                         :{STRING} {STRING} {STRING}
 STR_GO_TO_HANGAR                                                :{STRING} {STATION} Hangar
 
-STR_TIMETABLE_GO_TO                                             :{STRING3} {STRING2}
+STR_ORDER_CONDITIONAL                                           :Conditional order jump
+STR_ORDER_CONDITIONAL_VARIABLE_TOOLTIP                          :{BLACK}Vehicle data to base jumping on
+STR_ORDER_CONDITIONAL_COMPARATOR_TOOLTIP                        :{BLACK}How to compare the vehicle data to the given value
+STR_ORDER_CONDITIONAL_VALUE_TOOLTIP                             :{BLACK}The value to compare the vehicle data against
+STR_ORDER_CONDITIONAL_VALUE_CAPT                                :{WHITE}Enter value to compare against
+STR_ORDER_CONDITIONAL_LOAD_PERCENTAGE                           :Load percentage
+STR_ORDER_CONDITIONAL_RELIABILITY                               :Reliability
+STR_ORDER_CONDITIONAL_MAX_SPEED                                 :Maximum speed
+STR_ORDER_CONDITIONAL_AGE                                       :Vehicle age (years)
+STR_ORDER_CONDITIONAL_REQUIRES_SERVICE                          :Requires service
+STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS                         :is equal to
+STR_ORDER_CONDITIONAL_COMPARATOR_NOT_EQUALS                     :is not equal to
+STR_ORDER_CONDITIONAL_COMPARATOR_LESS_THAN                      :is less than
+STR_ORDER_CONDITIONAL_COMPARATOR_LESS_EQUALS                    :is less or equal to
+STR_ORDER_CONDITIONAL_COMPARATOR_MORE_THAN                      :is more than
+STR_ORDER_CONDITIONAL_COMPARATOR_MORE_EQUALS                    :is more or equal to
+STR_ORDER_CONDITIONAL_COMPARATOR_IS_TRUE                        :is true
+STR_ORDER_CONDITIONAL_COMPARATOR_IS_FALSE                       :is false
+STR_CONDITIONAL_VALUE                                           :{SKIP}{BLACK}{COMMA}
+STR_CONDITIONAL_NUM                                             :Jump to order {COMMA} when {STRING} {STRING} {COMMA}
+STR_CONDITIONAL_TRUE_FALSE                                      :Jump to order {COMMA} when {STRING} {STRING}
+
+STR_TIMETABLE_GO_TO                                             :{STRING4} {STRING2}
 STR_TIMETABLE_TRAVEL_NOT_TIMETABLED                             :Travel (not timetabled)
 STR_TIMETABLE_TRAVEL_FOR                                        :Travel for {STRING1}
 STR_TIMETABLE_STAY_FOR                                          :and stay for {STRING1}
--- a/src/order_base.h
+++ b/src/order_base.h
@@ -66,7 +66,7 @@
 	 * Get the type of order of this order.
 	 * @return the order type.
 	 */
-	inline OrderType GetType() const { return (OrderType)GB(this->type, 0, 6); }
+	inline OrderType GetType() const { return (OrderType)GB(this->type, 0, 4); }
 
 	/**
 	 * 'Free' the order
@@ -112,6 +112,12 @@
 	void MakeDummy();
 
 	/**
+	 * Makes this order an conditional order.
+	 * @param order the order to jump to.
+	 */
+	void MakeConditional(VehicleOrderID order);
+
+	/**
 	 * Free a complete order chain.
 	 * @note do not use on "current_order" vehicle orders!
 	 */
@@ -170,6 +176,14 @@
 	inline OrderDepotTypeFlags GetDepotOrderType() const { return (OrderDepotTypeFlags)GB(this->flags, 0, 4); }
 	/** What are we going to do when in the depot. */
 	inline OrderDepotActionFlags GetDepotActionType() const { return (OrderDepotActionFlags)GB(this->flags, 4, 4); }
+	/** What variable do we have to compare? */
+	inline OrderConditionVariable GetConditionVariable() const { return (OrderConditionVariable)GB(this->dest, 11, 5); }
+	/** What is the comparator to use? */
+	inline OrderConditionComparator GetConditionComparator() const { return (OrderConditionComparator)GB(this->type, 5, 3); }
+	/** Get the order to skip to. */
+	inline VehicleOrderID GetConditionSkipToOrder() const { return this->flags; }
+	/** Get the value to base the skip on. */
+	inline uint16 GetConditionValue() const { return GB(this->dest, 0, 11); }
 
 	/** Set how the consist must be loaded. */
 	inline void SetLoadType(OrderLoadFlags load_type) { SB(this->flags, 4, 4, load_type); }
@@ -180,7 +194,15 @@
 	/** Set the cause to go to the depot. */
 	inline void SetDepotOrderType(OrderDepotTypeFlags depot_order_type) { SB(this->flags, 0, 4, depot_order_type); }
 	/** Set what we are going to do in the depot. */
-	inline void SetDepotActionType(OrderDepotActionFlags depot_service_type) { SB(this->flags, 4, 4,  depot_service_type); }
+	inline void SetDepotActionType(OrderDepotActionFlags depot_service_type) { SB(this->flags, 4, 4, depot_service_type); }
+	/** Set variable we have to compare. */
+	inline void SetConditionVariable(OrderConditionVariable condition_variable) { SB(this->dest, 11, 5, condition_variable); }
+	/** Set the comparator to use. */
+	inline void SetConditionComparator(OrderConditionComparator condition_comparator) { SB(this->type, 5, 3, condition_comparator); }
+	/** Get the order to skip to. */
+	inline void SetConditionSkipToOrder(VehicleOrderID order_id) { this->flags = order_id; }
+	/** Set the value to base the skip on. */
+	inline void SetConditionValue(uint16 value) { SB(this->dest, 0, 11, value); }
 
 	bool ShouldStopAtStation(const Vehicle *v, StationID station) const;
 
--- a/src/order_cmd.cpp
+++ b/src/order_cmd.cpp
@@ -93,6 +93,13 @@
 	this->flags = 0;
 }
 
+void Order::MakeConditional(VehicleOrderID order)
+{
+	this->type = OT_CONDITIONAL;
+	this->flags = 0;
+	this->dest = order;
+}
+
 void Order::SetRefit(CargoID cargo, byte subtype)
 {
 	this->refit_cargo = cargo;
@@ -427,6 +434,14 @@
 			break;
 		}
 
+		case OT_CONDITIONAL: {
+			if (!IsPlayerBuildableVehicleType(v)) return CMD_ERROR;
+
+			VehicleOrderID skip_to = new_order.GetConditionSkipToOrder();
+			if (skip_to >= v->num_orders) return CMD_ERROR;
+			if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) return CMD_ERROR;
+		} break;
+
 		default: return CMD_ERROR;
 	}
 
@@ -512,6 +527,22 @@
 			InvalidateVehicleOrder(u);
 		}
 
+		/* As we insert an order, the order to skip to will be 'wrong'. */
+		VehicleOrderID cur_order_id = 0;
+		Order *order;
+		FOR_VEHICLE_ORDERS(v, order) {
+			if (order->IsType(OT_CONDITIONAL)) {
+				VehicleOrderID order_id = order->GetConditionSkipToOrder();
+				if (order_id >= sel_ord) {
+					order->SetConditionSkipToOrder(order_id + 1);
+				}
+				if (order_id == cur_order_id) {
+					order->SetConditionSkipToOrder((order_id + 1) % v->num_orders);
+				}
+			}
+			cur_order_id++;
+		}
+
 		/* Make sure to rebuild the whole list */
 		RebuildVehicleLists();
 	}
@@ -625,6 +656,21 @@
 			InvalidateVehicleOrder(u);
 		}
 
+		/* As we delete an order, the order to skip to will be 'wrong'. */
+		VehicleOrderID cur_order_id = 0;
+		FOR_VEHICLE_ORDERS(v, order) {
+			if (order->IsType(OT_CONDITIONAL)) {
+				VehicleOrderID order_id = order->GetConditionSkipToOrder();
+				if (order_id >= sel_ord) {
+					order->SetConditionSkipToOrder(max(order_id - 1, 0));
+				}
+				if (order_id == cur_order_id) {
+					order->SetConditionSkipToOrder((order_id + 1) % v->num_orders);
+				}
+			}
+			cur_order_id++;
+		}
+
 		RebuildVehicleLists();
 	}
 
@@ -746,6 +792,22 @@
 			InvalidateVehicleOrder(u);
 		}
 
+		/* As we move an order, the order to skip to will be 'wrong'. */
+		Order *order;
+		FOR_VEHICLE_ORDERS(v, order) {
+			if (order->IsType(OT_CONDITIONAL)) {
+				VehicleOrderID order_id = order->GetConditionSkipToOrder();
+				if (order_id == moving_order) {
+					order_id = target_order;
+				} else if(order_id > moving_order && order_id <= target_order) {
+					order_id--;
+				} else if(order_id < moving_order && order_id >= target_order) {
+					order_id++;
+				}
+				order->SetConditionSkipToOrder(order_id);
+			}
+		}
+
 		/* Make sure to rebuild the whole list */
 		RebuildVehicleLists();
 	}
@@ -762,16 +824,17 @@
  *                        the order will be inserted before that one
  *                        only the first 8 bits used currently (bit 16 - 23) (max 255)
  * @param p2 various bitstuffed elements
- *  - p2 = (bit 0 - 1) - what data to modify (@see ModifyOrderFlags)
- *  - p2 = (bit 2 - 5) - the data to modify
+ *  - p2 = (bit 0 -  3) - what data to modify (@see ModifyOrderFlags)
+ *  - p2 = (bit 4 - 15) - the data to modify
  */
 CommandCost CmdModifyOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 {
 	VehicleOrderID sel_ord = GB(p1, 16, 16); // XXX - automatically truncated to 8 bits.
 	VehicleID veh          = GB(p1,  0, 16);
-	ModifyOrderFlags mof   = (ModifyOrderFlags)GB(p2,  0,  2);
-	uint8 data             = GB(p2,  2,  4);
+	ModifyOrderFlags mof   = (ModifyOrderFlags)GB(p2,  0,  4);
+	uint16 data             = GB(p2, 4, 11);
 
+	if (mof >= MOF_END) return CMD_ERROR;
 	if (!IsValidVehicleID(veh)) return CMD_ERROR;
 
 	Vehicle *v = GetVehicle(veh);
@@ -783,22 +846,28 @@
 	Order *order = GetVehicleOrder(v, sel_ord);
 	switch (order->GetType()) {
 		case OT_GOTO_STATION:
-			if (mof == MOF_DEPOT_ACTION || GetStation(order->GetDestination())->IsBuoy()) return CMD_ERROR;
+			if (mof == MOF_COND_VARIABLE || mof == MOF_COND_COMPARATOR || mof == MOF_DEPOT_ACTION || mof == MOF_COND_VALUE || GetStation(order->GetDestination())->IsBuoy()) return CMD_ERROR;
 			break;
 
 		case OT_GOTO_DEPOT:
-			if (mof == MOF_UNLOAD || mof == MOF_LOAD) return CMD_ERROR;
+			if (mof != MOF_NON_STOP && mof != MOF_DEPOT_ACTION) return CMD_ERROR;
 			break;
 
 		case OT_GOTO_WAYPOINT:
 			if (mof != MOF_NON_STOP) return CMD_ERROR;
 			break;
 
+		case OT_CONDITIONAL:
+			if (mof != MOF_COND_VARIABLE && mof != MOF_COND_COMPARATOR && mof != MOF_COND_VALUE) return CMD_ERROR;
+			break;
+
 		default:
 			return CMD_ERROR;
 	}
 
 	switch (mof) {
+		default: NOT_REACHED();
+
 		case MOF_NON_STOP:
 			if (data >= ONSF_END) return CMD_ERROR;
 			if (data == order->GetNonStopType()) return CMD_ERROR;
@@ -819,6 +888,36 @@
 		case MOF_DEPOT_ACTION:
 			if (data != 0) return CMD_ERROR;
 			break;
+
+		case MOF_COND_VARIABLE:
+			if (data >= OCV_END) return CMD_ERROR;
+			break;
+
+		case MOF_COND_COMPARATOR:
+			if (data >= OCC_END) return CMD_ERROR;
+			switch (order->GetConditionVariable()) {
+				case OCV_REQUIRES_SERVICE:
+					if (data != OCC_IS_TRUE && data != OCC_IS_FALSE) return CMD_ERROR;
+					break;
+
+				default:
+					if (data == OCC_IS_TRUE || data == OCC_IS_FALSE) return CMD_ERROR;
+					break;
+			}
+			break;
+
+		case MOF_COND_VALUE:
+			switch (order->GetConditionVariable()) {
+				case OCV_LOAD_PERCENTAGE:
+				case OCV_RELIABILITY:
+					if (data > 100) return CMD_ERROR;
+					break;
+
+				default:
+					if (data > 2047) return CMD_ERROR;
+					break;
+			}
+			break;
 	}
 
 	if (flags & DC_EXEC) {
@@ -846,6 +945,33 @@
 				order->SetDepotOrderType((OrderDepotTypeFlags)(order->GetDepotOrderType() ^ ODTFB_SERVICE));
 				break;
 
+			case MOF_COND_VARIABLE: {
+				order->SetConditionVariable((OrderConditionVariable)data);
+
+				OrderConditionComparator occ = order->GetConditionComparator();
+				switch (order->GetConditionVariable()) {
+					case OCV_REQUIRES_SERVICE:
+						if (occ != OCC_IS_TRUE && occ != OCC_IS_FALSE) order->SetConditionComparator(OCC_IS_TRUE);
+						break;
+
+					case OCV_LOAD_PERCENTAGE:
+					case OCV_RELIABILITY:
+						if (order->GetConditionValue() > 100) order->SetConditionValue(100);
+						/* FALL THROUGH */
+					default:
+						if (occ == OCC_IS_TRUE || occ == OCC_IS_FALSE) order->SetConditionComparator(OCC_EQUALS);
+						break;
+				}
+			} break;
+
+			case MOF_COND_COMPARATOR:
+				order->SetConditionComparator((OrderConditionComparator)data);
+				break;
+
+			case MOF_COND_VALUE:
+				order->SetConditionValue(data);
+				break;
+
 			default: NOT_REACHED();
 		}
 
@@ -1417,6 +1543,24 @@
 }
 
 /**
+ * Compare the variable and value based on the given comparator.
+ */
+static bool OrderConditionCompare(OrderConditionComparator occ, int variable, int value)
+{
+	switch (occ) {
+		case OCC_EQUALS:      return variable == value;
+		case OCC_NOT_EQUALS:  return variable != value;
+		case OCC_LESS_THAN:   return variable <  value;
+		case OCC_LESS_EQUALS: return variable <= value;
+		case OCC_MORE_THAN:   return variable >  value;
+		case OCC_MORE_EQUALS: return variable >= value;
+		case OCC_IS_TRUE:     return variable != 0;
+		case OCC_IS_FALSE:    return variable == 0;
+		default: NOT_REACHED();
+	}
+}
+
+/**
  * Handle the orders of a vehicle and determine the next place
  * to go to if needed.
  * @param v the vehicle to do this for.
@@ -1551,6 +1695,27 @@
 			v->dest_tile = GetWaypoint(order->GetDestination())->xy;
 			break;
 
+		case OT_CONDITIONAL: {
+			bool skip_order = false;
+			OrderConditionComparator occ = order->GetConditionComparator();
+			uint16 value = order->GetConditionValue();
+
+			switch (order->GetConditionVariable()) {
+				case OCV_LOAD_PERCENTAGE:  skip_order = OrderConditionCompare(occ, CalcPercentVehicleFilled(v, NULL), value); break;
+				case OCV_RELIABILITY:      skip_order = OrderConditionCompare(occ, v->reliability * 100 >> 16,        value); break;
+				case OCV_MAX_SPEED:        skip_order = OrderConditionCompare(occ, v->GetDisplayMaxSpeed(),           value); break;
+				case OCV_AGE:              skip_order = OrderConditionCompare(occ, v->age / 366,                      value); break;
+				case OCV_REQUIRES_SERVICE: skip_order = OrderConditionCompare(occ, v->NeedsServicing(),               value); break;
+				default: NOT_REACHED();
+			}
+			UpdateVehicleTimetable(v, true);
+			if (skip_order) {
+				v->cur_order_index = order->GetConditionSkipToOrder();
+			} else {
+				v->cur_order_index++;
+			}
+		} return false;
+
 		default:
 			v->dest_tile = 0;
 			return false;
--- a/src/order_gui.cpp
+++ b/src/order_gui.cpp
@@ -27,6 +27,8 @@
 #include "player_func.h"
 #include "newgrf_cargo.h"
 #include "widgets/dropdown_func.h"
+#include "textbuf_gui.h"
+#include "string_func.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"
@@ -46,13 +48,23 @@
 	ORDER_WIDGET_UNLOAD,
 	ORDER_WIDGET_REFIT,
 	ORDER_WIDGET_SERVICE,
+	ORDER_WIDGET_COND_VARIABLE,
+	ORDER_WIDGET_COND_COMPARATOR,
+	ORDER_WIDGET_COND_VALUE,
 	ORDER_WIDGET_RESIZE_BAR,
 	ORDER_WIDGET_SHARED_ORDER_LIST,
 	ORDER_WIDGET_RESIZE,
 };
 
+/** Under what reason are we using the PlaceObject functionality? */
+enum OrderPlaceObjectState {
+	OPOS_GOTO,
+	OPOS_CONDITIONAL,
+};
+
 struct order_d {
 	int sel;
+	OrderPlaceObjectState goto_type;
 };
 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(order_d));
 
@@ -89,7 +101,7 @@
 	 * 15 = 14 (w->widget[ORDER_WIDGET_ORDER_LIST].top) + 1 (frame-line)
 	 * 10 = order text hight
 	 */
-	int sel = (y - 15) / 10;
+	int sel = (y - w->widget[ORDER_WIDGET_ORDER_LIST].top - 1) / 10;
 
 	if ((uint)sel >= w->vscroll.cap) return INVALID_ORDER;
 
@@ -162,15 +174,42 @@
 static const StringID _order_goto_dropdown[] = {
 	STR_ORDER_GO_TO,
 	STR_ORDER_GO_TO_NEAREST_DEPOT,
+	STR_ORDER_CONDITIONAL,
 	INVALID_STRING_ID
 };
 
 static const StringID _order_goto_dropdown_aircraft[] = {
 	STR_ORDER_GO_TO,
 	STR_ORDER_GO_TO_NEAREST_HANGAR,
+	STR_ORDER_CONDITIONAL,
 	INVALID_STRING_ID
 };
 
+static const StringID _order_conditional_variable[] = {
+	STR_ORDER_CONDITIONAL_LOAD_PERCENTAGE,
+	STR_ORDER_CONDITIONAL_RELIABILITY,
+	STR_ORDER_CONDITIONAL_MAX_SPEED,
+	STR_ORDER_CONDITIONAL_AGE,
+	STR_ORDER_CONDITIONAL_REQUIRES_SERVICE,
+	INVALID_STRING_ID,
+};
+
+static const StringID _order_conditional_condition[] = {
+	STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS,
+	STR_ORDER_CONDITIONAL_COMPARATOR_NOT_EQUALS,
+	STR_ORDER_CONDITIONAL_COMPARATOR_LESS_THAN,
+	STR_ORDER_CONDITIONAL_COMPARATOR_LESS_EQUALS,
+	STR_ORDER_CONDITIONAL_COMPARATOR_MORE_THAN,
+	STR_ORDER_CONDITIONAL_COMPARATOR_MORE_EQUALS,
+	STR_ORDER_CONDITIONAL_COMPARATOR_IS_TRUE,
+	STR_ORDER_CONDITIONAL_COMPARATOR_IS_FALSE,
+	INVALID_STRING_ID,
+};
+
+extern uint ConvertSpeedToDisplaySpeed(uint speed);
+extern uint ConvertDisplaySpeedToSpeed(uint speed);
+
+
 static void DrawOrdersWindow(Window *w)
 {
 	const Vehicle *v = GetVehicle(w->window_number);
@@ -183,9 +222,11 @@
 
 	if (v->owner == _local_player) {
 		/* Set the strings for the dropdown boxes. */
-		w->widget[ORDER_WIDGET_NON_STOP].data  = _order_non_stop_drowdown[order == NULL ? 0 : order->GetNonStopType()];
-		w->widget[ORDER_WIDGET_FULL_LOAD].data = _order_full_load_drowdown[order == NULL ? 0 : order->GetLoadType()];
-		w->widget[ORDER_WIDGET_UNLOAD].data    = _order_unload_drowdown[order == NULL ? 0 : order->GetUnloadType()];
+		w->widget[ORDER_WIDGET_NON_STOP].data        = _order_non_stop_drowdown[order == NULL ? 0 : order->GetNonStopType()];
+		w->widget[ORDER_WIDGET_FULL_LOAD].data       = _order_full_load_drowdown[order == NULL ? 0 : order->GetLoadType()];
+		w->widget[ORDER_WIDGET_UNLOAD].data          = _order_unload_drowdown[order == NULL ? 0 : order->GetUnloadType()];
+		w->widget[ORDER_WIDGET_COND_VARIABLE].data   = _order_conditional_variable[order == NULL ? 0 : order->GetConditionVariable()];
+		w->widget[ORDER_WIDGET_COND_COMPARATOR].data = _order_conditional_condition[order == NULL ? 0 : order->GetConditionComparator()];
 
 		/* skip */
 		w->SetWidgetDisabledState(ORDER_WIDGET_SKIP, v->num_orders <= 1);
@@ -204,10 +245,13 @@
 		w->SetWidgetDisabledState(ORDER_WIDGET_SERVICE,   order == NULL); // Refit
 		w->HideWidget(ORDER_WIDGET_REFIT); // Refit
 		w->HideWidget(ORDER_WIDGET_SERVICE); // Service
-	} else {
-		w->DisableWidget(ORDER_WIDGET_FULL_LOAD);
+
+		w->HideWidget(ORDER_WIDGET_COND_VARIABLE);
+		w->HideWidget(ORDER_WIDGET_COND_COMPARATOR);
+		w->HideWidget(ORDER_WIDGET_COND_VALUE);
 	}
 
+	w->ShowWidget(ORDER_WIDGET_NON_STOP);
 	w->ShowWidget(ORDER_WIDGET_UNLOAD);
 	w->ShowWidget(ORDER_WIDGET_FULL_LOAD);
 
@@ -232,6 +276,22 @@
 				w->ShowWidget(ORDER_WIDGET_SERVICE);
 				break;
 
+			case OT_CONDITIONAL: {
+				w->HideWidget(ORDER_WIDGET_NON_STOP);
+				w->HideWidget(ORDER_WIDGET_UNLOAD);
+				w->HideWidget(ORDER_WIDGET_FULL_LOAD);
+				w->ShowWidget(ORDER_WIDGET_COND_VARIABLE);
+				w->ShowWidget(ORDER_WIDGET_COND_COMPARATOR);
+				w->ShowWidget(ORDER_WIDGET_COND_VALUE);
+
+				OrderConditionVariable ocv = order->GetConditionVariable();
+				w->SetWidgetDisabledState(ORDER_WIDGET_COND_VALUE, ocv == OCV_REQUIRES_SERVICE);
+
+				uint value = order->GetConditionValue();
+				if (order->GetConditionVariable() == OCV_MAX_SPEED) value = ConvertSpeedToDisplaySpeed(value);
+				SetDParam(1, value);
+			} break;
+
 			default: // every other orders
 				w->DisableWidget(ORDER_WIDGET_NON_STOP);
 				w->DisableWidget(ORDER_WIDGET_FULL_LOAD);
@@ -249,7 +309,7 @@
 	StringID str;
 	while (order != NULL) {
 		str = (v->cur_order_index == i) ? STR_8805 : STR_8804;
-		SetDParam(5, STR_EMPTY);
+		SetDParam(6, STR_EMPTY);
 
 		if (i - w->vscroll.pos < w->vscroll.cap) {
 			switch (order->GetType()) {
@@ -302,8 +362,8 @@
 					}
 
 					if (order->IsRefit()) {
-						SetDParam(5, STR_REFIT_ORDER);
-						SetDParam(6, GetCargo(order->GetRefitCargo())->name);
+						SetDParam(6, STR_REFIT_ORDER);
+						SetDParam(7, GetCargo(order->GetRefitCargo())->name);
 					}
 					break;
 
@@ -312,6 +372,18 @@
 					SetDParam(2, order->GetDestination());
 					break;
 
+				case OT_CONDITIONAL: {
+					OrderConditionComparator occ = order->GetConditionComparator();
+					SetDParam(1, (occ == OCC_IS_TRUE || occ == OCC_IS_FALSE) ? STR_CONDITIONAL_TRUE_FALSE : STR_CONDITIONAL_NUM);
+					SetDParam(2, order->GetConditionSkipToOrder() + 1);
+					SetDParam(3, STR_ORDER_CONDITIONAL_LOAD_PERCENTAGE + order->GetConditionVariable());
+					SetDParam(4, STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS + occ);
+
+					uint value = order->GetConditionValue();
+					if (order->GetConditionVariable() == OCV_MAX_SPEED) value = ConvertSpeedToDisplaySpeed(value);
+					SetDParam(5, value);
+				} break;
+
 				default: NOT_REACHED();
 			}
 
@@ -462,6 +534,7 @@
 	if (w->IsWidgetLowered(ORDER_WIDGET_GOTO)) {
 		_place_clicked_vehicle = NULL;
 		SetObjectToPlaceWnd(ANIMCURSOR_PICKSTATION, PAL_NONE, VHM_RECT, w);
+		WP(w, order_d).goto_type = OPOS_GOTO;
 	} else {
 		ResetObjectToPlace();
 	}
@@ -490,7 +563,7 @@
 			default: NOT_REACHED();
 		}
 	}
-	DoCommandP(v->tile, v->index + (sel_ord << 16), MOF_LOAD | (load_type << 2), NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER));
+	DoCommandP(v->tile, v->index + (sel_ord << 16), MOF_LOAD | (load_type << 4), NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER));
 }
 
 /**
@@ -522,6 +595,20 @@
 }
 
 /**
+ * Handle the click on the conditional order button.
+ *
+ * @param w current window
+ * @param v current vehicle
+ */
+static void OrderClick_Conditional(Window *w, const Vehicle *v, int i)
+{
+	w->InvalidateWidget(ORDER_WIDGET_GOTO);
+	w->LowerWidget(ORDER_WIDGET_GOTO);
+	SetObjectToPlaceWnd(ANIMCURSOR_PICKSTATION, PAL_NONE, VHM_RECT, w);
+	WP(w, order_d).goto_type = OPOS_CONDITIONAL;
+}
+
+/**
  * Handle the click on the unload button.
  *
  * @param w current window
@@ -544,7 +631,7 @@
 		}
 	}
 
-	DoCommandP(v->tile, v->index + (sel_ord << 16), MOF_UNLOAD | (unload_type << 2), NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER));
+	DoCommandP(v->tile, v->index + (sel_ord << 16), MOF_UNLOAD | (unload_type << 4), NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER));
 }
 
 /**
@@ -566,7 +653,7 @@
 		non_stop = (order->GetNonStopType() + 1) % ONSF_END;
 	}
 
-	DoCommandP(v->tile, v->index + (sel_ord << 16), MOF_NON_STOP | non_stop << 2,  NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER));
+	DoCommandP(v->tile, v->index + (sel_ord << 16), MOF_NON_STOP | non_stop << 4,  NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER));
 }
 
 /**
@@ -580,7 +667,7 @@
 	VehicleOrderID sel_ord = OrderGetSel(w);
 	const Order *order = GetVehicleOrder(v, sel_ord);
 
-	DoCommandP(v->tile, v->index + (sel_ord << 16), MOF_UNLOAD | ((order->GetUnloadType() & ~OUFB_NO_UNLOAD) ^ OUFB_TRANSFER) << 2, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER));
+	DoCommandP(v->tile, v->index + (sel_ord << 16), MOF_UNLOAD | ((order->GetUnloadType() & ~OUFB_NO_UNLOAD) ^ OUFB_TRANSFER) << 4, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER));
 }
 
 /**
@@ -763,12 +850,50 @@
 					ShowTimetableWindow(v);
 					break;
 
+				case ORDER_WIDGET_COND_VARIABLE:
+					ShowDropDownMenu(w, _order_conditional_variable, GetVehicleOrder(v, OrderGetSel(w))->GetConditionVariable(), ORDER_WIDGET_COND_VARIABLE, 0, 0);
+					break;
+
+				case ORDER_WIDGET_COND_COMPARATOR: {
+					const Order *o = GetVehicleOrder(v, OrderGetSel(w));
+					ShowDropDownMenu(w, _order_conditional_condition, o->GetConditionComparator(), ORDER_WIDGET_COND_COMPARATOR, 0, (o->GetConditionVariable() == OCV_REQUIRES_SERVICE) ? 0x3F : 0xC0);
+				} break;
+
+				case ORDER_WIDGET_COND_VALUE: {
+					const Order *order = GetVehicleOrder(v, OrderGetSel(w));
+					uint value = order->GetConditionValue();
+					if (order->GetConditionVariable() == OCV_MAX_SPEED) value = ConvertSpeedToDisplaySpeed(value);
+					SetDParam(0, value);
+					ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_ORDER_CONDITIONAL_VALUE_CAPT, 5, 100, w, CS_NUMERAL);
+				} break;
+
 				case ORDER_WIDGET_SHARED_ORDER_LIST:
 					ShowVehicleListWindow(v);
 					break;
 			}
 			break;
 
+		case WE_ON_EDIT_TEXT:
+			if (!StrEmpty(e->we.edittext.str)) {
+				VehicleOrderID sel = OrderGetSel(w);
+				uint value = atoi(e->we.edittext.str);
+
+				switch (GetVehicleOrder(v, sel)->GetConditionVariable()) {
+					case OCV_MAX_SPEED:
+						value = ConvertDisplaySpeedToSpeed(value);
+						break;
+
+					case OCV_RELIABILITY:
+					case OCV_LOAD_PERCENTAGE:
+						value = Clamp(value, 0, 100);
+
+					default:
+						break;
+				}
+				DoCommandP(v->tile, v->index + (sel << 16), MOF_COND_VALUE | Clamp(value, 0, 2047) << 4, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER));
+			}
+			break;
+
 		case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list
 			switch (e->we.dropdown.button) {
 				case ORDER_WIDGET_NON_STOP:
@@ -785,10 +910,24 @@
 
 				case ORDER_WIDGET_GOTO:
 					switch (e->we.dropdown.index) {
-						case 0: OrderClick_Goto(w, v, 0); break;
+						case 0:
+							w->ToggleWidgetLoweredState(ORDER_WIDGET_GOTO);
+							OrderClick_Goto(w, v, 0);
+							break;
+
 						case 1: OrderClick_NearestDepot(w, v, 0); break;
+						case 2: OrderClick_Conditional(w, v, 0); break;
 						default: NOT_REACHED();
 					}
+					break;
+
+				case ORDER_WIDGET_COND_VARIABLE:
+					DoCommandP(v->tile, v->index + (OrderGetSel(w) << 16), MOF_COND_VARIABLE | e->we.dropdown.index << 4,  NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER));
+					break;
+
+				case ORDER_WIDGET_COND_COMPARATOR:
+					DoCommandP(v->tile, v->index + (OrderGetSel(w) << 16), MOF_COND_COMPARATOR | e->we.dropdown.index << 4,  NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER));
+					break;
 			}
 			break;
 
@@ -827,10 +966,29 @@
 			break;
 
 		case WE_PLACE_OBJ:
-			OrdersPlaceObj(GetVehicle(w->window_number), e->we.place.tile, w);
+			if (WP(w, order_d).goto_type == OPOS_GOTO) {
+				OrdersPlaceObj(GetVehicle(w->window_number), e->we.place.tile, w);
+			}
 			break;
 
 		case WE_ABORT_PLACE_OBJ:
+			if (WP(w, order_d).goto_type == OPOS_CONDITIONAL) {
+				WP(w, order_d).goto_type = OPOS_GOTO;
+				if (_cursor.pos.x >= (w->left + w->widget[ORDER_WIDGET_ORDER_LIST].left) &&
+						_cursor.pos.y >= (w->top  + w->widget[ORDER_WIDGET_ORDER_LIST].top) &&
+						_cursor.pos.x <= (w->left + w->widget[ORDER_WIDGET_ORDER_LIST].right) &&
+						_cursor.pos.y <= (w->top  + w->widget[ORDER_WIDGET_ORDER_LIST].bottom)) {
+					int order_id = GetOrderFromOrderWndPt(w, _cursor.pos.y - w->top, v);
+					if (order_id != INVALID_ORDER) {
+						Order order;
+						order.next = NULL;
+						order.index = 0;
+						order.MakeConditional(order_id);
+
+						DoCommandP(v->tile, v->index + (OrderGetSel(w) << 16), order.Pack(), NULL, CMD_INSERT_ORDER | CMD_MSG(STR_8833_CAN_T_INSERT_NEW_ORDER));
+					}
+				}
+			}
 			w->RaiseWidget(ORDER_WIDGET_GOTO);
 			w->InvalidateWidget(ORDER_WIDGET_GOTO);
 			break;
@@ -891,6 +1049,10 @@
 	{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   124,   247,    76,    87, STR_REFIT,               STR_REFIT_TIP},                       // ORDER_WIDGET_REFIT
 	{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   248,   371,    76,    87, STR_SERVICE,             STR_SERVICE_HINT},                    // ORDER_WIDGET_SERVICE
 
+	{   WWT_DROPDOWN,   RESIZE_TB,      14,     0,   123,    76,    87, STR_NULL,                STR_ORDER_CONDITIONAL_VARIABLE_TOOLTIP},   // ORDER_WIDGET_COND_VARIABLE
+	{   WWT_DROPDOWN,   RESIZE_TB,      14,   124,   247,    76,    87, STR_NULL,                STR_ORDER_CONDITIONAL_COMPARATOR_TOOLTIP}, // ORDER_WIDGET_COND_COMPARATOR
+	{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   248,   371,    76,    87, STR_CONDITIONAL_VALUE,   STR_ORDER_CONDITIONAL_VALUE_TOOLTIP},      // ORDER_WIDGET_COND_VALUE
+
 	{      WWT_PANEL,   RESIZE_RTB,     14,   372,   373,    76,    99, 0x0,                     STR_NULL},                            // ORDER_WIDGET_RESIZE_BAR
 	{ WWT_PUSHIMGBTN,   RESIZE_LRTB,    14,   372,   385,    76,    87, SPR_SHARED_ORDERS_ICON,  STR_VEH_WITH_SHARED_ORDERS_LIST_TIP}, // ORDER_WIDGET_SHARED_ORDER_LIST
 
@@ -924,9 +1086,13 @@
 	{    WWT_TEXTBTN,   RESIZE_TB,      14,   248,   359,    88,    99, STR_8826_GO_TO,          STR_8856_INSERT_A_NEW_ORDER_BEFORE},  // ORDER_WIDGET_GOTO
 	{   WWT_DROPDOWN,   RESIZE_TB,      14,   360,   371,    88,    99, STR_EMPTY,               STR_ORDER_GO_TO_DROPDOWN_TOOLTIP},    // ORDER_WIDGET_GOTO_DROPDOWN
 	{   WWT_DROPDOWN,   RESIZE_TB,      14,     0,   185,    76,    87, STR_NULL,                STR_ORDER_TOOLTIP_FULL_LOAD},         // ORDER_WIDGET_FULL_LOAD
-	{   WWT_DROPDOWN,   RESIZE_TB,      14,   186,   372,    76,    87, STR_NULL,                STR_ORDER_TOOLTIP_UNLOAD},            // ORDER_WIDGET_UNLOAD
+	{   WWT_DROPDOWN,   RESIZE_TB,      14,   186,   371,    76,    87, STR_NULL,                STR_ORDER_TOOLTIP_UNLOAD},            // ORDER_WIDGET_UNLOAD
 	{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,     0,   185,    76,    87, STR_REFIT,               STR_REFIT_TIP},                       // ORDER_WIDGET_REFIT
-	{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   186,   372,    76,    87, STR_SERVICE,             STR_SERVICE_HINT},                    // ORDER_WIDGET_SERVICE
+	{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   186,   371,    76,    87, STR_SERVICE,             STR_SERVICE_HINT},                    // ORDER_WIDGET_SERVICE
+
+	{   WWT_DROPDOWN,   RESIZE_TB,      14,     0,   123,    76,    87, STR_NULL,                STR_ORDER_CONDITIONAL_VARIABLE_TOOLTIP},   // ORDER_WIDGET_COND_VARIABLE
+	{   WWT_DROPDOWN,   RESIZE_TB,      14,   124,   247,    76,    87, STR_NULL,                STR_ORDER_CONDITIONAL_COMPARATOR_TOOLTIP}, // ORDER_WIDGET_COND_COMPARATOR
+	{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   248,   371,    76,    87, STR_CONDITIONAL_VALUE,   STR_ORDER_CONDITIONAL_VALUE_TOOLTIP},      // ORDER_WIDGET_COND_VALUE
 
 	{      WWT_PANEL,   RESIZE_RTB,     14,   372,   373,    76,    99, 0x0,                     STR_NULL},                            // ORDER_WIDGET_RESIZE_BAR
 	{ WWT_PUSHIMGBTN,   RESIZE_LRTB,    14,   372,   385,    76,    87, SPR_SHARED_ORDERS_ICON,  STR_VEH_WITH_SHARED_ORDERS_LIST_TIP}, // ORDER_WIDGET_SHARED_ORDER_LIST
@@ -965,10 +1131,14 @@
 	{      WWT_EMPTY,   RESIZE_NONE,    14,     0,     0,    76,    87, 0x0,                STR_NULL},                            // ORDER_WIDGET_REFIT
 	{      WWT_EMPTY,   RESIZE_NONE,    14,     0,     0,    76,    87, 0x0,                STR_NULL},                            // ORDER_WIDGET_SERVICE
 
+	{      WWT_EMPTY,   RESIZE_NONE,    14,     0,     0,    76,    87, 0x0,                STR_NULL},                            // ORDER_WIDGET_COND_VARIABLE
+	{      WWT_EMPTY,   RESIZE_NONE,    14,     0,     0,    76,    87, 0x0,                STR_NULL},                            // ORDER_WIDGET_COND_COMPARATOR
+	{      WWT_EMPTY,   RESIZE_NONE,    14,     0,     0,    76,    87, 0x0,                STR_NULL},                            // ORDER_WIDGET_COND_VALUE
+
 	{      WWT_PANEL,   RESIZE_RTB,     14,     0,   373,    76,    87, 0x0,                STR_NULL},                            // ORDER_WIDGET_RESIZE_BAR
 	{      WWT_EMPTY,   RESIZE_TB,      14,     0,     0,    76,    87, 0x0,                STR_NULL},                            // ORDER_WIDGET_SHARED_ORDER_LIST
 
-	{  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   374,   385,    88,    99, 0x0,                STR_RESIZE_BUTTON},              // ORDER_WIDGET_RESIZE
+	{  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   374,   385,    88,    99, 0x0,                STR_RESIZE_BUTTON},                   // ORDER_WIDGET_RESIZE
 	{   WIDGETS_END},
 };
 
--- a/src/order_type.h
+++ b/src/order_type.h
@@ -27,6 +27,7 @@
 	OT_LEAVESTATION  = 4,
 	OT_DUMMY         = 5,
 	OT_GOTO_WAYPOINT = 6,
+	OT_CONDITIONAL   = 7,
 	OT_END
 };
 
@@ -86,13 +87,45 @@
 };
 
 /**
+ * Variables (of a vehicle) to 'cause' skipping on.
+ */
+enum OrderConditionVariable {
+	OCV_LOAD_PERCENTAGE,  ///< Skip based on the amount of load
+	OCV_RELIABILITY,      ///< Skip based on the reliability
+	OCV_MAX_SPEED,        ///< Skip based on the maximum speed
+	OCV_AGE,              ///< Skip based on the age
+	OCV_REQUIRES_SERVICE, ///< Skip when the vehicle requires service
+	OCV_END
+};
+
+/**
+ * Comparator for the skip reasoning.
+ */
+enum OrderConditionComparator {
+	OCC_EQUALS,      ///< Skip if both values are equal
+	OCC_NOT_EQUALS,  ///< Skip if both values are not equal
+	OCC_LESS_THAN,   ///< Skip if the value is less than the limit
+	OCC_LESS_EQUALS, ///< Skip if the value is less or equal to the limit
+	OCC_MORE_THAN,   ///< Skip if the value is more than the limit
+	OCC_MORE_EQUALS, ///< Skip if the value is more or equal to the limit
+	OCC_IS_TRUE,     ///< Skip if the variable is true
+	OCC_IS_FALSE,    ///< Skip if the variable is false
+	OCC_END
+};
+
+
+/**
  * Enumeration for the data to set in CmdModifyOrder.
  */
 enum ModifyOrderFlags {
-	MOF_NON_STOP,      ///< Passes a OrderNonStopFlags.
-	MOF_UNLOAD,        ///< Passes an OrderUnloadType.
-	MOF_LOAD,          ///< Passes an OrderLoadType
-	MOF_DEPOT_ACTION,  ///< Toggle the 'service' if needed flag.
+	MOF_NON_STOP,        ///< Passes a OrderNonStopFlags.
+	MOF_UNLOAD,          ///< Passes an OrderUnloadType.
+	MOF_LOAD,            ///< Passes an OrderLoadType
+	MOF_DEPOT_ACTION,    ///< Toggle the 'service' if needed flag.
+	MOF_COND_VARIABLE,   ///< A conditional variable changes.
+	MOF_COND_COMPARATOR, ///< A comparator changes.
+	MOF_COND_VALUE,      ///< The value to set the condition to.
+	MOF_END
 };
 
 
--- a/src/timetable_gui.cpp
+++ b/src/timetable_gui.cpp
@@ -116,7 +116,7 @@
 		if (i - w->vscroll.pos >= w->vscroll.cap) break;
 
 		if (i % 2 == 0) {
-			SetDParam(2, STR_EMPTY);
+			SetDParam(5, STR_EMPTY);
 
 			switch (order->GetType()) {
 				case OT_DUMMY:
@@ -130,8 +130,8 @@
 					SetDParam(3, STR_EMPTY);
 
 					if (order->wait_time > 0) {
-						SetDParam(4, STR_TIMETABLE_STAY_FOR);
-						SetTimetableParams(5, 6, order->wait_time);
+						SetDParam(5, STR_TIMETABLE_STAY_FOR);
+						SetTimetableParams(6, 7, order->wait_time);
 					} else {
 						SetDParam(4, STR_EMPTY);
 					}
@@ -178,6 +178,20 @@
 					SetDParam(1, order->GetDestination());
 					break;
 
+
+				case OT_CONDITIONAL: {
+					extern uint ConvertSpeedToDisplaySpeed(uint speed);
+					OrderConditionComparator occ = order->GetConditionComparator();
+					SetDParam(0, (occ == OCC_IS_TRUE || occ == OCC_IS_FALSE) ? STR_CONDITIONAL_TRUE_FALSE : STR_CONDITIONAL_NUM);
+					SetDParam(1, order->GetConditionSkipToOrder() + 1);
+					SetDParam(2, STR_ORDER_CONDITIONAL_LOAD_PERCENTAGE + order->GetConditionVariable());
+					SetDParam(3, STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS + occ);
+
+					uint value = order->GetConditionValue();
+					if (order->GetConditionVariable() == OCV_MAX_SPEED) value = ConvertSpeedToDisplaySpeed(value);
+					SetDParam(4, value);
+				} break;
+
 				default: break;
 			}