diff --git a/src/cargotype.h b/src/cargotype.h index e56e3c5955..511fa8c23e 100644 --- a/src/cargotype.h +++ b/src/cargotype.h @@ -90,6 +90,15 @@ struct CargoSpec { return this - CargoSpec::array; } + /** + * Determine CargoTypes bit of this cargospec + * @return CargoTypes bit + */ + inline CargoTypes CargoTypesBit() const + { + return static_cast(1) << this->Index(); + } + /** * Tests for validity of this cargospec * @return is this cargospec valid? diff --git a/src/date.cpp b/src/date.cpp index 05cf97656f..1ba02b22a9 100644 --- a/src/date.cpp +++ b/src/date.cpp @@ -184,6 +184,7 @@ extern void CompaniesMonthlyLoop(); extern void EnginesMonthlyLoop(); extern void TownsMonthlyLoop(); extern void IndustryMonthlyLoop(); +extern void StationDailyLoop(); extern void StationMonthlyLoop(); extern void SubsidyMonthlyLoop(); @@ -276,6 +277,7 @@ static void OnNewDay() DisasterDailyLoop(); IndustryDailyLoop(); + StationDailyLoop(); if (!_settings_time.time_in_minutes || _settings_client.gui.date_with_time > 0) { SetWindowWidgetDirty(WC_STATUS_BAR, 0, WID_S_LEFT); diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp index 0abc45ab04..56506672db 100644 --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -31,6 +31,7 @@ #include #include "safeguards.h" +#include "station_base.h" /* Bitmasks of company and cargo indices that shouldn't be drawn. */ static CompanyMask _legend_excluded_companies; @@ -1649,3 +1650,277 @@ void InitializeGraphGui() _legend_excluded_companies = 0; _legend_excluded_cargo = 0; } + +/*************************/ +/* STATION CARGO HISTORY */ +/*************************/ +struct StationCargoGraphWindow final : BaseGraphWindow { + StationID station_id; + uint line_height {}; ///< Pixel height of each cargo type row. + Scrollbar *vscroll; ///< Cargo list scrollbar. + uint legend_width {}; ///< Width of legend 'blob'. + CargoTypes legend_excluded_cargo; + CargoTypes present_cargoes; + + StationCargoGraphWindow(WindowDesc *desc, WindowNumber window) : + BaseGraphWindow(desc, WID_SCG_GRAPH, STR_JUST_COMMA) + { + station_id = static_cast(window); + + this->num_on_x_axis = MAX_STATION_CARGO_HISTORY_DAYS; // Four weeks + this->num_vert_lines = MAX_STATION_CARGO_HISTORY_DAYS; + this->month = 0xFF; + this->x_values_start = 2; + this->x_values_increment = 2; + + this->CreateNestedTree(); + this->vscroll = this->GetScrollbar(WID_SCG_MATRIX_SCROLLBAR); + + /* Initialise the data set */ + this->FillGraphData(); + + this->FinishInitNested(window); + } + + void OnInit() override + { + /* Width of the legend blob. */ + this->legend_width = (FONT_HEIGHT_SMALL - ScaleFontTrad(1)) * 8 / 5; + this->legend_excluded_cargo = 0; + } + + void SetStringParameters(int widget) const override + { + if (widget == WID_SCG_CAPTION) { + SetDParam(0, this->station_id); + } + if (widget == WID_SCG_FOOTER) { + SetDParam(0, 48); + } + } + + void UpdateExcludedData() + { + this->excluded_data = 0; + + uint8 i = 0; + const CargoSpec *cs; + FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) { + if (!HasBit(this->present_cargoes, cs->Index())) continue; + if (HasBit(legend_excluded_cargo, cs->Index())) SetBit(this->excluded_data, i); + i++; + } + } + + void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override + { + if (widget < WID_SCG_MATRIX) { + BaseGraphWindow::UpdateWidgetSize(widget, size, padding, fill, resize); + return; + } + + const CargoSpec *cs; + FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) { + SetDParam(0, cs->name); + Dimension d = GetStringBoundingBox(STR_GRAPH_CARGO_PAYMENT_CARGO); + d.width += this->legend_width + 4; // color field + d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; + d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; + *size = maxdim(d, *size); + } + + this->line_height = size->height; + size->height = this->line_height * 11; /* Default number of cargo types in most climates. */ + resize->width = 0; + resize->height = this->line_height; + } + + void DrawWidget(const Rect &r, int widget) const override + { + if (widget < WID_SCG_MATRIX) { + BaseGraphWindow::DrawWidget(r, widget); + return; + } + + const bool rtl = _current_text_dir == TD_RTL; + + int x = r.left + WD_FRAMERECT_LEFT; + int y = r.top; + const uint row_height = FONT_HEIGHT_SMALL; + const int padding = ScaleFontTrad(1); + + int pos = this->vscroll->GetPosition(); + int max = pos + this->vscroll->GetCapacity(); + + const CargoSpec *cs; + FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) { + if (!HasBit(this->present_cargoes, cs->Index())) continue; + if (pos-- > 0) continue; + if (--max < 0) break; + + const bool lowered = !HasBit(legend_excluded_cargo, cs->Index()); + + /* Redraw box if lowered */ + if (lowered) DrawFrameRect(r.left, y, r.right, y + this->line_height - 1, COLOUR_BROWN, lowered ? FR_LOWERED : FR_NONE); + + const byte clk_dif = lowered ? 1 : 0; + const int rect_x = clk_dif + (rtl ? r.right - this->legend_width - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT); + + GfxFillRect(rect_x, y + padding + clk_dif, rect_x + this->legend_width, y + row_height - 1 + clk_dif, PC_BLACK); + GfxFillRect(rect_x + 1, y + padding + 1 + clk_dif, rect_x + this->legend_width - 1, y + row_height - 2 + clk_dif, cs->legend_colour); + SetDParam(0, cs->name); + DrawString(rtl ? r.left : x + this->legend_width + 4 + clk_dif, (rtl ? r.right - this->legend_width - 4 + clk_dif : r.right), y + clk_dif, STR_GRAPH_CARGO_PAYMENT_CARGO); + + y += this->line_height; + } + } + + void OnClick(Point pt, int widget, int click_count) override + { + switch (widget) { + case WID_SCG_ENABLE_CARGOES: + /* Remove all cargoes from the excluded lists. */ + this->legend_excluded_cargo = 0; + this->excluded_data = 0; + this->SetDirty(); + break; + + case WID_SCG_DISABLE_CARGOES: { + /* Add all cargoes to the excluded lists. */ + this->legend_excluded_cargo = ~static_cast(0); + int i = 0; + const CargoSpec *cs; + FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) { + if (!HasBit(this->present_cargoes, cs->Index())) continue; + SetBit(this->excluded_data, i); + i++; + } + this->SetDirty(); + break; + } + + case WID_SCG_MATRIX: { + uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SCG_MATRIX); + if (row >= this->vscroll->GetCount()) return; + + const CargoSpec *cs; + FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) { + if (!HasBit(this->present_cargoes, cs->Index())) continue; + if (row-- > 0) continue; + + ToggleBit(legend_excluded_cargo, cs->Index()); + this->UpdateExcludedData(); + this->SetDirty(); + break; + } + break; + } + } + } + + void OnResize() override + { + this->vscroll->SetCapacityFromWidget(this, WID_SCG_MATRIX); + } + + void OnGameTick() override + { + /* Override default OnGameTick */ + } + + /** + * Some data on this window has become invalid. + * @param data Information about the changed data. + * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details. + */ + void OnInvalidateData(int data = 0, bool gui_scope = true) override + { + if (!gui_scope) return; + this->FillGraphData(); + } + + void FillGraphData() + { + const Station* station = Station::GetIfValid(this->station_id); + if (station == nullptr) return; + + this->present_cargoes = station->station_cargo_history_cargoes; + this->vscroll->SetCount(CountBits(this->present_cargoes)); + + this->UpdateExcludedData(); + + uint8 i = 0; + const CargoSpec *cs; + FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) { + if (!HasBit(this->present_cargoes, cs->Index())) continue; + this->colours[i] = cs->legend_colour; + + const auto &history = station->station_cargo_history[CountBits(this->present_cargoes & (cs->CargoTypesBit() - 1))]; + + uint offset = station->station_cargo_history_offset; + for (uint j = 0; j < MAX_STATION_CARGO_HISTORY_DAYS; j++) { + this->cost[i][j] = history[offset]; + offset++; + if (offset == MAX_STATION_CARGO_HISTORY_DAYS) offset = 0; + } + i++; + } + + this->num_dataset = i; + + this->SetDirty(); + } +}; + + +static const NWidgetPart _nested_station_cargo_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_GREY), + NWidget(WWT_CAPTION, COLOUR_GREY, WID_SCG_CAPTION), SetDataTip(STR_GRAPH_STATION_CARGO_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_SHADEBOX, COLOUR_GREY), + NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), + NWidget(WWT_STICKYBOX, COLOUR_GREY), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, WID_SCG_BACKGROUND), SetMinimalSize(568, 128), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0), + NWidget(WWT_TEXT, COLOUR_GREY, WID_SCG_HEADER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_GRAPH_STATION_CARGO_TITLE, STR_NULL), + NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_EMPTY, COLOUR_GREY, WID_SCG_GRAPH), SetMinimalSize(495, 0), SetFill(1, 1), SetResize(1, 1), + NWidget(NWID_VERTICAL), + NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 0), SetResize(0, 1), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SCG_ENABLE_CARGOES), SetDataTip(STR_GRAPH_CARGO_ENABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_ENABLE_ALL), SetFill(1, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SCG_DISABLE_CARGOES), SetDataTip(STR_GRAPH_CARGO_DISABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL), SetFill(1, 0), + NWidget(NWID_SPACER), SetMinimalSize(0, 4), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_MATRIX, COLOUR_BROWN, WID_SCG_MATRIX), SetResize(0, 2), SetMatrixDataTip(1, 0, STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO), SetScrollbar(WID_SCG_MATRIX_SCROLLBAR), + NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_SCG_MATRIX_SCROLLBAR), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1), SetResize(0, 1), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(0, 1), SetResize(0, 1), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(WD_RESIZEBOX_WIDTH, 0), SetFill(1, 0), SetResize(1, 0), + NWidget(WWT_TEXT, COLOUR_GREY, WID_SCG_FOOTER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_GRAPH_STATION_CARGO_X_LABEL, STR_NULL), + NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0), + NWidget(WWT_RESIZEBOX, COLOUR_GREY, WID_SCG_RESIZE), + EndContainer(), + EndContainer(), +}; + +static WindowDesc _station_cargo_desc( + WDP_AUTO, "graph_station_cargo", 0, 0, + WC_STATION_CARGO, WC_NONE, + 0, + _nested_station_cargo_widgets, lengthof(_nested_station_cargo_widgets) +); + + +void ShowStationCargo(StationID station_id) +{ + AllocateWindowDescFront(&_station_cargo_desc, station_id); +} + diff --git a/src/graph_gui.h b/src/graph_gui.h index 07a0c2f66a..b1d08b8e7b 100644 --- a/src/graph_gui.h +++ b/src/graph_gui.h @@ -12,6 +12,8 @@ extern uint8 _cargo_payment_x_mode; +typedef uint16 StationID; + void ShowOperatingProfitGraph(); void ShowIncomeGraph(); void ShowDeliveredCargoGraph(); @@ -20,5 +22,6 @@ void ShowCompanyValueGraph(); void ShowCargoPaymentRates(); void ShowCompanyLeagueTable(); void ShowPerformanceRatingDetail(); +void ShowStationCargo(StationID); #endif /* GRAPH_GUI_H */ diff --git a/src/lang/english.txt b/src/lang/english.txt index 24a86cd5af..cf10df3237 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -613,6 +613,10 @@ STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL :{BLACK}Display STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO :{BLACK}Toggle graph for cargo type on/off STR_GRAPH_CARGO_PAYMENT_CARGO :{TINY_FONT}{BLACK}{STRING} +STR_GRAPH_STATION_CARGO_CAPTION :{WHITE}{STATION} - Waiting Cargo History +STR_GRAPH_STATION_CARGO_X_LABEL :{TINY_FONT}{BLACK}Development over the last {NUM} days +STR_GRAPH_STATION_CARGO_TITLE :{TINY_FONT}{BLACK}Units of cargo waiting at the station + STR_GRAPH_CARGO_DAYS_MODE :{TINY_FONT}{BLACK}Days in transit STR_GRAPH_CARGO_SPEED_MODE :{TINY_FONT}{BLACK}Average speed STR_GRAPH_CARGO_TOOLTIP_DAYS_MODE :{BLACK}Display days in transit on the x-axis of the graph @@ -4184,6 +4188,9 @@ STR_STATION_VIEW_GROUP_D_V_S :Destination-Via STR_STATION_VIEW_DEPARTURES_BUTTON :{BLACK}Departures STR_STATION_VIEW_DEPARTURES_TOOLTIP :{BLACK}Show list of scheduled departures +STR_STATION_VIEW_HISTORY_BUTTON :{BLACK}History +STR_STATION_VIEW_HISTORY_TOOLTIP :{BLACK}Show waiting cargo history + ############ range for rating starts STR_CARGO_RATING_APPALLING :Appalling STR_CARGO_RATING_VERY_POOR :Very Poor diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index cd61ed8a1d..552119c5b7 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -151,6 +151,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_WATER_FLOODING, XSCF_NULL, 2, 2, "water_flooding", nullptr, nullptr, nullptr }, { XSLFI_MORE_HOUSES, XSCF_NULL, 2, 2, "more_houses", nullptr, nullptr, nullptr }, { XSLFI_CUSTOM_TOWN_ZONE, XSCF_IGNORABLE_UNKNOWN, 1, 1, "custom_town_zone", nullptr, nullptr, nullptr }, + { XSLFI_STATION_CARGO_HISTORY, XSCF_NULL, 1, 1, "station_cargo_history", nullptr, nullptr, nullptr }, { XSLFI_NULL, XSCF_NULL, 0, 0, nullptr, nullptr, nullptr, nullptr },// This is the end marker }; diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h index 1cc9c89de1..bd347e754f 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -105,6 +105,7 @@ enum SlXvFeatureIndex { XSLFI_WATER_FLOODING, ///< Water flooding map bit XSLFI_MORE_HOUSES, ///< More house types XSLFI_CUSTOM_TOWN_ZONE, ///< Custom town zones + XSLFI_STATION_CARGO_HISTORY, ///< Station waiting cargo history XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit XSLFI_HEIGHT_8_BIT, ///< Map tile height is 8 bit instead of 4 bit, but savegame version may be before this became true in trunk diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp index 4a6a63787b..0572d55ba0 100644 --- a/src/saveload/station_sl.cpp +++ b/src/saveload/station_sl.cpp @@ -459,6 +459,7 @@ static const SaveLoad _station_desc[] = { SLE_CONDVAR(Station, always_accepted, SLE_FILE_U32 | SLE_VAR_U64, SLV_127, SLV_EXTEND_CARGOTYPES), SLE_CONDVAR(Station, always_accepted, SLE_UINT64, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION), SLE_CONDNULL_X(32 * 24, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP, SL_JOKER_1_22)), + SLE_CONDVAR_X(Station, station_cargo_history_cargoes, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_STATION_CARGO_HISTORY)), SLE_END() }; @@ -547,6 +548,17 @@ static void RealSave_STNN(BaseStation *bst) SlObjectSaveFiltered(const_cast(&(*it)), _cargo_list_desc); // _cargo_list_desc has no conditionals } } + + assert(st->station_cargo_history.size() == CountBits(st->station_cargo_history_cargoes)); + dumper->CheckBytes(st->station_cargo_history.size() * MAX_STATION_CARGO_HISTORY_DAYS * 2); + for (const auto &history : st->station_cargo_history) { + uint i = st->station_cargo_history_offset; + do { + dumper->RawWriteUint16(history[i]); + i++; + if (i == MAX_STATION_CARGO_HISTORY_DAYS) i = 0; + } while (i != st->station_cargo_history_offset); + } } for (uint i = 0; i < bst->num_specs; i++) { @@ -656,6 +668,15 @@ static void Load_STNN() } if (SlXvIsFeatureMissing(XSLFI_ST_LAST_VEH_TYPE)) st->goods[i].last_vehicle_type = _old_last_vehicle_type; } + + st->station_cargo_history.resize(CountBits(st->station_cargo_history_cargoes)); + buffer->CheckBytes(st->station_cargo_history.size() * MAX_STATION_CARGO_HISTORY_DAYS * 2); + for (auto &history : st->station_cargo_history) { + for (uint16 &amount : history) { + amount = buffer->RawReadUint16(); + } + } + st->station_cargo_history_offset = 0; } if (bst->num_specs != 0) { diff --git a/src/station.cpp b/src/station.cpp index 2ccc42e1a0..effc2c898d 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -62,6 +62,7 @@ BaseStation::~BaseStation() DeleteWindowById(WC_SHIPS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_SHIP, this->owner, this->index).Pack()); DeleteWindowById(WC_AIRCRAFT_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_AIRCRAFT, this->owner, this->index).Pack()); DeleteWindowById(WC_DEPARTURES_BOARD, this->index); + DeleteWindowById(WC_STATION_CARGO, this->index); if (HasBit(_display_opt, Station::IsExpected(this) ? DO_SHOW_STATION_NAMES : DO_SHOW_WAYPOINT_NAMES) && !(_local_company != this->owner && this->owner != OWNER_NONE && !HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS))) { @@ -76,7 +77,9 @@ Station::Station(TileIndex tile) : ship_station(INVALID_TILE, 0, 0), indtype(IT_INVALID), time_since_load(255), - time_since_unload(255) + time_since_unload(255), + station_cargo_history_cargoes(0), + station_cargo_history_offset(0) { /* this->random_bits is set in Station::AddFacility() */ } diff --git a/src/station_base.h b/src/station_base.h index dc011231a6..c11f1fa24c 100644 --- a/src/station_base.h +++ b/src/station_base.h @@ -23,6 +23,7 @@ #include "core/endian_type.hpp" #include #include +#include #include #include #include @@ -808,6 +809,10 @@ public: IndustryList industries_near; ///< Cached list of industries near the station that can accept cargo, @see DeliverGoodsToIndustry() Industry *industry; ///< NOSAVE: Associated industry for neutral stations. (Rebuilt on load from Industry->st) + CargoTypes station_cargo_history_cargoes; ///< Bitmask of cargoes in station_cargo_history + uint8 station_cargo_history_offset; ///< Start offset in station_cargo_history cargo ring buffer + std::vector> station_cargo_history; ///< Station history of waiting cargo. + Station(TileIndex tile = INVALID_TILE); ~Station(); @@ -817,6 +822,8 @@ public: void UpdateVirtCoord() override; + void UpdateCargoHistory(); + void MoveSign(TileIndex new_xy) override; void AfterStationTileSetChange(bool adding, StationType type); diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 966312d04c..7c45135f35 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -404,6 +404,33 @@ void Station::GetTileArea(TileArea *ta, StationType type) const } } +/** + * Update the cargo history. + */ +void Station::UpdateCargoHistory() +{ + uint storage_offset = 0; + bool update_window = false; + for (const CargoSpec *cs : CargoSpec::Iterate()) { + uint amount = this->goods[cs->Index()].cargo.TotalCount(); + if (!HasBit(this->station_cargo_history_cargoes, cs->Index())) { + if (amount == 0) { + /* No cargo present, and no history stored for this cargo, no work to do */ + continue; + } else { + if (this->station_cargo_history_cargoes == 0) update_window = true; + SetBit(this->station_cargo_history_cargoes, cs->Index()); + this->station_cargo_history.emplace(this->station_cargo_history.begin() + storage_offset); + } + } + this->station_cargo_history[storage_offset][this->station_cargo_history_offset] = static_cast(std::clamp(amount, (uint)0, (uint)UINT16_MAX)); + storage_offset++; + } + this->station_cargo_history_offset++; + if (this->station_cargo_history_offset == MAX_STATION_CARGO_HISTORY_DAYS) this->station_cargo_history_offset = 0; + if (update_window) InvalidateWindowData(WC_STATION_VIEW, this->index, -1); +} + /** * Update the virtual coords needed to draw the station sign. */ @@ -3771,7 +3798,7 @@ bool GetNewGrfRating(const Station *st, const CargoSpec *cs, const GoodsEntry *g { *new_grf_rating = 0; bool is_using_newgrf_rating = false; - + /* Perform custom station rating. If it succeeds the speed, days in transit and * waiting cargo ratings must not be executed. */ @@ -3805,7 +3832,7 @@ int GetSpeedRating(const GoodsEntry *ge) int GetWaitTimeRating(const CargoSpec *cs, const GoodsEntry *ge) { int rating = 0; - + uint wait_time = ge->time_since_pickup; if (_settings_game.station.cargo_class_rating_wait_time) { @@ -3878,7 +3905,7 @@ int GetTargetRating(const Station *st, const CargoSpec *cs, const GoodsEntry *ge } else if (HasBit(cs->callback_mask, CBM_CARGO_STATION_RATING_CALC)) { int new_grf_rating; - + if (GetNewGrfRating(st, cs, ge, &new_grf_rating)) { skip = true; rating += new_grf_rating; @@ -4240,6 +4267,18 @@ void OnTick_Station() } } +/** Daily loop for stations. */ +void StationDailyLoop() +{ + // Only record cargo history every second day. + if (_date % 2 != 0) { + for (Station *st : Station::Iterate()) { + st->UpdateCargoHistory(); + } + InvalidateWindowClassesData(WC_STATION_CARGO); + } +} + /** Monthly loop for stations. */ void StationMonthlyLoop() { diff --git a/src/station_gui.cpp b/src/station_gui.cpp index cf2ff5d1f9..8477a7ad74 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -32,6 +32,7 @@ #include "linkgraph/linkgraph.h" #include "zoom_func.h" #include "departures_gui.h" +#include "graph_gui.h" #include "zoning.h" #include "newgrf_debug.h" @@ -831,6 +832,8 @@ static const NWidgetPart _nested_station_view_widgets[] = { NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_ACCEPTS_RATINGS), SetMinimalSize(46, 12), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_STATION_VIEW_RATINGS_BUTTON, STR_STATION_VIEW_RATINGS_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_HISTORY), SetMinimalSize(60, 12), SetResize(1, 0), SetFill(1, 1), + SetDataTip(STR_STATION_VIEW_HISTORY_BUTTON, STR_STATION_VIEW_HISTORY_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_DEPARTURES), SetMinimalSize(46, 12), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_STATION_VIEW_DEPARTURES_BUTTON, STR_STATION_VIEW_DEPARTURES_TOOLTIP), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SV_CLOSE_AIRPORT), SetMinimalSize(45, 12), SetResize(1, 0), SetFill(1, 1), @@ -1363,6 +1366,12 @@ struct StationViewWindow : public Window { SetViewportCatchmentStation(Station::Get(this->window_number), false); } + void OnInit() override + { + const Station *st = Station::Get(this->window_number); + SetWidgetDisabledState(WID_SV_HISTORY, st->station_cargo_history_cargoes == 0); + } + /** * Show a certain cargo entry characterized by source/next/dest station, cargo ID and amount of cargo at the * right place in the cargo view. I.e. update as many rows as are expanded following that characterization. @@ -2052,6 +2061,11 @@ struct StationViewWindow : public Window { break; } + case WID_SV_HISTORY: { + ShowStationCargo((StationID)this->window_number); + break; + } + case WID_SV_DEPARTURES: { ShowStationDepartures((StationID)this->window_number); break; diff --git a/src/station_type.h b/src/station_type.h index 0ea2175e86..a8e657efc1 100644 --- a/src/station_type.h +++ b/src/station_type.h @@ -27,6 +27,8 @@ struct Waypoint; static const StationID NEW_STATION = 0xFFFE; static const StationID INVALID_STATION = 0xFFFF; +static const uint MAX_STATION_CARGO_HISTORY_DAYS = 24; + typedef SmallStack StationIDStack; /** Station types */ diff --git a/src/widgets/graph_widget.h b/src/widgets/graph_widget.h index 14205b47b7..1ab0d81142 100644 --- a/src/widgets/graph_widget.h +++ b/src/widgets/graph_widget.h @@ -53,6 +53,20 @@ enum CargoPaymentRatesWidgets { WID_CPR_SPEED, ///< Speed mode. }; +/** Widget of the #StationCargoGraphWindow class. */ +enum StationCargoWidgets { + WID_SCG_CAPTION, ///< Window title + WID_SCG_BACKGROUND, ///< Background of the window. + WID_SCG_HEADER, ///< Header. + WID_SCG_GRAPH, ///< Graph itself. + WID_SCG_RESIZE, ///< Resize button. + WID_SCG_FOOTER, ///< Footer. + WID_SCG_ENABLE_CARGOES, ///< Enable cargoes button. + WID_SCG_DISABLE_CARGOES, ///< Disable cargoes button. + WID_SCG_MATRIX, ///< Cargo list. + WID_SCG_MATRIX_SCROLLBAR, ///< Cargo list scrollbar. +}; + /** Widget of the #CompanyLeagueWindow class. */ enum CompanyLeagueWidgets { WID_CL_BACKGROUND, ///< Background of the window. diff --git a/src/widgets/station_widget.h b/src/widgets/station_widget.h index d8951182d3..ed36fe5b0c 100644 --- a/src/widgets/station_widget.h +++ b/src/widgets/station_widget.h @@ -30,6 +30,7 @@ enum StationViewWidgets { WID_SV_PLANES, ///< List of scheduled planes button. WID_SV_CATCHMENT, ///< Toggle catchment area highlight. WID_SV_DEPARTURES, ///< Departures button. + WID_SV_HISTORY, ///< Cargo history button. }; /** Widgets of the #CompanyStationsWindow class. */ diff --git a/src/window_type.h b/src/window_type.h index c84d6c8a9c..66f7535527 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -581,6 +581,12 @@ enum WindowClass { */ WC_PAYMENT_RATES, + /** + * Station cargo graph; %Window numbers: + * - #StationID = #StationCargoWidgets + */ + WC_STATION_CARGO, + /** * Performance detail window; %Window numbers: * - 0 = #PerformanceRatingDetailsWidgets