bjarni@4800: /* $Id$ */ bjarni@4800: 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 "functions.h" bjarni@4800: #include "table/sprites.h" bjarni@4800: #include "table/strings.h" bjarni@4800: #include "window.h" bjarni@4800: #include "gui.h" bjarni@4800: #include "vehicle.h" bjarni@4800: #include "gfx.h" bjarni@4800: #include "station.h" bjarni@4800: #include "command.h" bjarni@4800: #include "engine.h" bjarni@4800: #include "player.h" bjarni@4800: #include "depot.h" bjarni@4800: #include "airport.h" bjarni@4800: #include "vehicle_gui.h" bjarni@4800: #include "newgrf_engine.h" bjarni@4800: #include "date.h" bjarni@4800: #include "strings.h" bjarni@4800: bjarni@4800: 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_TEXT, 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, Darkvater@5273: }; bjarni@4800: bjarni@4800: static const Widget _build_vehicle_widgets[] = { bjarni@4800: { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, bjarni@5779: { WWT_CAPTION, RESIZE_RIGHT, 14, 11, 227, 0, 13, 0x0, STR_018C_WINDOW_TITLE_DRAG_THIS }, bjarni@4800: { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 80, 14, 25, STR_SORT_BY, STR_SORT_ORDER_TIP}, bjarni@5779: { WWT_PANEL, RESIZE_RIGHT, 14, 81, 215, 14, 25, 0x0, STR_SORT_CRITERIA_TIP}, bjarni@5779: { WWT_TEXTBTN, RESIZE_LR, 14, 216, 227, 14, 25, STR_0225, STR_SORT_CRITERIA_TIP}, bjarni@5779: { WWT_MATRIX, RESIZE_RB, 14, 0, 215, 26, 121, 0x0, STR_NULL }, bjarni@5779: { WWT_SCROLLBAR, RESIZE_LRB, 14, 216, 227, 26, 121, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST }, bjarni@5816: { WWT_PANEL, RESIZE_RTB, 14, 0, 227, 122, 223, 0x0, STR_NULL }, bjarni@4800: bjarni@5816: { WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 114, 224, 235, 0x0, STR_NULL }, bjarni@5816: { WWT_PUSHTXTBTN, RESIZE_RTB, 14, 115, 215, 224, 235, 0x0, STR_NULL }, bjarni@5816: { WWT_RESIZEBOX, RESIZE_LRTB, 14, 216, 227, 224, 235, 0x0, STR_RESIZE_BUTTON }, bjarni@4800: { WIDGETS_END}, bjarni@4800: }; bjarni@4800: bjarni@5779: /* Setup widget strings to fit the different types of vehicles */ bjarni@5790: static void SetupWindowStrings(Window *w, byte type) bjarni@5779: { bjarni@5779: switch (type) { bjarni@5779: case VEH_Train: bjarni@5779: w->widget[BUILD_VEHICLE_WIDGET_CAPTION].data = STR_JUST_STRING; bjarni@5779: w->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips = STR_8843_TRAIN_VEHICLE_SELECTION; bjarni@5779: w->widget[BUILD_VEHICLE_WIDGET_BUILD].data = STR_881F_BUILD_VEHICLE; bjarni@5779: w->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips = STR_8844_BUILD_THE_HIGHLIGHTED_TRAIN; bjarni@5779: w->widget[BUILD_VEHICLE_WIDGET_RENAME].data = STR_8820_RENAME; bjarni@5779: w->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_8845_RENAME_TRAIN_VEHICLE_TYPE; bjarni@5779: break; bjarni@5786: case VEH_Road: bjarni@5786: w->widget[BUILD_VEHICLE_WIDGET_CAPTION].data = STR_9006_NEW_ROAD_VEHICLES; bjarni@5786: w->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips = STR_9026_ROAD_VEHICLE_SELECTION; bjarni@5786: w->widget[BUILD_VEHICLE_WIDGET_BUILD].data = STR_9007_BUILD_VEHICLE; bjarni@5786: w->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips = STR_9027_BUILD_THE_HIGHLIGHTED_ROAD; bjarni@5786: w->widget[BUILD_VEHICLE_WIDGET_RENAME].data = STR_9034_RENAME; bjarni@5786: w->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_9035_RENAME_ROAD_VEHICLE_TYPE; bjarni@5786: break; bjarni@5783: case VEH_Ship: bjarni@5783: w->widget[BUILD_VEHICLE_WIDGET_CAPTION].data = STR_9808_NEW_SHIPS; bjarni@5783: w->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips = STR_9825_SHIP_SELECTION_LIST_CLICK; bjarni@5783: w->widget[BUILD_VEHICLE_WIDGET_BUILD].data = STR_9809_BUILD_SHIP; bjarni@5783: w->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips = STR_9826_BUILD_THE_HIGHLIGHTED_SHIP; bjarni@5783: w->widget[BUILD_VEHICLE_WIDGET_RENAME].data = STR_9836_RENAME; bjarni@5783: w->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_9837_RENAME_SHIP_TYPE; bjarni@5783: break; bjarni@5779: case VEH_Aircraft: bjarni@5779: w->widget[BUILD_VEHICLE_WIDGET_CAPTION].data = STR_A005_NEW_AIRCRAFT; bjarni@5779: w->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips = STR_A025_AIRCRAFT_SELECTION_LIST; bjarni@5779: w->widget[BUILD_VEHICLE_WIDGET_BUILD].data = STR_A006_BUILD_AIRCRAFT; bjarni@5779: w->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips = STR_A026_BUILD_THE_HIGHLIGHTED_AIRCRAFT; bjarni@5779: w->widget[BUILD_VEHICLE_WIDGET_RENAME].data = STR_A037_RENAME; bjarni@5779: w->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_A038_RENAME_AIRCRAFT_TYPE; bjarni@5779: break; bjarni@5779: } bjarni@5779: } bjarni@5779: bjarni@4800: static bool _internal_sort_order; // descending/ascending bjarni@5779: 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; 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 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; Darkvater@4912: GetString(last_name[0], GetCustomEngineName(va), lastof(last_name[0])); bjarni@4800: } bjarni@4800: bjarni@4804: if (vb != last_engine[1]) { bjarni@4804: last_engine[1] = vb; Darkvater@4912: GetString(last_name[1], GetCustomEngineName(vb), 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: { bjarni@5779: int va = RailVehInfo(*(const EngineID*)a)->base_cost; bjarni@5779: int vb = RailVehInfo(*(const EngineID*)b)->base_cost; 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: belugas@5868: int va = rvi_a->power << (rvi_a->railveh_type == RAILVEH_MULTIHEAD ? 1 : 0); belugas@5868: int vb = rvi_b->power << (rvi_b->railveh_type == RAILVEH_MULTIHEAD ? 1 : 0); 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: belugas@5868: int va = rvi_a->running_cost_base * _price.running_rail[rvi_a->running_cost_class] * (rvi_a->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1); belugas@5868: int vb = rvi_b->running_cost_base * _price.running_rail[rvi_b->running_cost_class] * (rvi_b->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1); 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 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. */ bjarni@5779: int va = (rvi_a->running_cost_base * _price.running_rail[rvi_a->running_cost_class]) / max((uint16)1, rvi_a->power); bjarni@5779: int vb = (rvi_b->running_cost_base * _price.running_rail[rvi_b->running_cost_class]) / max((uint16)1, rvi_b->power); bjarni@5779: int r = vb - va; bjarni@5779: bjarni@5779: return _internal_sort_order ? -r : r; bjarni@5779: } bjarni@5779: bjarni@6023: static int CDECL TrainEngineNumberSorter(const void *a, const void *b) bjarni@6023: { bjarni@6023: const EngineID va = *(const EngineID*)a; bjarni@6023: const EngineID vb = *(const EngineID*)b; bjarni@6023: int r = ListPositionOfEngine(va) - ListPositionOfEngine(vb); bjarni@6023: bjarni@6023: return _internal_sort_order ? -r : r; bjarni@6023: } bjarni@6023: 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@4800: /* Aircraft sorting functions */ bjarni@4800: bjarni@4800: static int CDECL AircraftEngineCostSorter(const void *a, const void *b) bjarni@4800: { bjarni@4800: const int va = AircraftVehInfo(*(const EngineID*)a)->base_cost; bjarni@4800: const int vb = AircraftVehInfo(*(const EngineID*)b)->base_cost; 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@4800: const int va = AircraftVehInfo(*(const EngineID*)a)->passenger_capacity; bjarni@4800: const int vb = AircraftVehInfo(*(const EngineID*)b)->passenger_capacity; 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@5785: static EngList_SortTypeFunction * const _sorter[][9] = {{ bjarni@5790: /* Trains */ bjarni@6023: &TrainEngineNumberSorter, 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@5785: },{ bjarni@5790: /* Road vehicles */ bjarni@5785: &EngineNumberSorter, bjarni@5785: &EngineIntroDateSorter, bjarni@5785: &EngineNameSorter, bjarni@5785: &EngineReliabilitySorter, bjarni@5785: },{ bjarni@5790: /* Ships */ bjarni@5785: &EngineNumberSorter, bjarni@5785: &EngineIntroDateSorter, bjarni@5785: &EngineNameSorter, bjarni@5785: &EngineReliabilitySorter, bjarni@5785: },{ 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@5785: static const StringID _sort_listing[][10] = {{ 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@5779: INVALID_STRING_ID bjarni@5785: },{ bjarni@5790: /* Road vehicles */ bjarni@5783: STR_ENGINE_SORT_ENGINE_ID, bjarni@5783: STR_ENGINE_SORT_INTRO_DATE, bjarni@5783: STR_SORT_BY_DROPDOWN_NAME, bjarni@5783: STR_SORT_BY_RELIABILITY, bjarni@5783: INVALID_STRING_ID bjarni@5785: },{ bjarni@5790: /* Ships */ bjarni@5785: STR_ENGINE_SORT_ENGINE_ID, bjarni@5785: STR_ENGINE_SORT_INTRO_DATE, bjarni@5785: STR_SORT_BY_DROPDOWN_NAME, bjarni@5785: STR_SORT_BY_RELIABILITY, bjarni@5785: INVALID_STRING_ID bjarni@5785: },{ 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: 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 */ bjarni@5779: SetDParam(0, (rvi->base_cost * _price.build_railwagon) >> 8); bjarni@5779: DrawString(x, y, STR_PURCHASE_INFO_COST, 0); bjarni@5779: y += 10; bjarni@5779: bjarni@5779: /* Wagon weight - (including cargo) */ bjarni@5779: SetDParam(0, rvi->weight); bjarni@5779: SetDParam(1, (_cargoc.weights[rvi->cargo_type] * rvi->capacity >> 4) + rvi->weight); bjarni@5779: DrawString(x, y, STR_PURCHASE_INFO_WEIGHT_CWEIGHT, 0); bjarni@5779: y += 10; bjarni@5779: bjarni@5779: /* Wagon speed limit, displayed if above zero */ bjarni@5779: if (rvi->max_speed > 0 && _patches.wagon_speed_limits) { peter1138@5874: SetDParam(0, rvi->max_speed * 10 / 16); bjarni@5779: DrawString(x,y, STR_PURCHASE_INFO_SPEED, 0); bjarni@5779: y += 10; bjarni@5779: } 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); bjarni@5779: bjarni@5779: /* Purchase Cost - Engine weight */ bjarni@5779: SetDParam(0, rvi->base_cost * (_price.build_railvehicle >> 3) >> 5); bjarni@5779: SetDParam(1, rvi->weight << multihead); bjarni@5779: DrawString(x,y, STR_PURCHASE_INFO_COST_WEIGHT, 0); bjarni@5779: y += 10; bjarni@5779: bjarni@5779: /* Max speed - Engine power */ peter1138@5874: SetDParam(0, rvi->max_speed * 10 / 16); bjarni@5779: SetDParam(1, rvi->power << multihead); bjarni@5779: DrawString(x,y, STR_PURCHASE_INFO_SPEED_POWER, 0); bjarni@5779: y += 10; bjarni@5779: bjarni@5779: /* Max tractive effort - not applicable if old acceleration or maglev */ tron@5823: if (_patches.realistic_acceleration && rvi->railtype != RAILTYPE_MAGLEV) { bjarni@5779: SetDParam(0, ((rvi->weight << multihead) * 10 * rvi->tractive_effort) / 256); bjarni@5779: DrawString(x, y, STR_PURCHASE_INFO_MAX_TE, 0); bjarni@5779: y += 10; bjarni@5779: } bjarni@5779: bjarni@5779: /* Running cost */ bjarni@5779: SetDParam(0, (rvi->running_cost_base * _price.running_rail[rvi->running_cost_class] >> 8) << multihead); bjarni@5779: DrawString(x,y, STR_PURCHASE_INFO_RUNNINGCOST, 0); bjarni@5779: y += 10; 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); bjarni@5779: DrawString(x,y, STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT, 0); 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: bool refittable = (_engine_info[engine_number].refit_mask != 0); bjarni@5786: bjarni@5786: /* Purchase cost - Max speed */ bjarni@5786: SetDParam(0, rvi->base_cost * (_price.roadveh_base>>3)>>5); peter1138@5874: SetDParam(1, rvi->max_speed * 10 / 32); bjarni@5786: DrawString(x, y, STR_PURCHASE_INFO_COST_SPEED, 0); bjarni@5786: y += 10; bjarni@5786: bjarni@5786: /* Running cost */ bjarni@5786: SetDParam(0, rvi->running_cost * _price.roadveh_running >> 8); bjarni@5786: DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, 0); bjarni@5786: y += 10; bjarni@5786: bjarni@5786: /* Cargo type + capacity */ bjarni@5786: SetDParam(0, rvi->cargo_type); bjarni@5786: SetDParam(1, rvi->capacity); bjarni@5786: SetDParam(2, refittable ? STR_9842_REFITTABLE : STR_EMPTY); bjarni@5786: DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, 0); bjarni@5786: y += 10; bjarni@5786: bjarni@5786: return y; 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 */ bjarni@5783: SetDParam(0, svi->base_cost * (_price.ship_base>>3)>>5); peter1138@5874: SetDParam(1, svi->max_speed * 10 / 32); bjarni@5783: DrawString(x,y, STR_PURCHASE_INFO_COST_SPEED, 0); bjarni@5783: y += 10; bjarni@5783: bjarni@5783: /* Cargo type + capacity */ bjarni@5783: SetDParam(0, svi->cargo_type); bjarni@5783: SetDParam(1, svi->capacity); bjarni@5783: SetDParam(2, svi->refittable ? STR_9842_REFITTABLE : STR_EMPTY); bjarni@5783: DrawString(x,y, STR_PURCHASE_INFO_CAPACITY, 0); bjarni@5783: y += 10; bjarni@5783: bjarni@5783: /* Running cost */ bjarni@5783: SetDParam(0, svi->running_cost * _price.ship_running >> 8); bjarni@5783: DrawString(x,y, STR_PURCHASE_INFO_RUNNINGCOST, 0); 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 */ bjarni@4800: SetDParam(0, avi->base_cost * (_price.aircraft_base>>3)>>5); peter1138@5874: SetDParam(1, avi->max_speed * 8); bjarni@4800: DrawString(x, y, STR_PURCHASE_INFO_COST_SPEED, 0); 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); bjarni@4800: DrawString(x, y, STR_PURCHASE_INFO_AIRCRAFT_CAPACITY, 0); 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); bjarni@4800: SetDParam(1, AircraftDefaultCargoCapacity(cargo, engine_number)); bjarni@4800: SetDParam(2, STR_9842_REFITTABLE); bjarni@4800: DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, 0); bjarni@4800: } bjarni@4800: y += 10; bjarni@4800: bjarni@4800: /* Running cost */ bjarni@4800: SetDParam(0, avi->running_cost * _price.aircraft_running >> 8); bjarni@4800: DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, 0); 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 bjarni@5779: */ bjarni@5779: void 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); bjarni@5790: bool refitable = false; bjarni@5779: bjarni@5779: switch (e->type) { bjarni@5779: case VEH_Train: { bjarni@5779: const RailVehicleInfo *rvi = RailVehInfo(engine_number); bjarni@5779: bjarni@5819: refitable = (EngInfo(engine_number)->refit_mask != 0) && (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 */ bjarni@5779: if (rvi->capacity == 0) { bjarni@5779: SetDParam(0, CT_INVALID); bjarni@5779: SetDParam(2, STR_EMPTY); bjarni@5779: } else { belugas@5868: int multihead = (rvi->railveh_type == RAILVEH_MULTIHEAD ? 1 : 0); bjarni@5815: bjarni@5779: SetDParam(0, rvi->cargo_type); bjarni@5779: SetDParam(1, (rvi->capacity * (CountArticulatedParts(engine_number) + 1)) << multihead); bjarni@5819: SetDParam(2, refitable ? STR_9842_REFITTABLE : STR_EMPTY); bjarni@5779: } bjarni@5779: DrawString(x,y, STR_PURCHASE_INFO_CAPACITY, 0); bjarni@5779: y += 10; bjarni@5779: } bjarni@5779: break; bjarni@5786: case VEH_Road: bjarni@5817: y = DrawRoadVehPurchaseInfo(x, y, engine_number, RoadVehInfo(engine_number)); bjarni@5790: refitable = true; bjarni@5786: break; bjarni@5790: case VEH_Ship: { bjarni@5790: const ShipVehicleInfo *svi = ShipVehInfo(engine_number); bjarni@5817: y = DrawShipPurchaseInfo(x, y, engine_number, svi); bjarni@5790: refitable = svi->refittable; bjarni@5790: } break; bjarni@5779: case VEH_Aircraft: bjarni@5817: y = DrawAircraftPurchaseInfo(x, y, engine_number, AircraftVehInfo(engine_number)); bjarni@5790: refitable = true; bjarni@5779: break; bjarni@5779: } bjarni@5779: bjarni@5779: /* Draw details, that applies to all types except rail wagons */ belugas@5868: 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); bjarni@5779: DrawString(x, y, STR_PURCHASE_INFO_DESIGNED_LIFE, 0); bjarni@5779: y += 10; bjarni@5779: bjarni@5779: /* Reliability */ bjarni@5779: SetDParam(0, e->reliability * 100 >> 16); bjarni@5779: DrawString(x, y, STR_PURCHASE_INFO_RELIABILITY, 0); bjarni@5779: y += 10; bjarni@5779: } bjarni@4800: bjarni@4800: /* Additional text from NewGRF */ peter1138@4930: y += ShowAdditionalText(x, y, w, engine_number); bjarni@5790: if (refitable) y += ShowRefitOptionsList(x, y, w, engine_number); bjarni@4800: } bjarni@4800: bjarni@5779: /* Figure out what train EngineIDs to put in the list */ bjarni@5779: static void GenerateBuildTrainList(Window *w) bjarni@5779: { bjarni@5779: EngineID eid, sel_id; bjarni@5779: int num_engines = 0; bjarni@5779: int num_wagons = 0; bjarni@5779: buildvehicle_d *bv = &WP(w, buildvehicle_d); bjarni@5779: bjarni@5779: bv->filter.railtype = (w->window_number == 0) ? RAILTYPE_END : GetRailType(w->window_number); bjarni@5779: bjarni@5779: EngList_RemoveAll(&bv->eng_list); bjarni@5779: bjarni@5779: /* Make list of all available train engines and wagons. bjarni@5779: * Also check to see if the previously selected engine is still available, bjarni@5779: * and if not, reset selection to INVALID_ENGINE. This could be the case bjarni@5779: * when engines become obsolete and are removed */ bjarni@5779: for (sel_id = INVALID_ENGINE, eid = 0; eid < NUM_TRAIN_ENGINES; eid++) { bjarni@5779: const RailVehicleInfo *rvi = RailVehInfo(eid); bjarni@5779: tron@5823: if (bv->filter.railtype != RAILTYPE_END && !HasPowerOnRail(rvi->railtype, bv->filter.railtype)) continue; bjarni@5779: if (!IsEngineBuildable(eid, VEH_Train, _local_player)) continue; bjarni@5779: bjarni@5779: EngList_Add(&bv->eng_list, eid); belugas@5868: if (rvi->railveh_type != RAILVEH_WAGON) { bjarni@5779: num_engines++; bjarni@5779: } else { bjarni@5779: num_wagons++; bjarni@5779: } bjarni@5779: bjarni@5779: if (eid == bv->sel_engine) sel_id = eid; bjarni@5779: } bjarni@5779: bjarni@5779: bv->sel_engine = sel_id; bjarni@5779: bjarni@5779: // make engines first, and then wagons, sorted by ListPositionOfEngine() bjarni@5779: _internal_sort_order = false; bjarni@5779: EngList_Sort(&bv->eng_list, TrainEnginesThenWagonsSorter); bjarni@5779: bjarni@5779: // and then sort engines bjarni@5779: _internal_sort_order = bv->descending_sort_order; bjarni@5785: EngList_SortPartial(&bv->eng_list, _sorter[0][bv->sort_criteria], 0, num_engines); bjarni@5779: bjarni@5779: // and finally sort wagons bjarni@5785: EngList_SortPartial(&bv->eng_list, _sorter[0][bv->sort_criteria], num_engines, num_wagons); bjarni@5779: } bjarni@5779: bjarni@5786: /* Figure out what road vehicle EngineIDs to put in the list */ bjarni@5786: static void GenerateBuildRoadVehList(Window *w) bjarni@5786: { bjarni@5786: EngineID eid, sel_id; bjarni@5786: buildvehicle_d *bv = &WP(w, buildvehicle_d); bjarni@5786: bjarni@5786: EngList_RemoveAll(&bv->eng_list); bjarni@5786: bjarni@5786: sel_id = INVALID_ENGINE; bjarni@5786: bjarni@5786: for (eid = ROAD_ENGINES_INDEX; eid < ROAD_ENGINES_INDEX + NUM_ROAD_ENGINES; eid++) { bjarni@5786: if (!IsEngineBuildable(eid, VEH_Road, _local_player)) continue; bjarni@5786: EngList_Add(&bv->eng_list, eid); bjarni@5786: bjarni@5786: if (eid == bv->sel_engine) sel_id = eid; bjarni@5786: } bjarni@5786: bv->sel_engine = sel_id; bjarni@5786: } bjarni@5786: bjarni@5786: /* Figure out what ship EngineIDs to put in the list */ bjarni@5783: static void GenerateBuildShipList(Window *w) bjarni@5783: { bjarni@5783: EngineID eid, sel_id; bjarni@5783: buildvehicle_d *bv = &WP(w, buildvehicle_d); bjarni@5783: bjarni@5783: EngList_RemoveAll(&bv->eng_list); bjarni@5783: bjarni@5783: sel_id = INVALID_ENGINE; bjarni@5783: bjarni@5783: for (eid = SHIP_ENGINES_INDEX; eid < SHIP_ENGINES_INDEX + NUM_SHIP_ENGINES; eid++) { bjarni@5783: if (!IsEngineBuildable(eid, VEH_Ship, _local_player)) continue; bjarni@5783: EngList_Add(&bv->eng_list, eid); bjarni@5783: bjarni@5783: if (eid == bv->sel_engine) sel_id = eid; bjarni@5783: } bjarni@5783: bv->sel_engine = sel_id; bjarni@5783: } bjarni@5783: bjarni@5783: /* Figure out what aircraft EngineIDs to put in the list */ KUDr@5187: static void GenerateBuildAircraftList(Window *w) bjarni@4800: { Darkvater@5200: EngineID eid, sel_id; KUDr@5187: buildvehicle_d *bv = &WP(w, buildvehicle_d); bjarni@4800: KUDr@5187: EngList_RemoveAll(&bv->eng_list); bjarni@4800: Darkvater@5200: /* Make list of all available planes. Darkvater@5200: * Also check to see if the previously selected plane is still available, Darkvater@5200: * and if not, reset selection to INVALID_ENGINE. This could be the case Darkvater@5200: * when planes become obsolete and are removed */ Darkvater@5200: sel_id = INVALID_ENGINE; bjarni@4800: for (eid = AIRCRAFT_ENGINES_INDEX; eid < AIRCRAFT_ENGINES_INDEX + NUM_AIRCRAFT_ENGINES; eid++) { peter1138@5215: if (IsEngineBuildable(eid, VEH_Aircraft, _local_player)) { KUDr@5187: EngList_Add(&bv->eng_list, eid); Darkvater@5200: Darkvater@5200: if (eid == bv->sel_engine) sel_id = eid; bjarni@4800: } bjarni@4800: } Darkvater@5200: Darkvater@5200: bv->sel_engine = sel_id; bjarni@4800: } bjarni@4800: bjarni@5779: /* Generate the list of vehicles */ bjarni@4800: static void GenerateBuildList(Window *w) bjarni@4800: { KUDr@5187: buildvehicle_d *bv = &WP(w, buildvehicle_d); KUDr@5187: KUDr@5187: switch (bv->vehicle_type) { bjarni@5779: case VEH_Train: bjarni@5779: GenerateBuildTrainList(w); bjarni@5785: return; // trains should not reach the last sorting bjarni@5786: case VEH_Road: bjarni@5786: GenerateBuildRoadVehList(w); bjarni@5786: break; bjarni@5783: case VEH_Ship: bjarni@5783: GenerateBuildShipList(w); bjarni@5783: break; bjarni@4800: case VEH_Aircraft: KUDr@5187: GenerateBuildAircraftList(w); bjarni@4800: break; bjarni@4800: } bjarni@5785: _internal_sort_order = bv->descending_sort_order; bjarni@5955: EngList_Sort(&bv->eng_list, _sorter[bv->vehicle_type][bv->sort_criteria]); bjarni@4800: } bjarni@4800: bjarni@5800: static void DrawVehicleEngine(byte type, int x, int y, EngineID engine, SpriteID pal) bjarni@5800: { bjarni@5800: switch (type) { bjarni@5800: case VEH_Train: DrawTrainEngine( x, y, engine, pal); break; bjarni@5800: case VEH_Road: DrawRoadVehEngine( x, y, engine, pal); break; bjarni@5800: case VEH_Ship: DrawShipEngine( x, y, engine, pal); break; bjarni@5800: 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@5944: * @param show_count Display the number of vehicles (used by autoreplace) bjarni@5800: */ bjarni@5944: void DrawEngineList(byte type, int x, int y, const EngineList eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count) bjarni@5800: { bjarni@5800: byte step_size = GetVehicleListHeight(type); bjarni@5800: byte x_offset = 0; bjarni@5800: byte y_offset = 0; bjarni@5944: Player *p = GetPlayer(_local_player); bjarni@5800: bjarni@5802: assert(max <= EngList_Count(&eng_list)); bjarni@5802: bjarni@5800: switch (type) { bjarni@5800: 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 */ bjarni@5800: case VEH_Road: bjarni@5800: x += 26; bjarni@5800: x_offset = 30; bjarni@5800: y += 2; bjarni@5800: y_offset = 4; bjarni@5800: break; bjarni@5800: case VEH_Ship: bjarni@5800: x += 35; bjarni@5800: x_offset = 40; bjarni@5800: y += 7; bjarni@5800: y_offset = 3; bjarni@5800: break; bjarni@5800: 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: bjarni@5800: for (; min < max; min++, y += step_size) { bjarni@5800: const EngineID engine = eng_list[min]; bjarni@5800: bjarni@5800: DrawString(x + x_offset, y, GetCustomEngineName(engine), engine == selected_id ? 0xC : 0x10); bjarni@5944: DrawVehicleEngine(type, x, y + y_offset, engine, (show_count && p->num_engines[engine] == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_player)); bjarni@5944: if (show_count) { bjarni@5944: SetDParam(0, p->num_engines[engine]); bjarni@5944: DrawStringRightAligned(213, y + (GetVehicleListHeight(type) == 14 ? 3 : 8), STR_TINY_BLACK, 0); bjarni@5944: } bjarni@5800: } bjarni@5800: } bjarni@5800: bjarni@5779: static void DrawBuildVehicleWindow(Window *w) bjarni@4800: { Darkvater@5200: const buildvehicle_d *bv = &WP(w, buildvehicle_d); bjarni@5800: uint max = min(w->vscroll.pos + w->vscroll.cap, EngList_Count(&bv->eng_list)); bjarni@4800: bjarni@4800: SetWindowWidgetDisabledState(w, BUILD_VEHICLE_WIDGET_BUILD, w->window_number == 0); bjarni@4800: Darkvater@5201: SetVScrollCount(w, EngList_Count(&bv->eng_list)); bjarni@5779: SetDParam(0, bv->filter.railtype + STR_881C_NEW_RAIL_VEHICLES); // This should only affect rail vehicles bjarni@4800: DrawWindowWidgets(w); bjarni@4800: bjarni@5944: DrawEngineList(bv->vehicle_type, 2, 27, bv->eng_list, w->vscroll.pos, max, bv->sel_engine, false); bjarni@5786: bjarni@5800: if (bv->sel_engine != INVALID_ENGINE) { bjarni@5800: const Widget *wi = &w->widget[BUILD_VEHICLE_WIDGET_PANEL]; bjarni@5800: DrawVehiclePurchaseInfo(2, wi->top + 1, wi->right - wi->left - 2, bv->sel_engine); bjarni@5800: } bjarni@5783: bjarni@5955: DrawString(85, 15, _sort_listing[bv->vehicle_type][bv->sort_criteria], 0x10); bjarni@5785: DoDrawString(bv->descending_sort_order ? DOWNARROW : UPARROW, 69, 15, 0x10); bjarni@4800: } bjarni@4800: bjarni@5779: static void BuildVehicleClickEvent(Window *w, WindowEvent *e) bjarni@4800: { KUDr@5187: buildvehicle_d *bv = &WP(w, buildvehicle_d); bjarni@4800: bjarni@4800: switch (e->we.click.widget) { bjarni@4800: case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING: Darkvater@5199: bv->descending_sort_order ^= true; bjarni@5955: _last_sort_order[bv->vehicle_type] = bv->descending_sort_order; bjarni@5781: bv->regenerate_list = true; bjarni@4800: SetWindowDirty(w); bjarni@4800: break; bjarni@4800: bjarni@4800: case BUILD_VEHICLE_WIDGET_LIST: { bjarni@5779: uint i = (e->we.click.pt.y - 26) / GetVehicleListHeight(bv->vehicle_type) + w->vscroll.pos; Darkvater@5201: uint num_items = EngList_Count(&bv->eng_list); Darkvater@5201: bv->sel_engine = (i < num_items) ? bv->eng_list[i] : INVALID_ENGINE; Darkvater@5201: SetWindowDirty(w); KUDr@5187: break; KUDr@5187: } bjarni@4800: bjarni@5779: case BUILD_VEHICLE_WIDGET_SORT_TEXT: case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN: // Select sorting criteria dropdown menu bjarni@5955: ShowDropDownMenu(w, _sort_listing[bv->vehicle_type], bv->sort_criteria, BUILD_VEHICLE_WIDGET_SORT_DROPDOWN, 0, 0); bjarni@5785: break; bjarni@4800: bjarni@4800: case BUILD_VEHICLE_WIDGET_BUILD: { KUDr@5187: EngineID sel_eng = bv->sel_engine; peter1138@5192: if (sel_eng != INVALID_ENGINE) { bjarni@5779: switch (bv->vehicle_type) { bjarni@5779: case VEH_Train: belugas@5868: DoCommandP(w->window_number, sel_eng, 0, (RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) ? CcBuildWagon : CcBuildLoco, bjarni@5779: CMD_BUILD_RAIL_VEHICLE | CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE)); bjarni@5779: break; bjarni@5786: case VEH_Road: bjarni@5786: DoCommandP(w->window_number, sel_eng, 0, CcBuildRoadVeh, CMD_BUILD_ROAD_VEH | CMD_MSG(STR_9009_CAN_T_BUILD_ROAD_VEHICLE)); bjarni@5786: break; bjarni@5783: case VEH_Ship: bjarni@5783: DoCommandP(w->window_number, sel_eng, 0, CcBuildShip, CMD_BUILD_SHIP | CMD_MSG(STR_980D_CAN_T_BUILD_SHIP)); bjarni@5783: break; bjarni@5779: case VEH_Aircraft: bjarni@5779: DoCommandP(w->window_number, sel_eng, 0, CcBuildAircraft, CMD_BUILD_AIRCRAFT | CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT)); bjarni@5779: break; bjarni@5779: } peter1138@5192: } KUDr@5187: break; KUDr@5187: } bjarni@4800: bjarni@4800: case BUILD_VEHICLE_WIDGET_RENAME: { KUDr@5187: EngineID sel_eng = bv->sel_engine; bjarni@4800: if (sel_eng != INVALID_ENGINE) { bjarni@5779: StringID str = STR_NULL; bjarni@5779: KUDr@5187: bv->rename_engine = sel_eng; bjarni@5779: switch (bv->vehicle_type) { bjarni@5779: case VEH_Train: str = STR_886A_RENAME_TRAIN_VEHICLE_TYPE; break; bjarni@5786: case VEH_Road: str = STR_9036_RENAME_ROAD_VEHICLE_TYPE; break; bjarni@5783: case VEH_Ship: str = STR_9838_RENAME_SHIP_TYPE; break; bjarni@5779: case VEH_Aircraft: str = STR_A039_RENAME_AIRCRAFT_TYPE; break; bjarni@5779: } bjarni@5779: ShowQueryString(GetCustomEngineName(sel_eng), str, 31, 160, w, CS_ALPHANUMERAL); bjarni@4800: } KUDr@5187: break; KUDr@5187: } bjarni@4800: } bjarni@4800: } bjarni@4800: bjarni@5779: static void NewVehicleWndProc(Window *w, WindowEvent *e) bjarni@4800: { KUDr@5187: buildvehicle_d *bv = &WP(w, buildvehicle_d); KUDr@5187: bjarni@4800: switch (e->event) { bjarni@4800: case WE_INVALIDATE_DATA: bjarni@5781: bv->regenerate_list = true; bjarni@5781: SetWindowDirty(w); bjarni@4800: break; bjarni@4800: bjarni@4800: case WE_DESTROY: KUDr@5187: EngList_Destroy(&bv->eng_list); bjarni@4800: break; bjarni@4800: bjarni@4800: case WE_PAINT: bjarni@5781: if (bv->regenerate_list) { bjarni@5781: bv->regenerate_list = false; bjarni@5781: GenerateBuildList(w); bjarni@5781: } bjarni@5779: DrawBuildVehicleWindow(w); bjarni@4800: break; bjarni@4800: bjarni@4800: case WE_CLICK: bjarni@5779: BuildVehicleClickEvent(w, e); bjarni@4800: break; bjarni@4800: bjarni@4800: case WE_ON_EDIT_TEXT: { bjarni@4800: if (e->we.edittext.str[0] != '\0') { bjarni@5779: StringID str = STR_NULL; bjarni@4800: _cmd_text = e->we.edittext.str; bjarni@5779: switch (bv->vehicle_type) { bjarni@5779: case VEH_Train: str = STR_886B_CAN_T_RENAME_TRAIN_VEHICLE; break; bjarni@5786: case VEH_Road: str = STR_9037_CAN_T_RENAME_ROAD_VEHICLE; break; bjarni@5783: case VEH_Ship: str = STR_9839_CAN_T_RENAME_SHIP_TYPE; break; bjarni@5779: case VEH_Aircraft: str = STR_A03A_CAN_T_RENAME_AIRCRAFT_TYPE; break; bjarni@5779: } bjarni@5779: DoCommandP(0, bv->rename_engine, 0, NULL, CMD_RENAME_ENGINE | CMD_MSG(str)); bjarni@4800: } KUDr@5187: break; KUDr@5187: } bjarni@4800: bjarni@4800: case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */ KUDr@5187: if (bv->sort_criteria != e->we.dropdown.index) { bjarni@5779: bv->sort_criteria = e->we.dropdown.index; bjarni@5955: _last_sort_criteria[bv->vehicle_type] = bv->sort_criteria; bjarni@5781: bv->regenerate_list = true; bjarni@4800: } bjarni@4800: SetWindowDirty(w); peter1138@5192: break; bjarni@4800: peter1138@5192: case WE_RESIZE: bjarni@5824: if (e->we.sizing.diff.x != 0) ResizeButtons(w, BUILD_VEHICLE_WIDGET_BUILD, BUILD_VEHICLE_WIDGET_RENAME); bjarni@5779: if (e->we.sizing.diff.y == 0) break; bjarni@5779: bjarni@6026: w->vscroll.cap += e->we.sizing.diff.y / (int)GetVehicleListHeight(bv->vehicle_type); peter1138@5192: w->widget[BUILD_VEHICLE_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1; peter1138@5192: break; bjarni@4800: } bjarni@4800: } bjarni@4800: bjarni@4800: static const WindowDesc _build_vehicle_desc = { bjarni@5816: WDP_AUTO, WDP_AUTO, 228, 236, 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@5779: NewVehicleWndProc bjarni@4800: }; bjarni@4800: bjarni@4800: void ShowBuildVehicleWindow(TileIndex tile, byte type) bjarni@4800: { KUDr@5187: buildvehicle_d *bv; bjarni@4800: Window *w; bjarni@4800: bjarni@5779: assert(IsPlayerBuildableVehicleType(type)); bjarni@5779: bjarni@4800: DeleteWindowById(WC_BUILD_VEHICLE, tile); bjarni@5779: bjarni@4800: w = AllocateWindowDescFront(&_build_vehicle_desc, tile); bjarni@5779: bjarni@4800: if (w == NULL) return; bjarni@4800: Darkvater@5199: w->caption_color = (tile != 0) ? GetTileOwner(tile) : _local_player; KUDr@5187: w->resize.step_height = GetVehicleListHeight(type); bjarni@5779: w->vscroll.cap = w->resize.step_height == 24 ? 4 : 8; KUDr@5187: w->widget[BUILD_VEHICLE_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1; bjarni@4800: KUDr@5187: bv = &WP(w, buildvehicle_d); KUDr@5187: EngList_Create(&bv->eng_list); bjarni@5781: bv->sel_engine = INVALID_ENGINE; KUDr@5187: bjarni@5781: bv->vehicle_type = type; bjarni@5781: bv->regenerate_list = false; KUDr@5187: bjarni@5955: bv->sort_criteria = _last_sort_criteria[type]; bjarni@5955: bv->descending_sort_order = _last_sort_order[type]; bjarni@5784: bjarni@4800: switch (type) { bjarni@5779: case VEH_Train: bjarni@5779: WP(w,buildvehicle_d).filter.railtype = (tile == 0) ? RAILTYPE_END : GetRailType(tile); bjarni@5779: ResizeWindow(w, 0, 16); bjarni@5779: break; bjarni@5786: case VEH_Road: bjarni@5786: ResizeWindow(w, 20, 16); bjarni@5783: case VEH_Ship: bjarni@5783: ResizeWindow(w, 27, 0); bjarni@5783: break; bjarni@5783: case VEH_Aircraft: tron@6024: bv->filter.flags = tron@6024: tile == 0 ? AirportFTAClass::ALL : GetAirport(GetStationByTile(tile)->airport_type)->flags; bjarni@5779: ResizeWindow(w, 12, 0); KUDr@5187: break; bjarni@4800: } bjarni@5779: SetupWindowStrings(w, type); bjarni@5824: ResizeButtons(w, BUILD_VEHICLE_WIDGET_BUILD, BUILD_VEHICLE_WIDGET_RENAME); bjarni@5779: bjarni@5779: w->resize.width = w->width; bjarni@5779: w->resize.height = w->height; bjarni@4800: bjarni@5781: GenerateBuildList(w); // generate the list, since we need it in the next line bjarni@5779: /* Select the first engine in the list as default when opening the window */ Darkvater@5200: if (EngList_Count(&bv->eng_list) > 0) bv->sel_engine = bv->eng_list[0]; bjarni@4800: }