Allow filtering the vehicle list by station or cargo (#997)

This commit is contained in:
Aaron van Geffen 2021-06-26 11:12:20 +02:00 committed by GitHub
parent d6a0b7a0ff
commit 6a10da15ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 267 additions and 11 deletions

View File

@ -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.

View File

@ -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"

View File

@ -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;
}

View File

@ -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)
{