From 117599ec7f80b0628223177663635b55531f2cc3 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 9 Aug 2015 23:39:55 +0100 Subject: [PATCH 1/2] Order occupancy: Add column to orders GUI to show occupancy running average. This is an exponentially weighted moving average of occupancies updated when any vehicle in the shared order set leaves the station of that order. The weighting factor is an adv setting. --- src/lang/english.txt | 8 +++++ src/order_base.h | 14 +++++++++ src/order_cmd.cpp | 1 + src/order_gui.cpp | 62 +++++++++++++++++++++++++++++++++++++- src/saveload/order_sl.cpp | 1 + src/saveload/saveload.cpp | 2 +- src/settings_gui.cpp | 1 + src/settings_type.h | 1 + src/table/settings.ini | 14 +++++++++ src/vehicle.cpp | 22 ++++++++++++++ src/widgets/order_widget.h | 3 ++ 11 files changed, 127 insertions(+), 2 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index ad29b3d596..9beedeb86e 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1703,6 +1703,9 @@ STR_CONFIG_SETTING_REVERSE_AT_SIGNALS_HELPTEXT :Allow trains to STR_CONFIG_SETTING_QUERY_CAPTION :{WHITE}Change setting value +STR_CONFIG_OCCUPANCY_SMOOTHNESS :Smoothness of order occupancy measurement: {STRING2} +STR_CONFIG_OCCUPANCY_SMOOTHNESS_HELPTEXT :0% sets the measurement to the most recent value, 100% leaves it unchanged + # Config errors STR_CONFIG_ERROR :{WHITE}Error with the configuration file... STR_CONFIG_ERROR_ARRAY :{WHITE}... error in array '{RAW_STRING}' @@ -3819,6 +3822,11 @@ STR_ORDERS_GO_TO_TOOLTIP :{BLACK}Insert a STR_ORDERS_VEH_WITH_SHARED_ORDERS_LIST_TOOLTIP :{BLACK}Show all vehicles that share this schedule +STR_ORDERS_OCCUPANCY_BUTTON :{BLACK}% +STR_ORDERS_OCCUPANCY_BUTTON_TOOLTIP :{BLACK}Show occupancy running averages +STR_ORDERS_OCCUPANCY_LIST_TOOLTIP :{BLACK}Order occupancy - this shows runnings averages of recent occupancy levels when leaving a station, for all vehicles sharing these orders +STR_ORDERS_OCCUPANCY_PERCENT :{NUM}% + # String parts to build the order string STR_ORDER_GO_TO_WAYPOINT :Go via {WAYPOINT} STR_ORDER_GO_NON_STOP_TO_WAYPOINT :Go non-stop via {WAYPOINT} diff --git a/src/order_base.h b/src/order_base.h index a67cf69bb4..506eff0497 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -43,6 +43,8 @@ private: CargoID refit_cargo; ///< Refit CargoID + uint8 occupancy; ///< Estimate of vehicle occupancy on departure, for the current order, 0 indicates invalid, 1 - 101 indicate 0 - 100% + uint16 wait_time; ///< How long in ticks to wait at the destination. uint16 travel_time; ///< How long in ticks the journey to this destination should take. uint16 max_speed; ///< How fast the vehicle may go on the way to the destination. @@ -217,6 +219,18 @@ public: */ inline void SetMaxSpeed(uint16 speed) { this->max_speed = speed; } + /** + * Get the occupancy value + * @return occupancy + */ + inline uint8 GetOccupancy() const { return this->occupancy; } + + /** + * Set the occupancy value + * @param occupancy The occupancy to set + */ + inline void SetOccupancy(uint8 occupancy) { this->occupancy = occupancy; } + bool ShouldStopAtStation(const Vehicle *v, StationID station) const; bool CanLoadOrUnload() const; bool CanLeaveWithCargo(bool has_cargo) const; diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 57b29f3f53..a6ce476373 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -239,6 +239,7 @@ Order::Order(uint32 packed) this->dest = GB(packed, 16, 16); this->next = NULL; this->refit_cargo = CT_NO_REFIT; + this->occupancy = 0; this->wait_time = 0; this->travel_time = 0; this->max_speed = UINT16_MAX; diff --git a/src/order_gui.cpp b/src/order_gui.cpp index d4feae35ca..0f86f44689 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -770,6 +770,7 @@ public: this->CreateNestedTree(); this->vscroll = this->GetScrollbar(WID_O_SCROLLBAR); + this->GetWidget(WID_O_SEL_OCCUPANCY)->SetDisplayedPlane(SZSP_NONE); this->FinishInitNested(v->index); if (v->owner == _local_company) { this->DisableWidget(WID_O_EMPTY); @@ -798,6 +799,12 @@ public: virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) { switch (widget) { + case WID_O_OCCUPANCY_LIST: + SetDParamMaxValue(0, 100); + size->width = WD_FRAMERECT_LEFT + GetStringBoundingBox(STR_ORDERS_OCCUPANCY_PERCENT).width + 10 + WD_FRAMERECT_RIGHT; + /* FALL THROUGH */ + + case WID_O_SEL_OCCUPANCY: case WID_O_ORDER_LIST: resize->height = FONT_HEIGHT_NORMAL; size->height = 6 * resize->height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; @@ -1064,6 +1071,8 @@ public: /* Disable list of vehicles with the same shared orders if there is no list */ this->SetWidgetDisabledState(WID_O_SHARED_ORDER_LIST, !shared_orders); + this->GetWidget(WID_O_SEL_OCCUPANCY)->SetDisplayedPlane(IsWidgetLowered(WID_O_OCCUPANCY_TOGGLE) ? 0 : SZSP_NONE); + this->SetDirty(); } @@ -1079,8 +1088,19 @@ public: virtual void DrawWidget(const Rect &r, int widget) const { - if (widget != WID_O_ORDER_LIST) return; + switch (widget) { + case WID_O_ORDER_LIST: + DrawOrderListWidget(r); + break; + + case WID_O_OCCUPANCY_LIST: + DrawOccupancyListWidget(r); + break; + } + } + void DrawOrderListWidget(const Rect &r) const + { bool rtl = _current_text_dir == TD_RTL; SetDParamMaxValue(0, this->vehicle->GetNumOrders(), 2); int index_column_width = GetStringBoundingBox(STR_ORDER_INDEX).width + 2 * GetSpriteSize(rtl ? SPR_ARROW_RIGHT : SPR_ARROW_LEFT).width + 3; @@ -1135,6 +1155,30 @@ public: } } + void DrawOccupancyListWidget(const Rect &r) const + { + int y = r.top + WD_FRAMERECT_TOP; + int line_height = this->GetWidget(WID_O_ORDER_LIST)->resize_y; + + int i = this->vscroll->GetPosition(); + const Order *order = this->vehicle->GetOrder(i); + /* Draw the orders. */ + while (order != NULL) { + /* Don't draw anything if it extends past the end of the window. */ + if (!this->vscroll->IsVisible(i)) break; + + uint8 occupancy = order->GetOccupancy(); + if (occupancy > 0) { + SetDParam(0, occupancy - 1); + DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, y, STR_ORDERS_OCCUPANCY_PERCENT, (i == this->selected_order) ? TC_WHITE : TC_BLACK); + } + y += line_height; + + i++; + order = order->next; + } + } + virtual void SetStringParameters(int widget) const { switch (widget) { @@ -1318,6 +1362,12 @@ public: case WID_O_SHARED_ORDER_LIST: ShowVehicleListWindow(this->vehicle); break; + + case WID_O_OCCUPANCY_TOGGLE: + ToggleWidgetLoweredState(WID_O_OCCUPANCY_TOGGLE); + this->UpdateButtonState(); + this->ReInit(); + break; } } @@ -1541,6 +1591,10 @@ static const NWidgetPart _nested_orders_train_widgets[] = { EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(WWT_PANEL, COLOUR_GREY, WID_O_ORDER_LIST), SetMinimalSize(372, 62), SetDataTip(0x0, STR_ORDERS_LIST_TOOLTIP), SetResize(1, 1), SetScrollbar(WID_O_SCROLLBAR), EndContainer(), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_OCCUPANCY), + NWidget(WWT_PANEL, COLOUR_GREY, WID_O_OCCUPANCY_LIST), SetMinimalSize(50, 0), SetFill(0, 1), SetDataTip(STR_NULL, STR_ORDERS_OCCUPANCY_LIST_TOOLTIP), + SetScrollbar(WID_O_SCROLLBAR), EndContainer(), + EndContainer(), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_O_SCROLLBAR), EndContainer(), @@ -1578,6 +1632,7 @@ static const NWidgetPart _nested_orders_train_widgets[] = { SetDataTip(STR_BLACK_COMMA, STR_ORDER_CONDITIONAL_VALUE_TOOLTIP), SetResize(1, 0), EndContainer(), EndContainer(), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_O_OCCUPANCY_TOGGLE), SetMinimalSize(12, 12), SetDataTip(STR_ORDERS_OCCUPANCY_BUTTON, STR_ORDERS_OCCUPANCY_BUTTON_TOOLTIP), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_O_SHARED_ORDER_LIST), SetMinimalSize(12, 12), SetDataTip(SPR_SHARED_ORDERS_ICON, STR_ORDERS_VEH_WITH_SHARED_ORDERS_LIST_TOOLTIP), EndContainer(), @@ -1619,6 +1674,10 @@ static const NWidgetPart _nested_orders_widgets[] = { EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(WWT_PANEL, COLOUR_GREY, WID_O_ORDER_LIST), SetMinimalSize(372, 62), SetDataTip(0x0, STR_ORDERS_LIST_TOOLTIP), SetResize(1, 1), SetScrollbar(WID_O_SCROLLBAR), EndContainer(), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_OCCUPANCY), + NWidget(WWT_PANEL, COLOUR_GREY, WID_O_OCCUPANCY_LIST), SetMinimalSize(50, 0), SetFill(0, 1), SetDataTip(STR_NULL, STR_ORDERS_OCCUPANCY_LIST_TOOLTIP), + SetScrollbar(WID_O_SCROLLBAR), EndContainer(), + EndContainer(), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_O_SCROLLBAR), EndContainer(), @@ -1653,6 +1712,7 @@ static const NWidgetPart _nested_orders_widgets[] = { EndContainer(), EndContainer(), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_O_OCCUPANCY_TOGGLE), SetMinimalSize(12, 12), SetDataTip(STR_ORDERS_OCCUPANCY_BUTTON, STR_ORDERS_OCCUPANCY_BUTTON_TOOLTIP), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_O_SHARED_ORDER_LIST), SetMinimalSize(12, 12), SetDataTip(SPR_SHARED_ORDERS_ICON, STR_ORDERS_VEH_WITH_SHARED_ORDERS_LIST_TOOLTIP), EndContainer(), diff --git a/src/saveload/order_sl.cpp b/src/saveload/order_sl.cpp index d55d577cb6..237a7a7ea0 100644 --- a/src/saveload/order_sl.cpp +++ b/src/saveload/order_sl.cpp @@ -110,6 +110,7 @@ const SaveLoad *GetOrderDescription() SLE_REF(Order, next, REF_ORDER), SLE_CONDVAR(Order, refit_cargo, SLE_UINT8, 36, SL_MAX_VERSION), SLE_CONDNULL(1, 36, 181), // refit_subtype + SLE_CONDVAR(Order, occupancy, SLE_UINT8, 300, SL_MAX_VERSION), SLE_CONDVAR(Order, wait_time, SLE_UINT16, 67, SL_MAX_VERSION), SLE_CONDVAR(Order, travel_time, SLE_UINT16, 67, SL_MAX_VERSION), SLE_CONDVAR(Order, max_speed, SLE_UINT16, 172, SL_MAX_VERSION), diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index bd3c83d139..d9c1dea04e 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -263,7 +263,7 @@ * 193 26802 * 194 26881 1.5.x */ -extern const uint16 SAVEGAME_VERSION = 194; ///< Current savegame version of OpenTTD. +extern const uint16 SAVEGAME_VERSION = 300; ///< Current savegame version of OpenTTD. SavegameType _savegame_type; ///< type of savegame we are loading diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index f1af4804e4..1f7e1bb56e 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1626,6 +1626,7 @@ static SettingsContainer &GetSettingsTree() vehicles->Add(new SettingEntry("order.no_servicing_if_no_breakdowns")); vehicles->Add(new SettingEntry("order.serviceathelipad")); + vehicles->Add(new SettingEntry("order.occupancy_smoothness")); } SettingsPage *limitations = main->Add(new SettingsPage(STR_CONFIG_SETTING_LIMITATIONS)); diff --git a/src/settings_type.h b/src/settings_type.h index 41366a7719..1b2c3c05d8 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -439,6 +439,7 @@ struct OrderSettings { bool selectgoods; ///< only send the goods to station if a train has been there bool no_servicing_if_no_breakdowns; ///< don't send vehicles to depot when breakdowns are disabled bool serviceathelipad; ///< service helicopters at helipads automatically (no need to send to depot) + uint8 occupancy_smoothness; ///< percentage smoothness of occupancy measurement changes }; /** Settings related to vehicles. */ diff --git a/src/table/settings.ini b/src/table/settings.ini index f314f21e92..625446dbe7 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -2145,6 +2145,20 @@ min = 0 max = 1000000 cat = SC_EXPERT +[SDT_VAR] +base = GameSettings +var = order.occupancy_smoothness +type = SLE_UINT8 +from = 300 +def = 75 +min = 0 +max = 100 +interval = 10 +str = STR_CONFIG_OCCUPANCY_SMOOTHNESS +strhelp = STR_CONFIG_OCCUPANCY_SMOOTHNESS_HELPTEXT +strval = STR_CONFIG_SETTING_PERCENTAGE +cat = SC_EXPERT + ## [SDT_VAR] base = GameSettings diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 46f98fe63b..5f079afb88 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -2115,6 +2115,28 @@ void Vehicle::LeaveStation() SetBit(Train::From(this)->flags, VRF_LEAVING_STATION); } + if (this->cur_real_order_index < this->GetNumOrders()) { + Order *real_current_order = this->GetOrder(this->cur_real_order_index); + uint current_occupancy = CalcPercentVehicleFilled(this, NULL); + uint old_occupancy = real_current_order->GetOccupancy(); + uint new_occupancy; + if (old_occupancy == 0) { + new_occupancy = current_occupancy; + } else { + // Exponential weighted moving average using occupancy_smoothness + new_occupancy = (old_occupancy - 1) * _settings_game.order.occupancy_smoothness; + new_occupancy += current_occupancy * (100 - _settings_game.order.occupancy_smoothness); + new_occupancy += 50; // round to nearest integer percent, rather than just floor + new_occupancy /= 100; + } + if (new_occupancy + 1 != old_occupancy) { + real_current_order->SetOccupancy(static_cast(new_occupancy + 1)); + for (const Vehicle *v = this->FirstShared(); v != NULL; v = v->NextShared()) { + SetWindowDirty(WC_VEHICLE_ORDERS, v->index); + } + } + } + this->MarkDirty(); } diff --git a/src/widgets/order_widget.h b/src/widgets/order_widget.h index 825f791ea8..aaf86a0ea9 100644 --- a/src/widgets/order_widget.h +++ b/src/widgets/order_widget.h @@ -39,6 +39,9 @@ enum OrderWidgets { WID_O_SEL_TOP_ROW, ///< #NWID_SELECTION widget for the top row of the 'your non-trains' order window. WID_O_SEL_BOTTOM_MIDDLE, ///< #NWID_SELECTION widget for the middle part of the bottom row of the 'your train' order window. WID_O_SHARED_ORDER_LIST, ///< Open list of shared vehicles. + WID_O_SEL_OCCUPANCY, ///< #NWID_SELECTION widget for the occupancy list panel. + WID_O_OCCUPANCY_LIST, ///< Occupancy list panel. + WID_O_OCCUPANCY_TOGGLE, ///< Toggle display of occupancy measures. }; #endif /* WIDGETS_ORDER_WIDGET_H */ From fdce64ddaef4e2347d55b0cd3a540d87176588bc Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 9 Aug 2015 23:43:55 +0100 Subject: [PATCH 2/2] Save/load changes for order occupancy --- src/saveload/extended_ver_sl.cpp | 1 + src/saveload/extended_ver_sl.h | 1 + src/saveload/order_sl.cpp | 2 +- src/table/settings.ini | 3 ++- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index be40cad17b..a6dd7f0340 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -45,6 +45,7 @@ std::vector _sl_xv_discardable_chunk_ids; ///< list of chunks static const uint32 _sl_xv_slxi_chunk_version = 0; ///< current version os SLXI chunk const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { + { XSLFI_ORDER_OCCUPANCY, XSCF_NULL, 1, 1, "order_occupancy", NULL, NULL, NULL }, { XSLFI_NULL, XSCF_NULL, 0, 0, NULL, NULL, NULL, NULL },// This is the end marker }; diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h index 39a03478c2..031b8d706e 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -21,6 +21,7 @@ */ enum SlXvFeatureIndex { XSLFI_NULL = 0, ///< Unused value, to indicate that no extended feature test is in use + XSLFI_ORDER_OCCUPANCY, ///< Running average of order occupancy XSLFI_SIZE, ///< Total count of features, including null feature }; diff --git a/src/saveload/order_sl.cpp b/src/saveload/order_sl.cpp index 237a7a7ea0..f3ddcb31af 100644 --- a/src/saveload/order_sl.cpp +++ b/src/saveload/order_sl.cpp @@ -110,7 +110,7 @@ const SaveLoad *GetOrderDescription() SLE_REF(Order, next, REF_ORDER), SLE_CONDVAR(Order, refit_cargo, SLE_UINT8, 36, SL_MAX_VERSION), SLE_CONDNULL(1, 36, 181), // refit_subtype - SLE_CONDVAR(Order, occupancy, SLE_UINT8, 300, SL_MAX_VERSION), + SLE_CONDVAR_X(Order, occupancy, SLE_UINT8, 0, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_OCCUPANCY)), SLE_CONDVAR(Order, wait_time, SLE_UINT16, 67, SL_MAX_VERSION), SLE_CONDVAR(Order, travel_time, SLE_UINT16, 67, SL_MAX_VERSION), SLE_CONDVAR(Order, max_speed, SLE_UINT16, 172, SL_MAX_VERSION), diff --git a/src/table/settings.ini b/src/table/settings.ini index c1e9e8a8ce..c71148448f 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -2151,7 +2151,6 @@ cat = SC_EXPERT base = GameSettings var = order.occupancy_smoothness type = SLE_UINT8 -from = 300 def = 75 min = 0 max = 100 @@ -2160,6 +2159,8 @@ str = STR_CONFIG_OCCUPANCY_SMOOTHNESS strhelp = STR_CONFIG_OCCUPANCY_SMOOTHNESS_HELPTEXT strval = STR_CONFIG_SETTING_PERCENTAGE cat = SC_EXPERT +extver = SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_OCCUPANCY) +patxname = ""order_occupancy.order.occupancy_smoothness"" ## [SDT_VAR]