diff --git a/src/departures.cpp b/src/departures.cpp index 8579efcd3f..ffc6570527 100644 --- a/src/departures.cpp +++ b/src/departures.cpp @@ -195,14 +195,14 @@ static void ScheduledDispatchDepartureLocalFix(DepartureList *departure_list) /** * Compute an up-to-date list of departures for a station. * @param station the station to compute the departures of - * @param show_vehicle_types the types of vehicles to include in the departure list + * @param vehicles set of all the vehicles stopping at this station, of all vehicles types that we are interested in * @param type the type of departures to get (departures or arrivals) * @param show_vehicles_via whether to include vehicles that have this station in their orders but do not stop at it * @param show_pax whether to include passenger vehicles * @param show_freight whether to include freight vehicles * @return a list of departures, which is empty if an error occurred */ -DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5], DepartureType type, bool show_vehicles_via, bool show_pax, bool show_freight) +DepartureList* MakeDepartureList(StationID station, const std::vector &vehicles, DepartureType type, bool show_vehicles_via, bool show_pax, bool show_freight) { /* This function is the meat of the departure boards functionality. */ /* As an overview, it works by repeatedly considering the best possible next departure to show. */ @@ -231,24 +231,7 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5], /* Cache for scheduled departure time */ schdispatch_cache_t schdispatch_last_planned_dispatch; - /* Get all the vehicles stopping at this station. */ - /* We do this to get the order which is the first time they will stop at this station. */ - /* This order is stored along with some more information. */ - /* We keep a pointer to the `least' order (the one with the soonest expected completion time). */ - for (uint i = 0; i < 4; ++i) { - VehicleList vehicles; - - if (!show_vehicle_types[i]) { - /* Don't show vehicles whose type we're not interested in. */ - continue; - } - - /* MAX_COMPANIES is probably the wrong thing to put here, but it works. GenerateVehicleSortList doesn't check the company when the type of list is VL_STATION_LIST (r20801). */ - if (!GenerateVehicleSortList(&vehicles, VehicleListIdentifier(VL_STATION_LIST, (VehicleType)(VEH_TRAIN + i), MAX_COMPANIES, station))) { - /* Something went wrong: panic! */ - return result; - } - + { /* Get the first order for each vehicle for the station we're interested in that doesn't have No Loading set. */ /* We find the least order while we're at it. */ for (const Vehicle *v : vehicles) { diff --git a/src/departures_func.h b/src/departures_func.h index d7aeb78acf..f00a19f35c 100644 --- a/src/departures_func.h +++ b/src/departures_func.h @@ -16,7 +16,9 @@ #include "core/smallvec_type.hpp" #include "departures_type.h" -DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[4], DepartureType type = D_DEPARTURE, +#include + +DepartureList* MakeDepartureList(StationID station, const std::vector &vehicles, DepartureType type = D_DEPARTURE, bool show_vehicles_via = false, bool show_pax = true, bool show_freight = true); #endif /* DEPARTURES_FUNC_H */ diff --git a/src/departures_gui.cpp b/src/departures_gui.cpp index a5519b0732..765d9f7b55 100644 --- a/src/departures_gui.cpp +++ b/src/departures_gui.cpp @@ -74,9 +74,16 @@ static WindowDesc _departures_desc( static uint cached_date_width = 0; ///< The cached maximum width required to display a date. static uint cached_status_width = 0; ///< The cached maximum width required to show the status field. static uint cached_date_arrow_width = 0; ///< The cached width of the red/green arrows that may be displayed alongside times. +static uint cached_veh_type_width = 0; ///< The cached width of the vehicle type icon. static bool cached_date_display_method; ///< Whether the above cached values refers to original (d,m,y) dates or the 24h clock. static bool cached_arr_dep_display_method; ///< Whether to show departures and arrivals on a single line. +void FlushDeparturesWindowTextCaches() +{ + cached_date_width = cached_status_width = cached_date_arrow_width = cached_veh_type_width = 0; + InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0); +} + template struct DeparturesWindow : public Window { protected: @@ -93,6 +100,10 @@ protected: bool cargo_buttons_disabled;///< Show pax/freight buttons disabled uint min_width; ///< The minimum width of this window. Scrollbar *vscroll; + std::vector vehicles; /// current set of vehicles + int veh_width; /// current width of vehicle field + int group_width; /// current width of group field + int toc_width; /// current width of company field virtual uint GetMinWidth() const; static void RecomputeDateWidth(); @@ -122,6 +133,81 @@ protected: } } + void FillVehicleList() + { + this->vehicles.clear(); + this->veh_width = 0; + this->group_width = 0; + this->toc_width = 0; + + btree::btree_set groups; + CompanyMask companies = 0; + int unitnumber_max[4] = { -1, -1, -1, -1 }; + + const Vehicle *v; + FOR_ALL_VEHICLES(v) { + if (v->type < 4 && this->show_types[v->type] && v->IsPrimaryVehicle()) { + const Order *order; + + FOR_VEHICLE_ORDERS(v, order) { + if ((order->IsType(OT_GOTO_STATION) || order->IsType(OT_GOTO_WAYPOINT) || order->IsType(OT_IMPLICIT)) + && order->GetDestination() == this->station) { + this->vehicles.push_back(v); + + if (v->name == nullptr) { + if (v->unitnumber > unitnumber_max[v->type]) unitnumber_max[v->type] = v->unitnumber; + } else { + SetDParam(0, (uint64)(v->index)); + int width = (GetStringBoundingBox(STR_DEPARTURES_VEH)).width; + if (width > this->veh_width) this->veh_width = width; + } + + if (v->group_id != INVALID_GROUP && v->group_id != DEFAULT_GROUP) { + groups.insert(v->group_id); + } + + SetBit(companies, v->owner); + break; + } + } + } + } + + for (uint i = 0; i < 4; i++) { + if (unitnumber_max[i] >= 0) { + uint unitnumber_digits = 2; + if (unitnumber_max[i] >= 10000) { + unitnumber_digits = 5; + } else if (unitnumber_max[i] >= 1000) { + unitnumber_digits = 4; + } else if (unitnumber_max[i] >= 100) { + unitnumber_digits = 3; + } + SetDParamMaxDigits(0, unitnumber_digits); + int width = (GetStringBoundingBox(STR_SV_TRAIN_NAME + i)).width; + if (width > this->veh_width) this->veh_width = width; + } + } + + for (GroupID gid : groups) { + SetDParam(0, (uint64)gid); + int width = (GetStringBoundingBox(STR_DEPARTURES_GROUP)).width; + if (width > this->group_width) this->group_width = width; + } + + uint owner; + FOR_EACH_SET_BIT(owner, companies) { + SetDParam(0, owner); + int width = (GetStringBoundingBox(STR_DEPARTURES_TOC)).width; + if (width > this->toc_width) this->toc_width = width; + } + } + + void RefreshVehicleList() { + this->FillVehicleList(); + this->calc_tick_countdown = 0; + } + public: DeparturesWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc), @@ -171,6 +257,12 @@ public: this->LowerWidget(WID_DB_SHOW_VIA); } + if (cached_veh_type_width == 0) { + cached_veh_type_width = GetStringBoundingBox(STR_DEPARTURES_TYPE_PLANE).width; + } + + this->RefreshVehicleList(); + if (_pause_mode != PM_UNPAUSED) this->OnGameTick(); } @@ -227,7 +319,7 @@ public: this->SetWidgetDirty(widget); } /* We need to recompute the departures list. */ - this->calc_tick_countdown = 0; + this->RefreshVehicleList(); /* We need to redraw the button that was pressed. */ if (_pause_mode != PM_UNPAUSED) this->OnGameTick(); break; @@ -349,8 +441,8 @@ public: this->DeleteDeparturesList(this->arrivals); bool show_pax = _settings_client.gui.departure_only_passengers ? true : this->show_pax; bool show_freight = _settings_client.gui.departure_only_passengers ? false : this->show_freight; - this->departures = (this->departure_types[0] ? MakeDepartureList(this->station, this->show_types, D_DEPARTURE, Twaypoint || this->departure_types[2], show_pax, show_freight) : new DepartureList()); - this->arrivals = (this->departure_types[1] && !_settings_client.gui.departure_show_both ? MakeDepartureList(this->station, this->show_types, D_ARRIVAL, false, show_pax, show_freight) : new DepartureList()); + this->departures = (this->departure_types[0] ? MakeDepartureList(this->station, this->vehicles, D_DEPARTURE, Twaypoint || this->departure_types[2], show_pax, show_freight) : new DepartureList()); + this->arrivals = (this->departure_types[1] && !_settings_client.gui.departure_show_both ? MakeDepartureList(this->station, this->vehicles, D_ARRIVAL, false, show_pax, show_freight) : new DepartureList()); this->SetWidgetDirty(WID_DB_LIST); } @@ -400,6 +492,16 @@ public: this->vscroll->SetCapacityFromWidget(this, WID_DB_LIST); this->GetWidget(WID_DB_LIST)->widget_data = (this->vscroll->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START); } + + /** + * 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 + { + this->RefreshVehicleList(); + } }; /** @@ -459,49 +561,12 @@ uint DeparturesWindow::GetMinWidth() const result = cached_date_width; /* Vehicle type icon */ - result += _settings_client.gui.departure_show_vehicle_type ? (GetStringBoundingBox(STR_DEPARTURES_TYPE_PLANE)).width : 0; + result += _settings_client.gui.departure_show_vehicle_type ? cached_veh_type_width : 0; /* Status */ result += cached_status_width; - /* Find the maximum company name width. */ - int toc_width = 0; - - /* Find the maximum company name width. */ - int group_width = 0; - - /* Find the maximum vehicle name width. */ - int veh_width = 0; - - if (_settings_client.gui.departure_show_vehicle || _settings_client.gui.departure_show_company || _settings_client.gui.departure_show_group) { - for (uint i = 0; i < 4; ++i) { - VehicleList vehicles; - - /* MAX_COMPANIES is probably the wrong thing to put here, but it works. GenerateVehicleSortList doesn't check the company when the type of list is VL_STATION_LIST (r20801). */ - if (!GenerateVehicleSortList(&vehicles, VehicleListIdentifier(VL_STATION_LIST, (VehicleType)(VEH_TRAIN + i), MAX_COMPANIES, station))) { - /* Something went wrong: panic! */ - continue; - } - - for (const Vehicle *v : vehicles) { - SetDParam(0, (uint64)(v->index)); - int width = (GetStringBoundingBox(STR_DEPARTURES_VEH)).width; - if (_settings_client.gui.departure_show_vehicle && width > veh_width) veh_width = width; - - if (v->group_id != INVALID_GROUP && v->group_id != DEFAULT_GROUP) { - SetDParam(0, (uint64)(v->group_id)); - width = (GetStringBoundingBox(STR_DEPARTURES_GROUP)).width; - if (_settings_client.gui.departure_show_group && width > group_width) group_width = width; - } - - SetDParam(0, (uint64)(v->owner)); - width = (GetStringBoundingBox(STR_DEPARTURES_TOC)).width; - if (_settings_client.gui.departure_show_company && width > toc_width) toc_width = width; - } - } - } - - result += toc_width + veh_width + group_width; + result += this->toc_width + this->veh_width + this->group_width; return result + 140; } @@ -588,7 +653,7 @@ void DeparturesWindow::DrawDeparturesListItems(const Rect &r) const } /* Vehicle type icon */ - int type_width = _settings_client.gui.departure_show_vehicle_type ? (GetStringBoundingBox(STR_DEPARTURES_TYPE_PLANE)).width : 0; + int type_width = _settings_client.gui.departure_show_vehicle_type ? cached_veh_type_width : 0; /* Find the maximum width of the status field */ int status_width = cached_status_width; @@ -597,41 +662,13 @@ void DeparturesWindow::DrawDeparturesListItems(const Rect &r) const int calling_at_width = (GetStringBoundingBox(_settings_client.gui.departure_larger_font ? STR_DEPARTURES_CALLING_AT_LARGE : STR_DEPARTURES_CALLING_AT)).width; /* Find the maximum company name width. */ - int toc_width = 0; + int toc_width = _settings_client.gui.departure_show_company ? this->toc_width : 0; /* Find the maximum group name width. */ - int group_width = 0; + int group_width = _settings_client.gui.departure_show_group ? this->group_width : 0; /* Find the maximum vehicle name width. */ - int veh_width = 0; - - if (_settings_client.gui.departure_show_vehicle || _settings_client.gui.departure_show_company || _settings_client.gui.departure_show_group) { - for (uint i = 0; i < 4; ++i) { - VehicleList vehicles; - - /* MAX_COMPANIES is probably the wrong thing to put here, but it works. GenerateVehicleSortList doesn't check the company when the type of list is VL_STATION_LIST (r20801). */ - if (!GenerateVehicleSortList(&vehicles, VehicleListIdentifier(VL_STATION_LIST, (VehicleType)(VEH_TRAIN + i), MAX_COMPANIES, station))) { - /* Something went wrong: panic! */ - continue; - } - - for (const Vehicle *v : vehicles) { - SetDParam(0, (uint64)(v->index)); - int width = (GetStringBoundingBox(STR_DEPARTURES_VEH)).width; - if (_settings_client.gui.departure_show_vehicle && width > veh_width) veh_width = width; - - if (v->group_id != INVALID_GROUP && v->group_id != DEFAULT_GROUP) { - SetDParam(0, (uint64)(v->group_id)); - width = (GetStringBoundingBox(STR_DEPARTURES_GROUP)).width; - if (_settings_client.gui.departure_show_group && width > group_width) group_width = width; - } - - SetDParam(0, (uint64)(v->owner)); - width = (GetStringBoundingBox(STR_DEPARTURES_TOC)).width; - if (_settings_client.gui.departure_show_company && width > toc_width) toc_width = width; - } - } - } + int veh_width = _settings_client.gui.departure_show_vehicle ? this->veh_width : 0; uint departure = 0; uint arrival = 0; diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index c9c082aeb1..b87d09f177 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -1228,6 +1228,7 @@ void InsertOrder(Vehicle *v, Order *new_o, VehicleOrderID sel_ord) /* Make sure to rebuild the whole list */ InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0); + InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0); } /** @@ -1246,6 +1247,7 @@ static CommandCost DecloneOrder(Vehicle *dst, DoCommandFlag flags) DeleteVehicleOrders(dst); InvalidateVehicleOrder(dst, VIWD_REMOVE_ALL_ORDERS); InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0); + InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0); CheckMarkDirtyFocusedRoutePaths(dst); } return CommandCost(); @@ -1383,6 +1385,7 @@ void DeleteOrder(Vehicle *v, VehicleOrderID sel_ord) } InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0); + InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0); } /** @@ -2056,6 +2059,7 @@ CommandCost CmdCloneOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0); + InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0); CheckMarkDirtyFocusedRoutePaths(dst); CheckAdvanceVehicleOrdersAfterClone(dst, flags); @@ -2159,6 +2163,7 @@ CommandCost CmdCloneOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 InvalidateVehicleOrder(dst, VIWD_REMOVE_ALL_ORDERS); InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0); + InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0); CheckMarkDirtyFocusedRoutePaths(dst); CheckAdvanceVehicleOrdersAfterClone(dst, flags); diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 608f71f843..ca9bbc8ed1 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -41,6 +41,7 @@ #include "safeguards.h" +extern void FlushDeparturesWindowTextCaches(); static const StringID _driveside_dropdown[] = { STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT, @@ -529,6 +530,7 @@ struct GameOptionsWindow : Window { ClearAllCachedNames(); UpdateAllVirtCoords(); ReInitAllWindows(); + FlushDeparturesWindowTextCaches(); break; case WID_GO_RESOLUTION_DROPDOWN: // Change resolution @@ -544,6 +546,7 @@ struct GameOptionsWindow : Window { UpdateAllVirtCoords(); FixTitleGameZoom(); ReInitAllWindows(); + FlushDeparturesWindowTextCaches(); break; case WID_GO_FONT_ZOOM_DROPDOWN: @@ -554,6 +557,7 @@ struct GameOptionsWindow : Window { UpdateFontHeightCache(); LoadStringWidthTable(); UpdateAllVirtCoords(); + FlushDeparturesWindowTextCaches(); break; case WID_GO_BASE_GRF_DROPDOWN: diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 6e2a1c3b32..45adbd7260 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1009,6 +1009,7 @@ void Vehicle::PreDestructor() OrderBackup::ClearVehicle(this); } InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0); + InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0); this->cargo.Truncate(); DeleteVehicleOrders(this); diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp index ddecc72c5f..2f6c9a97d1 100644 --- a/src/vehicle_cmd.cpp +++ b/src/vehicle_cmd.cpp @@ -168,6 +168,7 @@ CommandCost CmdBuildVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint if (flags & DC_EXEC) { InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); InvalidateWindowClassesData(GetWindowClassForVehicleType(type), 0); + InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0); SetWindowDirty(WC_COMPANY, _current_company); if (IsLocalCompany()) { InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the auto replace window (must be called before incrementing num_engines) @@ -560,6 +561,7 @@ CommandCost CmdRefitVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint if (!free_wagon) { InvalidateWindowData(WC_VEHICLE_DETAILS, front->index); InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0); + InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0); } /* virtual vehicles get their cargo changed by the TemplateCreateWindow, so set this dirty instead of a depot window */ if (HasBit(v->subtype, GVSF_VIRTUAL)) { @@ -1616,6 +1618,7 @@ CommandCost CmdRenameVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uin free(v->name); v->name = reset ? nullptr : stredup(text); InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 1); + InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0); MarkWholeScreenDirty(); }