bjarni@4800: /* $Id$ */ bjarni@4800: rubidium@9111: /** @file build_vehicle_gui.cpp GUI for building vehicles. */ belugas@6123: bjarni@4800: #include "stdafx.h" bjarni@4800: #include "openttd.h" bjarni@5779: #include "train.h" bjarni@5786: #include "roadveh.h" bjarni@5783: #include "ship.h" bjarni@4800: #include "aircraft.h" bjarni@4800: #include "debug.h" bjarni@4800: #include "gui.h" maedhros@6772: #include "articulated_vehicles.h" rubidium@8107: #include "textbuf_gui.h" rubidium@8116: #include "command_func.h" rubidium@10208: #include "company_func.h" bjarni@4800: #include "airport.h" bjarni@4800: #include "vehicle_gui.h" bjarni@4800: #include "newgrf_engine.h" peter1138@6091: #include "cargotype.h" rubidium@6643: #include "group.h" rubidium@6685: #include "road_map.h" rubidium@8114: #include "strings_func.h" rubidium@8131: #include "window_func.h" rubidium@8140: #include "date_func.h" rubidium@8144: #include "vehicle_func.h" rubidium@8211: #include "settings_type.h" rubidium@8224: #include "gfx_func.h" peter1138@8284: #include "widgets/dropdown_func.h" rubidium@9164: #include "string_func.h" rubidium@9274: #include "window_gui.h" peter1138@9380: #include "engine_gui.h" bjarni@4800: rubidium@8264: #include "table/sprites.h" rubidium@8264: #include "table/strings.h" rubidium@8264: Darkvater@5273: enum BuildVehicleWidgets { bjarni@4800: BUILD_VEHICLE_WIDGET_CLOSEBOX = 0, bjarni@4800: BUILD_VEHICLE_WIDGET_CAPTION, bjarni@4800: BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING, bjarni@4800: BUILD_VEHICLE_WIDGET_SORT_DROPDOWN, bjarni@4800: BUILD_VEHICLE_WIDGET_LIST, bjarni@4800: BUILD_VEHICLE_WIDGET_SCROLLBAR, bjarni@4800: BUILD_VEHICLE_WIDGET_PANEL, bjarni@4800: BUILD_VEHICLE_WIDGET_BUILD, bjarni@4800: BUILD_VEHICLE_WIDGET_RENAME, bjarni@4800: BUILD_VEHICLE_WIDGET_RESIZE, maedhros@6385: BUILD_VEHICLE_WIDGET_END Darkvater@5273: }; bjarni@4800: bjarni@4800: static const Widget _build_vehicle_widgets[] = { belugas@9751: { WWT_CLOSEBOX, RESIZE_NONE, COLOUR_GREY, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, belugas@9751: { WWT_CAPTION, RESIZE_RIGHT, COLOUR_GREY, 11, 239, 0, 13, 0x0, STR_018C_WINDOW_TITLE_DRAG_THIS }, belugas@9751: { WWT_PUSHTXTBTN, RESIZE_NONE, COLOUR_GREY, 0, 80, 14, 25, STR_SORT_BY, STR_SORT_ORDER_TIP}, belugas@9751: { WWT_DROPDOWN, RESIZE_RIGHT, COLOUR_GREY, 81, 239, 14, 25, 0x0, STR_SORT_CRITERIA_TIP}, belugas@9751: { WWT_MATRIX, RESIZE_RB, COLOUR_GREY, 0, 227, 26, 39, 0x101, STR_NULL }, belugas@9751: { WWT_SCROLLBAR, RESIZE_LRB, COLOUR_GREY, 228, 239, 26, 39, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST }, belugas@9751: { WWT_PANEL, RESIZE_RTB, COLOUR_GREY, 0, 239, 40, 161, 0x0, STR_NULL }, bjarni@4800: belugas@9751: { WWT_PUSHTXTBTN, RESIZE_TB, COLOUR_GREY, 0, 114, 162, 173, 0x0, STR_NULL }, belugas@9751: { WWT_PUSHTXTBTN, RESIZE_RTB, COLOUR_GREY, 115, 227, 162, 173, 0x0, STR_NULL }, belugas@9751: { WWT_RESIZEBOX, RESIZE_LRTB, COLOUR_GREY, 228, 239, 162, 173, 0x0, STR_RESIZE_BUTTON }, bjarni@4800: { WIDGETS_END}, bjarni@4800: }; bjarni@4800: bjarni@4800: static bool _internal_sort_order; // descending/ascending bjarni@5784: static byte _last_sort_criteria[] = {0, 0, 0, 0}; bjarni@5784: static bool _last_sort_order[] = {false, false, false, false}; bjarni@4800: bjarni@4800: static int CDECL EngineNumberSorter(const void *a, const void *b) bjarni@4800: { bjarni@4800: const EngineID va = *(const EngineID*)a; bjarni@4800: const EngineID vb = *(const EngineID*)b; peter1138@9671: int r = ListPositionOfEngine(va) - ListPositionOfEngine(vb); bjarni@4800: bjarni@4800: return _internal_sort_order ? -r : r; bjarni@4800: } bjarni@4800: bjarni@4800: static int CDECL EngineIntroDateSorter(const void *a, const void *b) bjarni@4800: { bjarni@4800: const int va = GetEngine(*(const EngineID*)a)->intro_date; bjarni@4800: const int vb = GetEngine(*(const EngineID*)b)->intro_date; bjarni@4800: const int r = va - vb; bjarni@4800: bjarni@4800: if (r == 0) { bjarni@4800: /* Use EngineID to sort instead since we want consistent sorting */ bjarni@4800: return EngineNumberSorter(a, b); bjarni@4800: } bjarni@4800: return _internal_sort_order ? -r : r; bjarni@4800: } bjarni@4800: bjarni@4800: static int CDECL EngineNameSorter(const void *a, const void *b) bjarni@4800: { bjarni@4804: static EngineID last_engine[2] = { INVALID_ENGINE, INVALID_ENGINE }; bjarni@4804: static char last_name[2][64] = { "\0", "\0" }; bjarni@4804: bjarni@4800: const EngineID va = *(const EngineID*)a; bjarni@4800: const EngineID vb = *(const EngineID*)b; bjarni@4800: int r; bjarni@4800: bjarni@4804: if (va != last_engine[0]) { bjarni@4804: last_engine[0] = va; peter1138@7059: SetDParam(0, va); peter1138@7059: GetString(last_name[0], STR_ENGINE_NAME, lastof(last_name[0])); bjarni@4800: } bjarni@4800: bjarni@4804: if (vb != last_engine[1]) { bjarni@4804: last_engine[1] = vb; peter1138@7059: SetDParam(0, vb); peter1138@7059: GetString(last_name[1], STR_ENGINE_NAME, lastof(last_name[1])); bjarni@4804: } bjarni@4804: bjarni@4804: r = strcmp(last_name[0], last_name[1]); // sort by name bjarni@4800: bjarni@4800: if (r == 0) { bjarni@4800: /* Use EngineID to sort instead since we want consistent sorting */ bjarni@4800: return EngineNumberSorter(a, b); bjarni@4800: } bjarni@4804: return _internal_sort_order ? -r : r; bjarni@4800: } bjarni@4800: bjarni@4800: static int CDECL EngineReliabilitySorter(const void *a, const void *b) bjarni@4800: { bjarni@4800: const int va = GetEngine(*(const EngineID*)a)->reliability; bjarni@4800: const int vb = GetEngine(*(const EngineID*)b)->reliability; bjarni@4800: const int r = va - vb; bjarni@4800: bjarni@4800: if (r == 0) { bjarni@4800: /* Use EngineID to sort instead since we want consistent sorting */ bjarni@4800: return EngineNumberSorter(a, b); bjarni@4800: } bjarni@4800: return _internal_sort_order ? -r : r; bjarni@4800: } bjarni@4800: bjarni@5779: /* Train sorting functions */ bjarni@5779: static int CDECL TrainEngineCostSorter(const void *a, const void *b) bjarni@5779: { peter1138@9923: int va = RailVehInfo(*(const EngineID*)a)->cost_factor; peter1138@9923: int vb = RailVehInfo(*(const EngineID*)b)->cost_factor; bjarni@5779: int r = va - vb; bjarni@5779: bjarni@5779: return _internal_sort_order ? -r : r; bjarni@5779: } bjarni@5779: bjarni@5779: static int CDECL TrainEngineSpeedSorter(const void *a, const void *b) bjarni@5779: { bjarni@5779: int va = RailVehInfo(*(const EngineID*)a)->max_speed; bjarni@5779: int vb = RailVehInfo(*(const EngineID*)b)->max_speed; bjarni@5779: int r = va - vb; bjarni@5779: bjarni@5779: return _internal_sort_order ? -r : r; bjarni@5779: } bjarni@5779: bjarni@5779: static int CDECL TrainEnginePowerSorter(const void *a, const void *b) bjarni@5779: { bjarni@5779: const RailVehicleInfo *rvi_a = RailVehInfo(*(const EngineID*)a); bjarni@5779: const RailVehicleInfo *rvi_b = RailVehInfo(*(const EngineID*)b); bjarni@5779: peter1138@9208: int va = rvi_a->power; peter1138@9208: int vb = rvi_b->power; bjarni@5779: int r = va - vb; bjarni@5779: bjarni@5779: return _internal_sort_order ? -r : r; bjarni@5779: } bjarni@5779: bjarni@5779: static int CDECL TrainEngineRunningCostSorter(const void *a, const void *b) bjarni@5779: { bjarni@5779: const RailVehicleInfo *rvi_a = RailVehInfo(*(const EngineID*)a); bjarni@5779: const RailVehicleInfo *rvi_b = RailVehInfo(*(const EngineID*)b); bjarni@5779: frosch@10071: Money va = rvi_a->running_cost * GetPriceByIndex(rvi_a->running_cost_class); frosch@10071: Money vb = rvi_b->running_cost * GetPriceByIndex(rvi_b->running_cost_class); rubidium@6990: int r = ClampToI32(va - vb); bjarni@5779: bjarni@5779: return _internal_sort_order ? -r : r; bjarni@5779: } bjarni@5779: bjarni@5779: static int CDECL TrainEnginePowerVsRunningCostSorter(const void *a, const void *b) bjarni@5779: { bjarni@5779: const RailVehicleInfo *rvi_a = RailVehInfo(*(const EngineID*)a); bjarni@5779: const RailVehicleInfo *rvi_b = RailVehInfo(*(const EngineID*)b); bjarni@5779: bjarni@5779: /* Here we are using a few tricks to get the right sort. bjarni@5779: * We want power/running cost, but since we usually got higher running cost than power and we store the result in an int, bjarni@5779: * we will actually calculate cunning cost/power (to make it more than 1). bjarni@5779: * Because of this, the return value have to be reversed as well and we return b - a instead of a - b. bjarni@5779: * Another thing is that both power and running costs should be doubled for multiheaded engines. bjarni@5779: * Since it would be multipling with 2 in both numerator and denumerator, it will even themselves out and we skip checking for multiheaded. */ peter1138@8626: Money va = (rvi_a->running_cost * GetPriceByIndex(rvi_a->running_cost_class)) / max(1U, (uint)rvi_a->power); peter1138@8626: Money vb = (rvi_b->running_cost * GetPriceByIndex(rvi_b->running_cost_class)) / max(1U, (uint)rvi_b->power); rubidium@6990: int r = ClampToI32(vb - va); bjarni@5779: bjarni@5779: return _internal_sort_order ? -r : r; bjarni@5779: } bjarni@5779: bjarni@6786: static int CDECL TrainEngineCapacitySorter(const void *a, const void *b) bjarni@6786: { frosch@10071: const RailVehicleInfo *rvi_a = RailVehInfo(*(const EngineID*)a); frosch@10071: const RailVehicleInfo *rvi_b = RailVehInfo(*(const EngineID*)b); frosch@10071: frosch@10071: int va = rvi_a->capacity * (rvi_a->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1); frosch@10071: int vb = rvi_b->capacity * (rvi_b->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1); bjarni@6786: int r = va - vb; bjarni@6786: bjarni@6786: if (r == 0) { bjarni@6786: /* Use EngineID to sort instead since we want consistent sorting */ bjarni@6786: return EngineNumberSorter(a, b); bjarni@6786: } bjarni@6786: return _internal_sort_order ? -r : r; bjarni@6786: } bjarni@6786: bjarni@5779: static int CDECL TrainEnginesThenWagonsSorter(const void *a, const void *b) bjarni@5779: { bjarni@5779: EngineID va = *(const EngineID*)a; bjarni@5779: EngineID vb = *(const EngineID*)b; bjarni@5871: int val_a = (RailVehInfo(va)->railveh_type == RAILVEH_WAGON ? 1 : 0); bjarni@5871: int val_b = (RailVehInfo(vb)->railveh_type == RAILVEH_WAGON ? 1 : 0); bjarni@5779: int r = val_a - val_b; bjarni@5779: bjarni@5779: /* Use EngineID to sort instead since we want consistent sorting */ bjarni@5779: if (r == 0) return EngineNumberSorter(a, b); bjarni@5779: bjarni@5779: return _internal_sort_order ? -r : r; bjarni@5779: } bjarni@5779: bjarni@8148: /* Road vehicle sorting functions */ bjarni@8148: static int CDECL RoadVehEngineCostSorter(const void *a, const void *b) bjarni@8148: { peter1138@9923: int va = RoadVehInfo(*(const EngineID*)a)->cost_factor; peter1138@9923: int vb = RoadVehInfo(*(const EngineID*)b)->cost_factor; bjarni@8148: int r = va - vb; bjarni@8148: bjarni@8148: return _internal_sort_order ? -r : r; bjarni@8148: } bjarni@8148: bjarni@8148: static int CDECL RoadVehEngineSpeedSorter(const void *a, const void *b) bjarni@8148: { bjarni@8148: int va = RoadVehInfo(*(const EngineID*)a)->max_speed; bjarni@8148: int vb = RoadVehInfo(*(const EngineID*)b)->max_speed; bjarni@8148: int r = va - vb; bjarni@8148: bjarni@8148: return _internal_sort_order ? -r : r; bjarni@8148: } bjarni@8148: bjarni@8148: static int CDECL RoadVehEngineRunningCostSorter(const void *a, const void *b) bjarni@8148: { peter1138@8626: const RoadVehicleInfo *rvi_a = RoadVehInfo(*(const EngineID*)a); peter1138@8626: const RoadVehicleInfo *rvi_b = RoadVehInfo(*(const EngineID*)b); peter1138@8626: peter1138@8626: Money va = rvi_a->running_cost * GetPriceByIndex(rvi_a->running_cost_class); peter1138@8626: Money vb = rvi_b->running_cost * GetPriceByIndex(rvi_b->running_cost_class); peter1138@8626: int r = ClampToI32(va - vb); bjarni@8148: bjarni@8148: if (r == 0) { bjarni@8148: /* Use EngineID to sort instead since we want consistent sorting */ bjarni@8148: return EngineNumberSorter(a, b); bjarni@8148: } bjarni@8148: return _internal_sort_order ? -r : r; bjarni@8148: } bjarni@8148: bjarni@6786: static int CDECL RoadVehEngineCapacitySorter(const void *a, const void *b) bjarni@6786: { bjarni@6786: int va = RoadVehInfo(*(const EngineID*)a)->capacity; bjarni@6786: int vb = RoadVehInfo(*(const EngineID*)b)->capacity; bjarni@6786: int r = va - vb; bjarni@6786: bjarni@6786: if (r == 0) { bjarni@6786: /* Use EngineID to sort instead since we want consistent sorting */ bjarni@6786: return EngineNumberSorter(a, b); bjarni@6786: } bjarni@6786: return _internal_sort_order ? -r : r; bjarni@6786: } bjarni@6786: bjarni@8148: /* Road vehicle sorting functions */ bjarni@8148: static int CDECL ShipEngineCostSorter(const void *a, const void *b) bjarni@8148: { peter1138@9923: int va = ShipVehInfo(*(const EngineID*)a)->cost_factor; peter1138@9923: int vb = ShipVehInfo(*(const EngineID*)b)->cost_factor; bjarni@8148: int r = va - vb; bjarni@8148: bjarni@8148: return _internal_sort_order ? -r : r; bjarni@8148: } bjarni@8148: bjarni@8148: static int CDECL ShipEngineSpeedSorter(const void *a, const void *b) bjarni@8148: { bjarni@8148: int va = ShipVehInfo(*(const EngineID*)a)->max_speed; bjarni@8148: int vb = ShipVehInfo(*(const EngineID*)b)->max_speed; bjarni@8148: int r = va - vb; bjarni@8148: bjarni@8148: return _internal_sort_order ? -r : r; bjarni@8148: } bjarni@8148: bjarni@8148: static int CDECL ShipEngineRunningCostSorter(const void *a, const void *b) bjarni@8148: { bjarni@8148: const int va = ShipVehInfo(*(const EngineID*)a)->running_cost; bjarni@8148: const int vb = ShipVehInfo(*(const EngineID*)b)->running_cost; bjarni@8148: const int r = va - vb; bjarni@8148: bjarni@8148: if (r == 0) { bjarni@8148: /* Use EngineID to sort instead since we want consistent sorting */ bjarni@8148: return EngineNumberSorter(a, b); bjarni@8148: } bjarni@8148: return _internal_sort_order ? -r : r; bjarni@8148: } bjarni@8148: bjarni@6786: static int CDECL ShipEngineCapacitySorter(const void *a, const void *b) bjarni@6786: { bjarni@6786: int va = ShipVehInfo(*(const EngineID*)a)->capacity; bjarni@6786: int vb = ShipVehInfo(*(const EngineID*)b)->capacity; bjarni@6786: int r = va - vb; bjarni@6786: bjarni@6786: if (r == 0) { bjarni@6786: /* Use EngineID to sort instead since we want consistent sorting */ bjarni@6786: return EngineNumberSorter(a, b); bjarni@6786: } bjarni@6786: return _internal_sort_order ? -r : r; bjarni@6786: } bjarni@6786: bjarni@4800: /* Aircraft sorting functions */ bjarni@4800: bjarni@4800: static int CDECL AircraftEngineCostSorter(const void *a, const void *b) bjarni@4800: { peter1138@9923: const int va = AircraftVehInfo(*(const EngineID*)a)->cost_factor; peter1138@9923: const int vb = AircraftVehInfo(*(const EngineID*)b)->cost_factor; bjarni@4800: int r = va - vb; bjarni@4800: bjarni@4800: return _internal_sort_order ? -r : r; bjarni@4800: } bjarni@4800: bjarni@4800: static int CDECL AircraftEngineSpeedSorter(const void *a, const void *b) bjarni@4800: { bjarni@4800: const int va = AircraftVehInfo(*(const EngineID*)a)->max_speed; bjarni@4800: const int vb = AircraftVehInfo(*(const EngineID*)b)->max_speed; bjarni@4800: const int r = va - vb; bjarni@4800: bjarni@4800: if (r == 0) { bjarni@4800: /* Use EngineID to sort instead since we want consistent sorting */ bjarni@4800: return EngineNumberSorter(a, b); bjarni@4800: } bjarni@4800: return _internal_sort_order ? -r : r; bjarni@4800: } bjarni@4800: bjarni@4800: static int CDECL AircraftEngineRunningCostSorter(const void *a, const void *b) bjarni@4800: { bjarni@4800: const int va = AircraftVehInfo(*(const EngineID*)a)->running_cost; bjarni@4800: const int vb = AircraftVehInfo(*(const EngineID*)b)->running_cost; bjarni@4800: const int r = va - vb; bjarni@4800: bjarni@4800: if (r == 0) { bjarni@4800: /* Use EngineID to sort instead since we want consistent sorting */ bjarni@4800: return EngineNumberSorter(a, b); bjarni@4800: } bjarni@4800: return _internal_sort_order ? -r : r; bjarni@4800: } bjarni@4800: bjarni@4800: static int CDECL AircraftEngineCargoSorter(const void *a, const void *b) bjarni@4800: { bjarni@6787: int va = AircraftVehInfo(*(const EngineID*)a)->passenger_capacity; bjarni@6787: int vb = AircraftVehInfo(*(const EngineID*)b)->passenger_capacity; bjarni@6787: int r = va - vb; bjarni@4800: bjarni@4800: if (r == 0) { bjarni@6787: /* The planes has the same passenger capacity. Check mail capacity instead */ bjarni@6787: va = AircraftVehInfo(*(const EngineID*)a)->mail_capacity; bjarni@6787: vb = AircraftVehInfo(*(const EngineID*)b)->mail_capacity; bjarni@6787: r = va - vb; bjarni@6787: bjarni@6787: if (r == 0) { bjarni@6787: /* Use EngineID to sort instead since we want consistent sorting */ bjarni@6787: return EngineNumberSorter(a, b); bjarni@6787: } bjarni@4800: } bjarni@4800: return _internal_sort_order ? -r : r; bjarni@4800: } bjarni@4800: bjarni@6786: static EngList_SortTypeFunction * const _sorter[][10] = {{ bjarni@5790: /* Trains */ peter1138@9671: &EngineNumberSorter, bjarni@5779: &TrainEngineCostSorter, bjarni@5779: &TrainEngineSpeedSorter, bjarni@5779: &TrainEnginePowerSorter, bjarni@5779: &EngineIntroDateSorter, bjarni@5779: &EngineNameSorter, bjarni@5779: &TrainEngineRunningCostSorter, bjarni@5779: &TrainEnginePowerVsRunningCostSorter, bjarni@5779: &EngineReliabilitySorter, bjarni@6786: &TrainEngineCapacitySorter, rubidium@6491: }, { bjarni@5790: /* Road vehicles */ bjarni@5785: &EngineNumberSorter, bjarni@8148: &RoadVehEngineCostSorter, bjarni@8148: &RoadVehEngineSpeedSorter, bjarni@5785: &EngineIntroDateSorter, bjarni@5785: &EngineNameSorter, bjarni@8148: &RoadVehEngineRunningCostSorter, bjarni@5785: &EngineReliabilitySorter, bjarni@6786: &RoadVehEngineCapacitySorter, rubidium@6491: }, { bjarni@5790: /* Ships */ bjarni@5785: &EngineNumberSorter, bjarni@8148: &ShipEngineCostSorter, bjarni@8148: &ShipEngineSpeedSorter, bjarni@5785: &EngineIntroDateSorter, bjarni@5785: &EngineNameSorter, bjarni@8148: &ShipEngineRunningCostSorter, bjarni@5785: &EngineReliabilitySorter, bjarni@6786: &ShipEngineCapacitySorter, rubidium@6491: }, { bjarni@5790: /* Aircraft */ bjarni@5785: &EngineNumberSorter, bjarni@5785: &AircraftEngineCostSorter, bjarni@5785: &AircraftEngineSpeedSorter, bjarni@5785: &EngineIntroDateSorter, bjarni@5785: &EngineNameSorter, bjarni@5785: &AircraftEngineRunningCostSorter, bjarni@5785: &EngineReliabilitySorter, bjarni@5785: &AircraftEngineCargoSorter, bjarni@5785: }}; bjarni@5779: bjarni@6786: static const StringID _sort_listing[][11] = {{ bjarni@5790: /* Trains */ bjarni@5779: STR_ENGINE_SORT_ENGINE_ID, bjarni@5779: STR_ENGINE_SORT_COST, bjarni@5779: STR_SORT_BY_MAX_SPEED, bjarni@5779: STR_ENGINE_SORT_POWER, bjarni@5779: STR_ENGINE_SORT_INTRO_DATE, bjarni@5779: STR_SORT_BY_DROPDOWN_NAME, bjarni@5779: STR_ENGINE_SORT_RUNNING_COST, bjarni@5779: STR_ENGINE_SORT_POWER_VS_RUNNING_COST, bjarni@5779: STR_SORT_BY_RELIABILITY, bjarni@6786: STR_ENGINE_SORT_CARGO_CAPACITY, bjarni@5779: INVALID_STRING_ID rubidium@6491: }, { bjarni@5790: /* Road vehicles */ bjarni@5783: STR_ENGINE_SORT_ENGINE_ID, bjarni@8148: STR_ENGINE_SORT_COST, bjarni@8148: STR_SORT_BY_MAX_SPEED, bjarni@5783: STR_ENGINE_SORT_INTRO_DATE, bjarni@5783: STR_SORT_BY_DROPDOWN_NAME, bjarni@8148: STR_ENGINE_SORT_RUNNING_COST, bjarni@5783: STR_SORT_BY_RELIABILITY, bjarni@6786: STR_ENGINE_SORT_CARGO_CAPACITY, bjarni@5783: INVALID_STRING_ID rubidium@6491: }, { bjarni@5790: /* Ships */ bjarni@5785: STR_ENGINE_SORT_ENGINE_ID, bjarni@8148: STR_ENGINE_SORT_COST, bjarni@8148: STR_SORT_BY_MAX_SPEED, bjarni@5785: STR_ENGINE_SORT_INTRO_DATE, bjarni@5785: STR_SORT_BY_DROPDOWN_NAME, bjarni@8148: STR_ENGINE_SORT_RUNNING_COST, bjarni@5785: STR_SORT_BY_RELIABILITY, bjarni@6786: STR_ENGINE_SORT_CARGO_CAPACITY, bjarni@5785: INVALID_STRING_ID rubidium@6491: }, { bjarni@5790: /* Aircraft */ bjarni@4800: STR_ENGINE_SORT_ENGINE_ID, bjarni@4800: STR_ENGINE_SORT_COST, bjarni@4800: STR_SORT_BY_MAX_SPEED, bjarni@4800: STR_ENGINE_SORT_INTRO_DATE, bjarni@4800: STR_SORT_BY_DROPDOWN_NAME, bjarni@4800: STR_ENGINE_SORT_RUNNING_COST, bjarni@4800: STR_SORT_BY_RELIABILITY, bjarni@4800: STR_ENGINE_SORT_CARGO_CAPACITY, bjarni@4800: INVALID_STRING_ID bjarni@5785: }}; bjarni@4800: peter1138@8559: static int DrawCargoCapacityInfo(int x, int y, EngineID engine, VehicleType type, bool refittable) peter1138@8559: { peter1138@8559: uint16 *cap = GetCapacityOfArticulatedParts(engine, type); peter1138@8559: peter1138@8559: for (uint c = 0; c < NUM_CARGO; c++) { peter1138@8559: if (cap[c] == 0) continue; peter1138@8559: peter1138@8559: SetDParam(0, c); peter1138@8559: SetDParam(1, cap[c]); peter1138@8559: SetDParam(2, refittable ? STR_9842_REFITTABLE : STR_EMPTY); peter1138@8559: DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, TC_FROMSTRING); peter1138@8559: y += 10; peter1138@8559: peter1138@8559: /* Only show as refittable once */ peter1138@8559: refittable = false; peter1138@8559: } peter1138@8559: peter1138@8559: return y; peter1138@8559: } peter1138@8559: bjarni@5779: /* Draw rail wagon specific details */ bjarni@5817: static int DrawRailWagonPurchaseInfo(int x, int y, EngineID engine_number, const RailVehicleInfo *rvi) bjarni@4800: { bjarni@5779: /* Purchase cost */ peter1138@9923: SetDParam(0, (GetEngineProperty(engine_number, 0x17, rvi->cost_factor) * _price.build_railwagon) >> 8); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_COST, TC_FROMSTRING); bjarni@5779: y += 10; bjarni@5779: bjarni@5779: /* Wagon weight - (including cargo) */ peter1138@6595: uint weight = GetEngineProperty(engine_number, 0x16, rvi->weight); peter1138@6595: SetDParam(0, weight); peter1138@6608: SetDParam(1, (GetCargo(rvi->cargo_type)->weight * GetEngineProperty(engine_number, 0x14, rvi->capacity) >> 4) + weight); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_WEIGHT_CWEIGHT, TC_FROMSTRING); bjarni@5779: y += 10; bjarni@5779: bjarni@5779: /* Wagon speed limit, displayed if above zero */ rubidium@9413: if (_settings_game.vehicle.wagon_speed_limits) { peter1138@6595: uint max_speed = GetEngineProperty(engine_number, 0x09, rvi->max_speed); peter1138@6595: if (max_speed > 0) { peter1138@6595: SetDParam(0, max_speed * 10 / 16); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_SPEED, TC_FROMSTRING); peter1138@6595: y += 10; peter1138@6595: } bjarni@5779: } peter1138@8626: peter1138@8626: /* Running cost */ peter1138@8626: if (rvi->running_cost_class != 0xFF) { peter1138@8626: SetDParam(0, GetEngineProperty(engine_number, 0x0D, rvi->running_cost) * GetPriceByIndex(rvi->running_cost_class) >> 8); peter1138@8626: DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, TC_FROMSTRING); peter1138@8626: y += 10; peter1138@8626: } peter1138@8626: bjarni@5779: return y; bjarni@5779: } bjarni@5779: bjarni@5779: /* Draw locomotive specific details */ tron@5823: static int DrawRailEnginePurchaseInfo(int x, int y, EngineID engine_number, const RailVehicleInfo *rvi) bjarni@5779: { belugas@5868: int multihead = (rvi->railveh_type == RAILVEH_MULTIHEAD ? 1 : 0); peter1138@6595: uint weight = GetEngineProperty(engine_number, 0x16, rvi->weight); bjarni@5779: bjarni@5779: /* Purchase Cost - Engine weight */ peter1138@9923: SetDParam(0, GetEngineProperty(engine_number, 0x17, rvi->cost_factor) * (_price.build_railvehicle >> 3) >> 5); peter1138@6595: SetDParam(1, weight << multihead); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_COST_WEIGHT, TC_FROMSTRING); bjarni@5779: y += 10; bjarni@5779: bjarni@5779: /* Max speed - Engine power */ peter1138@6595: SetDParam(0, GetEngineProperty(engine_number, 0x09, rvi->max_speed) * 10 / 16); peter1138@9208: SetDParam(1, GetEngineProperty(engine_number, 0x0B, rvi->power)); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_SPEED_POWER, TC_FROMSTRING); bjarni@5779: y += 10; bjarni@5779: bjarni@5779: /* Max tractive effort - not applicable if old acceleration or maglev */ rubidium@9413: if (_settings_game.vehicle.realistic_acceleration && rvi->railtype != RAILTYPE_MAGLEV) { peter1138@6595: SetDParam(0, ((weight << multihead) * 10 * GetEngineProperty(engine_number, 0x1F, rvi->tractive_effort)) / 256); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_MAX_TE, TC_FROMSTRING); bjarni@5779: y += 10; bjarni@5779: } bjarni@5779: bjarni@5779: /* Running cost */ peter1138@8626: if (rvi->running_cost_class != 0xFF) { peter1138@9208: SetDParam(0, GetEngineProperty(engine_number, 0x0D, rvi->running_cost) * GetPriceByIndex(rvi->running_cost_class) >> 8); peter1138@8626: DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, TC_FROMSTRING); peter1138@8626: y += 10; peter1138@8626: } bjarni@5779: bjarni@5779: /* Powered wagons power - Powered wagons extra weight */ bjarni@5779: if (rvi->pow_wag_power != 0) { bjarni@5779: SetDParam(0, rvi->pow_wag_power); bjarni@5779: SetDParam(1, rvi->pow_wag_weight); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT, TC_FROMSTRING); bjarni@5779: y += 10; bjarni@5779: }; bjarni@5779: bjarni@5779: return y; bjarni@5779: } bjarni@5779: bjarni@5786: /* Draw road vehicle specific details */ bjarni@5817: static int DrawRoadVehPurchaseInfo(int x, int y, EngineID engine_number, const RoadVehicleInfo *rvi) bjarni@5786: { bjarni@5786: /* Purchase cost - Max speed */ peter1138@9923: SetDParam(0, GetEngineProperty(engine_number, 0x11, rvi->cost_factor) * (_price.roadveh_base >> 3) >> 5); peter1138@5874: SetDParam(1, rvi->max_speed * 10 / 32); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_COST_SPEED, TC_FROMSTRING); bjarni@5786: y += 10; bjarni@5786: bjarni@5786: /* Running cost */ peter1138@8626: SetDParam(0, rvi->running_cost * GetPriceByIndex(rvi->running_cost_class) >> 8); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, TC_FROMSTRING); bjarni@5786: y += 10; bjarni@5786: bjarni@5786: /* Cargo type + capacity */ rubidium@10430: return DrawCargoCapacityInfo(x, y, engine_number, VEH_ROAD, IsEngineRefittable(engine_number)); bjarni@5786: } bjarni@5786: bjarni@5783: /* Draw ship specific details */ bjarni@5817: static int DrawShipPurchaseInfo(int x, int y, EngineID engine_number, const ShipVehicleInfo *svi) bjarni@5783: { bjarni@5783: /* Purchase cost - Max speed */ peter1138@9923: SetDParam(0, GetEngineProperty(engine_number, 0x0A, svi->cost_factor) * (_price.ship_base >> 3) >> 5); peter1138@6595: SetDParam(1, GetEngineProperty(engine_number, 0x0B, svi->max_speed) * 10 / 32); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_COST_SPEED, TC_FROMSTRING); bjarni@5783: y += 10; bjarni@5783: bjarni@5783: /* Cargo type + capacity */ bjarni@5783: SetDParam(0, svi->cargo_type); peter1138@6608: SetDParam(1, GetEngineProperty(engine_number, 0x0D, svi->capacity)); rubidium@10430: SetDParam(2, IsEngineRefittable(engine_number) ? STR_9842_REFITTABLE : STR_EMPTY); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, TC_FROMSTRING); bjarni@5783: y += 10; bjarni@5783: bjarni@5783: /* Running cost */ rubidium@7002: SetDParam(0, GetEngineProperty(engine_number, 0x0F, svi->running_cost) * _price.ship_running >> 8); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, TC_FROMSTRING); bjarni@5783: y += 10; bjarni@5783: bjarni@5783: return y; bjarni@5783: } bjarni@5783: bjarni@5779: /* Draw aircraft specific details */ bjarni@5817: static int DrawAircraftPurchaseInfo(int x, int y, EngineID engine_number, const AircraftVehicleInfo *avi) bjarni@5779: { bjarni@4800: CargoID cargo; bjarni@4800: bjarni@4800: /* Purchase cost - Max speed */ peter1138@9923: SetDParam(0, GetEngineProperty(engine_number, 0x0B, avi->cost_factor) * (_price.aircraft_base >> 3) >> 5); celestar@6193: SetDParam(1, avi->max_speed * 10 / 16); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_COST_SPEED, TC_FROMSTRING); bjarni@4800: y += 10; bjarni@4800: bjarni@4800: /* Cargo capacity */ bjarni@4800: cargo = FindFirstRefittableCargo(engine_number); bjarni@4800: if (cargo == CT_INVALID || cargo == CT_PASSENGERS) { bjarni@4800: SetDParam(0, avi->passenger_capacity); bjarni@4800: SetDParam(1, avi->mail_capacity); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_AIRCRAFT_CAPACITY, TC_FROMSTRING); bjarni@4800: } else { bjarni@4800: /* Note, if the default capacity is selected by the refit capacity bjarni@4800: * callback, then the capacity shown is likely to be incorrect. */ peter1138@4896: SetDParam(0, cargo); tron@6087: SetDParam(1, AircraftDefaultCargoCapacity(cargo, avi)); bjarni@4800: SetDParam(2, STR_9842_REFITTABLE); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, TC_FROMSTRING); bjarni@4800: } bjarni@4800: y += 10; bjarni@4800: bjarni@4800: /* Running cost */ rubidium@7002: SetDParam(0, GetEngineProperty(engine_number, 0x0E, avi->running_cost) * _price.aircraft_running >> 8); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, TC_FROMSTRING); bjarni@4800: y += 10; bjarni@4800: bjarni@5779: return y; bjarni@5779: } bjarni@4800: bjarni@5779: /** bjarni@5779: * Draw the purchase info details of a vehicle at a given location. bjarni@5779: * @param x,y location where to draw the info bjarni@5779: * @param w how wide are the text allowed to be (size of widget/window to Draw in) bjarni@5779: * @param engine_number the engine of which to draw the info of maedhros@6385: * @return y after drawing all the text bjarni@5779: */ maedhros@6385: int DrawVehiclePurchaseInfo(int x, int y, uint w, EngineID engine_number) bjarni@5779: { bjarni@5779: const Engine *e = GetEngine(engine_number); bjarni@5779: YearMonthDay ymd; bjarni@5779: ConvertDateToYMD(e->intro_date, &ymd); rubidium@10430: bool refittable = IsEngineRefittable(engine_number); bjarni@5779: bjarni@5779: switch (e->type) { rubidium@6638: default: NOT_REACHED(); rubidium@6259: case VEH_TRAIN: { bjarni@5779: const RailVehicleInfo *rvi = RailVehInfo(engine_number); rubidium@10430: refittable &= GetEngineProperty(engine_number, 0x14, rvi->capacity) > 0; bjarni@5819: belugas@5868: if (rvi->railveh_type == RAILVEH_WAGON) { bjarni@5817: y = DrawRailWagonPurchaseInfo(x, y, engine_number, rvi); bjarni@5779: } else { tron@5823: y = DrawRailEnginePurchaseInfo(x, y, engine_number, rvi); bjarni@5779: } bjarni@5779: bjarni@5779: /* Cargo type + capacity, or N/A */ rubidium@10430: int new_y = DrawCargoCapacityInfo(x, y, engine_number, VEH_TRAIN, refittable); peter1138@8559: peter1138@8559: if (new_y == y) { bjarni@5779: SetDParam(0, CT_INVALID); bjarni@5779: SetDParam(2, STR_EMPTY); peter1138@8559: DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, TC_FROMSTRING); peter1138@8559: y += 10; bjarni@5779: } else { peter1138@8559: y = new_y; bjarni@5779: } peter1138@8559: break; bjarni@5779: } rubidium@6259: case VEH_ROAD: bjarni@5817: y = DrawRoadVehPurchaseInfo(x, y, engine_number, RoadVehInfo(engine_number)); bjarni@5786: break; rubidium@10430: case VEH_SHIP: rubidium@10430: y = DrawShipPurchaseInfo(x, y, engine_number, ShipVehInfo(engine_number)); rubidium@10430: break; rubidium@6259: case VEH_AIRCRAFT: bjarni@5817: y = DrawAircraftPurchaseInfo(x, y, engine_number, AircraftVehInfo(engine_number)); bjarni@5779: break; bjarni@5779: } bjarni@5779: bjarni@5779: /* Draw details, that applies to all types except rail wagons */ rubidium@6259: if (e->type != VEH_TRAIN || RailVehInfo(engine_number)->railveh_type != RAILVEH_WAGON) { bjarni@5779: /* Design date - Life length */ bjarni@5779: SetDParam(0, ymd.year); bjarni@5779: SetDParam(1, e->lifelength); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_DESIGNED_LIFE, TC_FROMSTRING); bjarni@5779: y += 10; bjarni@5779: bjarni@5779: /* Reliability */ bjarni@5779: SetDParam(0, e->reliability * 100 >> 16); belugas@7824: DrawString(x, y, STR_PURCHASE_INFO_RELIABILITY, TC_FROMSTRING); bjarni@5779: y += 10; bjarni@5779: } bjarni@4800: bjarni@4800: /* Additional text from NewGRF */ peter1138@4930: y += ShowAdditionalText(x, y, w, engine_number); rubidium@10430: if (refittable) y += ShowRefitOptionsList(x, y, w, engine_number); maedhros@6385: maedhros@6385: return y; bjarni@4800: } bjarni@4800: peter1138@8479: static void DrawVehicleEngine(VehicleType type, int x, int y, EngineID engine, SpriteID pal) bjarni@5800: { bjarni@5800: switch (type) { rubidium@6259: case VEH_TRAIN: DrawTrainEngine( x, y, engine, pal); break; rubidium@6259: case VEH_ROAD: DrawRoadVehEngine( x, y, engine, pal); break; rubidium@6259: case VEH_SHIP: DrawShipEngine( x, y, engine, pal); break; rubidium@6259: case VEH_AIRCRAFT: DrawAircraftEngine(x, y, engine, pal); break; bjarni@5800: default: NOT_REACHED(); bjarni@5800: } bjarni@5800: } bjarni@5800: bjarni@5800: /** Engine drawing loop bjarni@5800: * @param type Type of vehicle (VEH_*) bjarni@5800: * @param x,y Where should the list start bjarni@5800: * @param eng_list What engines to draw bjarni@5800: * @param min where to start in the list bjarni@5802: * @param max where in the list to end bjarni@5800: * @param selected_id what engine to highlight as selected, if any bjarni@9027: * @param count_location Offset to print the engine count (used by autoreplace). 0 means it's off bjarni@5800: */ peter1138@9679: void DrawEngineList(VehicleType type, int x, int r, int y, const GUIEngineList *eng_list, uint16 min, uint16 max, EngineID selected_id, int count_location, GroupID selected_group) bjarni@5800: { bjarni@5800: byte step_size = GetVehicleListHeight(type); bjarni@5800: byte x_offset = 0; bjarni@5800: byte y_offset = 0; bjarni@5800: peter1138@9403: assert(max <= eng_list->Length()); bjarni@5802: bjarni@5800: switch (type) { rubidium@6259: case VEH_TRAIN: bjarni@5800: x++; // train and road vehicles use the same offset, except trains are one more pixel to the right bjarni@5800: /* Fallthough */ rubidium@6259: case VEH_ROAD: bjarni@5800: x += 26; bjarni@5800: x_offset = 30; bjarni@5800: y += 2; bjarni@5800: y_offset = 4; bjarni@5800: break; rubidium@6259: case VEH_SHIP: bjarni@5800: x += 35; bjarni@5800: x_offset = 40; bjarni@5800: y += 7; bjarni@5800: y_offset = 3; bjarni@5800: break; rubidium@6259: case VEH_AIRCRAFT: bjarni@5800: x += 27; bjarni@5800: x_offset = 33; bjarni@5800: y += 7; bjarni@5800: y_offset = 3; bjarni@5800: break; bjarni@5800: default: NOT_REACHED(); bjarni@5800: } bjarni@5800: peter1138@9679: uint maxw = r - x - x_offset; peter1138@9679: bjarni@5800: for (; min < max; min++, y += step_size) { peter1138@9395: const EngineID engine = (*eng_list)[min]; rubidium@10207: /* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */ rubidium@10207: const uint num_engines = GetGroupNumEngines(_local_company, selected_group, engine); bjarni@5800: peter1138@7059: SetDParam(0, engine); peter1138@9679: DrawStringTruncated(x + x_offset, y, STR_ENGINE_NAME, engine == selected_id ? TC_WHITE : TC_BLACK, maxw); rubidium@10207: DrawVehicleEngine(type, x, y + y_offset, engine, (count_location != 0 && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_company)); bjarni@9027: if (count_location != 0) { rubidium@6643: SetDParam(0, num_engines); bjarni@9027: DrawStringRightAligned(count_location, y + (GetVehicleListHeight(type) == 14 ? 3 : 8), STR_TINY_BLACK, TC_FROMSTRING); bjarni@5944: } bjarni@5800: } bjarni@5800: } bjarni@5800: peter1138@8338: rubidium@9202: struct BuildVehicleWindow : Window { rubidium@9202: VehicleType vehicle_type; rubidium@9202: union { rubidium@9202: RailTypeByte railtype; rubidium@9202: AirportFTAClass::Flags flags; rubidium@9202: RoadTypes roadtypes; rubidium@9202: } filter; rubidium@9202: bool descending_sort_order; rubidium@9202: byte sort_criteria; rubidium@9202: bool regenerate_list; belugas@9633: bool listview_mode; rubidium@9202: EngineID sel_engine; rubidium@9202: EngineID rename_engine; peter1138@9403: GUIEngineList eng_list; bjarni@5786: rubidium@9203: BuildVehicleWindow(const WindowDesc *desc, TileIndex tile, VehicleType type) : Window(desc, tile == 0 ? (int)type : tile) rubidium@9202: { rubidium@9202: this->vehicle_type = type; rubidium@9202: int vlh = GetVehicleListHeight(this->vehicle_type); maedhros@6385: rubidium@9202: ResizeWindow(this, 0, vlh - 14); rubidium@9202: this->resize.step_height = vlh; rubidium@9202: this->vscroll.cap = 1; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_LIST].data = 0x101; rubidium@9202: rubidium@9202: this->resize.width = this->width; rubidium@9202: this->resize.height = this->height; rubidium@9202: rubidium@10207: this->caption_color = (tile != 0) ? GetTileOwner(tile) : _local_company; rubidium@9202: rubidium@9202: this->sel_engine = INVALID_ENGINE; rubidium@9202: this->regenerate_list = false; rubidium@9202: rubidium@9202: this->sort_criteria = _last_sort_criteria[type]; rubidium@9202: this->descending_sort_order = _last_sort_order[type]; rubidium@9202: rubidium@9202: switch (type) { rubidium@9202: default: NOT_REACHED(); rubidium@9202: case VEH_TRAIN: rubidium@9202: this->filter.railtype = (tile == 0) ? RAILTYPE_END : GetRailType(tile); rubidium@9202: break; rubidium@9202: case VEH_ROAD: rubidium@9202: this->filter.roadtypes = (tile == 0) ? ROADTYPES_ALL : GetRoadTypes(tile); rubidium@9202: case VEH_SHIP: rubidium@9202: break; rubidium@9202: case VEH_AIRCRAFT: rubidium@9202: this->filter.flags = rubidium@9202: tile == 0 ? AirportFTAClass::ALL : GetStationByTile(tile)->Airport()->flags; rubidium@9202: break; rubidium@9202: } rubidium@9202: this->SetupWindowStrings(type); belugas@9633: belugas@9633: this->listview_mode = (this->window_number <= VEH_END); belugas@9633: /* If we are just viewing the list of vehicles, we do not need the Build button. belugas@9633: * So we just hide it, and enlarge the Rename buton by the now vacant place. */ belugas@9633: if (this->listview_mode) { belugas@9633: this->HideWidget(BUILD_VEHICLE_WIDGET_BUILD); belugas@9633: this->widget[BUILD_VEHICLE_WIDGET_RENAME].left = this->widget[BUILD_VEHICLE_WIDGET_BUILD].left; belugas@9633: } else { belugas@9633: /* Both are visible, adjust the size of each */ belugas@9633: ResizeButtons(this, BUILD_VEHICLE_WIDGET_BUILD, BUILD_VEHICLE_WIDGET_RENAME); belugas@9633: } rubidium@9202: rubidium@9202: this->GenerateBuildList(); // generate the list, since we need it in the next line rubidium@9202: /* Select the first engine in the list as default when opening the window */ peter1138@9403: if (this->eng_list.Length() > 0) this->sel_engine = this->eng_list[0]; rubidium@9202: rubidium@9202: this->FindWindowPlacementAndResize(desc); rubidium@9202: } rubidium@9202: rubidium@9202: /* Setup widget strings to fit the different types of vehicles */ rubidium@9202: void SetupWindowStrings(VehicleType type) rubidium@9202: { rubidium@9202: switch (type) { rubidium@9202: default: NOT_REACHED(); rubidium@9202: rubidium@9202: case VEH_TRAIN: belugas@9633: this->widget[BUILD_VEHICLE_WIDGET_CAPTION].data = this->listview_mode ? STR_AVAILABLE_TRAINS : STR_JUST_STRING; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips = STR_8843_TRAIN_VEHICLE_SELECTION; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_BUILD].data = STR_881F_BUILD_VEHICLE; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips = STR_8844_BUILD_THE_HIGHLIGHTED_TRAIN; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_RENAME].data = STR_8820_RENAME; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_8845_RENAME_TRAIN_VEHICLE_TYPE; rubidium@9202: break; rubidium@9202: rubidium@9202: case VEH_ROAD: belugas@9633: this->widget[BUILD_VEHICLE_WIDGET_CAPTION].data = this->listview_mode ? STR_AVAILABLE_ROAD_VEHICLES : STR_9006_NEW_ROAD_VEHICLES; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips = STR_9026_ROAD_VEHICLE_SELECTION; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_BUILD].data = STR_9007_BUILD_VEHICLE; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips = STR_9027_BUILD_THE_HIGHLIGHTED_ROAD; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_RENAME].data = STR_9034_RENAME; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_9035_RENAME_ROAD_VEHICLE_TYPE; rubidium@9202: break; rubidium@9202: rubidium@9202: case VEH_SHIP: belugas@9633: this->widget[BUILD_VEHICLE_WIDGET_CAPTION].data = this->listview_mode ? STR_AVAILABLE_SHIPS : STR_9808_NEW_SHIPS; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips = STR_9825_SHIP_SELECTION_LIST_CLICK; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_BUILD].data = STR_9809_BUILD_SHIP; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips = STR_9826_BUILD_THE_HIGHLIGHTED_SHIP; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_RENAME].data = STR_9836_RENAME; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_9837_RENAME_SHIP_TYPE; rubidium@9202: break; rubidium@9202: rubidium@9202: case VEH_AIRCRAFT: belugas@9633: this->widget[BUILD_VEHICLE_WIDGET_CAPTION].data = this->listview_mode ? STR_AVAILABLE_AIRCRAFT : STR_A005_NEW_AIRCRAFT; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips = STR_A025_AIRCRAFT_SELECTION_LIST; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_BUILD].data = STR_A006_BUILD_AIRCRAFT; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips = STR_A026_BUILD_THE_HIGHLIGHTED_AIRCRAFT; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_RENAME].data = STR_A037_RENAME; rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_A038_RENAME_AIRCRAFT_TYPE; rubidium@9202: break; peter1138@8418: } bjarni@5800: } bjarni@5783: rubidium@9202: /* Figure out what train EngineIDs to put in the list */ rubidium@9202: void GenerateBuildTrainList() rubidium@9202: { rubidium@9202: EngineID sel_id = INVALID_ENGINE; rubidium@9202: int num_engines = 0; rubidium@9202: int num_wagons = 0; bjarni@4800: belugas@9633: this->filter.railtype = (this->listview_mode) ? RAILTYPE_END : GetRailType(this->window_number); bjarni@4800: peter1138@9403: this->eng_list.Clear(); rubidium@9202: rubidium@9202: /* Make list of all available train engines and wagons. rubidium@9202: * Also check to see if the previously selected engine is still available, rubidium@9202: * and if not, reset selection to INVALID_ENGINE. This could be the case rubidium@9202: * when engines become obsolete and are removed */ rubidium@9202: const Engine *e; rubidium@9202: FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) { rubidium@9202: EngineID eid = e->index; rubidium@9202: const RailVehicleInfo *rvi = &e->u.rail; rubidium@9202: rubidium@9202: if (this->filter.railtype != RAILTYPE_END && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue; rubidium@10207: if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue; rubidium@9202: peter1138@9403: *this->eng_list.Append() = eid; peter1138@9403: rubidium@9202: if (rvi->railveh_type != RAILVEH_WAGON) { rubidium@9202: num_engines++; rubidium@9202: } else { rubidium@9202: num_wagons++; rubidium@9202: } rubidium@9202: rubidium@9202: if (eid == this->sel_engine) sel_id = eid; KUDr@5187: } bjarni@4800: rubidium@9202: this->sel_engine = sel_id; bjarni@4800: rubidium@9202: /* make engines first, and then wagons, sorted by ListPositionOfEngine() */ rubidium@9202: _internal_sort_order = false; rubidium@9202: EngList_Sort(&this->eng_list, TrainEnginesThenWagonsSorter); rubidium@9202: rubidium@9202: /* and then sort engines */ rubidium@9202: _internal_sort_order = this->descending_sort_order; rubidium@9202: EngList_SortPartial(&this->eng_list, _sorter[0][this->sort_criteria], 0, num_engines); rubidium@9202: rubidium@9202: /* and finally sort wagons */ rubidium@9202: EngList_SortPartial(&this->eng_list, _sorter[0][this->sort_criteria], num_engines, num_wagons); rubidium@9202: } rubidium@9202: rubidium@9202: /* Figure out what road vehicle EngineIDs to put in the list */ rubidium@9202: void GenerateBuildRoadVehList() rubidium@9202: { rubidium@9202: EngineID sel_id = INVALID_ENGINE; rubidium@9202: peter1138@9403: this->eng_list.Clear(); rubidium@9202: rubidium@9202: const Engine *e; rubidium@9202: FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) { rubidium@9202: EngineID eid = e->index; rubidium@10207: if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue; rubidium@9202: if (!HasBit(this->filter.roadtypes, HasBit(EngInfo(eid)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD)) continue; peter1138@9403: *this->eng_list.Append() = eid; rubidium@9202: rubidium@9202: if (eid == this->sel_engine) sel_id = eid; rubidium@9202: } rubidium@9202: this->sel_engine = sel_id; rubidium@9202: } rubidium@9202: rubidium@9202: /* Figure out what ship EngineIDs to put in the list */ rubidium@9202: void GenerateBuildShipList() rubidium@9202: { rubidium@9202: EngineID sel_id = INVALID_ENGINE; peter1138@9403: this->eng_list.Clear(); rubidium@9202: rubidium@9202: const Engine *e; rubidium@9202: FOR_ALL_ENGINES_OF_TYPE(e, VEH_SHIP) { rubidium@9202: EngineID eid = e->index; rubidium@10207: if (!IsEngineBuildable(eid, VEH_SHIP, _local_company)) continue; peter1138@9403: *this->eng_list.Append() = eid; rubidium@9202: rubidium@9202: if (eid == this->sel_engine) sel_id = eid; rubidium@9202: } rubidium@9202: this->sel_engine = sel_id; rubidium@9202: } rubidium@9202: rubidium@9202: /* Figure out what aircraft EngineIDs to put in the list */ rubidium@9202: void GenerateBuildAircraftList() rubidium@9202: { rubidium@9202: EngineID sel_id = INVALID_ENGINE; rubidium@9202: peter1138@9403: this->eng_list.Clear(); rubidium@9202: rubidium@9202: /* Make list of all available planes. rubidium@9202: * Also check to see if the previously selected plane is still available, rubidium@9202: * and if not, reset selection to INVALID_ENGINE. This could be the case rubidium@9202: * when planes become obsolete and are removed */ rubidium@9202: const Engine *e; rubidium@9202: FOR_ALL_ENGINES_OF_TYPE(e, VEH_AIRCRAFT) { rubidium@9202: EngineID eid = e->index; rubidium@10207: if (!IsEngineBuildable(eid, VEH_AIRCRAFT, _local_company)) continue; rubidium@9202: /* First VEH_END window_numbers are fake to allow a window open for all different types at once */ belugas@9633: if (!this->listview_mode && !CanAircraftUseStation(eid, this->window_number)) continue; rubidium@9202: peter1138@9403: *this->eng_list.Append() = eid; rubidium@9202: if (eid == this->sel_engine) sel_id = eid; KUDr@5187: } bjarni@4800: rubidium@9202: this->sel_engine = sel_id; rubidium@9202: } bjarni@5779: rubidium@9202: /* Generate the list of vehicles */ rubidium@9202: void GenerateBuildList() rubidium@9202: { rubidium@9202: switch (this->vehicle_type) { rubidium@9202: default: NOT_REACHED(); rubidium@9202: case VEH_TRAIN: rubidium@9202: this->GenerateBuildTrainList(); rubidium@9202: return; // trains should not reach the last sorting rubidium@9202: case VEH_ROAD: rubidium@9202: this->GenerateBuildRoadVehList(); rubidium@9202: break; rubidium@9202: case VEH_SHIP: rubidium@9202: this->GenerateBuildShipList(); rubidium@9202: break; rubidium@9202: case VEH_AIRCRAFT: rubidium@9202: this->GenerateBuildAircraftList(); rubidium@9202: break; rubidium@9202: } rubidium@9202: _internal_sort_order = this->descending_sort_order; rubidium@9202: EngList_Sort(&this->eng_list, _sorter[this->vehicle_type][this->sort_criteria]); rubidium@9202: } rubidium@9202: rubidium@9202: void OnClick(Point pt, int widget) rubidium@9202: { rubidium@9202: switch (widget) { rubidium@9202: case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING: rubidium@9202: this->descending_sort_order ^= true; rubidium@9202: _last_sort_order[this->vehicle_type] = this->descending_sort_order; rubidium@9202: this->regenerate_list = true; rubidium@9202: this->SetDirty(); rubidium@9202: break; rubidium@9202: rubidium@9202: case BUILD_VEHICLE_WIDGET_LIST: { rubidium@9202: uint i = (pt.y - this->widget[BUILD_VEHICLE_WIDGET_LIST].top) / GetVehicleListHeight(this->vehicle_type) + this->vscroll.pos; peter1138@9403: size_t num_items = this->eng_list.Length(); rubidium@9202: this->sel_engine = (i < num_items) ? this->eng_list[i] : INVALID_ENGINE; rubidium@9202: this->SetDirty(); rubidium@9202: break; rubidium@9202: } rubidium@9202: rubidium@9202: case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN: // Select sorting criteria dropdown menu rubidium@9202: ShowDropDownMenu(this, _sort_listing[this->vehicle_type], this->sort_criteria, BUILD_VEHICLE_WIDGET_SORT_DROPDOWN, 0, 0); rubidium@9202: break; rubidium@9202: rubidium@9202: case BUILD_VEHICLE_WIDGET_BUILD: { rubidium@9202: EngineID sel_eng = this->sel_engine; rubidium@9202: if (sel_eng != INVALID_ENGINE) { rubidium@9202: switch (this->vehicle_type) { rubidium@9202: default: NOT_REACHED(); rubidium@9202: case VEH_TRAIN: rubidium@9202: DoCommandP(this->window_number, sel_eng, 0, (RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) ? CcBuildWagon : CcBuildLoco, rubidium@9202: CMD_BUILD_RAIL_VEHICLE | CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE)); rubidium@9202: break; rubidium@9202: case VEH_ROAD: rubidium@9202: DoCommandP(this->window_number, sel_eng, 0, CcBuildRoadVeh, CMD_BUILD_ROAD_VEH | CMD_MSG(STR_9009_CAN_T_BUILD_ROAD_VEHICLE)); rubidium@9202: break; rubidium@9202: case VEH_SHIP: rubidium@9202: DoCommandP(this->window_number, sel_eng, 0, CcBuildShip, CMD_BUILD_SHIP | CMD_MSG(STR_980D_CAN_T_BUILD_SHIP)); rubidium@9202: break; rubidium@9202: case VEH_AIRCRAFT: rubidium@9202: DoCommandP(this->window_number, sel_eng, 0, CcBuildAircraft, CMD_BUILD_AIRCRAFT | CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT)); rubidium@9202: break; rubidium@9202: } bjarni@5779: } rubidium@9202: break; bjarni@4800: } rubidium@9202: rubidium@9202: case BUILD_VEHICLE_WIDGET_RENAME: { rubidium@9202: EngineID sel_eng = this->sel_engine; rubidium@9202: if (sel_eng != INVALID_ENGINE) { rubidium@9202: StringID str = STR_NULL; rubidium@9202: rubidium@9202: this->rename_engine = sel_eng; rubidium@9202: switch (this->vehicle_type) { rubidium@9202: default: NOT_REACHED(); rubidium@9202: case VEH_TRAIN: str = STR_886A_RENAME_TRAIN_VEHICLE_TYPE; break; rubidium@9202: case VEH_ROAD: str = STR_9036_RENAME_ROAD_VEHICLE_TYPE; break; rubidium@9202: case VEH_SHIP: str = STR_9838_RENAME_SHIP_TYPE; break; rubidium@9202: case VEH_AIRCRAFT: str = STR_A039_RENAME_AIRCRAFT_TYPE; break; rubidium@9202: } rubidium@9202: SetDParam(0, sel_eng); smatz@10148: ShowQueryString(STR_ENGINE_NAME, str, MAX_LENGTH_ENGINE_NAME_BYTES, MAX_LENGTH_ENGINE_NAME_PIXELS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT); rubidium@9202: } rubidium@9202: break; rubidium@9202: } KUDr@5187: } bjarni@4800: } bjarni@4800: rubidium@9202: virtual void OnInvalidateData(int data) rubidium@9202: { rubidium@9202: this->regenerate_list = true; rubidium@9202: } bjarni@4800: rubidium@9202: virtual void OnPaint() rubidium@9202: { rubidium@9202: if (this->regenerate_list) { rubidium@9202: this->regenerate_list = false; rubidium@9202: this->GenerateBuildList(); KUDr@5187: } bjarni@4800: peter1138@9403: uint max = min(this->vscroll.pos + this->vscroll.cap, this->eng_list.Length()); bjarni@4800: peter1138@9403: SetVScrollCount(this, this->eng_list.Length()); peter1138@10000: if (this->vehicle_type == VEH_TRAIN) { peter1138@10000: if (this->filter.railtype == RAILTYPE_END) { peter1138@10000: SetDParam(0, STR_ALL_AVAIL_RAIL_VEHICLES); peter1138@10000: } else { peter1138@10000: const RailtypeInfo *rti = GetRailTypeInfo(this->filter.railtype); peter1138@10000: SetDParam(0, rti->strings.build_caption); peter1138@10000: } peter1138@10000: } rubidium@9202: rubidium@9202: /* Set text of sort by dropdown */ rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_SORT_DROPDOWN].data = _sort_listing[this->vehicle_type][this->sort_criteria]; rubidium@9202: rubidium@9273: this->DrawWidgets(); rubidium@9202: peter1138@9679: DrawEngineList(this->vehicle_type, this->widget[BUILD_VEHICLE_WIDGET_LIST].left + 2, this->widget[BUILD_VEHICLE_WIDGET_LIST].right, this->widget[BUILD_VEHICLE_WIDGET_LIST].top + 1, &this->eng_list, this->vscroll.pos, max, this->sel_engine, 0, DEFAULT_GROUP); rubidium@9202: rubidium@9202: if (this->sel_engine != INVALID_ENGINE) { rubidium@9202: const Widget *wi = &this->widget[BUILD_VEHICLE_WIDGET_PANEL]; rubidium@9202: int text_end = DrawVehiclePurchaseInfo(2, wi->top + 1, wi->right - wi->left - 2, this->sel_engine); rubidium@9202: rubidium@9202: if (text_end > wi->bottom) { rubidium@9202: this->SetDirty(); rubidium@9202: ResizeWindowForWidget(this, BUILD_VEHICLE_WIDGET_PANEL, 0, text_end - wi->bottom); rubidium@9202: this->SetDirty(); rubidium@9202: } rubidium@9202: } rubidium@9202: rubidium@9273: this->DrawSortButtonState(BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING, this->descending_sort_order ? SBS_DOWN : SBS_UP); bjarni@4800: } rubidium@9202: rubidium@9202: virtual void OnDoubleClick(Point pt, int widget) rubidium@9202: { rubidium@9202: if (widget == BUILD_VEHICLE_WIDGET_LIST) { rubidium@9202: /* When double clicking, we want to buy a vehicle */ rubidium@9202: this->OnClick(pt, BUILD_VEHICLE_WIDGET_BUILD); rubidium@9202: } rubidium@9202: } rubidium@9202: rubidium@9202: virtual void OnQueryTextFinished(char *str) rubidium@9202: { smatz@10148: if (str == NULL) return; smatz@10148: smatz@10148: StringID err_str = STR_NULL; smatz@10148: _cmd_text = str; smatz@10148: switch (this->vehicle_type) { smatz@10148: default: NOT_REACHED(); smatz@10148: case VEH_TRAIN: err_str = STR_886B_CAN_T_RENAME_TRAIN_VEHICLE; break; smatz@10148: case VEH_ROAD: err_str = STR_9037_CAN_T_RENAME_ROAD_VEHICLE; break; smatz@10148: case VEH_SHIP: err_str = STR_9839_CAN_T_RENAME_SHIP_TYPE; break; smatz@10148: case VEH_AIRCRAFT: err_str = STR_A03A_CAN_T_RENAME_AIRCRAFT_TYPE; break; rubidium@9202: } smatz@10148: DoCommandP(0, this->rename_engine, 0, NULL, CMD_RENAME_ENGINE | CMD_MSG(err_str)); rubidium@9202: } rubidium@9202: rubidium@9202: virtual void OnDropdownSelect(int widget, int index) rubidium@9202: { rubidium@9202: if (this->sort_criteria != index) { rubidium@9202: this->sort_criteria = index; rubidium@9202: _last_sort_criteria[this->vehicle_type] = this->sort_criteria; rubidium@9202: this->regenerate_list = true; rubidium@9202: } rubidium@9202: this->SetDirty(); rubidium@9202: } rubidium@9202: rubidium@9202: virtual void OnResize(Point new_size, Point delta) rubidium@9202: { belugas@9633: if (delta.x != 0 && !this->listview_mode) { belugas@9633: ResizeButtons(this, BUILD_VEHICLE_WIDGET_BUILD, BUILD_VEHICLE_WIDGET_RENAME); belugas@9633: } rubidium@9202: if (delta.y == 0) return; rubidium@9202: rubidium@9202: this->vscroll.cap += delta.y / (int)GetVehicleListHeight(this->vehicle_type); rubidium@9202: this->widget[BUILD_VEHICLE_WIDGET_LIST].data = (this->vscroll.cap << 8) + 1; rubidium@9202: } rubidium@9202: }; bjarni@4800: bjarni@4800: static const WindowDesc _build_vehicle_desc = { rubidium@7368: WDP_AUTO, WDP_AUTO, 240, 174, 240, 256, rubidium@5893: WC_BUILD_VEHICLE, WC_NONE, bjarni@4800: WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, bjarni@4800: _build_vehicle_widgets, bjarni@4800: }; bjarni@4800: rubidium@6638: void ShowBuildVehicleWindow(TileIndex tile, VehicleType type) bjarni@4800: { truelight@7246: /* We want to be able to open both Available Train as Available Ships, truelight@7246: * so if tile == 0 (Available XXX Window), use 'type' as unique number. truelight@7246: * As it always is a low value, it won't collide with any real tile truelight@7246: * number. */ truelight@7246: uint num = (tile == 0) ? (int)type : tile; bjarni@4800: rubidium@10207: assert(IsCompanyBuildableVehicleType(type)); bjarni@5779: truelight@7246: DeleteWindowById(WC_BUILD_VEHICLE, num); bjarni@5779: rubidium@9202: new BuildVehicleWindow(&_build_vehicle_desc, tile, type); bjarni@4800: }