From 775735bbf28ff3d785d05948b8476f88ad7cb729 Mon Sep 17 00:00:00 2001 From: casper Date: Mon, 15 Jul 2019 21:37:26 +0300 Subject: [PATCH 1/2] Feature: Build vehicle smart GUI --- projects/openttd_vs142.vcxproj | 8 +- projects/openttd_vs142.vcxproj.in | 8 +- projects/strgen_vs142.vcxproj | 6 +- src/build_vehicle_gui.cpp | 618 +++++++++++++++++++++++++- src/engine_gui.h | 6 +- src/gfx_layout.cpp | 2 +- src/lang/arabic_egypt.txt | 19 +- src/lang/english.txt | 35 ++ src/music/dmusic.cpp | 32 +- src/music/win32_m.cpp | 133 +++--- src/order_cmd.cpp | 2 +- src/rail_cmd.cpp | 7 +- src/road_cmd.cpp | 5 +- src/script/api/script_vehiclelist.cpp | 3 +- src/script/api/script_window.hpp | 20 + src/settings_gui.cpp | 1 + src/settings_type.h | 2 + src/table/settings.ini | 9 + src/train.h | 66 ++- src/widgets/build_vehicle_widget.h | 20 + src/widgets/dropdown.cpp | 24 +- src/widgets/dropdown_func.h | 1 + 22 files changed, 891 insertions(+), 136 deletions(-) diff --git a/projects/openttd_vs142.vcxproj b/projects/openttd_vs142.vcxproj index ee2886e717d3..a591a9dc3ce5 100644 --- a/projects/openttd_vs142.vcxproj +++ b/projects/openttd_vs142.vcxproj @@ -78,16 +78,16 @@ $(SolutionDir)..\objs\$(Platform)\$(Configuration)\ $(SolutionDir)..\objs\$(Platform)\$(Configuration)\ - AllRules.ruleset + NativeMinimumRules.ruleset - AllRules.ruleset + NativeMinimumRules.ruleset - AllRules.ruleset + NativeMinimumRules.ruleset - AllRules.ruleset + NativeMinimumRules.ruleset $(SolutionDir)..\objs\$(Platform)\$(Configuration)\ diff --git a/projects/openttd_vs142.vcxproj.in b/projects/openttd_vs142.vcxproj.in index 6b5c18d25c06..83befcd8d078 100644 --- a/projects/openttd_vs142.vcxproj.in +++ b/projects/openttd_vs142.vcxproj.in @@ -78,16 +78,16 @@ $(SolutionDir)..\objs\$(Platform)\$(Configuration)\ $(SolutionDir)..\objs\$(Platform)\$(Configuration)\ - AllRules.ruleset + NativeMinimumRules.ruleset - AllRules.ruleset + NativeMinimumRules.ruleset - AllRules.ruleset + NativeMinimumRules.ruleset - AllRules.ruleset + NativeMinimumRules.ruleset $(SolutionDir)..\objs\$(Platform)\$(Configuration)\ diff --git a/projects/strgen_vs142.vcxproj b/projects/strgen_vs142.vcxproj index eb0657a30b86..554109b0458d 100644 --- a/projects/strgen_vs142.vcxproj +++ b/projects/strgen_vs142.vcxproj @@ -29,9 +29,9 @@ $(SolutionDir)..\objs\strgen\ $(SolutionDir)..\objs\strgen\ false - AllRules.ruleset - - + NativeMinimumRules.ruleset + + diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index 2778ccd55cee..c7ce54779914 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -22,11 +22,13 @@ #include "newgrf_engine.h" #include "newgrf_text.h" #include "group.h" +#include "train.h" #include "string_func.h" #include "strings_func.h" #include "window_func.h" #include "date_func.h" #include "vehicle_func.h" +#include "vehiclelist.h" #include "widgets/dropdown_func.h" #include "engine_gui.h" #include "cargotype.h" @@ -39,6 +41,55 @@ #include "safeguards.h" +struct CargoFilter { + CargoID type; + StringID name; +}; + +struct TrainFilter { + uint weight = 200; ///< train weight in tones + uint speed = 50; ///< train speed in km/h + uint length = 3; ///< train length in tiles +}; + + +bool operator==(const TrainFilter& a, const TrainFilter& b) +{ + return a.length == b.length && a.weight == b.weight && a.speed == b.speed; +} + +bool operator!=(const TrainFilter& a, const TrainFilter& b) +{ + return !(a == b); +} + +struct RailFilter { + bool wagon = true; + bool motor_wagon = false; + bool locomotive = false; + bool slope_used = false; + uint slope_length = 1; ///< slope length in tiles + TrainFilter train; +}; + +struct RoadFilter { + RoadTypes types; + bool wagon; + bool motor_wagon; + bool locomotive; + bool slope_used; + uint slope_length = 1; + TrainFilter train; +}; + +struct VehicleFilter { + bool descending_sort_order = false; + bool show_hidden_engines = false; ///< State of the 'show hidden engines' button. + CargoFilter cargo; + RailFilter rail; + RoadFilter road; +}; + /** * Get the height of a single 'entry' in the engine lists. * @param type the vehicle type to get the height of @@ -49,6 +100,64 @@ uint GetEngineListHeight(VehicleType type) return max(FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM, GetVehicleImageCellSize(type, EIT_PURCHASE).height); } +int SmartChangeValue(int value, int delta, int min, int max) { + int power = 1; + delta = Clamp(delta, -1, 1); + + if (value >= 8) power = 2; + if (value >= 20) power = 5; + if (value >= 40) power = 10; + if (value >= 80) power = 20; + if (value >= 200) power = 50; + if (value >= 400) power = 100; + if (value >= 800) power = 200; + if (value >= 2000) power = 500; + if (value >= 4000) power = 1000; + + int basic = value / power + delta; + return Clamp(basic * power, min, max); +} + +TrainFilter GetTrainFilterFromDepot(TileIndex tile) +{ + TrainFilter train_filter; + if (!IsRailDepotTile(tile)) { + return train_filter; + } + VehicleList engine_list; + VehicleList wagon_list; + BuildDepotVehicleList(VehicleType::VEH_TRAIN, tile, &engine_list, &wagon_list); + bool is_train_found = false; + if (!is_train_found) { + for (uint e = 0; e < engine_list.size(); e++) { + const Train* t = (Train*)engine_list[e]; + uint16 weight = 0; + for (Train* u = t->First(); u != NULL; u = u->Next()) { + if (u->GetEngine()->GetPower() != 0) continue; + weight += u->GetMaxWeight(); + is_train_found = true; + } + train_filter.weight = weight; + train_filter.length = t->GetSummaryLength() / TILE_SIZE; + train_filter.speed = t->GetDisplayMaxSpeed(); + } + } + if (!is_train_found) { + for (uint w = 0; w < wagon_list.size(); w++) { + const Train* t = (Train*)wagon_list[w]; + uint weight = t->GetSummaryMaxWeight(); + uint length = t->GetSummaryLength() / TILE_SIZE; + if (!is_train_found || train_filter.weight < weight) { + is_train_found = true; + train_filter.weight = weight; + train_filter.length = length; + train_filter.speed = t->GetDisplayMaxSpeed(); + } + } + } + return train_filter; +} + static const NWidgetPart _nested_build_vehicle_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), @@ -87,6 +196,91 @@ static const NWidgetPart _nested_build_vehicle_widgets[] = { EndContainer(), }; +static const NWidgetPart _nested_build_vehicle_smart_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_GREY), + NWidget(WWT_CAPTION, COLOUR_GREY, WID_BV_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_SHADEBOX, COLOUR_GREY), + NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), + NWidget(WWT_STICKYBOX, COLOUR_GREY), + EndContainer(), + /* Filters... */ + NWidget(NWID_HORIZONTAL), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BV_FILTER_BUTTON), SetResize(1, 0), SetFill(1, 0), SetMinimalSize(80, 20), SetDataTip(STR_BUILD_VEHICLE_FILTER, STR_BUILD_VEHICLE_FILTER_TOOLTIP), + EndContainer(), + NWidget(NWID_SELECTION, COLOUR_GREY, WID_BV_FILTER_CONTAINER), + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(NWID_VERTICAL), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BV_RAIL_WAGON), SetMinimalSize(20, 20), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_BUILD_VEHICLE_WAGONS, STR_NULL), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BV_RAIL_MOTOR_WAGON), SetMinimalSize(20, 20), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_BUILD_VEHICLE_RAILCARS, STR_NULL), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BV_RAIL_LOCOMOTIVE), SetMinimalSize(20, 20), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_BUILD_VEHICLE_LOCOMOTIVES, STR_NULL), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_TRAIN_FROM_DEPOT), SetResize(1, 0), SetFill(1, 0), SetMinimalSize(80, 20), SetDataTip(STR_BUILD_VEHICLE_AUTO_TRAIN, STR_BUILD_VEHICLE_AUTO_TRAIN_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_LABEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 0), SetMinimalSize(150, 20), SetDataTip(STR_BUILD_VEHICLE_CARGO_FILTER_LABEL, STR_BUILD_VEHICLE_CARGO_FILTER_LABEL_TOOLTIP), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_BV_CARGO_FILTER_DROPDOWN), SetResize(1, 0), SetMinimalSize(190, 20), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_FILTER_CRITERIA), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_LABEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 0), SetMinimalSize(150, 20), SetDataTip(STR_BUILD_VEHICLE_TRAIN_SPEED_LABEL, STR_BUILD_VEHICLE_TRAIN_WEIGHT_TOOLTIP), + NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_BV_RAIL_SPEED_DECREASE), SetMinimalSize(20, 20), SetDataTip(AWV_DECREASE, STR_BUILD_VEHICLE_TRAIN_SPEED_TOOLTIP), + NWidget(WWT_LABEL, COLOUR_GREY, WID_BV_RAIL_SPEED_EDIT), SetResize(1, 0), SetFill(1, 0), SetMinimalSize(150, 20), SetDataTip(STR_BUILD_VEHICLE_TRAIN_SPEED_EDITOR, STR_BUILD_VEHICLE_TRAIN_SPEED_TOOLTIP), + NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_BV_RAIL_SPEED_INCREASE), SetMinimalSize(20, 20), SetDataTip(AWV_INCREASE, STR_BUILD_VEHICLE_TRAIN_SPEED_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_LABEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 0), SetMinimalSize(150, 20), SetDataTip(STR_BUILD_VEHICLE_TRAIN_WEIGHT_LABEL, STR_BUILD_VEHICLE_TRAIN_WEIGHT_TOOLTIP), + NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_BV_RAIL_WEIGHT_DECREASE), SetMinimalSize(20, 20), SetDataTip(AWV_DECREASE, STR_BUILD_VEHICLE_TRAIN_WEIGHT_TOOLTIP), + NWidget(WWT_LABEL, COLOUR_GREY, WID_BV_RAIL_WEIGHT_EDIT), SetResize(1, 0), SetFill(1, 0), SetMinimalSize(150, 20), SetDataTip(STR_BUILD_VEHICLE_TRAIN_WEIGHT_EDITOR, STR_BUILD_VEHICLE_TRAIN_WEIGHT_TOOLTIP), + NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_BV_RAIL_WEIGHT_INCREASE), SetMinimalSize(20, 20), SetDataTip(AWV_INCREASE, STR_BUILD_VEHICLE_TRAIN_WEIGHT_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_LABEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 0), SetMinimalSize(150, 20), SetDataTip(STR_BUILD_VEHICLE_INCLINE_LABEL, STR_BUILD_VEHICLE_INCLINE_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BV_SLOPE), SetResize(1, 0), SetFill(1, 0), SetMinimalSize(190, 20), SetFill(1, 0), SetDataTip(STR_BUILD_VEHICLE_INCLINE_EDITOR, STR_BUILD_VEHICLE_INCLINE_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_LABEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 0), SetMinimalSize(150, 20), SetDataTip(STR_BUILD_VEHICLE_TRAIN_LENGTH_LABEL, STR_BUILD_VEHICLE_TRAIN_WEIGHT_TOOLTIP), + NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_BV_TRAIN_LENGTH_DECREASE), SetMinimalSize(20, 20), SetDataTip(AWV_DECREASE, STR_BUILD_VEHICLE_TRAIN_LENGTH_TOOLTIP), + NWidget(WWT_LABEL, COLOUR_GREY, WID_BV_TRAIN_LENGTH_EDIT), SetResize(1, 0), SetFill(1, 0), SetMinimalSize(150, 20), SetDataTip(STR_BUILD_VEHICLE_TRAIN_LENGTH_EDITOR, STR_BUILD_VEHICLE_TRAIN_LENGTH_TOOLTIP), + NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_BV_TRAIN_LENGTH_INCREASE), SetMinimalSize(20, 20), SetDataTip(AWV_INCREASE, STR_BUILD_VEHICLE_TRAIN_LENGTH_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_LABEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 0), SetMinimalSize(150, 20), SetDataTip(STR_BUILD_VEHICLE_SLOPE_LENGTH_LABEL, STR_BUILD_VEHICLE_TRAIN_WEIGHT_TOOLTIP), + NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_BV_SLOPE_LENGTH_DECREASE), SetMinimalSize(20, 20), SetDataTip(AWV_DECREASE, STR_BUILD_VEHICLE_SLOPE_LENGTH_TOOLTIP), + NWidget(WWT_LABEL, COLOUR_GREY, WID_BV_SLOPE_LENGTH_EDIT), SetResize(1, 0), SetFill(1, 0), SetMinimalSize(150, 20), SetDataTip(STR_BUILD_VEHICLE_SLOPE_LENGTH_EDITOR, STR_BUILD_VEHICLE_SLOPE_LENGTH_TOOLTIP), + NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_BV_SLOPE_LENGTH_INCREASE), SetMinimalSize(20, 20), SetDataTip(AWV_INCREASE, STR_BUILD_VEHICLE_SLOPE_LENGTH_TOOLTIP), + EndContainer(), + EndContainer(), + EndContainer(), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(NWID_VERTICAL), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_SORT_ASCENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_BV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA), + EndContainer(), + EndContainer(), + EndContainer(), + /* Vehicle list. */ + NWidget(NWID_HORIZONTAL), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_BV_LIST), SetResize(1, 1), SetFill(1, 0), SetMatrixDataTip(1, 0, STR_NULL), SetScrollbar(WID_BV_SCROLLBAR), + NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BV_SCROLLBAR), + EndContainer(), + /* Panel with details. */ + NWidget(WWT_PANEL, COLOUR_GREY, WID_BV_PANEL), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(), + /* Build/rename buttons, resize button. */ + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BV_BUILD_SEL), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_BUILD), SetResize(1, 0), SetFill(1, 0), + EndContainer(), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BV_SHOW_HIDDEN_ENGINES), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_SHOW_HIDE), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_NULL), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_RENAME), SetResize(1, 0), SetFill(1, 0), + NWidget(WWT_RESIZEBOX, COLOUR_GREY), + EndContainer(), +}; + /** Special cargo filter criteria */ static const CargoID CF_ANY = CT_NO_REFIT; ///< Show all vehicles independent of carried cargo (i.e. no filtering) static const CargoID CF_NONE = CT_INVALID; ///< Show only vehicles which do not carry cargo (e.g. train engines) @@ -96,6 +290,15 @@ byte _engine_sort_last_criteria[] = {0, 0, 0, 0}; ///< Las bool _engine_sort_last_order[] = {false, false, false, false}; ///< Last set direction of the sort order, for each vehicle type. bool _engine_sort_show_hidden_engines[] = {false, false, false, false}; ///< Last set 'show hidden engines' setting for each vehicle type. static CargoID _engine_sort_last_cargo_criteria[] = {CF_ANY, CF_ANY, CF_ANY, CF_ANY}; ///< Last set filter criteria, for each vehicle type. +bool _engine_use_filters = false; + +static const int INVALIDATE_FLAG_DEFAULT = 0; +static const int INVALIDATE_FLAG_ENGINE_LIST = 1; +static const int INVALIDATE_FLAG_TRAIN = 2; +static const int INVALIDATE_FLAG_POSITION = 4; +static const int INVALIDATE_FLAG_REBUILD_ENGINE_SORTERS = 8 | INVALIDATE_FLAG_ENGINE_LIST; +static const int INVALIDATE_FLAG_REBUILD_CARGO_FILTER = 16; +static const int INVALIDATE_FLAG_ALL = INVALIDATE_FLAG_ENGINE_LIST | INVALIDATE_FLAG_TRAIN | INVALIDATE_FLAG_POSITION | INVALIDATE_FLAG_REBUILD_ENGINE_SORTERS | INVALIDATE_FLAG_REBUILD_CARGO_FILTER; /** * Determines order of engines by engineID @@ -285,6 +488,84 @@ static bool EnginePowerVsRunningCostSorter(const EngineID &a, const EngineID &b) return _engine_sort_direction ? r > 0 : r < 0; } +/** + * Determines order of engines capacity by price + * @param a first engine to compare + * @param b second engine to compare + * @return for descending order: returns true if a < b. Vice versa for ascending order + */ +static bool EngineCostByCapacitySorter(const EngineID& a, const EngineID& b) +{ + const RailVehicleInfo* rvi_a = RailVehInfo(a); + const RailVehicleInfo* rvi_b = RailVehInfo(b); + + int ca = GetTotalCapacityOfArticulatedParts(a) * (rvi_a->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1); + int cb = GetTotalCapacityOfArticulatedParts(b) * (rvi_b->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1); + + const Engine* e_a = Engine::Get(a); + const Engine* e_b = Engine::Get(b); + + Money ma = e_a->GetCost(); + Money mb = e_b->GetCost(); + + /* positive or negative "mb / cb - ma / ca" eq: */ + int r = ma * cb - mb * ca; + + /* Use EngineID to sort instead since we want consistent sorting */ + if (r == 0) return EngineNumberSorter(a, b); + return _engine_sort_direction ? r > 0 : r < 0; +} + +/** + * Determines order of engines capacity by price + * @param a first engine to compare + * @param b second engine to compare + * @return for descending order: returns true if a < b. Vice versa for ascending order + */ +static bool EngineLengthByCapacitySorter(const EngineID& a, const EngineID& b) +{ + const RailVehicleInfo* rvi_a = RailVehInfo(a); + const RailVehicleInfo* rvi_b = RailVehInfo(b); + + int ca = GetTotalCapacityOfArticulatedParts(a) * (rvi_a->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1); + int cb = GetTotalCapacityOfArticulatedParts(b) * (rvi_b->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1); + + const Engine* e_a = Engine::Get(a); + const Engine* e_b = Engine::Get(b); + + int la = VEHICLE_LENGTH - e_a->u.rail.shorten_factor; + int lb = VEHICLE_LENGTH - e_b->u.rail.shorten_factor; + + /* positive or negative "mb / cb - ma / ca" eq: */ + int r = la * cb - lb * ca; + + /* Use EngineID to sort instead since we want consistent sorting */ + if (r == 0) return EngineNumberSorter(a, b); + return _engine_sort_direction ? r > 0 : r < 0; +} + +/** + * Determines order of engines capacity by price + * @param a first engine to compare + * @param b second engine to compare + * @return for descending order: returns true if a < b. Vice versa for ascending order + */ +static bool EngineLengthSorter(const EngineID& a, const EngineID& b) +{ + const Engine* e_a = Engine::Get(a); + const Engine* e_b = Engine::Get(b); + + int la = VEHICLE_LENGTH - e_a->u.rail.shorten_factor; + int lb = VEHICLE_LENGTH - e_b->u.rail.shorten_factor; + + /* positive or negative "mb / cb - ma / ca" eq: */ + int r = la - lb; + + /* Use EngineID to sort instead since we want consistent sorting */ + if (r == 0) return EngineNumberSorter(a, b); + return _engine_sort_direction ? r > 0 : r < 0; +} + /* Train sorting functions */ /** @@ -414,7 +695,7 @@ static bool AircraftRangeSorter(const EngineID &a, const EngineID &b) } /** Sort functions for the vehicle sort criteria, for each vehicle type. */ -EngList_SortTypeFunction * const _engine_sort_functions[][11] = {{ +EngList_SortTypeFunction * const _engine_sort_functions[][14] = {{ /* Trains */ &EngineNumberSorter, &EngineCostSorter, @@ -425,6 +706,9 @@ EngList_SortTypeFunction * const _engine_sort_functions[][11] = {{ &EngineNameSorter, &EngineRunningCostSorter, &EnginePowerVsRunningCostSorter, + &EngineCostByCapacitySorter, + &EngineLengthByCapacitySorter, + &EngineLengthSorter, &EngineReliabilitySorter, &TrainEngineCapacitySorter, }, { @@ -464,7 +748,7 @@ EngList_SortTypeFunction * const _engine_sort_functions[][11] = {{ }}; /** Dropdown menu strings for the vehicle sort criteria. */ -const StringID _engine_sort_listing[][12] = {{ +const StringID _engine_sort_listing[][15] = {{ /* Trains */ STR_SORT_BY_ENGINE_ID, STR_SORT_BY_COST, @@ -475,6 +759,9 @@ const StringID _engine_sort_listing[][12] = {{ STR_SORT_BY_NAME, STR_SORT_BY_RUNNING_COST, STR_SORT_BY_POWER_VS_RUNNING_COST, + STR_SORT_BY_PRICE_BY_CAPACITY, + STR_SORT_BY_LENGTH_BY_CAPACITY, + STR_SORT_BY_LENGTH, STR_SORT_BY_RELIABILITY, STR_SORT_BY_CARGO_CAPACITY, INVALID_STRING_ID @@ -518,7 +805,7 @@ const StringID _engine_sort_listing[][12] = {{ }}; /** Cargo filter functions */ -static bool CDECL CargoFilter(const EngineID *eid, const CargoID cid) +static bool CDECL CargoFilterFunc(const EngineID *eid, const CargoID cid) { if (cid == CF_ANY) return true; CargoTypes refit_mask = GetUnionOfArticulatedRefitMasks(*eid, true) & _standard_cargo_mask; @@ -526,9 +813,11 @@ static bool CDECL CargoFilter(const EngineID *eid, const CargoID cid) } static GUIEngineList::FilterFunction * const _filter_funcs[] = { - &CargoFilter, + &CargoFilterFunc, }; +VehicleFilter _engine_sort_filter[] = { VehicleFilter(), VehicleFilter(), VehicleFilter(), VehicleFilter() }; ///< Last set direction of the sort order, for each vehicle type. + static int DrawCargoCapacityInfo(int left, int right, int y, EngineID engine, TestedEngineDetails &te) { CargoArray cap; @@ -1010,6 +1299,67 @@ void DisplayVehicleSortDropDown(Window *w, VehicleType vehicle_type, int selecte ShowDropDownMenu(w, _engine_sort_listing[vehicle_type], selected, button, 0, hidden_mask); } +bool IsEnginePassFilter(const VehicleFilter& filter, const Engine* engine) { + if (!filter.show_hidden_engines && engine->IsHidden(_local_company)) return false; + if (!IsEngineBuildable(engine->index, VEH_TRAIN, _local_company)) return false; + + const RailFilter& rail_filter = filter.rail; + uint hasPower = engine->GetPower() > 0; + bool hasCapacity = engine->CanCarryCargo(); + + bool is_locomotive = !hasCapacity && hasPower; + bool is_motor_wagon = hasCapacity && hasPower; + bool is_wagon = hasCapacity && !hasPower; + + if (!is_locomotive && !is_motor_wagon && !is_wagon) { + return false; + } + if (is_locomotive && !rail_filter.locomotive) { + return false; + } + if (is_motor_wagon && !rail_filter.motor_wagon) { + return false; + } + if (is_wagon && !rail_filter.wagon) { + return false; + } + if (!is_locomotive && !CargoFilterFunc(&engine->index, filter.cargo.type)) { + return false; + } + if (is_wagon) { + // speed in km/h + uint speed = engine->GetDisplayMaxSpeed(); + + if (rail_filter.train.speed > speed) return false; + } + if (is_locomotive && rail_filter.train.weight > 0 && rail_filter.train.speed > 0) { + // speed in km/h + uint speed = engine->GetDisplayMaxSpeed(); + // power in kW + uint power = engine->GetPower() * 1000 / 735; + // weight in T + uint weight = rail_filter.train.weight + engine->GetDisplayWeight(); + // force in kN + uint tractive_effort = engine->GetDisplayMaxTractiveEffort(); + + uint length = rail_filter.train.length + (VEHICLE_LENGTH - engine->u.rail.shorten_factor); + + if (rail_filter.train.speed > speed) return false; + + uint min_tractive_effort = weight * 35 / 1000; + if (rail_filter.slope_used && rail_filter.train.length > 0) { + min_tractive_effort += _settings_game.vehicle.train_slope_steepness * weight / 10 * min(rail_filter.train.length, rail_filter.slope_length) / rail_filter.train.length; + } + + if (min_tractive_effort > tractive_effort) return false; + + uint min_power = min_tractive_effort * rail_filter.train.speed; + if (min_power > power) return false; + + } + return true; +} + /** GUI for building vehicles. */ struct BuildVehicleWindow : Window { VehicleType vehicle_type; ///< Type of vehicles shown in the window. @@ -1031,6 +1381,8 @@ struct BuildVehicleWindow : Window { Scrollbar *vscroll; TestedEngineDetails te; ///< Tested cost and capacity after refit. + BuildVehicleWidgets widget_for_query; + void SetBuyVehicleText() { NWidgetCore *widget = this->GetWidget(WID_BV_BUILD); @@ -1059,6 +1411,10 @@ struct BuildVehicleWindow : Window { this->descending_sort_order = _engine_sort_last_order[type]; this->show_hidden_engines = _engine_sort_show_hidden_engines[type]; + VehicleFilter& filter = _engine_sort_filter[this->vehicle_type]; + filter.descending_sort_order = this->descending_sort_order; + filter.show_hidden_engines = this->show_hidden_engines; + this->UpdateFilterByTile(); this->CreateNestedTree(); @@ -1101,8 +1457,20 @@ struct BuildVehicleWindow : Window { } else { this->SelectEngine(INVALID_ENGINE); } + InvalidateData(); } + + const VehicleFilter& getFilter() const { + if (this->vehicle_type < 0 || this->vehicle_type >= sizeof _engine_sort_filter) throw "Invalid vehicle type: " + this->vehicle_type; + return _engine_sort_filter[this->vehicle_type]; + }; + + VehicleFilter& setFilter() { + if (this->vehicle_type < 0 || this->vehicle_type >= sizeof _engine_sort_filter) throw "Invalid vehicle type: " + this->vehicle_type; + return _engine_sort_filter[this->vehicle_type]; + }; + /** Set the filter type according to the depot type */ void UpdateFilterByTile() { @@ -1172,9 +1540,10 @@ struct BuildVehicleWindow : Window { break; } } - - this->eng_list.SetFilterFuncs(_filter_funcs); - this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY); + CargoID cargo = this->cargo_filter[this->cargo_filter_criteria]; + this->setFilter().cargo.type = cargo; + //this->eng_list.SetFilterFuncs(_filter_funcs); + //this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY); } void SelectEngine(EngineID engine) @@ -1215,6 +1584,9 @@ struct BuildVehicleWindow : Window { void OnInit() override { this->SetCargoFilterArray(); + if (IsSmartMenu()) { + GetWidget(WID_BV_FILTER_CONTAINER)->SetDisplayedPlane(_engine_use_filters ? 0 : SZSP_NONE); + } } /** Filter the engine list against the currently selected cargo filter */ @@ -1232,7 +1604,11 @@ struct BuildVehicleWindow : Window { bool FilterSingleEngine(EngineID eid) { CargoID filter_type = this->cargo_filter[this->cargo_filter_criteria]; - return (filter_type == CF_ANY || CargoFilter(&eid, filter_type)); + return (filter_type == CF_ANY || CargoFilterFunc(&eid, filter_type)); + } + + bool IsSmartMenu() { + return (window_desc->nwid_length == lengthof(_nested_build_vehicle_smart_widgets)); } /* Figure out what train EngineIDs to put in the list */ @@ -1242,6 +1618,8 @@ struct BuildVehicleWindow : Window { int num_engines = 0; int num_wagons = 0; + const bool is_smart_menu = IsSmartMenu(); + VehicleFilter& filter = _engine_sort_filter[this->vehicle_type]; this->eng_list.clear(); /* Make list of all available train engines and wagons. @@ -1257,8 +1635,10 @@ struct BuildVehicleWindow : Window { if (this->filter.railtype != INVALID_RAILTYPE && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue; if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue; + if (is_smart_menu && _engine_use_filters && !IsEnginePassFilter(filter, e)) continue; + /* Filter now! So num_engines and num_wagons is valid */ - if (!FilterSingleEngine(eid)) continue; + if (!is_smart_menu && !FilterSingleEngine(eid)) continue; this->eng_list.push_back(eid); @@ -1389,10 +1769,94 @@ struct BuildVehicleWindow : Window { void OnClick(Point pt, int widget, int click_count) override { + VehicleFilter& filter = this->setFilter(); switch (widget) { + case WID_BV_FILTER_BUTTON: + _engine_use_filters = !_engine_use_filters; + this->ReInit(); + this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); + break; + case WID_BV_TRAIN_FROM_DEPOT: + this->InvalidateData(INVALIDATE_FLAG_TRAIN); + break; + case WID_BV_SLOPE: + filter.rail.slope_used = !filter.rail.slope_used; + this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); + break; + case WID_BV_RAIL_WEIGHT_EDIT: + SetDParam(0, filter.rail.train.weight); + this->widget_for_query = WID_BV_RAIL_WEIGHT_EDIT; + ShowQueryString(STR_JUST_INT, STR_BUILD_VEHICLE_EDIT_TRAIN_WEIGHT, 8, this, CS_NUMERAL, QSF_ENABLE_DEFAULT); + break; + case WID_BV_RAIL_WEIGHT_DECREASE: + filter.rail.train.weight = SmartChangeValue(filter.rail.train.weight, -1, 1, 50000); + this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); + break; + case WID_BV_RAIL_WEIGHT_INCREASE: + filter.rail.train.weight = SmartChangeValue(filter.rail.train.weight, 1, 1, 50000); + this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); + break; + case WID_BV_RAIL_SPEED_EDIT: + SetDParam(0, filter.rail.train.speed); + this->widget_for_query = WID_BV_RAIL_SPEED_EDIT; + ShowQueryString(STR_JUST_INT, STR_BUILD_VEHICLE_EDIT_TRAIN_SPEED, 8, this, CS_NUMERAL, QSF_ENABLE_DEFAULT); + break; + case WID_BV_RAIL_SPEED_DECREASE: + filter.rail.train.speed = SmartChangeValue(filter.rail.train.speed, -1, 1, 3000); + this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); + break; + case WID_BV_RAIL_SPEED_INCREASE: + filter.rail.train.speed = SmartChangeValue(filter.rail.train.speed, 1, 1, 3000); + this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); + break; + case WID_BV_TRAIN_LENGTH_EDIT: + SetDParam(0, filter.rail.train.length); + this->widget_for_query = WID_BV_TRAIN_LENGTH_EDIT; + ShowQueryString(STR_JUST_INT, STR_BUILD_VEHICLE_EDIT_TRAIN_LENGTH, 2, this, CS_NUMERAL, QSF_ENABLE_DEFAULT); + break; + case WID_BV_TRAIN_LENGTH_DECREASE: + filter.rail.train.length = SmartChangeValue(filter.rail.train.length, -1, 1, 99); + this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); + break; + case WID_BV_TRAIN_LENGTH_INCREASE: + filter.rail.train.length = SmartChangeValue(filter.rail.train.length, 1, 1, 99); + this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); + break; + case WID_BV_SLOPE_LENGTH_EDIT: + SetDParam(0, filter.rail.slope_length); + this->widget_for_query = WID_BV_SLOPE_LENGTH_EDIT; + ShowQueryString(STR_JUST_INT, STR_BUILD_VEHICLE_EDIT_SLOPE_LENGTH, 3, this, CS_NUMERAL, QSF_ENABLE_DEFAULT); + break; + case WID_BV_SLOPE_LENGTH_DECREASE: + filter.rail.slope_length = SmartChangeValue(filter.rail.slope_length, -1, 1, 999); + this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); + break; + case WID_BV_SLOPE_LENGTH_INCREASE: + filter.rail.slope_length = SmartChangeValue(filter.rail.slope_length, 1, 1, 999); + this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); + break; + case WID_BV_RAIL_WAGON: + filter.rail.wagon = true; + filter.rail.motor_wagon = false; + filter.rail.locomotive = false; + this->InvalidateData(INVALIDATE_FLAG_REBUILD_ENGINE_SORTERS); + break; + case WID_BV_RAIL_MOTOR_WAGON: + filter.rail.motor_wagon = true; + filter.rail.wagon = false; + filter.rail.locomotive = false; + this->InvalidateData(INVALIDATE_FLAG_REBUILD_ENGINE_SORTERS); + break; + case WID_BV_RAIL_LOCOMOTIVE: + filter.rail.locomotive = true; + filter.rail.wagon = false; + filter.rail.motor_wagon = false; + this->InvalidateData(INVALIDATE_FLAG_REBUILD_ENGINE_SORTERS); + break; case WID_BV_SORT_ASCENDING_DESCENDING: this->descending_sort_order ^= true; _engine_sort_last_order[this->vehicle_type] = this->descending_sort_order; + filter.descending_sort_order = descending_sort_order; this->eng_list.ForceRebuild(); this->SetDirty(); break; @@ -1400,6 +1864,7 @@ struct BuildVehicleWindow : Window { case WID_BV_SHOW_HIDDEN_ENGINES: this->show_hidden_engines ^= true; _engine_sort_show_hidden_engines[this->vehicle_type] = this->show_hidden_engines; + filter.show_hidden_engines = show_hidden_engines; this->eng_list.ForceRebuild(); this->SetWidgetLoweredState(widget, this->show_hidden_engines); this->SetDirty(); @@ -1450,6 +1915,7 @@ struct BuildVehicleWindow : Window { if (sel_eng != INVALID_ENGINE) { this->rename_engine = sel_eng; SetDParam(0, sel_eng); + this->widget_for_query = WID_BV_RENAME; ShowQueryString(STR_ENGINE_NAME, STR_QUERY_RENAME_TRAIN_TYPE_CAPTION + this->vehicle_type, MAX_LENGTH_ENGINE_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS); } break; @@ -1465,6 +1931,49 @@ struct BuildVehicleWindow : Window { void OnInvalidateData(int data = 0, bool gui_scope = true) override { if (!gui_scope) return; + + if (data & INVALIDATE_FLAG_TRAIN) { + if (!this->listview_mode) { + TileIndex tile = this->window_number; + this->setFilter().rail.train = GetTrainFilterFromDepot(tile); + data |= INVALIDATE_FLAG_ENGINE_LIST; + } + } + + + const VehicleFilter& filter = this->getFilter(); + if (this->IsSmartMenu()) { + if (this->vehicle_type == VehicleType::VEH_TRAIN) { + bool wagon_used = filter.rail.wagon; + bool motor_wagon_used = filter.rail.motor_wagon; + bool locomotive_used = filter.rail.locomotive; + bool slope_used = filter.rail.slope_used && filter.rail.slope_length > 0 && filter.rail.train.length > 0; + bool lock_cargo = !filter.rail.wagon && !filter.rail.motor_wagon; + bool lock_slope = !filter.rail.locomotive && !filter.rail.motor_wagon; + bool power_for_slope_used = (motor_wagon_used || locomotive_used) && slope_used; + + this->SetWidgetLoweredState(WID_BV_RAIL_WAGON, wagon_used); + this->SetWidgetLoweredState(WID_BV_RAIL_MOTOR_WAGON, motor_wagon_used); + this->SetWidgetLoweredState(WID_BV_RAIL_LOCOMOTIVE, locomotive_used); + this->SetWidgetLoweredState(WID_BV_SLOPE, slope_used); + + this->SetWidgetDisabledState(WID_BV_CARGO_FILTER_DROPDOWN, lock_cargo); + this->SetWidgetDisabledState(WID_BV_SLOPE, lock_slope); + this->SetWidgetDisabledState(WID_BV_RAIL_WEIGHT_DECREASE, !locomotive_used); + this->SetWidgetDisabledState(WID_BV_RAIL_WEIGHT_INCREASE, !locomotive_used); + this->SetWidgetDisabledState(WID_BV_RAIL_WEIGHT_EDIT, !locomotive_used); + this->SetWidgetDisabledState(WID_BV_TRAIN_LENGTH_DECREASE, !power_for_slope_used); + this->SetWidgetDisabledState(WID_BV_TRAIN_LENGTH_INCREASE, !power_for_slope_used); + this->SetWidgetDisabledState(WID_BV_TRAIN_LENGTH_EDIT, !power_for_slope_used); + this->SetWidgetDisabledState(WID_BV_SLOPE_LENGTH_DECREASE, !power_for_slope_used); + this->SetWidgetDisabledState(WID_BV_SLOPE_LENGTH_INCREASE, !power_for_slope_used); + this->SetWidgetDisabledState(WID_BV_SLOPE_LENGTH_EDIT, !power_for_slope_used); + } + this->SetWidgetLoweredState(WID_BV_FILTER_BUTTON, _engine_use_filters); + } + this->SetWidgetLoweredState(WID_BV_SHOW_HIDDEN_ENGINES, filter.show_hidden_engines); + this->SetWidgetDisabledState(WID_BV_SHOW_HIDE, this->sel_engine == INVALID_ENGINE); + /* When switching to original acceleration model for road vehicles, clear the selected sort criteria if it is not available now. */ if (this->vehicle_type == VEH_ROAD && _settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL && @@ -1478,6 +1987,26 @@ struct BuildVehicleWindow : Window { void SetStringParameters(int widget) const override { switch (widget) { + case WID_BV_SLOPE: + SetDParam(0, _settings_game.vehicle.train_slope_steepness); + break; + + case WID_BV_RAIL_WEIGHT_EDIT: + SetDParam(0, getFilter().rail.train.weight); + break; + + case WID_BV_RAIL_SPEED_EDIT: + SetDParam(0, getFilter().rail.train.speed); + break; + + case WID_BV_TRAIN_LENGTH_EDIT: + SetDParam(0, getFilter().rail.train.length); + break; + + case WID_BV_SLOPE_LENGTH_EDIT: + SetDParam(0, getFilter().rail.slope_length); + break; + case WID_BV_CAPTION: if (this->vehicle_type == VEH_TRAIN && !this->listview_mode) { const RailtypeInfo *rti = GetRailTypeInfo(this->filter.railtype); @@ -1513,6 +2042,21 @@ struct BuildVehicleWindow : Window { void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override { switch (widget) { + case WID_BV_SLOPE: + SetDParam(0, _settings_game.vehicle.train_slope_steepness); + break; + case WID_BV_RAIL_WEIGHT_EDIT: + SetDParam(0, getFilter().rail.train.weight); + break; + case WID_BV_RAIL_SPEED_EDIT: + SetDParam(0, getFilter().rail.train.speed); + break; + case WID_BV_TRAIN_LENGTH_EDIT: + SetDParam(0, getFilter().rail.train.length); + break; + case WID_BV_SLOPE_LENGTH_EDIT: + SetDParam(0, getFilter().rail.slope_length); + break; case WID_BV_LIST: resize->height = GetEngineListHeight(this->vehicle_type); size->height = 3 * resize->height; @@ -1590,8 +2134,39 @@ struct BuildVehicleWindow : Window { void OnQueryTextFinished(char *str) override { if (str == nullptr) return; + VehicleFilter& filter = _engine_sort_filter[this->vehicle_type]; - DoCommandP(0, this->rename_engine, 0, CMD_RENAME_ENGINE | CMD_MSG(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type), nullptr, str); + switch (this->widget_for_query) { + case WID_BV_RAIL_SPEED_EDIT: + if (!StrEmpty(str)) { + filter.rail.train.speed = atoi(str); + this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); + } + break; + case WID_BV_RAIL_WEIGHT_EDIT: + if (!StrEmpty(str)) { + filter.rail.train.weight = atoi(str); + this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); + } + break; + case WID_BV_TRAIN_LENGTH_EDIT: + if (!StrEmpty(str)) { + filter.rail.train.length = atoi(str); + this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); + } + break; + case WID_BV_SLOPE_LENGTH_EDIT: + if (!StrEmpty(str)) { + filter.rail.slope_length = atoi(str); + this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); + } + break; + case WID_BV_RENAME: + DoCommandP(0, this->rename_engine, 0, CMD_RENAME_ENGINE | CMD_MSG(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type), nullptr, str); + break; + default: + break; + } } void OnDropdownSelect(int widget, int index) override @@ -1610,9 +2185,13 @@ struct BuildVehicleWindow : Window { this->cargo_filter_criteria = index; _engine_sort_last_cargo_criteria[this->vehicle_type] = this->cargo_filter[this->cargo_filter_criteria]; /* deactivate filter if criteria is 'Show All', activate it otherwise */ - this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY); - this->eng_list.ForceRebuild(); - this->SelectEngine(this->sel_engine); + EngineID current = sel_engine; + CargoID cargo = this->cargo_filter[this->cargo_filter_criteria]; + this->setFilter().cargo.type = cargo; + //this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY); + //this->eng_list.ForceRebuild(); + this->InvalidateData(); + this->SelectEngine(current); } break; } @@ -1632,6 +2211,13 @@ static WindowDesc _build_vehicle_desc( _nested_build_vehicle_widgets, lengthof(_nested_build_vehicle_widgets) ); +static WindowDesc _build_vehicle_smart_desc( + WDP_AUTO, "build_vehicle_smart", 240, 268, + WC_BUILD_VEHICLE, WC_NONE, + WDF_CONSTRUCTION, + _nested_build_vehicle_smart_widgets, lengthof(_nested_build_vehicle_smart_widgets) +); + void ShowBuildVehicleWindow(TileIndex tile, VehicleType type) { /* We want to be able to open both Available Train as Available Ships, @@ -1644,5 +2230,9 @@ void ShowBuildVehicleWindow(TileIndex tile, VehicleType type) DeleteWindowById(WC_BUILD_VEHICLE, num); - new BuildVehicleWindow(&_build_vehicle_desc, tile, type); + if (_settings_client.gui.build_vehicle_smart_gui && type == VEH_TRAIN) { + new BuildVehicleWindow(&_build_vehicle_smart_desc, tile, type); + } else { + new BuildVehicleWindow(&_build_vehicle_desc, tile, type); + } } diff --git a/src/engine_gui.h b/src/engine_gui.h index e95cc13e4def..2211052a8361 100644 --- a/src/engine_gui.h +++ b/src/engine_gui.h @@ -13,6 +13,7 @@ #define ENGINE_GUI_H #include "engine_type.h" +#include "group_type.h" #include "sortlist_type.h" #include "gfx_type.h" #include "vehicle_type.h" @@ -36,10 +37,11 @@ extern bool _engine_sort_direction; extern byte _engine_sort_last_criteria[]; extern bool _engine_sort_last_order[]; extern bool _engine_sort_show_hidden_engines[]; -extern const StringID _engine_sort_listing[][12]; -extern EngList_SortTypeFunction * const _engine_sort_functions[][11]; +extern const StringID _engine_sort_listing[][15]; +extern EngList_SortTypeFunction * const _engine_sort_functions[][14]; uint GetEngineListHeight(VehicleType type); +void DrawEngineList(VehicleType type, int l, int r, int y, const GUIEngineList* eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group); void DisplayVehicleSortDropDown(Window *w, VehicleType vehicle_type, int selected, int button); #endif /* ENGINE_GUI_H */ diff --git a/src/gfx_layout.cpp b/src/gfx_layout.cpp index 74a2a77595c8..4f8b3b621f1c 100644 --- a/src/gfx_layout.cpp +++ b/src/gfx_layout.cpp @@ -208,7 +208,7 @@ class ICUParagraphLayoutFactory { LEErrorCode status = LE_NO_ERROR; /* ParagraphLayout does not copy "buff", so it must stay valid. * "runs" is copied according to the ICU source, but the documentation does not specify anything, so this might break somewhen. */ - icu::ParagraphLayout *p = new icu::ParagraphLayout(buff, length, &runs, nullptr, nullptr, nullptr, _current_text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, false, status); + icu::ParagraphLayout *p = new icu::ParagraphLayout(buff, length, &runs, nullptr, nullptr, nullptr, _current_text_dir == TD_RTL ? 1 : 0, false, status); if (status != LE_NO_ERROR) { delete p; return nullptr; diff --git a/src/lang/arabic_egypt.txt b/src/lang/arabic_egypt.txt index a2227de0ef29..cb558aa3a4ec 100644 --- a/src/lang/arabic_egypt.txt +++ b/src/lang/arabic_egypt.txt @@ -282,7 +282,7 @@ STR_SORT_BY_TIMETABLE_DELAY :تاخير جد STR_SORT_BY_FACILITY :نوع المحطة STR_SORT_BY_RATING_MAX :اعلى نسبة شحن STR_SORT_BY_RATING_MIN :اقل نسبة شحن -STR_SORT_BY_ENGINE_ID :نوع المحرك (قياسي( +STR_SORT_BY_ENGINE_ID :نوع المحرك (قياسي) STR_SORT_BY_COST :التكلفة STR_SORT_BY_POWER :الطاقة STR_SORT_BY_TRACTIVE_EFFORT :قوة الجذب @@ -1147,7 +1147,7 @@ STR_CONFIG_SETTING_INDUSTRY_DENSITY :الكثافة STR_CONFIG_SETTING_INDUSTRY_DENSITY_HELPTEXT :إختر مقدار الصناعات التي يجب تكوينها وعلى اي مستوى يجب ان تكون خلال اللعبة STR_CONFIG_SETTING_SNOWLINE_HEIGHT :ارتفاع خط الثلج: {STRING} STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :تحكم على اي ارتفاع يبدأ نزول الثلج في المناطق القطبية,تؤثر الثلوج على مستوى تطور القطاع الصناعي ونمو المدن -STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN :خشونة التضاريس (صفر التكوين فقط ) :({STRING} +STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN :خشونة التضاريس: {STRING} STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_HELPTEXT :(TerraGenesis only)إختر تكرار الهضبات: الاراض المستويه تحتوي على البضع منها,هضبات موزعه عرضيا اكثر,الاراض الوعرة تحتوي الكثير من الهضاب,التي من الممكن ان تكون متكررة STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH :ناعم جدا STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_SMOOTH :ناعم @@ -1941,7 +1941,7 @@ STR_RAIL_TOOLBAR_MAGLEV_CONSTRUCTION_CAPTION :بناء الس STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TRACK :{BLACK}بناء سكة حديد STR_RAIL_TOOLBAR_TOOLTIP_BUILD_AUTORAIL :{BLACK}بناء سكة القطار باستخدام البناء التلقائي -STR_RAIL_TOOLBAR_TOOLTIP_BUILD_TRAIN_DEPOT_FOR_BUILDING :{BLACK}بناء ورشة قطارات (لصيانة و شراء القطارات). +STR_RAIL_TOOLBAR_TOOLTIP_BUILD_TRAIN_DEPOT_FOR_BUILDING :{BLACK}بناء ورشة قطارات (لصيانة و شراء القطارات) STR_RAIL_TOOLBAR_TOOLTIP_CONVERT_RAIL_TO_WAYPOINT :{BLACK}غير السكة الى نقطة عبور STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_STATION :{BLACK}بناء محطة قطار. مفتاح كنترول يسمح بضم المحطات STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_SIGNALS :{BLACK}بناء إشارات السكك الحديدية. @@ -2023,7 +2023,7 @@ STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION :{BLACK}بناء STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOROAD :{BLACK}بناء الطرق باستخدام النظام الآلي STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM :{BLACK}بناء سكة القطار باستخدام النظام الآلي STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT :{BLACK}بناء ورشة صيانة لعربات الطرق (لشراء و صيانة العربات). -STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT :{BLACK}بناء ورشة لصيانة عربات الترام (لشراء و صيانة عربات الترام). +STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT :{BLACK}بناء ورشة لصيانة عربات الترام (لشراء و صيانة عربات الترام) STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION :{BLACK}بناء محطة باصات STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION :{BLACK}بناء محطة ركاب ترام. STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRUCK_LOADING_BAY :{BLACK}بناء محطة تحميل عربات. مفتاح كنترول يسمح بمجاورة المحطات. @@ -2586,7 +2586,8 @@ STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}ادخل # Town directory window STR_TOWN_DIRECTORY_CAPTION :{WHITE}مدن/ بلدات STR_TOWN_DIRECTORY_NONE :{ORANGE}-بدون- -STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ) {COMMA}) +STR_TOWN_DIRECTORY_TOWN :{ORANGE}{RLE}{TOWN}{BLACK} {RLM}({COMMA}) +STR_TOWN_DIRECTORY_CITY :{ORANGE}{RLE}{TOWN}{YELLOW} (مدينة){BLACK} {RLM}({COMMA}) STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}اسم المدينة - اضغط على الاسم لتوسيط الشاشة عليها. اضغط + كنترول لفتح شاشة عرض جديدة للضاحية. STR_TOWN_POPULATION :{BLACK}سكان العالم: {COMMA} @@ -2674,7 +2675,7 @@ STR_STATION_LIST_STATION :{YELLOW}{STATIO STR_STATION_LIST_WAYPOINT :{YELLOW}{WAYPOINT} STR_STATION_LIST_NONE :{YELLOW}- بدون - STR_STATION_LIST_SELECT_ALL_FACILITIES :{BLACK}اختر جميع المرافق -STR_STATION_LIST_SELECT_ALL_TYPES :{BLACK}اختر كل انواع الشحن (حتى غير المنتظرة( +STR_STATION_LIST_SELECT_ALL_TYPES :{BLACK}اختر كل انواع الشحن (حتى غير المنتظرة) STR_STATION_LIST_NO_WAITING_CARGO :{BLACK}لا يوجد اي شحنة منتظرة # Station view window @@ -2897,7 +2898,7 @@ STR_PURCHASE_INFO_SPEED_OCEAN :{BLACK}السر STR_PURCHASE_INFO_SPEED_CANAL :{BLACK}السرعة في القناة/النهر: {GOLD}{VELOCITY} STR_PURCHASE_INFO_RUNNINGCOST :{BLACK}تكلفة التشغيل: {GOLD}{CURRENCY_LONG}/ سنة STR_PURCHASE_INFO_CAPACITY :{BLACK}السعة: {GOLD}{CARGO_LONG} {STRING} -STR_PURCHASE_INFO_REFITTABLE :)قابل لتغيير( +STR_PURCHASE_INFO_REFITTABLE :(قابل لتغيي) STR_PURCHASE_INFO_DESIGNED_LIFE :{BLACK}تصميم: {GOLD}{NUM}{BLACK} العمر الافتراضي: {GOLD}{COMMA} سنة STR_PURCHASE_INFO_RELIABILITY :{BLACK}الاعتمادية القصوى: {GOLD}{COMMA}% STR_PURCHASE_INFO_COST :{BLACK}التكلفة: {GOLD}{CURRENCY_LONG} @@ -3199,7 +3200,7 @@ STR_VEHICLE_DETAILS_TRAIN_TOTAL_CARGO_TOOLTIP :{BLACK} عرض STR_VEHICLE_DETAILS_TRAIN_ARTICULATED_RV_CAPACITY :{BLACK}السعة: {LTBLUE} # Vehicle refit -STR_REFIT_CAPTION :{WHITE}{VEHICLE} )تغيير( +STR_REFIT_CAPTION :{WHITE}{VEHICLE} (تغيير) STR_REFIT_TITLE :{GOLD}اختر نوع الحمولة ... STR_REFIT_NEW_CAPACITY_COST_OF_REFIT :{BLACK}السعة الجديدة: {GOLD}{CARGO_LONG}{}{BLACK}تكلفة التغيير: {RED}{CURRENCY_LONG} STR_REFIT_NEW_CAPACITY_COST_OF_AIRCRAFT_REFIT :{BLACK}السعة الجديدة: {GOLD}{CARGO_LONG}, {GOLD}{CARGO_LONG}{}{BLACK}تكلفة اعادة التهيئة: {RED}{CURRENCY_LONG} @@ -3421,7 +3422,7 @@ STR_TIMETABLE_RESET_LATENESS :{BLACK}اعد STR_TIMETABLE_RESET_LATENESS_TOOLTIP :{BLACK}عدل وقت التاخير للعداد حتى تصل المركبة في الوقت المحدد STR_TIMETABLE_AUTOFILL :{BLACK}تهيئة تلقائية -STR_TIMETABLE_AUTOFILL_TOOLTIP :{BLACK}املأ الجدولة تلقائيا بقيم الرحلة التالية (مفتاح كنترول لمحاولة ابقائ وقت الانتظار). +STR_TIMETABLE_AUTOFILL_TOOLTIP :{BLACK}مفتاح كنترول لمحاولة ابقائ وقت الانتظار (املأ الجدولة تلقائيا بقيم الرحلة التالية) STR_TIMETABLE_EXPECTED :{BLACK}متوقع STR_TIMETABLE_SCHEDULED :{BLACK}مجدول diff --git a/src/lang/english.txt b/src/lang/english.txt index 5115a0eb5f73..c830183e2963 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -316,6 +316,8 @@ STR_SORT_BY_CARGO_CAPACITY :Cargo capacity STR_SORT_BY_RANGE :Range STR_SORT_BY_POPULATION :Population STR_SORT_BY_RATING :Rating +STR_SORT_BY_PRICE_BY_CAPACITY :Price/capacity +STR_SORT_BY_LENGTH_BY_CAPACITY :Length/capacity # Tooltips for the main toolbar STR_TOOLBAR_TOOLTIP_PAUSE_GAME :{BLACK}Pause game @@ -5128,3 +5130,36 @@ STR_PLANE :{BLACK}{PLANE} STR_SHIP :{BLACK}{SHIP} STR_TOOLBAR_RAILTYPE_VELOCITY :{STRING} ({VELOCITY}) + +STR_BUILD_VEHICLE_SMART :Smart build vehicle menu +STR_BUILD_VEHICLE_SMART_HELPTEXT :Smart build vehicle menu +STR_BUILD_VEHICLE_DETAIL :{BLACK}Detail +STR_BUILD_VEHICLE_DETAIL_TOOLTIP :{BLACK}Extended options for comfortable unit select +STR_BUILD_VEHICLE_AUTO_TRAIN :{BLACK}Take the train parameters from the from +STR_BUILD_VEHICLE_AUTO_TRAIN_TOOLTIP :{BLACK}Take parameters from the first train from the depot (maximum weight, available speed, train length) +STR_BUILD_VEHICLE_FILTER :{BLACK}Use filters... +STR_BUILD_VEHICLE_FILTER_TOOLTIP :{BLACK}Open filters panel +STR_BUILD_VEHICLE_INCLINE_LABEL :{BLACK}Allow slopes: +STR_BUILD_VEHICLE_INCLINE_EDITOR :{ORANGE}{NUM}% +STR_BUILD_VEHICLE_INCLINE_TOOLTIP :{BLACK}An opportunity to overcome the incline +STR_BUILD_VEHICLE_LOCOMOTIVES :{BLACK}Locomotives +STR_BUILD_VEHICLE_RAILCARS :{BLACK}Railcars +STR_BUILD_VEHICLE_WAGONS :{BLACK}Wagons +STR_BUILD_VEHICLE_TRAIN_WEIGHT_LABEL :{BLACK}Train weight: +STR_BUILD_VEHICLE_TRAIN_WEIGHT_EDITOR :{ORANGE}{WEIGHT_SHORT} +STR_BUILD_VEHICLE_TRAIN_WEIGHT_TOOLTIP :{BLACK}Preferred train weight (without locomotive) +STR_BUILD_VEHICLE_EDIT_TRAIN_WEIGHT :{BLACK}Enter preferred train weight +STR_BUILD_VEHICLE_CARGO_FILTER_LABEL :{BLACK}Cargo filter: +STR_BUILD_VEHICLE_CARGO_FILTER_LABEL_TOOLTIP :{BLACK}Cargo filter +STR_BUILD_VEHICLE_TRAIN_SPEED_LABEL :{BLACK}Train speed: +STR_BUILD_VEHICLE_TRAIN_SPEED_EDITOR :{ORANGE}{VELOCITY} +STR_BUILD_VEHICLE_TRAIN_SPEED_TOOLTIP :{BLACK}Preferred train speed +STR_BUILD_VEHICLE_EDIT_TRAIN_SPEED :{BLACK}Enter preferred train speed +STR_BUILD_VEHICLE_TRAIN_LENGTH_LABEL :{BLACK}Train length: +STR_BUILD_VEHICLE_TRAIN_LENGTH_EDITOR :{ORANGE}{NUM} +STR_BUILD_VEHICLE_TRAIN_LENGTH_TOOLTIP :{BLACK}Expected train length (power calculation for overcoming inclines) +STR_BUILD_VEHICLE_EDIT_TRAIN_LENGTH :{BLACK}Enter expected train length +STR_BUILD_VEHICLE_SLOPE_LENGTH_LABEL :{BLACK}Slope length: +STR_BUILD_VEHICLE_SLOPE_LENGTH_EDITOR :{ORANGE}{NUM} +STR_BUILD_VEHICLE_SLOPE_LENGTH_TOOLTIP :{BLACK}Expected slope length (power calculation for overcoming inclines) +STR_BUILD_VEHICLE_EDIT_SLOPE_LENGTH :{BLACK}Enter expected slope length diff --git a/src/music/dmusic.cpp b/src/music/dmusic.cpp index 65e2d4c8305c..7236e89f7d47 100644 --- a/src/music/dmusic.cpp +++ b/src/music/dmusic.cpp @@ -574,27 +574,16 @@ static void TransmitNotesOff(IDirectMusicBuffer *buffer, REFERENCE_TIME block_ti TransmitChannelMsg(_buffer, block_time + 10, MIDIST_CONTROLLER | ch, MIDICT_SUSTAINSW, 0); TransmitChannelMsg(_buffer, block_time + 10, MIDIST_CONTROLLER | ch, MIDICT_MODE_RESETALLCTRL, 0); } - /* Explicitly flush buffer to make sure the note off messages are processed - * before we send any additional control messages. */ + + /* Performing a GM reset stops all sound and resets all parameters. */ + TransmitStandardSysex(_buffer, block_time + 20, MidiSysexMessage::ResetGM); + TransmitStandardSysex(_buffer, block_time + 30, MidiSysexMessage::RolandSetReverb); + + /* Explicitly flush buffer to make sure the messages are processed, + * as we want sound to stop immediately. */ _port->PlayBuffer(_buffer); _buffer->Flush(); - /* Some songs change the "Pitch bend range" registered parameter. If - * this doesn't get reset, everything else will start sounding wrong. */ - for (int ch = 0; ch < 16; ch++) { - /* Running status, only need status for first message - * Select RPN 00.00, set value to 02.00, and de-select again */ - TransmitChannelMsg(_buffer, block_time + 10, MIDIST_CONTROLLER | ch, MIDICT_RPN_SELECT_LO, 0x00); - TransmitChannelMsg(_buffer, block_time + 10, MIDICT_RPN_SELECT_HI, 0x00); - TransmitChannelMsg(_buffer, block_time + 10, MIDICT_DATAENTRY, 0x02); - TransmitChannelMsg(_buffer, block_time + 10, MIDICT_DATAENTRY_LO, 0x00); - TransmitChannelMsg(_buffer, block_time + 10, MIDICT_RPN_SELECT_LO, 0x7F); - TransmitChannelMsg(_buffer, block_time + 10, MIDICT_RPN_SELECT_HI, 0x7F); - - _port->PlayBuffer(_buffer); - _buffer->Flush(); - } - /* Wait until message time has passed. */ Sleep(Clamp((block_time - cur_time) / MS_TO_REFTIME, 5, 1000)); } @@ -619,11 +608,6 @@ static void MidiThreadProc() REFERENCE_TIME cur_time; clock->GetTime(&cur_time); - /* Standard "Enable General MIDI" message */ - TransmitStandardSysex(_buffer, block_time + 20, MidiSysexMessage::ResetGM); - /* Roland-specific reverb room control, used by the original game */ - TransmitStandardSysex(_buffer, block_time + 30, MidiSysexMessage::RolandSetReverb); - _port->PlayBuffer(_buffer); _buffer->Flush(); @@ -666,7 +650,7 @@ static void MidiThreadProc() _playback.do_start = false; } - /* Turn all notes off in case we are seeking between music titles. */ + /* Reset playback device between songs. */ clock->GetTime(&cur_time); TransmitNotesOff(_buffer, block_time, cur_time); diff --git a/src/music/win32_m.cpp b/src/music/win32_m.cpp index f5360df11368..21ffe1ed3deb 100644 --- a/src/music/win32_m.cpp +++ b/src/music/win32_m.cpp @@ -36,14 +36,14 @@ static struct { std::mutex lock; ///< synchronization for playback status fields bool playing; ///< flag indicating that playback is active - bool do_start; ///< flag for starting playback of next_file at next opportunity + int do_start; ///< flag for starting playback of next_file at next opportunity bool do_stop; ///< flag for stopping playback at next opportunity byte current_volume; ///< current effective volume setting byte new_volume; ///< volume setting to change to MidiFile current_file; ///< file currently being played from PlaybackSegment current_segment; ///< segment info for current playback - size_t playback_start_time; ///< timestamp current file began playback + DWORD playback_start_time; ///< timestamp current file began playback size_t current_block; ///< next block index to send MidiFile next_file; ///< upcoming file to play PlaybackSegment next_segment; ///< segment info for upcoming file @@ -115,74 +115,85 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW std::unique_lock mutex_lock(_midi.lock, std::defer_lock); if (!mutex_lock.try_lock()) return; - { - /* check for stop */ - if (_midi.do_stop) { - DEBUG(driver, 2, "Win32-MIDI: timer: do_stop is set"); - midiOutReset(_midi.midi_out); - _midi.playing = false; - _midi.do_stop = false; + /* check for stop */ + if (_midi.do_stop) { + DEBUG(driver, 2, "Win32-MIDI: timer: do_stop is set"); + midiOutReset(_midi.midi_out); + _midi.playing = false; + _midi.do_stop = false; + return; + } + + /* check for start/restart/change song */ + if (_midi.do_start != 0) { + /* Have a delay between playback start steps, prevents jumbled-together notes at the start of song */ + if (timeGetTime() - _midi.playback_start_time < 50) { return; } + DEBUG(driver, 2, "Win32-MIDI: timer: do_start step %d", _midi.do_start); - /* check for start/restart/change song */ - if (_midi.do_start) { - DEBUG(driver, 2, "Win32-MIDI: timer: do_start is set"); - if (_midi.playing) { - midiOutReset(_midi.midi_out); - /* Some songs change the "Pitch bend range" registered - * parameter. If this doesn't get reset, everything else - * will start sounding wrong. */ - for (int ch = 0; ch < 16; ch++) { - /* Running status, only need status for first message */ - /* Select RPN 00.00, set value to 02.00, and unselect again */ - TransmitChannelMsg(MIDIST_CONTROLLER | ch, MIDICT_RPN_SELECT_LO, 0x00); - TransmitChannelMsg(MIDICT_RPN_SELECT_HI, 0x00); - TransmitChannelMsg(MIDICT_DATAENTRY, 0x02); - TransmitChannelMsg(MIDICT_DATAENTRY_LO, 0x00); - TransmitChannelMsg(MIDICT_RPN_SELECT_LO, 0x7F); - TransmitChannelMsg(MIDICT_RPN_SELECT_HI, 0x7F); - } - } + if (_midi.do_start == 1) { + /* Send "all notes off" */ + midiOutReset(_midi.midi_out); + _midi.playback_start_time = timeGetTime(); + _midi.do_start = 2; + + return; + } else if (_midi.do_start == 2) { + /* Reset the device to General MIDI defaults */ + TransmitStandardSysex(MidiSysexMessage::ResetGM); + _midi.playback_start_time = timeGetTime(); + _midi.do_start = 3; + + return; + } else if (_midi.do_start == 3) { + /* Set up device-specific effects */ + TransmitStandardSysex(MidiSysexMessage::RolandSetReverb); + _midi.playback_start_time = timeGetTime(); + _midi.do_start = 4; + + return; + } else if (_midi.do_start == 4) { + /* Load the new file */ _midi.current_file.MoveFrom(_midi.next_file); std::swap(_midi.next_segment, _midi.current_segment); _midi.current_segment.start_block = 0; _midi.playback_start_time = timeGetTime(); _midi.playing = true; - _midi.do_start = false; + _midi.do_start = 0; _midi.current_block = 0; MemSetT(_midi.channel_volumes, 127, lengthof(_midi.channel_volumes)); - } else if (!_midi.playing) { - /* not playing, stop the timer */ - DEBUG(driver, 2, "Win32-MIDI: timer: not playing, stopping timer"); - timeKillEvent(uTimerID); - _midi.timer_id = 0; - return; } + } else if (!_midi.playing) { + /* not playing, stop the timer */ + DEBUG(driver, 2, "Win32-MIDI: timer: not playing, stopping timer"); + timeKillEvent(uTimerID); + _midi.timer_id = 0; + return; + } - /* check for volume change */ - static int volume_throttle = 0; - if (_midi.current_volume != _midi.new_volume) { - if (volume_throttle == 0) { - DEBUG(driver, 2, "Win32-MIDI: timer: volume change"); - _midi.current_volume = _midi.new_volume; - volume_throttle = 20 / _midi.time_period; - for (int ch = 0; ch < 16; ch++) { - int vol = ScaleVolume(_midi.channel_volumes[ch], _midi.current_volume); - TransmitChannelMsg(MIDIST_CONTROLLER | ch, MIDICT_CHANVOLUME, vol); - } - } else { - volume_throttle--; + /* check for volume change */ + static int volume_throttle = 0; + if (_midi.current_volume != _midi.new_volume) { + if (volume_throttle == 0) { + DEBUG(driver, 2, "Win32-MIDI: timer: volume change"); + _midi.current_volume = _midi.new_volume; + volume_throttle = 20 / _midi.time_period; + for (int ch = 0; ch < 16; ch++) { + byte vol = ScaleVolume(_midi.channel_volumes[ch], _midi.current_volume); + TransmitChannelMsg(MIDIST_CONTROLLER | ch, MIDICT_CHANVOLUME, vol); } + } else { + volume_throttle--; } } /* skip beginning of file? */ if (_midi.current_segment.start > 0 && _midi.current_block == 0 && _midi.current_segment.start_block == 0) { /* find first block after start time and pretend playback started earlier - * this is to allow all blocks prior to the actual start to still affect playback, - * as they may contain important controller and program changes */ + * this is to allow all blocks prior to the actual start to still affect playback, + * as they may contain important controller and program changes */ size_t preload_bytes = 0; for (size_t bl = 0; bl < _midi.current_file.blocks.size(); bl++) { MidiFile::DataBlock &block = _midi.current_file.blocks[bl]; @@ -199,7 +210,7 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW * The delay compensation is needed to avoid time-compression of following messages. */ DEBUG(driver, 2, "Win32-MIDI: timer: start from block %d (ticktime %d, realtime %.3f, bytes %d)", (int)bl, (int)block.ticktime, ((int)block.realtime) / 1000.0, (int)preload_bytes); - _midi.playback_start_time -= block.realtime / 1000 - preload_bytes * 1000 / 3125; + _midi.playback_start_time -= block.realtime / 1000 - (DWORD)(preload_bytes * 1000 / 3125); break; } } @@ -209,7 +220,7 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW /* play pending blocks */ DWORD current_time = timeGetTime(); - size_t playback_time = current_time - _midi.playback_start_time; + DWORD playback_time = current_time - _midi.playback_start_time; while (_midi.current_block < _midi.current_file.blocks.size()) { MidiFile::DataBlock &block = _midi.current_file.blocks[_midi.current_block]; @@ -312,17 +323,21 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW void MusicDriver_Win32::PlaySong(const MusicSongInfo &song) { DEBUG(driver, 2, "Win32-MIDI: PlaySong: entry"); - std::lock_guard mutex_lock(_midi.lock); - if (!_midi.next_file.LoadSong(song)) return; + MidiFile new_song; + if (!new_song.LoadSong(song)) return; + DEBUG(driver, 2, "Win32-MIDI: PlaySong: Loaded song"); + + std::lock_guard mutex_lock(_midi.lock); + _midi.next_file.MoveFrom(new_song); _midi.next_segment.start = song.override_start; _midi.next_segment.end = song.override_end; _midi.next_segment.loop = song.loop; DEBUG(driver, 2, "Win32-MIDI: PlaySong: setting flag"); _midi.do_stop = _midi.playing; - _midi.do_start = true; + _midi.do_start = 1; if (_midi.timer_id == 0) { DEBUG(driver, 2, "Win32-MIDI: PlaySong: starting timer"); @@ -340,7 +355,7 @@ void MusicDriver_Win32::StopSong() bool MusicDriver_Win32::IsSongPlaying() { - return _midi.playing || _midi.do_start; + return _midi.playing || (_midi.do_start != 0); } void MusicDriver_Win32::SetVolume(byte vol) @@ -371,12 +386,6 @@ const char *MusicDriver_Win32::Start(const char * const *parm) midiOutReset(_midi.midi_out); - /* Standard "Enable General MIDI" message */ - TransmitStandardSysex(MidiSysexMessage::ResetGM); - - /* Roland-specific reverb room control, used by the original game */ - TransmitStandardSysex(MidiSysexMessage::RolandSetReverb); - /* prepare multimedia timer */ TIMECAPS timecaps; if (timeGetDevCaps(&timecaps, sizeof(timecaps)) == MMSYSERR_NOERROR) { diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index ed2f444cc388..42311a5cd242 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -2198,7 +2198,7 @@ bool ProcessOrders(Vehicle *v) /* If it is unchanged, keep it. */ if (order->Equals(v->current_order) && (v->type == VEH_AIRCRAFT || v->dest_tile != 0) && - (v->type != VEH_SHIP || !order->IsType(OT_GOTO_STATION) || Station::Get(order->GetDestination())->ship_station.tile != INVALID_TILE > 0)) { + (v->type != VEH_SHIP || !order->IsType(OT_GOTO_STATION) || Station::Get(order->GetDestination())->ship_station.tile != INVALID_TILE)) { return false; } diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index df862d4f2152..1a4728c6c30b 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -1578,6 +1578,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 CommandCost cost(EXPENSES_CONSTRUCTION); CommandCost error = CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); // by default, there is no track to convert. + bool found_convertible_track = false; // whether we actually did convert some track (see bug #7633) TileIterator *iter = diagonal ? (TileIterator *)new DiagonalTileIterator(area_start, area_end) : new OrthogonalTileIterator(area_start, area_end); for (; (tile = *iter) != INVALID_TILE; ++(*iter)) { @@ -1673,6 +1674,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 InvalidateWindowData(WC_VEHICLE_DEPOT, tile); InvalidateWindowData(WC_BUILD_VEHICLE, tile); } + found_convertible_track = true; cost.AddCost(RailConvertCost(type, totype)); break; @@ -1684,6 +1686,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 YapfNotifyTrackLayoutChange(tile, RemoveFirstTrack(&tracks)); } } + found_convertible_track = true; cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile))); break; } @@ -1746,6 +1749,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 } } + found_convertible_track = true; cost.AddCost((GetTunnelBridgeLength(tile, endtile) + 2) * RailConvertCost(type, totype)); break; } @@ -1756,6 +1760,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 YapfNotifyTrackLayoutChange(tile, track); } + found_convertible_track = true; cost.AddCost(RailConvertCost(type, totype)); break; } @@ -1773,7 +1778,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 } delete iter; - return (cost.GetCost() == 0) ? error : cost; + return found_convertible_track ? cost : error; } static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags) diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 87da8186ce21..4f5519c31ec6 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -2345,6 +2345,7 @@ CommandCost CmdConvertRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 CommandCost cost(EXPENSES_CONSTRUCTION); CommandCost error = CommandCost((rtt == RTT_TRAM) ? STR_ERROR_NO_SUITABLE_TRAMWAY : STR_ERROR_NO_SUITABLE_ROAD); // by default, there is no road to convert. + bool found_convertible_road = false; // whether we actually did convert any road/tram (see bug #7633) TileIterator *iter = new OrthogonalTileIterator(area_start, area_end); for (; (tile = *iter) != INVALID_TILE; ++(*iter)) { @@ -2400,6 +2401,7 @@ CommandCost CmdConvertRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 } uint num_pieces = CountBits(GetAnyRoadBits(tile, rtt));; + found_convertible_road = true; cost.AddCost(num_pieces * RoadConvertCost(from_type, to_type)); if (flags & DC_EXEC) { // we can safely convert, too @@ -2446,6 +2448,7 @@ CommandCost CmdConvertRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 /* There are 2 pieces on *every* tile of the bridge or tunnel */ uint num_pieces = (GetTunnelBridgeLength(tile, endtile) + 2) * 2; + found_convertible_road = true; cost.AddCost(num_pieces * RoadConvertCost(from_type, to_type)); if (flags & DC_EXEC) { @@ -2481,7 +2484,7 @@ CommandCost CmdConvertRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 } delete iter; - return (cost.GetCost() == 0) ? error : cost; + return found_convertible_road ? cost : error; } diff --git a/src/script/api/script_vehiclelist.cpp b/src/script/api/script_vehiclelist.cpp index 48b290caebd9..5534f6f8a597 100644 --- a/src/script/api/script_vehiclelist.cpp +++ b/src/script/api/script_vehiclelist.cpp @@ -16,6 +16,7 @@ #include "script_station.hpp" #include "../../depot_map.h" #include "../../vehicle_base.h" +#include "../../train.h" #include "../../safeguards.h" @@ -23,7 +24,7 @@ ScriptVehicleList::ScriptVehicleList() { const Vehicle *v; FOR_ALL_VEHICLES(v) { - if ((v->owner == ScriptObject::GetCompany() || ScriptObject::GetCompany() == OWNER_DEITY) && v->IsPrimaryVehicle()) this->AddItem(v->index); + if ((v->owner == ScriptObject::GetCompany() || ScriptObject::GetCompany() == OWNER_DEITY) && (v->IsPrimaryVehicle() || (v->type == VEH_TRAIN && ::Train::From(v)->IsFreeWagon()))) this->AddItem(v->index); } } diff --git a/src/script/api/script_window.hpp b/src/script/api/script_window.hpp index 902b19244a8b..5cd97b15d3e8 100644 --- a/src/script/api/script_window.hpp +++ b/src/script/api/script_window.hpp @@ -1000,6 +1000,26 @@ class ScriptWindow : public ScriptObject { WID_BV_SHOW_HIDE = ::WID_BV_SHOW_HIDE, ///< Button to hide or show the selected engine. WID_BV_BUILD_SEL = ::WID_BV_BUILD_SEL, ///< Build button. WID_BV_RENAME = ::WID_BV_RENAME, ///< Rename button. + // build vehicle smart: + WID_BV_FILTER_BUTTON = ::WID_BV_FILTER_BUTTON, ///< Show filters panel. + WID_BV_FILTER_CONTAINER = ::WID_BV_FILTER_CONTAINER, ///< Filters container. + WID_BV_RAIL_WAGON = ::WID_BV_RAIL_WAGON, ///< Show/hide wagons. + WID_BV_RAIL_MOTOR_WAGON = ::WID_BV_RAIL_MOTOR_WAGON, ///< Show/hide motor-wagons. + WID_BV_RAIL_LOCOMOTIVE = ::WID_BV_RAIL_LOCOMOTIVE, ///< Show/hide locomotive. + WID_BV_RAIL_WEIGHT_INCREASE = ::WID_BV_RAIL_WEIGHT_INCREASE, ///< Increase train weight. + WID_BV_RAIL_WEIGHT_DECREASE = ::WID_BV_RAIL_WEIGHT_DECREASE, ///< Decrease train weight. + WID_BV_RAIL_WEIGHT_EDIT = ::WID_BV_RAIL_WEIGHT_EDIT, ///< Edit train weight directly. + WID_BV_RAIL_SPEED_INCREASE = ::WID_BV_RAIL_SPEED_INCREASE, ///< Increase train speed. + WID_BV_RAIL_SPEED_DECREASE = ::WID_BV_RAIL_SPEED_DECREASE, ///< Decrease train speed. + WID_BV_RAIL_SPEED_EDIT = ::WID_BV_RAIL_SPEED_EDIT, ///< Edit train speed directly. + WID_BV_TRAIN_LENGTH_INCREASE = ::WID_BV_TRAIN_LENGTH_INCREASE, ///< Increase train length. + WID_BV_TRAIN_LENGTH_DECREASE = ::WID_BV_TRAIN_LENGTH_DECREASE, ///< Decrease train length. + WID_BV_TRAIN_LENGTH_EDIT = ::WID_BV_TRAIN_LENGTH_EDIT, ///< Edit train length directly. + WID_BV_SLOPE_LENGTH_INCREASE = ::WID_BV_SLOPE_LENGTH_INCREASE, ///< Increase slope length. + WID_BV_SLOPE_LENGTH_DECREASE = ::WID_BV_SLOPE_LENGTH_DECREASE, ///< Decrease slope length. + WID_BV_SLOPE_LENGTH_EDIT = ::WID_BV_SLOPE_LENGTH_EDIT, ///< Edit slope length directly. + WID_BV_SLOPE = ::WID_BV_SLOPE, ///< Calculate the effect of the slopes. + WID_BV_TRAIN_FROM_DEPOT = ::WID_BV_TRAIN_FROM_DEPOT, ///< Take settings from train in depot. }; /* automatically generated from ../../widgets/cheat_widget.h */ diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index bde6e05579d4..beb58a79fac4 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1552,6 +1552,7 @@ static SettingsContainer &GetSettingsTree() SettingsPage *construction = interface->Add(new SettingsPage(STR_CONFIG_SETTING_INTERFACE_CONSTRUCTION)); { + construction->Add(new SettingEntry("gui.build_vehicle_smart_gui")); construction->Add(new SettingEntry("gui.link_terraform_toolbar")); construction->Add(new SettingEntry("gui.enable_signal_gui")); construction->Add(new SettingEntry("gui.persistent_buildingtools")); diff --git a/src/settings_type.h b/src/settings_type.h index 9315cdd968c1..577938a75ffa 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -151,6 +151,8 @@ struct GUISettings { uint16 console_backlog_timeout; ///< the minimum amount of time items should be in the console backlog before they will be removed in ~3 seconds granularity. uint16 console_backlog_length; ///< the minimum amount of items in the console backlog before items will be removed. + bool build_vehicle_smart_gui; ///< build vehicle smart gui + uint8 station_gui_group_order; ///< the order of grouping cargo entries in the station gui uint8 station_gui_sort_by; ///< sort cargo entries in the station gui by station name or amount uint8 station_gui_sort_order; ///< the sort order of entries in the station gui - ascending or descending diff --git a/src/table/settings.ini b/src/table/settings.ini index 800d6f79c182..cf6a7b59164e 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -3156,6 +3156,15 @@ str = STR_CONFIG_SETTING_EXPENSES_LAYOUT strhelp = STR_CONFIG_SETTING_EXPENSES_LAYOUT_HELPTEXT proc = RedrawScreen +[SDTC_BOOL] +base = GameSettings +var = gui.build_vehicle_smart_gui +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +def = true +str = STR_BUILD_VEHICLE_SMART +strhelp = STR_BUILD_VEHICLE_SMART_HELPTEXT +cat = SC_BASIC + [SDTC_VAR] var = gui.station_gui_group_order type = SLE_UINT8 diff --git a/src/train.h b/src/train.h index e122fcf2f993..f8a3d9f5d9c0 100644 --- a/src/train.h +++ b/src/train.h @@ -174,6 +174,68 @@ struct Train FINAL : public GroundVehicle { * vehicle. */ return this->gcache.cached_veh_length / 2 + (this->Next() != nullptr ? this->Next()->gcache.cached_veh_length + 1 : 0) / 2; } + /** + * @return max weight (with cargo) of all train + */ + uint16 GetSummaryMaxWeight() const + { + uint16 weight = 0; + for (Train* u = this->First(); u != NULL; u = u->Next()) { + weight += u->GetMaxWeight(); + } + return weight; + } + + /** + * @return current length of all train in tile units. + */ + uint16 GetSummaryLength() const + { + uint16 length = 0; + for (Train* u = this->First(); u != NULL; u = u->Next()) { + length += u->GetLength(); + } + return length; + } + + /** + * @return current weight of all train + */ + uint16 GetSummaryWeight() const + { + uint16 weight = 0; + for (Train* u = this->First(); u != NULL; u = u->Next()) { + weight += u->GetWeight(); + } + return weight; + } + + /** + * @return length value from the engine in tile units. + */ + inline uint16 GetLength() const + { + return VEHICLE_LENGTH - this->GetEngine()->u.rail.shorten_factor; + } + + /** + * Allows to know the weight value that this vehicle will use. + * @return Weight value from the engine in tonnes. + */ + inline uint16 GetWeight() const + { + return this->GetCustomWeight(this->cargo.StoredCount()); + } + + inline uint16 GetMinWeight() const + { + return this->GetCustomWeight(0); + } + + inline uint16 GetMaxWeight() const + { + return this->GetCustomWeight(this->cargo_cap); + } protected: // These functions should not be called outside acceleration code. @@ -212,9 +274,9 @@ struct Train FINAL : public GroundVehicle { * Allows to know the weight value that this vehicle will use. * @return Weight value from the engine in tonnes. */ - inline uint16 GetWeight() const + uint16 GetCustomWeight(uint16 cargo_amount) const { - uint16 weight = (CargoSpec::Get(this->cargo_type)->weight * this->cargo.StoredCount() * FreightWagonMult(this->cargo_type)) / 16; + uint16 weight = (CargoSpec::Get(this->cargo_type)->weight * cargo_amount * FreightWagonMult(this->cargo_type)) / 16; /* Vehicle weight is not added for articulated parts. */ if (!this->IsArticulatedPart()) { diff --git a/src/widgets/build_vehicle_widget.h b/src/widgets/build_vehicle_widget.h index ae548587e2f9..b39bdecaf3d0 100644 --- a/src/widgets/build_vehicle_widget.h +++ b/src/widgets/build_vehicle_widget.h @@ -26,6 +26,26 @@ enum BuildVehicleWidgets { WID_BV_SHOW_HIDE, ///< Button to hide or show the selected engine. WID_BV_BUILD_SEL, ///< Build button. WID_BV_RENAME, ///< Rename button. + /* build vehicle smart: */ + WID_BV_FILTER_BUTTON, ///< Show filters panel. + WID_BV_FILTER_CONTAINER, ///< Filters container. + WID_BV_RAIL_WAGON, ///< Show/hide wagons. + WID_BV_RAIL_MOTOR_WAGON, ///< Show/hide motor-wagons. + WID_BV_RAIL_LOCOMOTIVE, ///< Show/hide locomotive. + WID_BV_RAIL_WEIGHT_INCREASE, ///< Increase train weight. + WID_BV_RAIL_WEIGHT_DECREASE, ///< Decrease train weight. + WID_BV_RAIL_WEIGHT_EDIT, ///< Edit train weight directly. + WID_BV_RAIL_SPEED_INCREASE, ///< Increase train speed. + WID_BV_RAIL_SPEED_DECREASE, ///< Decrease train speed. + WID_BV_RAIL_SPEED_EDIT, ///< Edit train speed directly. + WID_BV_TRAIN_LENGTH_INCREASE, ///< Increase train length. + WID_BV_TRAIN_LENGTH_DECREASE, ///< Decrease train length. + WID_BV_TRAIN_LENGTH_EDIT, ///< Edit train length directly. + WID_BV_SLOPE_LENGTH_INCREASE, ///< Increase slope length. + WID_BV_SLOPE_LENGTH_DECREASE, ///< Decrease slope length. + WID_BV_SLOPE_LENGTH_EDIT, ///< Edit slope length directly. + WID_BV_SLOPE, ///< Calculate the effect of the slopes. + WID_BV_TRAIN_FROM_DEPOT, ///< Take settings from train in depot. }; #endif /* WIDGETS_BUILD_VEHICLE_WIDGET_H */ diff --git a/src/widgets/dropdown.cpp b/src/widgets/dropdown.cpp index 844c6470ee8a..375f978ea2ff 100644 --- a/src/widgets/dropdown.cpp +++ b/src/widgets/dropdown.cpp @@ -482,6 +482,19 @@ void ShowDropDownList(Window *w, DropDownList &&list, int selected, int button, ShowDropDownListAt(w, std::move(list), selected, button, wi_rect, wi_colour, auto_width, instant_close); } +void ShowDropDownMenu(Window* w, const std::vector& strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask, uint width) +{ + DropDownList list; + + for (uint i = 0; i < strings.size(); i++) { + if (!HasBit(hidden_mask, i)) { + list.emplace_back(new DropDownListStringItem(strings[i], i, HasBit(disabled_mask, i))); + } + } + + if (!list.empty()) ShowDropDownList(w, std::move(list), selected, button, width); +} + /** * Show a dropdown menu window near a widget of the parent window. * The result code of the items is their index in the \a strings list. @@ -495,17 +508,14 @@ void ShowDropDownList(Window *w, DropDownList &&list, int selected, int button, */ void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask, uint width) { - DropDownList list; - + std::vector vec; for (uint i = 0; strings[i] != INVALID_STRING_ID; i++) { - if (!HasBit(hidden_mask, i)) { - list.emplace_back(new DropDownListStringItem(strings[i], i, HasBit(disabled_mask, i))); - } + vec.push_back(strings[i]); } - - if (!list.empty()) ShowDropDownList(w, std::move(list), selected, button, width); + ShowDropDownMenu(w, vec, selected, button, disabled_mask, hidden_mask, width); } + /** * Delete the drop-down menu from window \a pw * @param pw Parent window of the drop-down menu window diff --git a/src/widgets/dropdown_func.h b/src/widgets/dropdown_func.h index 4c7e134562b9..aeb2bba11446 100644 --- a/src/widgets/dropdown_func.h +++ b/src/widgets/dropdown_func.h @@ -16,6 +16,7 @@ /* Show drop down menu containing a fixed list of strings */ void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask, uint width = 0); +void ShowDropDownMenu(Window* w, const std::vector& strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask, uint width = 0); /* Hide drop down menu of a parent window */ int HideDropDownMenu(Window *pw); From b9134e9d105829d556168c4bc241a52acf0b2f6e Mon Sep 17 00:00:00 2001 From: casper Date: Mon, 22 Jul 2019 13:44:45 +0300 Subject: [PATCH 2/2] Fix: Improve vehicle menu --- src/build_vehicle_gui.cpp | 166 +++++++++++++++++--------------------- src/lang/english.txt | 4 +- 2 files changed, 75 insertions(+), 95 deletions(-) diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index c7ce54779914..9e993006f4dc 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -83,7 +83,8 @@ struct RoadFilter { }; struct VehicleFilter { - bool descending_sort_order = false; + RailType railtype; ///< Rail type to show, or #INVALID_RAILTYPE. + RoadType roadtype; ///< Road type to show, or #INVALID_ROADTYPE. bool show_hidden_engines = false; ///< State of the 'show hidden engines' button. CargoFilter cargo; RailFilter rail; @@ -104,15 +105,15 @@ int SmartChangeValue(int value, int delta, int min, int max) { int power = 1; delta = Clamp(delta, -1, 1); - if (value >= 8) power = 2; - if (value >= 20) power = 5; - if (value >= 40) power = 10; - if (value >= 80) power = 20; - if (value >= 200) power = 50; - if (value >= 400) power = 100; - if (value >= 800) power = 200; - if (value >= 2000) power = 500; - if (value >= 4000) power = 1000; + if (value >= 10) power = 1; + if (value >= 20) power = 2; + if (value >= 40) power = 5; + if (value >= 80) power = 10; + if (value >= 200) power = 20; + if (value >= 400) power = 50; + if (value >= 800) power = 100; + if (value >= 2000) power = 200; + if (value >= 4000) power = 500; int basic = value / power + delta; return Clamp(basic * power, min, max); @@ -200,14 +201,12 @@ static const NWidgetPart _nested_build_vehicle_smart_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), NWidget(WWT_CAPTION, COLOUR_GREY, WID_BV_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BV_FILTER_BUTTON), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_BUILD_VEHICLE_FILTER, STR_BUILD_VEHICLE_FILTER_TOOLTIP), NWidget(WWT_SHADEBOX, COLOUR_GREY), NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), NWidget(WWT_STICKYBOX, COLOUR_GREY), EndContainer(), - /* Filters... */ - NWidget(NWID_HORIZONTAL), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BV_FILTER_BUTTON), SetResize(1, 0), SetFill(1, 0), SetMinimalSize(80, 20), SetDataTip(STR_BUILD_VEHICLE_FILTER, STR_BUILD_VEHICLE_FILTER_TOOLTIP), - EndContainer(), + /* Filters container... */ NWidget(NWID_SELECTION, COLOUR_GREY, WID_BV_FILTER_CONTAINER), NWidget(WWT_PANEL, COLOUR_GREY), NWidget(NWID_VERTICAL), @@ -1328,9 +1327,8 @@ bool IsEnginePassFilter(const VehicleFilter& filter, const Engine* engine) { } if (is_wagon) { // speed in km/h - uint speed = engine->GetDisplayMaxSpeed(); - - if (rail_filter.train.speed > speed) return false; + uint max_speed = engine->GetDisplayMaxSpeed(); + if (max_speed != 0 && rail_filter.train.speed > max_speed) return false; } if (is_locomotive && rail_filter.train.weight > 0 && rail_filter.train.speed > 0) { // speed in km/h @@ -1363,13 +1361,9 @@ bool IsEnginePassFilter(const VehicleFilter& filter, const Engine* engine) { /** GUI for building vehicles. */ struct BuildVehicleWindow : Window { VehicleType vehicle_type; ///< Type of vehicles shown in the window. - union { - RailType railtype; ///< Rail type to show, or #INVALID_RAILTYPE. - RoadType roadtype; ///< Road type to show, or #INVALID_ROADTYPE. - } filter; ///< Filter to apply. + VehicleFilter filter; ///< Smart filter to apply. bool descending_sort_order; ///< Sort direction, @see _engine_sort_direction byte sort_criteria; ///< Current sort criterium. - bool show_hidden_engines; ///< State of the 'show hidden engines' button. bool listview_mode; ///< If set, only display the available vehicles and do not show a 'build' button. EngineID sel_engine; ///< Currently selected engine, or #INVALID_ENGINE EngineID rename_engine; ///< Engine being renamed. @@ -1409,11 +1403,10 @@ struct BuildVehicleWindow : Window { this->sort_criteria = _engine_sort_last_criteria[type]; this->descending_sort_order = _engine_sort_last_order[type]; - this->show_hidden_engines = _engine_sort_show_hidden_engines[type]; - VehicleFilter& filter = _engine_sort_filter[this->vehicle_type]; - filter.descending_sort_order = this->descending_sort_order; - filter.show_hidden_engines = this->show_hidden_engines; + /* Load filter for this vehicle (copy) */ + this->filter = _engine_sort_filter[this->vehicle_type]; + this->filter.show_hidden_engines = _engine_sort_show_hidden_engines[type]; this->UpdateFilterByTile(); @@ -1441,7 +1434,7 @@ struct BuildVehicleWindow : Window { widget = this->GetWidget(WID_BV_SHOW_HIDDEN_ENGINES); widget->widget_data = STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN + type; widget->tool_tip = STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP + type; - widget->SetLowered(this->show_hidden_engines); + widget->SetLowered(this->filter.show_hidden_engines); this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9) * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; @@ -1460,17 +1453,6 @@ struct BuildVehicleWindow : Window { InvalidateData(); } - - const VehicleFilter& getFilter() const { - if (this->vehicle_type < 0 || this->vehicle_type >= sizeof _engine_sort_filter) throw "Invalid vehicle type: " + this->vehicle_type; - return _engine_sort_filter[this->vehicle_type]; - }; - - VehicleFilter& setFilter() { - if (this->vehicle_type < 0 || this->vehicle_type >= sizeof _engine_sort_filter) throw "Invalid vehicle type: " + this->vehicle_type; - return _engine_sort_filter[this->vehicle_type]; - }; - /** Set the filter type according to the depot type */ void UpdateFilterByTile() { @@ -1541,7 +1523,7 @@ struct BuildVehicleWindow : Window { } } CargoID cargo = this->cargo_filter[this->cargo_filter_criteria]; - this->setFilter().cargo.type = cargo; + this->filter.cargo.type = cargo; //this->eng_list.SetFilterFuncs(_filter_funcs); //this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY); } @@ -1619,7 +1601,6 @@ struct BuildVehicleWindow : Window { int num_wagons = 0; const bool is_smart_menu = IsSmartMenu(); - VehicleFilter& filter = _engine_sort_filter[this->vehicle_type]; this->eng_list.clear(); /* Make list of all available train engines and wagons. @@ -1628,14 +1609,14 @@ struct BuildVehicleWindow : Window { * when engines become obsolete and are removed */ const Engine *e; FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) { - if (!this->show_hidden_engines && e->IsHidden(_local_company)) continue; + if (!this->filter.show_hidden_engines && e->IsHidden(_local_company)) continue; EngineID eid = e->index; const RailVehicleInfo *rvi = &e->u.rail; if (this->filter.railtype != INVALID_RAILTYPE && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue; if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue; - if (is_smart_menu && _engine_use_filters && !IsEnginePassFilter(filter, e)) continue; + if (is_smart_menu && _engine_use_filters && !IsEnginePassFilter(this->filter, e)) continue; /* Filter now! So num_engines and num_wagons is valid */ if (!is_smart_menu && !FilterSingleEngine(eid)) continue; @@ -1674,7 +1655,7 @@ struct BuildVehicleWindow : Window { const Engine *e; FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) { - if (!this->show_hidden_engines && e->IsHidden(_local_company)) continue; + if (!this->filter.show_hidden_engines && e->IsHidden(_local_company)) continue; EngineID eid = e->index; if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue; if (this->filter.roadtype != INVALID_ROADTYPE && !HasPowerOnRoad(e->u.road.roadtype, this->filter.roadtype)) continue; @@ -1694,7 +1675,7 @@ struct BuildVehicleWindow : Window { const Engine *e; FOR_ALL_ENGINES_OF_TYPE(e, VEH_SHIP) { - if (!this->show_hidden_engines && e->IsHidden(_local_company)) continue; + if (!this->filter.show_hidden_engines && e->IsHidden(_local_company)) continue; EngineID eid = e->index; if (!IsEngineBuildable(eid, VEH_SHIP, _local_company)) continue; this->eng_list.push_back(eid); @@ -1719,7 +1700,7 @@ struct BuildVehicleWindow : Window { * when planes become obsolete and are removed */ const Engine *e; FOR_ALL_ENGINES_OF_TYPE(e, VEH_AIRCRAFT) { - if (!this->show_hidden_engines && e->IsHidden(_local_company)) continue; + if (!this->filter.show_hidden_engines && e->IsHidden(_local_company)) continue; EngineID eid = e->index; if (!IsEngineBuildable(eid, VEH_AIRCRAFT, _local_company)) continue; /* First VEH_END window_numbers are fake to allow a window open for all different types at once */ @@ -1740,6 +1721,11 @@ struct BuildVehicleWindow : Window { /* Update filter type in case the road/railtype of the depot got converted */ this->UpdateFilterByTile(); + /* Save filter for this vehicle (copy) */ + _engine_sort_filter[this->vehicle_type] = this->filter; + _engine_sort_show_hidden_engines[this->vehicle_type] = this->filter.show_hidden_engines; + _engine_sort_direction = this->descending_sort_order; + switch (this->vehicle_type) { default: NOT_REACHED(); case VEH_TRAIN: @@ -1760,7 +1746,6 @@ struct BuildVehicleWindow : Window { this->FilterEngineList(); - _engine_sort_direction = this->descending_sort_order; EngList_Sort(&this->eng_list, _engine_sort_functions[this->vehicle_type][this->sort_criteria]); this->eng_list.shrink_to_fit(); @@ -1769,7 +1754,6 @@ struct BuildVehicleWindow : Window { void OnClick(Point pt, int widget, int click_count) override { - VehicleFilter& filter = this->setFilter(); switch (widget) { case WID_BV_FILTER_BUTTON: _engine_use_filters = !_engine_use_filters; @@ -1780,33 +1764,33 @@ struct BuildVehicleWindow : Window { this->InvalidateData(INVALIDATE_FLAG_TRAIN); break; case WID_BV_SLOPE: - filter.rail.slope_used = !filter.rail.slope_used; + this->filter.rail.slope_used = !this->filter.rail.slope_used; this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); break; case WID_BV_RAIL_WEIGHT_EDIT: - SetDParam(0, filter.rail.train.weight); + SetDParam(0, this->filter.rail.train.weight); this->widget_for_query = WID_BV_RAIL_WEIGHT_EDIT; ShowQueryString(STR_JUST_INT, STR_BUILD_VEHICLE_EDIT_TRAIN_WEIGHT, 8, this, CS_NUMERAL, QSF_ENABLE_DEFAULT); break; case WID_BV_RAIL_WEIGHT_DECREASE: - filter.rail.train.weight = SmartChangeValue(filter.rail.train.weight, -1, 1, 50000); + this->filter.rail.train.weight = SmartChangeValue(this->filter.rail.train.weight, -1, 1, 50000); this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); break; case WID_BV_RAIL_WEIGHT_INCREASE: - filter.rail.train.weight = SmartChangeValue(filter.rail.train.weight, 1, 1, 50000); + this->filter.rail.train.weight = SmartChangeValue(this->filter.rail.train.weight, 1, 1, 50000); this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); break; case WID_BV_RAIL_SPEED_EDIT: - SetDParam(0, filter.rail.train.speed); + SetDParam(0, this->filter.rail.train.speed); this->widget_for_query = WID_BV_RAIL_SPEED_EDIT; ShowQueryString(STR_JUST_INT, STR_BUILD_VEHICLE_EDIT_TRAIN_SPEED, 8, this, CS_NUMERAL, QSF_ENABLE_DEFAULT); break; case WID_BV_RAIL_SPEED_DECREASE: - filter.rail.train.speed = SmartChangeValue(filter.rail.train.speed, -1, 1, 3000); + this->filter.rail.train.speed = SmartChangeValue(this->filter.rail.train.speed, -1, 1, 3000); this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); break; case WID_BV_RAIL_SPEED_INCREASE: - filter.rail.train.speed = SmartChangeValue(filter.rail.train.speed, 1, 1, 3000); + this->filter.rail.train.speed = SmartChangeValue(this->filter.rail.train.speed, 1, 1, 3000); this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); break; case WID_BV_TRAIN_LENGTH_EDIT: @@ -1828,45 +1812,43 @@ struct BuildVehicleWindow : Window { ShowQueryString(STR_JUST_INT, STR_BUILD_VEHICLE_EDIT_SLOPE_LENGTH, 3, this, CS_NUMERAL, QSF_ENABLE_DEFAULT); break; case WID_BV_SLOPE_LENGTH_DECREASE: - filter.rail.slope_length = SmartChangeValue(filter.rail.slope_length, -1, 1, 999); + this->filter.rail.slope_length = SmartChangeValue(this->filter.rail.slope_length, -1, 1, 999); this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); break; case WID_BV_SLOPE_LENGTH_INCREASE: - filter.rail.slope_length = SmartChangeValue(filter.rail.slope_length, 1, 1, 999); + this->filter.rail.slope_length = SmartChangeValue(this->filter.rail.slope_length, 1, 1, 999); this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); break; case WID_BV_RAIL_WAGON: - filter.rail.wagon = true; - filter.rail.motor_wagon = false; - filter.rail.locomotive = false; + this->filter.rail.wagon = true; + this->filter.rail.motor_wagon = false; + this->filter.rail.locomotive = false; this->InvalidateData(INVALIDATE_FLAG_REBUILD_ENGINE_SORTERS); break; case WID_BV_RAIL_MOTOR_WAGON: - filter.rail.motor_wagon = true; - filter.rail.wagon = false; - filter.rail.locomotive = false; + this->filter.rail.motor_wagon = true; + this->filter.rail.wagon = false; + this->filter.rail.locomotive = false; this->InvalidateData(INVALIDATE_FLAG_REBUILD_ENGINE_SORTERS); break; case WID_BV_RAIL_LOCOMOTIVE: - filter.rail.locomotive = true; - filter.rail.wagon = false; - filter.rail.motor_wagon = false; + this->filter.rail.locomotive = true; + this->filter.rail.wagon = false; + this->filter.rail.motor_wagon = false; this->InvalidateData(INVALIDATE_FLAG_REBUILD_ENGINE_SORTERS); break; case WID_BV_SORT_ASCENDING_DESCENDING: this->descending_sort_order ^= true; _engine_sort_last_order[this->vehicle_type] = this->descending_sort_order; - filter.descending_sort_order = descending_sort_order; + this->descending_sort_order = descending_sort_order; this->eng_list.ForceRebuild(); this->SetDirty(); break; case WID_BV_SHOW_HIDDEN_ENGINES: - this->show_hidden_engines ^= true; - _engine_sort_show_hidden_engines[this->vehicle_type] = this->show_hidden_engines; - filter.show_hidden_engines = show_hidden_engines; + this->filter.show_hidden_engines ^= true; this->eng_list.ForceRebuild(); - this->SetWidgetLoweredState(widget, this->show_hidden_engines); + this->SetWidgetLoweredState(widget, this->filter.show_hidden_engines); this->SetDirty(); break; @@ -1935,21 +1917,20 @@ struct BuildVehicleWindow : Window { if (data & INVALIDATE_FLAG_TRAIN) { if (!this->listview_mode) { TileIndex tile = this->window_number; - this->setFilter().rail.train = GetTrainFilterFromDepot(tile); + this->filter.rail.train = GetTrainFilterFromDepot(tile); data |= INVALIDATE_FLAG_ENGINE_LIST; } } - const VehicleFilter& filter = this->getFilter(); if (this->IsSmartMenu()) { if (this->vehicle_type == VehicleType::VEH_TRAIN) { - bool wagon_used = filter.rail.wagon; - bool motor_wagon_used = filter.rail.motor_wagon; - bool locomotive_used = filter.rail.locomotive; - bool slope_used = filter.rail.slope_used && filter.rail.slope_length > 0 && filter.rail.train.length > 0; - bool lock_cargo = !filter.rail.wagon && !filter.rail.motor_wagon; - bool lock_slope = !filter.rail.locomotive && !filter.rail.motor_wagon; + bool wagon_used = this->filter.rail.wagon; + bool motor_wagon_used = this->filter.rail.motor_wagon; + bool locomotive_used = this->filter.rail.locomotive; + bool slope_used = this->filter.rail.slope_used && this->filter.rail.slope_length > 0 && this->filter.rail.train.length > 0; + bool lock_cargo = !this->filter.rail.wagon && !this->filter.rail.motor_wagon; + bool lock_slope = !this->filter.rail.locomotive && !this->filter.rail.motor_wagon; bool power_for_slope_used = (motor_wagon_used || locomotive_used) && slope_used; this->SetWidgetLoweredState(WID_BV_RAIL_WAGON, wagon_used); @@ -1971,7 +1952,7 @@ struct BuildVehicleWindow : Window { } this->SetWidgetLoweredState(WID_BV_FILTER_BUTTON, _engine_use_filters); } - this->SetWidgetLoweredState(WID_BV_SHOW_HIDDEN_ENGINES, filter.show_hidden_engines); + this->SetWidgetLoweredState(WID_BV_SHOW_HIDDEN_ENGINES, this->filter.show_hidden_engines); this->SetWidgetDisabledState(WID_BV_SHOW_HIDE, this->sel_engine == INVALID_ENGINE); /* When switching to original acceleration model for road vehicles, clear the selected sort criteria if it is not available now. */ @@ -1992,19 +1973,19 @@ struct BuildVehicleWindow : Window { break; case WID_BV_RAIL_WEIGHT_EDIT: - SetDParam(0, getFilter().rail.train.weight); + SetDParam(0, this->filter.rail.train.weight); break; case WID_BV_RAIL_SPEED_EDIT: - SetDParam(0, getFilter().rail.train.speed); + SetDParam(0, this->filter.rail.train.speed); break; case WID_BV_TRAIN_LENGTH_EDIT: - SetDParam(0, getFilter().rail.train.length); + SetDParam(0, this->filter.rail.train.length); break; case WID_BV_SLOPE_LENGTH_EDIT: - SetDParam(0, getFilter().rail.slope_length); + SetDParam(0, this->filter.rail.slope_length); break; case WID_BV_CAPTION: @@ -2046,16 +2027,16 @@ struct BuildVehicleWindow : Window { SetDParam(0, _settings_game.vehicle.train_slope_steepness); break; case WID_BV_RAIL_WEIGHT_EDIT: - SetDParam(0, getFilter().rail.train.weight); + SetDParam(0, this->filter.rail.train.weight); break; case WID_BV_RAIL_SPEED_EDIT: - SetDParam(0, getFilter().rail.train.speed); + SetDParam(0, this->filter.rail.train.speed); break; case WID_BV_TRAIN_LENGTH_EDIT: - SetDParam(0, getFilter().rail.train.length); + SetDParam(0, this->filter.rail.train.length); break; case WID_BV_SLOPE_LENGTH_EDIT: - SetDParam(0, getFilter().rail.slope_length); + SetDParam(0, this->filter.rail.slope_length); break; case WID_BV_LIST: resize->height = GetEngineListHeight(this->vehicle_type); @@ -2134,30 +2115,29 @@ struct BuildVehicleWindow : Window { void OnQueryTextFinished(char *str) override { if (str == nullptr) return; - VehicleFilter& filter = _engine_sort_filter[this->vehicle_type]; switch (this->widget_for_query) { case WID_BV_RAIL_SPEED_EDIT: if (!StrEmpty(str)) { - filter.rail.train.speed = atoi(str); + this->filter.rail.train.speed = atoi(str); this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); } break; case WID_BV_RAIL_WEIGHT_EDIT: if (!StrEmpty(str)) { - filter.rail.train.weight = atoi(str); + this->filter.rail.train.weight = atoi(str); this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); } break; case WID_BV_TRAIN_LENGTH_EDIT: if (!StrEmpty(str)) { - filter.rail.train.length = atoi(str); + this->filter.rail.train.length = atoi(str); this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); } break; case WID_BV_SLOPE_LENGTH_EDIT: if (!StrEmpty(str)) { - filter.rail.slope_length = atoi(str); + this->filter.rail.slope_length = atoi(str); this->InvalidateData(INVALIDATE_FLAG_ENGINE_LIST); } break; @@ -2187,7 +2167,7 @@ struct BuildVehicleWindow : Window { /* deactivate filter if criteria is 'Show All', activate it otherwise */ EngineID current = sel_engine; CargoID cargo = this->cargo_filter[this->cargo_filter_criteria]; - this->setFilter().cargo.type = cargo; + this->filter.cargo.type = cargo; //this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY); //this->eng_list.ForceRebuild(); this->InvalidateData(); diff --git a/src/lang/english.txt b/src/lang/english.txt index c830183e2963..74ba386bbbc1 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5135,9 +5135,9 @@ STR_BUILD_VEHICLE_SMART :Smart build veh STR_BUILD_VEHICLE_SMART_HELPTEXT :Smart build vehicle menu STR_BUILD_VEHICLE_DETAIL :{BLACK}Detail STR_BUILD_VEHICLE_DETAIL_TOOLTIP :{BLACK}Extended options for comfortable unit select -STR_BUILD_VEHICLE_AUTO_TRAIN :{BLACK}Take the train parameters from the from +STR_BUILD_VEHICLE_AUTO_TRAIN :{BLACK}Take the train parameters from the depot STR_BUILD_VEHICLE_AUTO_TRAIN_TOOLTIP :{BLACK}Take parameters from the first train from the depot (maximum weight, available speed, train length) -STR_BUILD_VEHICLE_FILTER :{BLACK}Use filters... +STR_BUILD_VEHICLE_FILTER :{BLACK}Filter STR_BUILD_VEHICLE_FILTER_TOOLTIP :{BLACK}Open filters panel STR_BUILD_VEHICLE_INCLINE_LABEL :{BLACK}Allow slopes: STR_BUILD_VEHICLE_INCLINE_EDITOR :{ORANGE}{NUM}%