diff --git a/src/lang/english.txt b/src/lang/english.txt index 8b012e486b..b9a83ce0dd 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5662,10 +5662,13 @@ STR_ORDER_GO_TO_NEAREST_HANGAR :Go to nearest h STR_ORDER_CONDITIONAL :Conditional order jump STR_ORDER_SHARE :Share orders STR_ORDER_RELEASE_SLOT_BUTTON :Release slot +STR_ORDER_CHANGE_COUNTER_BUTTON :Change counter STR_ORDERS_GO_TO_TOOLTIP :{BLACK}Insert a new order before the highlighted order, or add to end of list. Ctrl makes station orders 'full load any cargo', waypoint orders 'non-stop' and depot orders 'service'. 'Share orders' or Ctrl lets this vehicle share orders with the selected vehicle. Clicking a vehicle copies the orders from that vehicle. A depot order disables automatic servicing of the vehicle STR_ORDER_RELEASE_SLOT_TOOLTIP :{BLACK}The train slot to release +STR_ORDER_CHANGE_COUNTER_TOOLTIP :{BLACK}The counter to change + STR_ORDERS_VEH_WITH_SHARED_ORDERS_LIST_TOOLTIP :{BLACK}Show all vehicles that share this schedule STR_ORDERS_OCCUPANCY_BUTTON :{BLACK}{STRING2}% diff --git a/src/order_base.h b/src/order_base.h index 29d3583031..067dd6949a 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -181,6 +181,7 @@ public: void MakeWaiting(); void MakeLoadingAdvance(StationID destination); void MakeReleaseSlot(); + void MakeChangeCounter(); /** * Is this a 'goto' order with a real destination? @@ -202,7 +203,7 @@ public: /** * Gets the destination of this order. - * @pre IsType(OT_GOTO_WAYPOINT) || IsType(OT_GOTO_DEPOT) || IsType(OT_GOTO_STATION) || IsType(OT_RELEASE_SLOT). + * @pre IsType(OT_GOTO_WAYPOINT) || IsType(OT_GOTO_DEPOT) || IsType(OT_GOTO_STATION) || IsType(OT_RELEASE_SLOT) || IsType(OT_COUNTER). * @return the destination of the order. */ inline DestinationID GetDestination() const { return this->dest; } @@ -210,7 +211,7 @@ public: /** * Sets the destination of this order. * @param destination the new destination of the order. - * @pre IsType(OT_GOTO_WAYPOINT) || IsType(OT_GOTO_DEPOT) || IsType(OT_GOTO_STATION) || IsType(OT_RELEASE_SLOT). + * @pre IsType(OT_GOTO_WAYPOINT) || IsType(OT_GOTO_DEPOT) || IsType(OT_GOTO_STATION) || IsType(OT_RELEASE_SLOT) || IsType(OT_COUNTER). */ inline void SetDestination(DestinationID destination) { this->dest = destination; } @@ -349,6 +350,8 @@ public: inline uint16 GetConditionValue() const { return GB(this->dest, 0, 11); } /** Get counter for the 'jump xx% of times' option */ inline int8 GetJumpCounter() const { return GB(this->GetXData(), 0, 8); } + /** Get counter operation */ + inline uint8 GetCounterOperation() const { return GB(this->flags, 0, 8); } /** Set how the consist must be loaded. */ inline void SetLoadType(OrderLoadFlags load_type) @@ -410,8 +413,10 @@ public: 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); } - /** Get counter for the 'jump xx% of times' option */ + /** Set counter for the 'jump xx% of times' option */ inline void SetJumpCounter(int8 jump_counter) { SB(this->GetXDataRef(), 0, 8, jump_counter); } + /** Set counter operation */ + inline void SetCounterOperation(uint8 op) { SB(this->flags, 0, 8, op); } /* As conditional orders write their "skip to" order all over the flags, we cannot check the * flags to find out if timetabling is enabled. However, as conditional orders are never diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 7d4d1b2103..c9e1b92255 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -243,6 +243,13 @@ void Order::MakeReleaseSlot() this->dest = INVALID_TRACE_RESTRICT_SLOT_ID; } +void Order::MakeChangeCounter() +{ + this->type = OT_COUNTER; + this->dest = INVALID_TRACE_RESTRICT_COUNTER_ID; + this->flags = 0; +} + /** * Make this depot/station order also a refit order. * @param cargo the cargo type to change to. @@ -656,7 +663,7 @@ CargoMaskedStationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, Ca }); if (invalid) return CargoMaskedStationIDStack(cargo_mask, INVALID_STATION); } - } while (next->IsType(OT_GOTO_DEPOT) || next->IsType(OT_RELEASE_SLOT) || next->GetDestination() == v->last_station_visited); + } while (next->IsType(OT_GOTO_DEPOT) || next->IsType(OT_RELEASE_SLOT) || next->IsType(OT_COUNTER) || next->GetDestination() == v->last_station_visited); return CargoMaskedStationIDStack(cargo_mask, next->GetDestination()); } @@ -1252,6 +1259,15 @@ CommandCost CmdInsertOrderIntl(DoCommandFlag flags, Vehicle *v, VehicleOrderID s break; } + case OT_COUNTER: { + TraceRestrictCounterID data = new_order.GetDestination(); + if (data != INVALID_TRACE_RESTRICT_COUNTER_ID) { + const TraceRestrictCounter *ctr = TraceRestrictCounter::GetIfValid(data); + if (ctr == nullptr) return CMD_ERROR; + } + break; + } + default: return CMD_ERROR; } @@ -1782,6 +1798,10 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 if (mof != MOF_SLOT) return CMD_ERROR; break; + case OT_COUNTER: + if (mof != MOF_COUNTER_ID && mof != MOF_COUNTER_OP && mof != MOF_COUNTER_VALUE) return CMD_ERROR; + break; + default: return CMD_ERROR; } @@ -1964,6 +1984,22 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 if (v->type != VEH_ROAD) return CMD_ERROR; if (data >= DIAGDIR_END && data != INVALID_DIAGDIR) return CMD_ERROR; break; + + case MOF_COUNTER_ID: + if (data != INVALID_TRACE_RESTRICT_COUNTER_ID) { + const TraceRestrictCounter *ctr = TraceRestrictCounter::GetIfValid(data); + if (ctr == nullptr) return CMD_ERROR; + } + break; + + case MOF_COUNTER_OP: + if (data != TRCCOF_INCREASE && data != TRCCOF_DECREASE && data != TRCCOF_SET) { + return CMD_ERROR; + } + break; + + case MOF_COUNTER_VALUE: + break; } if (flags & DC_EXEC) { @@ -2186,6 +2222,7 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 break; case MOF_SLOT: + case MOF_COUNTER_ID: order->SetDestination(data); break; @@ -2193,6 +2230,14 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 order->SetRoadVehTravelDirection((DiagDirection)data); break; + case MOF_COUNTER_OP: + order->SetCounterOperation(data); + break; + + case MOF_COUNTER_VALUE: + order->GetXDataRef() = data; + break; + default: NOT_REACHED(); } @@ -2903,6 +2948,7 @@ static StationID GetNextRealStation(const Vehicle *v, const Order *order) static std::vector _pco_deferred_slot_acquires; static std::vector _pco_deferred_slot_releases; +static btree::btree_map _pco_deferred_counter_values; static btree::btree_map _pco_deferred_original_percent_cond; /** @@ -3013,7 +3059,14 @@ VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v, Pro case OCV_REMAINING_LIFETIME: skip_order = OrderConditionCompare(occ, std::max(v->max_age - v->age + DAYS_IN_LEAP_YEAR - 1, 0) / DAYS_IN_LEAP_YEAR, value); break; case OCV_COUNTER_VALUE: { const TraceRestrictCounter* ctr = TraceRestrictCounter::GetIfValid(GB(order->GetXData(), 16, 16)); - if (ctr != nullptr) skip_order = OrderConditionCompare(occ, ctr->value, GB(order->GetXData(), 0, 16)); + if (ctr != nullptr) { + int32 value = ctr->value; + if (mode == PCO_DEFERRED) { + auto iter = _pco_deferred_counter_values.find(ctr->index); + if (iter != _pco_deferred_counter_values.end()) value = iter->second; + } + skip_order = OrderConditionCompare(occ, value, GB(order->GetXData(), 0, 16)); + } break; } case OCV_TIME_DATE: { @@ -3075,6 +3128,15 @@ VehicleOrderID AdvanceOrderIndexDeferred(const Vehicle *v, VehicleOrderID index) } break; + case OT_COUNTER: { + const TraceRestrictCounter* ctr = TraceRestrictCounter::GetIfValid(order->GetDestination()); + if (ctr != nullptr) { + auto result = _pco_deferred_counter_values.insert(std::make_pair(ctr->index, ctr->value)); + result.first->second = TraceRestrictCounter::ApplyValue(result.first->second, static_cast(order->GetCounterOperation()), order->GetXData()); + } + break; + } + case OT_CONDITIONAL: { VehicleOrderID next = ProcessConditionalOrder(order, v, PCO_DEFERRED); if (next != INVALID_VEH_ORDER_ID) { @@ -3110,6 +3172,9 @@ void FlushAdvanceOrderIndexDeferred(const Vehicle *v, bool apply) for (TraceRestrictSlotID slot : _pco_deferred_slot_releases) { TraceRestrictSlot::Get(slot)->Vacate(v->index); } + for (auto item : _pco_deferred_counter_values) { + TraceRestrictCounter::Get(item.first)->UpdateValue(item.second); + } } else { for (auto item : _pco_deferred_original_percent_cond) { item.first->SetJumpCounter(item.second); @@ -3118,6 +3183,7 @@ void FlushAdvanceOrderIndexDeferred(const Vehicle *v, bool apply) _pco_deferred_slot_acquires.clear(); _pco_deferred_slot_releases.clear(); + _pco_deferred_counter_values.clear(); _pco_deferred_original_percent_cond.clear(); } @@ -3235,6 +3301,18 @@ bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool v->IncrementRealOrderIndex(); break; + case OT_COUNTER: + assert(!pbs_look_ahead); + if (order->GetDestination() != INVALID_TRACE_RESTRICT_COUNTER_ID) { + TraceRestrictCounter *ctr = TraceRestrictCounter::GetIfValid(order->GetDestination()); + if (ctr != nullptr) { + ctr->ApplyUpdate(static_cast(order->GetCounterOperation()), order->GetXData()); + } + } + UpdateVehicleTimetable(v, true); + v->IncrementRealOrderIndex(); + break; + default: v->SetDestTile(0); return false; diff --git a/src/order_gui.cpp b/src/order_gui.cpp index c8509a992c..f7d59a270c 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -622,6 +622,7 @@ static const StringID _order_goto_dropdown[] = { STR_ORDER_CONDITIONAL, STR_ORDER_SHARE, STR_ORDER_RELEASE_SLOT_BUTTON, + STR_ORDER_CHANGE_COUNTER_BUTTON, INVALID_STRING_ID }; @@ -631,6 +632,7 @@ static const StringID _order_goto_dropdown_aircraft[] = { STR_ORDER_CONDITIONAL, STR_ORDER_SHARE, STR_ORDER_RELEASE_SLOT_BUTTON, + STR_ORDER_CHANGE_COUNTER_BUTTON, INVALID_STRING_ID }; @@ -1121,6 +1123,33 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int } break; + case OT_COUNTER: + switch (static_cast(order->GetCounterOperation())) { + case TRCCOF_INCREASE: + SetDParam(0, STR_TRACE_RESTRICT_COUNTER_INCREASE_ITEM); + break; + + case TRCCOF_DECREASE: + SetDParam(0, STR_TRACE_RESTRICT_COUNTER_DECREASE_ITEM); + break; + + case TRCCOF_SET: + SetDParam(0, STR_TRACE_RESTRICT_COUNTER_SET_ITEM); + break; + + default: + NOT_REACHED(); + break; + } + if (order->GetDestination() == INVALID_TRACE_RESTRICT_COUNTER_ID) { + SetDParam(1, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED_RED); + } else { + SetDParam(1, STR_TRACE_RESTRICT_COUNTER_NAME); + SetDParam(2, order->GetDestination()); + } + SetDParam(3, order->GetXData()); + break; + default: NOT_REACHED(); } @@ -1312,6 +1341,7 @@ private: DP_GROUNDVEHICLE_ROW_NORMAL = 0, ///< Display the row for normal/depot orders in the top row of the train/rv order window. DP_GROUNDVEHICLE_ROW_CONDITIONAL = 1, ///< Display the row for conditional orders in the top row of the train/rv order window. DP_GROUNDVEHICLE_ROW_SLOT = 2, ///< Display the row for release slot orders in the top row of the train/rv order window. + DP_GROUNDVEHICLE_ROW_COUNTER = 3, ///< Display the row for change counter orders in the top row of the train/rv order window. /* WID_O_SEL_TOP_LEFT */ DP_LEFT_LOAD = 0, ///< Display 'load' in the left button of the top row of the train/rv order window. @@ -1331,6 +1361,7 @@ private: DP_ROW_DEPOT = 1, ///< Display 'refit' / 'service' buttons in the top row of the ship/airplane order window. DP_ROW_CONDITIONAL = 2, ///< Display the conditional order buttons in the top row of the ship/airplane order window. DP_ROW_SLOT = 3, ///< Display the release slot buttons in the top row of the ship/airplane order window. + DP_ROW_COUNTER = 4, ///< Display the change counter buttons in the top row of the ship/airplane order window. /* WID_O_SEL_COND_VALUE */ DP_COND_VALUE_NUMBER = 0, ///< Display number widget @@ -1535,6 +1566,19 @@ private: this->InsertNewOrder(order.Pack()); } + /** + * Handle the click on the change counter button. + */ + void OrderClick_ChangeCounter() + { + Order order; + order.next = nullptr; + order.index = 0; + order.MakeChangeCounter(); + + this->InsertNewOrder(order.Pack()); + } + /** * Handle the click on the unload button. * @param unload_type Unload flag to apply. If matches existing unload type, toggles to default of 'unload if possible'. @@ -2131,6 +2175,19 @@ public: break; } + case OT_COUNTER: { + if (row_sel != nullptr) { + row_sel->SetDisplayedPlane(DP_ROW_COUNTER); + } else { + train_row_sel->SetDisplayedPlane(DP_GROUNDVEHICLE_ROW_COUNTER); + } + + TraceRestrictCounterID ctr_id = (order != nullptr && TraceRestrictCounter::IsValidID(order->GetDestination()) ? order->GetDestination() : INVALID_TRACE_RESTRICT_COUNTER_ID); + + this->GetWidget(WID_O_CHANGE_COUNTER)->widget_data = (ctr_id != INVALID_TRACE_RESTRICT_COUNTER_ID) ? STR_TRACE_RESTRICT_COUNTER_NAME : STR_TRACE_RESTRICT_VARIABLE_UNDEFINED; + break; + } + default: // every other order if (row_sel != nullptr) { row_sel->SetDisplayedPlane(DP_ROW_LOAD); @@ -2393,6 +2450,39 @@ public: } break; } + + case WID_O_COUNTER_OP: { + VehicleOrderID sel = this->OrderGetSel(); + const Order *order = this->vehicle->GetOrder(sel); + + if (order != nullptr && order->IsType(OT_COUNTER)) { + SetDParam(0, STR_TRACE_RESTRICT_COUNTER_INCREASE + order->GetCounterOperation()); + } else { + SetDParam(0, STR_EMPTY); + } + break; + } + + case WID_O_CHANGE_COUNTER: { + VehicleOrderID sel = this->OrderGetSel(); + const Order *order = this->vehicle->GetOrder(sel); + + if (order != nullptr && order->IsType(OT_COUNTER)) { + TraceRestrictCounterID value = order->GetDestination(); + SetDParam(0, value); + } + break; + } + + case WID_O_COUNTER_VALUE: { + VehicleOrderID sel = this->OrderGetSel(); + const Order *order = this->vehicle->GetOrder(sel); + + if (order != nullptr && order->IsType(OT_COUNTER)) { + SetDParam(0, order->GetXData()); + } + break; + } } } @@ -2542,8 +2632,24 @@ public: case OPOS_CONDITIONAL_RETARGET: sel = -1; break; default: NOT_REACHED(); } + uint32 hidden_mask = 0; + if (_settings_client.gui.show_adv_tracerestrict_features) { + bool have_counters = false; + for (const TraceRestrictCounter *ctr : TraceRestrictCounter::Iterate()) { + if (ctr->owner == this->vehicle->owner) { + have_counters = true; + break; + } + } + if (!have_counters) { + // Owner has no counters, don't both showing the menu item + hidden_mask |= 0x20; + } + } else { + hidden_mask |= 0x30; + } ShowDropDownMenu(this, this->vehicle->type == VEH_AIRCRAFT ? _order_goto_dropdown_aircraft : _order_goto_dropdown, sel, WID_O_GOTO, - 0, (_settings_client.gui.show_adv_tracerestrict_features ? 0 : 0x10), 0, DDSF_LOST_FOCUS); + 0, hidden_mask, 0, DDSF_LOST_FOCUS); } break; @@ -2784,6 +2890,32 @@ public: if (!list.empty()) ShowDropDownList(this, std::move(list), selected, WID_O_RELEASE_SLOT, 0, true); break; } + + case WID_O_COUNTER_OP: { + DropDownList list; + list.emplace_back(new DropDownListStringItem(STR_TRACE_RESTRICT_COUNTER_INCREASE, 0, false)); + list.emplace_back(new DropDownListStringItem(STR_TRACE_RESTRICT_COUNTER_DECREASE, 1, false)); + list.emplace_back(new DropDownListStringItem(STR_TRACE_RESTRICT_COUNTER_SET, 2, false)); + int selected = this->vehicle->GetOrder(this->OrderGetSel())->GetCounterOperation(); + ShowDropDownList(this, std::move(list), selected, WID_O_COUNTER_OP, 0, true); + break; + } + + case WID_O_CHANGE_COUNTER: { + int selected; + TraceRestrictCounterID value = this->vehicle->GetOrder(this->OrderGetSel())->GetDestination(); + DropDownList list = GetCounterDropDownList(this->vehicle->owner, value, selected); + if (!list.empty()) ShowDropDownList(this, std::move(list), selected, WID_O_CHANGE_COUNTER, 0, true); + break; + } + + case WID_O_COUNTER_VALUE: { + const Order *order = this->vehicle->GetOrder(this->OrderGetSel()); + this->query_text_widget = widget; + SetDParam(0, order->GetXData()); + ShowQueryString(STR_JUST_INT, STR_TRACE_RESTRICT_VALUE_CAPTION, 10, this, CS_NUMERAL, QSF_NONE); + break; + } } } @@ -2826,6 +2958,12 @@ public: this->ModifyOrder(sel, MOF_COND_VALUE | value << 8); } + if (this->query_text_widget == WID_O_COUNTER_VALUE && !StrEmpty(str)) { + VehicleOrderID sel = this->OrderGetSel(); + uint value = Clamp(atoi(str), 0, 0xFFFF); + this->ModifyOrder(sel, MOF_COUNTER_VALUE | value << 8); + } + if (this->query_text_widget == WID_O_ADD_VEH_GROUP) { DoCommandP(0, VehicleListIdentifier(VL_SINGLE_VEH, this->vehicle->type, this->vehicle->owner, this->vehicle->index).Pack(), 0, CMD_CREATE_GROUP_FROM_LIST | CMD_MSG(STR_ERROR_GROUP_CAN_T_CREATE), nullptr, str); } @@ -2853,6 +2991,7 @@ public: case 2: this->OrderClick_Goto(OPOS_CONDITIONAL); break; case 3: this->OrderClick_Goto(OPOS_SHARE); break; case 4: this->OrderClick_ReleaseSlot(); break; + case 5: this->OrderClick_ChangeCounter(); break; default: NOT_REACHED(); } break; @@ -2920,6 +3059,14 @@ public: this->ModifyOrder(this->OrderGetSel(), MOF_SLOT | index << 8); break; + case WID_O_COUNTER_OP: + this->ModifyOrder(this->OrderGetSel(), MOF_COUNTER_OP | index << 8); + break; + + case WID_O_CHANGE_COUNTER: + this->ModifyOrder(this->OrderGetSel(), MOF_COUNTER_ID | index << 8); + break; + case WID_O_MANAGE_LIST: case WID_O_MGMT_LIST_BTN: switch (index) { @@ -3225,6 +3372,14 @@ static const NWidgetPart _nested_orders_train_widgets[] = { NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_RELEASE_SLOT), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_NULL, STR_ORDER_RELEASE_SLOT_TOOLTIP), SetResize(1, 0), EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_COUNTER_OP), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_JUST_STRING, STR_TRACE_RESTRICT_COUNTER_OP_TOOLTIP), SetResize(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_CHANGE_COUNTER), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_NULL, STR_ORDER_CHANGE_COUNTER_TOOLTIP), SetResize(1, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_COUNTER_VALUE), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_BLACK_COMMA, STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP), SetResize(1, 0), + EndContainer(), EndContainer(), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_O_OCCUPANCY_TOGGLE), SetMinimalSize(36, 12), SetDataTip(STR_ORDERS_OCCUPANCY_BUTTON, STR_ORDERS_OCCUPANCY_BUTTON_TOOLTIP), NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_SHARED), @@ -3347,6 +3502,16 @@ static const NWidgetPart _nested_orders_widgets[] = { NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_RELEASE_SLOT), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_NULL, STR_ORDER_RELEASE_SLOT_TOOLTIP), SetResize(1, 0), EndContainer(), + + /* Buttons for changing a counter. */ + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_COUNTER_OP), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_JUST_STRING, STR_TRACE_RESTRICT_COUNTER_OP_TOOLTIP), SetResize(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_CHANGE_COUNTER), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_NULL, STR_ORDER_CHANGE_COUNTER_TOOLTIP), SetResize(1, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_COUNTER_VALUE), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_BLACK_COMMA, STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP), SetResize(1, 0), + EndContainer(), EndContainer(), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_O_OCCUPANCY_TOGGLE), SetMinimalSize(36, 12), SetDataTip(STR_ORDERS_OCCUPANCY_BUTTON, STR_ORDERS_OCCUPANCY_BUTTON_TOOLTIP), diff --git a/src/order_type.h b/src/order_type.h index eb915d1445..b851da060f 100644 --- a/src/order_type.h +++ b/src/order_type.h @@ -47,6 +47,7 @@ enum OrderType : byte { OT_WAITING = 9, OT_LOADING_ADVANCE = 10, OT_RELEASE_SLOT = 11, + OT_COUNTER = 12, OT_END }; @@ -198,6 +199,9 @@ enum ModifyOrderFlags { MOF_CARGO_TYPE_LOAD, ///< Passes an OrderLoadType and a CargoID. MOF_SLOT, ///< Change the slot value MOF_RV_TRAVEL_DIR, ///< Change the road vehicle travel direction. + MOF_COUNTER_ID, ///< Change the counter ID + MOF_COUNTER_OP, ///< Change the counter operation + MOF_COUNTER_VALUE, ///< Change the counter value MOF_END }; template <> struct EnumPropsT : MakeEnumPropsT {}; diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 2590a3b65b..f0f65bd207 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -99,7 +99,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_INFRA_SHARING, XSCF_NULL, 2, 2, "infra_sharing", nullptr, nullptr, "CPDP" }, { XSLFI_VARIABLE_DAY_LENGTH, XSCF_NULL, 2, 2, "variable_day_length", nullptr, nullptr, nullptr }, { XSLFI_ORDER_OCCUPANCY, XSCF_NULL, 2, 2, "order_occupancy", nullptr, nullptr, nullptr }, - { XSLFI_MORE_COND_ORDERS, XSCF_NULL, 12, 12, "more_cond_orders", nullptr, nullptr, nullptr }, + { XSLFI_MORE_COND_ORDERS, XSCF_NULL, 13, 13, "more_cond_orders", nullptr, nullptr, nullptr }, { XSLFI_EXTRA_LARGE_MAP, XSCF_NULL, 0, 1, "extra_large_map", nullptr, nullptr, nullptr }, { XSLFI_REVERSE_AT_WAYPOINT, XSCF_NULL, 1, 1, "reverse_at_waypoint", nullptr, nullptr, nullptr }, { XSLFI_VEH_LIFETIME_PROFIT, XSCF_NULL, 1, 1, "veh_lifetime_profit", nullptr, nullptr, nullptr }, diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp index 688117857b..3e91572647 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -686,8 +686,8 @@ std::vector PopulateSeparationState(const Vehicle *v_start) // Do not try to separate vehicles on depot service or halt orders separation_valid = false; } - if (order->IsType(OT_RELEASE_SLOT)) { - // Do not try to separate vehicles on release slot orders + if (order->IsType(OT_RELEASE_SLOT) || order->IsType(OT_COUNTER)) { + // Do not try to separate vehicles on release slot or change counter orders separation_valid = false; } int order_ticks; diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index 903d400d9c..63f96770c3 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -793,23 +793,7 @@ void TraceRestrictProgram::Execute(const Train* v, const TraceRestrictProgramInp if (!(input.permitted_slot_operations & TRPISP_CHANGE_COUNTER)) break; TraceRestrictCounter *ctr = TraceRestrictCounter::GetIfValid(GetTraceRestrictValue(item)); if (ctr == nullptr) break; - switch (static_cast(GetTraceRestrictCondOp(item))) { - case TRCCOF_INCREASE: - ctr->UpdateValue(ctr->value + value); - break; - - case TRCCOF_DECREASE: - ctr->UpdateValue(ctr->value - value); - break; - - case TRCCOF_SET: - ctr->UpdateValue(value); - break; - - default: - NOT_REACHED(); - break; - } + ctr->ApplyUpdate(static_cast(GetTraceRestrictCondOp(item)), value); break; } @@ -2604,6 +2588,24 @@ void TraceRestrictCounter::UpdateValue(int32 new_value) } } +int32 TraceRestrictCounter::ApplyValue(int32 current, TraceRestrictCounterCondOpField op, int32 value) +{ + switch (op) { + case TRCCOF_INCREASE: + return std::max(0, current + value); + + case TRCCOF_DECREASE: + return std::max(0, current - value); + + case TRCCOF_SET: + return std::max(0, value); + + default: + NOT_REACHED(); + break; + } +} + static bool IsUniqueCounterName(const char *name) { for (const TraceRestrictCounter *ctr : TraceRestrictCounter::Iterate()) { @@ -2636,6 +2638,10 @@ void TraceRestrictRemoveCounterID(TraceRestrictCounterID index) SB(o->GetXDataRef(), 16, 16, INVALID_TRACE_RESTRICT_COUNTER_ID); changed_order = true; } + if (o->IsType(OT_COUNTER) && o->GetDestination() == index) { + o->SetDestination(INVALID_TRACE_RESTRICT_COUNTER_ID); + changed_order = true; + } } // update windows diff --git a/src/tracerestrict.h b/src/tracerestrict.h index bce3a76ffb..2c4ce52999 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -1111,6 +1111,13 @@ struct TraceRestrictCounter : TraceRestrictCounterPool::PoolItem<&_tracerestrict } void UpdateValue(int32 new_value); + + static int32 ApplyValue(int32 current, TraceRestrictCounterCondOpField op, int32 value); + + void ApplyUpdate(TraceRestrictCounterCondOpField op, int32 value) + { + this->UpdateValue(TraceRestrictCounter::ApplyValue(this->value, op, value)); + } }; #endif /* TRACERESTRICT_H */ diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index aa33f3f8c8..a5b0ebbc15 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -4321,7 +4321,10 @@ static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, * Also check if the current order is a service order so we don't reserve a path to * the destination but instead to the next one if service isn't needed. */ CheckIfTrainNeedsService(v); - if (v->current_order.IsType(OT_DUMMY) || v->current_order.IsType(OT_CONDITIONAL) || v->current_order.IsType(OT_GOTO_DEPOT) || v->current_order.IsType(OT_RELEASE_SLOT)) ProcessOrders(v); + if (v->current_order.IsType(OT_DUMMY) || v->current_order.IsType(OT_CONDITIONAL) || v->current_order.IsType(OT_GOTO_DEPOT) || + v->current_order.IsType(OT_RELEASE_SLOT) || v->current_order.IsType(OT_COUNTER)) { + ProcessOrders(v); + } } /* Save the current train order. The destructor will restore the old order on function exit. */ @@ -6404,7 +6407,7 @@ static bool TrainLocoHandler(Train *v, bool mode) /* exit if train is stopped */ if ((v->vehstatus & VS_STOPPED) && v->cur_speed == 0) return true; - bool valid_order = !v->current_order.IsType(OT_NOTHING) && v->current_order.GetType() != OT_CONDITIONAL && !v->current_order.IsType(OT_RELEASE_SLOT); + bool valid_order = !v->current_order.IsType(OT_NOTHING) && v->current_order.GetType() != OT_CONDITIONAL && !v->current_order.IsType(OT_RELEASE_SLOT) && !v->current_order.IsType(OT_COUNTER); if (ProcessOrders(v) && CheckReverseTrain(v)) { v->wait_counter = 0; v->cur_speed = 0; diff --git a/src/widgets/order_widget.h b/src/widgets/order_widget.h index 77df413aef..9f6cbdb769 100644 --- a/src/widgets/order_widget.h +++ b/src/widgets/order_widget.h @@ -46,6 +46,9 @@ enum OrderWidgets { WID_O_COND_AUX_VIA, ///< Condition via button. WID_O_COND_SCHED_TEST, ///< Choose scheduled dispatch test. WID_O_RELEASE_SLOT, ///< Choose slot to release. + WID_O_COUNTER_OP, ///< Choose counter operation. + WID_O_CHANGE_COUNTER, ///< Choose counter to change. + WID_O_COUNTER_VALUE, ///< Choose counter value. WID_O_SEL_COND_VALUE, ///< Widget for conditional value or conditional cargo type. WID_O_SEL_COND_AUX, ///< Widget for auxiliary conditional cargo type. WID_O_SEL_COND_AUX2, ///< Widget for auxiliary conditional via button.