From ac94b6384b518390c7e7791446c06c78cb6ce1d2 Mon Sep 17 00:00:00 2001 From: rubidium Date: Sat, 12 Apr 2008 11:58:19 +0000 Subject: [PATCH] (svn r12667) -Feature: conditional 'skip/jump' orders. --- src/lang/english.txt | 30 +++++- src/order_base.h | 26 +++++- src/order_cmd.cpp | 177 ++++++++++++++++++++++++++++++++++-- src/order_gui.cpp | 206 ++++++++++++++++++++++++++++++++++++++---- src/order_type.h | 41 ++++++++- src/timetable_gui.cpp | 20 +++- 6 files changed, 463 insertions(+), 37 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 1db6e49c50..c51b686f6b 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2652,8 +2652,8 @@ STR_8800_TRAIN_DEPOT :{WHITE}{TOWN} T 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_FULL_LOAD :{BLACK}Change t 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_DEPOT :{STRING} {TOWN} 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} diff --git a/src/order_base.h b/src/order_base.h index 320eae3c50..f1661042f4 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -66,7 +66,7 @@ public: * 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 @@ -111,6 +111,12 @@ public: */ 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 @@ public: 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 @@ public: /** 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; diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 7682ba6607..9a08268713 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -93,6 +93,13 @@ void Order::MakeDummy() 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 @@ CommandCost CmdInsertOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) 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 @@ CommandCost CmdInsertOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) 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 @@ CommandCost CmdDeleteOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) 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 @@ CommandCost CmdMoveOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) 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 @@ CommandCost CmdMoveOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) * 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 @@ CommandCost CmdModifyOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) 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 @@ CommandCost CmdModifyOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) 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 @@ CommandCost CmdModifyOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) 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(); } @@ -1416,6 +1542,24 @@ static bool CheckForValidOrders(const Vehicle *v) return false; } +/** + * 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. @@ -1551,6 +1695,27 @@ bool ProcessOrders(Vehicle *v) 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; diff --git a/src/order_gui.cpp b/src/order_gui.cpp index 28f3f04f0d..4fc7a6b47a 100644 --- 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 @@ enum OrderWindowWidgets { 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 @@ static int GetOrderFromOrderWndPt(Window *w, int y, const Vehicle *v) * 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_unload_drowdown[] = { 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 @@ static void DrawOrdersWindow(Window *w) 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 @@ static void DrawOrdersWindow(Window *w) 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 @@ static void DrawOrdersWindow(Window *w) 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 @@ static void DrawOrdersWindow(Window *w) 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 @@ static void DrawOrdersWindow(Window *w) } 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 @@ static void DrawOrdersWindow(Window *w) 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 @@ static void OrderClick_Goto(Window *w, const Vehicle *v, int i) 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 @@ static void OrderClick_FullLoad(Window *w, const Vehicle *v, int load_type) 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)); } /** @@ -521,6 +594,20 @@ static void OrderClick_NearestDepot(Window *w, const Vehicle *v, int i) DoCommandP(v->tile, v->index + (OrderGetSel(w) << 16), order.Pack(), NULL, CMD_INSERT_ORDER | CMD_MSG(STR_8833_CAN_T_INSERT_NEW_ORDER)); } +/** + * 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. * @@ -544,7 +631,7 @@ static void OrderClick_Unload(Window *w, const Vehicle *v, int unload_type) } } - 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 @@ static void OrderClick_Nonstop(Window *w, const Vehicle *v, int non_stop) 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 @@ static void OrderClick_Transfer(Window *w, const Vehicle *v, int i) 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 @@ static void OrdersWndProc(Window *w, WindowEvent *e) 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 @@ static void OrdersWndProc(Window *w, WindowEvent *e) 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 @@ static void OrdersWndProc(Window *w, WindowEvent *e) 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 @@ static const Widget _orders_train_widgets[] = { { 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 @@ static const Widget _orders_widgets[] = { { 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 @@ static const Widget _other_orders_widgets[] = { { 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}, }; diff --git a/src/order_type.h b/src/order_type.h index a6273e6849..33de1bd835 100644 --- a/src/order_type.h +++ b/src/order_type.h @@ -27,6 +27,7 @@ enum OrderType { OT_LEAVESTATION = 4, OT_DUMMY = 5, OT_GOTO_WAYPOINT = 6, + OT_CONDITIONAL = 7, OT_END }; @@ -85,14 +86,46 @@ enum OrderDepotActionFlags { ODATFB_NEAREST_DEPOT = 1 << 1, ///< Send the vehicle to the nearest depot. }; +/** + * 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 }; diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index 54223f1e35..d77691552d 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -116,7 +116,7 @@ static void DrawTimetableWindow(Window *w) 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 @@ static void DrawTimetableWindow(Window *w) 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 @@ static void DrawTimetableWindow(Window *w) 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; }