Allow filtering the vehicle list by station or cargo (#997)
This commit is contained in:
parent
d6a0b7a0ff
commit
6a10da15ca
|
@ -1,5 +1,6 @@
|
|||
21.05+ (???)
|
||||
------------------------------------------------------------------------
|
||||
- Feature: [#856] Allow filtering the vehicle list by station or cargo type.
|
||||
- Fix: [#982] Incorrect rating calculation for cargo causing penalty for fast vehicles.
|
||||
- Fix: [#1008] Inability to decrease max altitude for trees in landscape editor.
|
||||
- Fix: [#1016] Incorrect detection of station causing incorrect smoke sounds.
|
||||
|
|
|
@ -2261,3 +2261,12 @@ strings:
|
|||
2206: "+10% everywhere"
|
||||
2207: "Min everywhere"
|
||||
2208: "Max everywhere"
|
||||
2209: "All vehicles"
|
||||
2210: "Stopping at station"
|
||||
2211: "Transporting cargo"
|
||||
2212: "{MOVE_X 10}Carrying {STRINGID} {SPRITE}"
|
||||
2213: "»{MOVE_X 10}Carrying {STRINGID} {SPRITE}"
|
||||
2214: "No station selected"
|
||||
2215: "No cargo type selected"
|
||||
2216: "{SMALLFONT}{COLOUR BLACK}Open a station window to filter by station"
|
||||
2217: "{SMALLFONT}{COLOUR BLACK}Select a cargo type from the list of available cargo"
|
||||
|
|
|
@ -1606,4 +1606,13 @@ namespace OpenLoco::StringIds
|
|||
constexpr string_id cheat_ratings_plus_10pct = 2206;
|
||||
constexpr string_id cheat_ratings_to_min = 2207;
|
||||
constexpr string_id cheat_ratings_to_max = 2208;
|
||||
constexpr string_id all_vehicles = 2209;
|
||||
constexpr string_id stopping_at_station = 2210;
|
||||
constexpr string_id transporting_cargo = 2211;
|
||||
constexpr string_id carrying_cargoid_sprite = 2212;
|
||||
constexpr string_id carrying_cargoid_sprite_selected = 2213;
|
||||
constexpr string_id no_station_selected = 2214;
|
||||
constexpr string_id no_cargo_selected = 2215;
|
||||
constexpr string_id tooltip_open_station_window_to_filter = 2216;
|
||||
constexpr string_id tooltip_select_cargo_type = 2217;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "../Interop/Interop.hpp"
|
||||
#include "../Localisation/FormatArguments.hpp"
|
||||
#include "../Localisation/StringIds.h"
|
||||
#include "../Objects/CargoObject.h"
|
||||
#include "../Objects/CompetitorObject.h"
|
||||
#include "../Objects/InterfaceSkinObject.h"
|
||||
#include "../OpenLoco.h"
|
||||
|
@ -50,6 +51,10 @@ namespace OpenLoco::Ui::Windows::VehicleList
|
|||
sort_age,
|
||||
sort_reliability,
|
||||
scrollview,
|
||||
filter_type,
|
||||
filter_type_btn,
|
||||
cargo_type,
|
||||
cargo_type_btn,
|
||||
};
|
||||
|
||||
Widget _widgets[] = {
|
||||
|
@ -69,11 +74,17 @@ namespace OpenLoco::Ui::Windows::VehicleList
|
|||
makeWidget({ 414, 43 }, { 65, 12 }, WidgetType::wt_14, WindowColour::secondary, StringIds::null, StringIds::tooltip_sort_by_age),
|
||||
makeWidget({ 479, 43 }, { 67, 12 }, WidgetType::wt_14, WindowColour::secondary, StringIds::null, StringIds::tooltip_sort_by_reliability),
|
||||
makeWidget({ 3, 56 }, { 544, 138 }, WidgetType::scrollview, WindowColour::secondary, Scrollbars::vertical),
|
||||
makeDropdownWidgets({ 280 - 16, 200 }, { 120, 12 }, WidgetType::wt_18, WindowColour::secondary, StringIds::empty),
|
||||
makeDropdownWidgets({ 402 - 16, 200 }, { 150, 12 }, WidgetType::wt_18, WindowColour::secondary, StringIds::empty),
|
||||
widgetEnd()
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
constexpr uint16_t _tabWidgets = (1 << Widx::tab_trains) | (1 << Widx::tab_buses) | (1 << Widx::tab_trucks) | (1 << Widx::tab_trams) | (1 << Widx::tab_aircraft) | (1 << Widx::tab_ships);
|
||||
constexpr uint64_t _enabledWidgets = (1 << Widx::close_button) | _tabWidgets | (1 << Widx::company_select) | (1 << Widx::sort_name) | (1 << Widx::sort_profit) | (1 << Widx::sort_age) | (1 << Widx::sort_reliability) | (1 << Widx::scrollview);
|
||||
constexpr uint64_t _enabledWidgets = (1ULL << Widx::close_button) | _tabWidgets | (1ULL << Widx::company_select) |
|
||||
(1ULL << Widx::sort_name) | (1ULL << Widx::sort_profit) | (1ULL << Widx::sort_age) | (1ULL << Widx::sort_reliability) |
|
||||
(1ULL << Widx::scrollview) | (1ULL << Widx::filter_type) | (1ULL << Widx::filter_type_btn) | (1ULL << Widx::cargo_type) | (1ULL << Widx::cargo_type_btn);
|
||||
// clang-format on
|
||||
|
||||
enum SortMode : uint16_t
|
||||
{
|
||||
|
@ -83,6 +94,13 @@ namespace OpenLoco::Ui::Windows::VehicleList
|
|||
Reliability,
|
||||
};
|
||||
|
||||
enum FilterMode : uint8_t
|
||||
{
|
||||
allVehicles,
|
||||
stoppingAt,
|
||||
transportingCargo,
|
||||
};
|
||||
|
||||
static const uint8_t row_heights[] = {
|
||||
28,
|
||||
28,
|
||||
|
@ -95,9 +113,74 @@ namespace OpenLoco::Ui::Windows::VehicleList
|
|||
static Widx getTabFromType(VehicleType type);
|
||||
static void initEvents();
|
||||
|
||||
constexpr bool isStationFilterActive(const Window* self, bool checkSelection = true)
|
||||
{
|
||||
return self->var_88A == static_cast<int16_t>(FilterMode::stoppingAt) && (!checkSelection || self->var_88C != -1);
|
||||
}
|
||||
|
||||
constexpr bool isCargoFilterActive(const Window* self, bool checkSelection = true)
|
||||
{
|
||||
return self->var_88A == static_cast<int16_t>(FilterMode::transportingCargo) && (!checkSelection || self->var_88C != -1);
|
||||
}
|
||||
|
||||
static bool refreshActiveStation(Window* self)
|
||||
{
|
||||
if (!isStationFilterActive(self, false))
|
||||
return false;
|
||||
|
||||
auto stationWindow = WindowManager::find(WindowType::station);
|
||||
if (stationWindow != nullptr)
|
||||
{
|
||||
self->var_88C = stationWindow->number;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->var_88C = -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
using Vehicles::VehicleHead;
|
||||
|
||||
static bool vehicleStopsAtActiveStation(const VehicleHead* head, StationId_t filterStationId)
|
||||
{
|
||||
auto orders = Vehicles::OrderRingView(head->orderTableOffset);
|
||||
for (auto& order : orders)
|
||||
{
|
||||
auto* stationOrder = order.as<Vehicles::OrderStation>();
|
||||
if (stationOrder == nullptr)
|
||||
continue;
|
||||
|
||||
const auto stationId = stationOrder->getStation();
|
||||
if (stationId == filterStationId)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool vehicleIsTransportingCargo(const VehicleHead* head, int16_t filterCargoId)
|
||||
{
|
||||
auto orders = Vehicles::OrderRingView(head->orderTableOffset);
|
||||
for (auto& order : orders)
|
||||
{
|
||||
Vehicles::OrderCargo* cargoOrder = order.as<Vehicles::OrderUnloadAll>();
|
||||
if (cargoOrder == nullptr)
|
||||
cargoOrder = order.as<Vehicles::OrderWaitFor>();
|
||||
if (cargoOrder == nullptr)
|
||||
continue;
|
||||
|
||||
const auto cargoId = cargoOrder->getCargo();
|
||||
if (cargoId == filterCargoId)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 0x004C1D4F
|
||||
static void refreshVehicleList(Window* self)
|
||||
{
|
||||
refreshActiveStation(self);
|
||||
self->row_count = 0;
|
||||
for (auto vehicle : EntityManager::VehicleList())
|
||||
{
|
||||
|
@ -107,12 +190,16 @@ namespace OpenLoco::Ui::Windows::VehicleList
|
|||
if (vehicle->owner != self->number)
|
||||
continue;
|
||||
|
||||
if (isStationFilterActive(self) && !vehicleStopsAtActiveStation(vehicle, self->var_88C))
|
||||
continue;
|
||||
|
||||
if (isCargoFilterActive(self) && !vehicleIsTransportingCargo(vehicle, self->var_88C))
|
||||
continue;
|
||||
|
||||
vehicle->var_0C &= ~Vehicles::Flags0C::sorted;
|
||||
}
|
||||
}
|
||||
|
||||
using Vehicles::VehicleHead;
|
||||
|
||||
// 0x004C1E4F
|
||||
static bool orderByName(const VehicleHead& lhs, const VehicleHead& rhs)
|
||||
{
|
||||
|
@ -190,6 +277,12 @@ namespace OpenLoco::Ui::Windows::VehicleList
|
|||
if (vehicle->var_0C & Vehicles::Flags0C::sorted)
|
||||
continue;
|
||||
|
||||
if (isStationFilterActive(self) && !vehicleStopsAtActiveStation(vehicle, self->var_88C))
|
||||
continue;
|
||||
|
||||
if (isCargoFilterActive(self) && !vehicleIsTransportingCargo(vehicle, self->var_88C))
|
||||
continue;
|
||||
|
||||
if (insertId == -1)
|
||||
{
|
||||
insertId = vehicle->id;
|
||||
|
@ -363,6 +456,8 @@ namespace OpenLoco::Ui::Windows::VehicleList
|
|||
self->sort_mode = 0;
|
||||
self->var_83C = 0;
|
||||
self->row_hover = -1;
|
||||
self->var_88A = static_cast<int16_t>(FilterMode::allVehicles);
|
||||
self->var_88C = -1;
|
||||
|
||||
refreshVehicleList(self);
|
||||
|
||||
|
@ -474,6 +569,33 @@ namespace OpenLoco::Ui::Windows::VehicleList
|
|||
self->widgets[Widx::sort_age].text = self->sort_mode == SortMode::Age ? StringIds::table_header_age_desc : StringIds::table_header_age;
|
||||
self->widgets[Widx::sort_reliability].text = self->sort_mode == SortMode::Reliability ? StringIds::table_header_reliability_desc : StringIds::table_header_reliability;
|
||||
|
||||
// Reposition filter dropdowns
|
||||
self->widgets[Widx::filter_type].top = self->height - 13;
|
||||
self->widgets[Widx::filter_type].bottom = self->height - 2;
|
||||
|
||||
self->widgets[Widx::filter_type_btn].top = self->height - 12;
|
||||
self->widgets[Widx::filter_type_btn].bottom = self->height - 3;
|
||||
|
||||
self->widgets[Widx::cargo_type].top = self->height - 13;
|
||||
self->widgets[Widx::cargo_type].bottom = self->height - 2;
|
||||
|
||||
self->widgets[Widx::cargo_type_btn].top = self->height - 12;
|
||||
self->widgets[Widx::cargo_type_btn].bottom = self->height - 3;
|
||||
|
||||
// Disable cargo dropdown if not applicable
|
||||
if (self->var_88A != FilterMode::transportingCargo)
|
||||
self->disabled_widgets |= (1 << Widx::cargo_type) | (1 << Widx::cargo_type_btn);
|
||||
else
|
||||
self->disabled_widgets &= ~((1 << Widx::cargo_type) | (1 << Widx::cargo_type_btn));
|
||||
|
||||
// Set appropriate tooltip
|
||||
static constexpr std::array<string_id, 3> filterTooltipByType = {
|
||||
StringIds::null,
|
||||
StringIds::tooltip_open_station_window_to_filter,
|
||||
StringIds::tooltip_select_cargo_type,
|
||||
};
|
||||
self->widgets[Widx::cargo_type_btn].tooltip = filterTooltipByType[self->var_88A];
|
||||
|
||||
setTransportTypeTabs(self);
|
||||
}
|
||||
|
||||
|
@ -500,11 +622,71 @@ namespace OpenLoco::Ui::Windows::VehicleList
|
|||
{ StringIds::num_ships_singular, StringIds::num_ships_plural },
|
||||
};
|
||||
|
||||
auto& footerStringPair = typeToFooterStringIds[self->current_tab];
|
||||
string_id footerStringId = self->var_83C == 1 ? footerStringPair.first : footerStringPair.second;
|
||||
FormatArguments args = {};
|
||||
|
||||
auto args = FormatArguments::common(footerStringId, self->var_83C);
|
||||
Gfx::drawString_494B3F(*context, self->x + 3, self->y + self->height - 13, Colour::black, StringIds::black_stringid, &args);
|
||||
{
|
||||
auto& footerStringPair = typeToFooterStringIds[self->current_tab];
|
||||
string_id footerStringId = self->var_83C == 1 ? footerStringPair.first : footerStringPair.second;
|
||||
|
||||
args = FormatArguments::common(footerStringId, self->var_83C);
|
||||
Gfx::drawString_494B3F(*context, self->x + 3, self->y + self->height - 13, Colour::black, StringIds::black_stringid, &args);
|
||||
}
|
||||
|
||||
static constexpr std::array<string_id, 3> typeToFilterStringIds{
|
||||
StringIds::all_vehicles,
|
||||
StringIds::stopping_at_station,
|
||||
StringIds::transporting_cargo,
|
||||
};
|
||||
|
||||
{
|
||||
// Show current filter type
|
||||
string_id filter = typeToFilterStringIds[self->var_88A];
|
||||
args = FormatArguments::common(filter);
|
||||
auto* widget = &self->widgets[Widx::filter_type];
|
||||
Gfx::drawString_494BBF(*context, self->x + widget->left + 1, self->y + widget->top, widget->width() - 15, Colour::black, StringIds::wcolour2_stringid, &args);
|
||||
}
|
||||
|
||||
auto* widget = &self->widgets[Widx::cargo_type];
|
||||
auto xPos = self->x + widget->left + 1;
|
||||
bool filterActive = false;
|
||||
|
||||
if (isStationFilterActive(self, false))
|
||||
{
|
||||
filterActive = true;
|
||||
if (self->var_88C != -1)
|
||||
{
|
||||
auto station = StationManager::get(self->var_88C);
|
||||
args = FormatArguments::common(station->name, station->town);
|
||||
}
|
||||
else
|
||||
{
|
||||
args = FormatArguments::common(StringIds::no_station_selected);
|
||||
}
|
||||
}
|
||||
|
||||
else if (isCargoFilterActive(self, false))
|
||||
{
|
||||
filterActive = true;
|
||||
if (self->var_88C != -1)
|
||||
{
|
||||
// Show current cargo
|
||||
auto cargoObj = ObjectManager::get<CargoObject>(self->var_88C);
|
||||
args = FormatArguments::common(StringIds::carrying_cargoid_sprite, cargoObj->name, cargoObj->unit_inline_sprite);
|
||||
|
||||
// NB: the -9 in the xpos is to compensate for a hack due to the cargo dropdown limitation (only three args per item)
|
||||
xPos = self->x + widget->left - 9;
|
||||
}
|
||||
else
|
||||
{
|
||||
args = FormatArguments::common(StringIds::no_cargo_selected);
|
||||
}
|
||||
}
|
||||
|
||||
if (filterActive)
|
||||
{
|
||||
// Draw filter text as prepared
|
||||
Gfx::drawString_494BBF(*context, xPos, self->y + widget->top, widget->width() - 15, Colour::black, StringIds::wcolour2_stringid, &args);
|
||||
}
|
||||
}
|
||||
|
||||
// 0x004B6D43
|
||||
|
@ -688,14 +870,49 @@ namespace OpenLoco::Ui::Windows::VehicleList
|
|||
{
|
||||
if (widgetIndex == Widx::company_select)
|
||||
Dropdown::populateCompanySelect(self, &self->widgets[widgetIndex]);
|
||||
|
||||
else if (widgetIndex == Widx::filter_type_btn)
|
||||
{
|
||||
Widget dropdown = self->widgets[Widx::filter_type];
|
||||
Dropdown::show(self->x + dropdown.left, self->y + dropdown.top, dropdown.width() - 4, dropdown.height(), self->getColour(WindowColour::secondary), 3, 0x80);
|
||||
|
||||
Dropdown::add(0, StringIds::dropdown_stringid, StringIds::all_vehicles);
|
||||
Dropdown::add(1, StringIds::dropdown_stringid, StringIds::stopping_at_station);
|
||||
Dropdown::add(2, StringIds::dropdown_stringid, StringIds::transporting_cargo);
|
||||
Dropdown::setItemSelected(self->var_88A);
|
||||
}
|
||||
else if (widgetIndex == Widx::cargo_type_btn)
|
||||
{
|
||||
auto index = 0;
|
||||
auto selectedIndex = -1;
|
||||
for (uint16_t cargoId = 0; cargoId < ObjectManager::getMaxObjects(ObjectType::cargo); ++cargoId)
|
||||
{
|
||||
auto cargoObj = ObjectManager::get<CargoObject>(cargoId);
|
||||
if (cargoObj == nullptr)
|
||||
continue;
|
||||
|
||||
FormatArguments args{};
|
||||
args.push(cargoObj->name);
|
||||
args.push(cargoObj->unit_inline_sprite);
|
||||
args.push(cargoId);
|
||||
Dropdown::add(index, StringIds::carrying_cargoid_sprite, args);
|
||||
|
||||
if (index == self->var_88C)
|
||||
selectedIndex = index;
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
Widget dropdown = self->widgets[Widx::cargo_type];
|
||||
Dropdown::showText(self->x + dropdown.left, self->y + dropdown.top, dropdown.width() - 4, dropdown.height(), self->getColour(WindowColour::secondary), index, 0);
|
||||
if (selectedIndex != -1)
|
||||
Dropdown::setItemSelected(selectedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// 0x004C243F
|
||||
static void onDropdown(Ui::Window* self, WidgetIndex_t widgetIndex, int16_t itemIndex)
|
||||
static void onCompanyDropdown(Ui::Window* self, int16_t itemIndex)
|
||||
{
|
||||
if (widgetIndex != Widx::company_select)
|
||||
return;
|
||||
|
||||
if (itemIndex == -1)
|
||||
return;
|
||||
|
||||
|
@ -728,6 +945,26 @@ namespace OpenLoco::Ui::Windows::VehicleList
|
|||
self->invalidate();
|
||||
}
|
||||
|
||||
static void onDropdown(Ui::Window* self, WidgetIndex_t widgetIndex, int16_t itemIndex)
|
||||
{
|
||||
if (widgetIndex == Widx::company_select)
|
||||
return onCompanyDropdown(self, itemIndex);
|
||||
|
||||
if (widgetIndex == filter_type_btn && itemIndex != -1)
|
||||
{
|
||||
if (self->var_88A != itemIndex)
|
||||
{
|
||||
self->var_88A = itemIndex;
|
||||
self->var_88C = -1;
|
||||
}
|
||||
}
|
||||
|
||||
else if (widgetIndex == cargo_type_btn && itemIndex != -1)
|
||||
{
|
||||
self->var_88C = Dropdown::getItemArgument(itemIndex, 3);
|
||||
}
|
||||
}
|
||||
|
||||
// 0x004C24CA
|
||||
static std::optional<FormatArguments> tooltip(Window* self, WidgetIndex_t widgetIndex)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue