diff options
author | darkvater <darkvater@openttd.org> | 2004-09-06 18:15:13 +0000 |
---|---|---|
committer | darkvater <darkvater@openttd.org> | 2004-09-06 18:15:13 +0000 |
commit | bf0652d3fce57024fe56f75d43898a261fea7570 (patch) | |
tree | ff59ad94248c5c270741fe954c139e1f953b197c | |
parent | df1397a47e68cf07d0a0d4fe02758f4b4f8c469f (diff) | |
download | openttd-bf0652d3fce57024fe56f75d43898a261fea7570.tar.xz |
(svn r165) -Feature: Option to sort vehicles in vehicle-list window by different criteria. Total independent sort for all types and players. Periodic resort of list every 10 TTD days. Thank you for your graphical inspiration follow and buxo (since none of you provided any code).
-Fix: Sorter icon pointing down 'v' sorts in every window lowest value first, '^' highest value first
-CodeChange: move Dropdownlist from settings_gui.c to widget.c. More in place there.
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | aircraft_cmd.c | 2 | ||||
-rw-r--r-- | aircraft_gui.c | 273 | ||||
-rw-r--r-- | gui.h | 2 | ||||
-rw-r--r-- | industry_gui.c | 43 | ||||
-rw-r--r-- | lang/english.txt | 38 | ||||
-rw-r--r-- | misc.c | 1 | ||||
-rw-r--r-- | roadveh_cmd.c | 3 | ||||
-rw-r--r-- | roadveh_gui.c | 269 | ||||
-rw-r--r-- | settings_gui.c | 184 | ||||
-rw-r--r-- | ship_cmd.c | 2 | ||||
-rw-r--r-- | ship_gui.c | 278 | ||||
-rw-r--r-- | station_gui.c | 58 | ||||
-rw-r--r-- | town_gui.c | 8 | ||||
-rw-r--r-- | train_cmd.c | 7 | ||||
-rw-r--r-- | train_gui.c | 285 | ||||
-rw-r--r-- | ttd.dsp | 8 | ||||
-rw-r--r-- | ttd.h | 10 | ||||
-rw-r--r-- | ttd.vcproj | 6 | ||||
-rw-r--r-- | unix.c | 2 | ||||
-rw-r--r-- | vehicle.c | 4 | ||||
-rw-r--r-- | vehicle.h | 3 | ||||
-rw-r--r-- | vehicle_gui.c | 147 | ||||
-rw-r--r-- | vehicle_gui.h | 79 | ||||
-rw-r--r-- | water_cmd.c | 2 | ||||
-rw-r--r-- | widget.c | 183 | ||||
-rw-r--r-- | win32.c | 2 | ||||
-rw-r--r-- | window.h | 1 |
28 files changed, 1286 insertions, 616 deletions
@@ -446,7 +446,7 @@ ttd_SOURCES = \ smallmap_gui.c sound.c spritecache.c station_cmd.c station_gui.c \ strings.c subsidy_gui.c terraform_gui.c texteff.c town_cmd.c \ town_gui.c train_cmd.c train_gui.c tree_cmd.c ttd.c tunnelbridge_cmd.c \ - unmovable_cmd.c vehicle.c viewport.c water_cmd.c widget.c window.c \ + unmovable_cmd.c vehicle.c vehicle_gui.c viewport.c water_cmd.c widget.c window.c \ ifdef WITH_SDL ttd_SOURCES += sdl.c diff --git a/aircraft_cmd.c b/aircraft_cmd.c index c842f0bee..a9530636b 100644 --- a/aircraft_cmd.c +++ b/aircraft_cmd.c @@ -336,6 +336,7 @@ int32 CmdBuildAircraft(int x, int y, uint32 flags, uint32 p1, uint32 p2) } InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); + _vehicle_sort_dirty[VEHAIRCRAFT] = true; // build aircraft InvalidateWindow(WC_AIRCRAFT_LIST, v->owner); InvalidateWindow(WC_COMPANY, v->owner); } @@ -367,6 +368,7 @@ static bool CheckStoppedInHangar(Vehicle *v) void DoDeleteAircraft(Vehicle *v) { DeleteWindowById(WC_VEHICLE_VIEW, v->index); + _vehicle_sort_dirty[VEHAIRCRAFT] = true; // delete aircraft InvalidateWindow(WC_AIRCRAFT_LIST, v->owner); InvalidateWindow(WC_COMPANY, v->owner); DeleteVehicleChain(v); diff --git a/aircraft_gui.c b/aircraft_gui.c index 450c612cd..0a723f895 100644 --- a/aircraft_gui.c +++ b/aircraft_gui.c @@ -864,102 +864,175 @@ static void DrawSmallSchedule(Vehicle *v, int x, int y) { } } -static void PlayerAircraftWndProc(Window *w, WindowEvent *e) -{ - switch(e->event) { - case WE_PAINT: - /* determine amount of items for scroller */ - { - Vehicle *v; - int num = 0; - byte owner = (byte)w->window_number; +// used to get a sorted list of the vehicles +static SortStruct _aircraft_sort[NUM_NORMAL_VEHICLES]; +static uint16 _num_aircraft_sort[MAX_PLAYERS]; - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_Aircraft && v->subtype <= 2 && v->owner == owner) - num++; +static void MakeSortedAircraftList(byte owner) +{ + SortStruct *firstelement; + Vehicle *v; + uint32 n = 0; + uint16 *i; + + if (_vehicle_sort_dirty[VEHAIRCRAFT]) { // only resort the whole array if vehicles have been added/removed + // reset to 0 just to be sure + for (i = _num_aircraft_sort; i != endof(_num_aircraft_sort); i++) {*i = 0;} + + FOR_ALL_VEHICLES(v) { + if(v->type == VEH_Aircraft && v->subtype <= 2) { + _aircraft_sort[n].index = v->index; + _aircraft_sort[n++].owner = v->owner; + _num_aircraft_sort[v->owner]++; // add number of aircraft of player } + } + + // create cumulative aircraft-ownage + // aircraft are stored as a cummulative index, eg 25, 41, 43. This means + // Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2 + for (i = &_num_aircraft_sort[1]; i != endof(_num_aircraft_sort); i++) {*i += *(i-1);} + + + // sort by owner, then only subsort the requested owner-vehicles + qsort(_aircraft_sort, n, sizeof(_aircraft_sort[0]), GeneralOwnerSorter); + + _last_vehicle_idx = 0; // used for "cache" in namesorting + _vehicle_sort_dirty[VEHAIRCRAFT] = false; + } + + if (owner == 0) { // first element starts at 0th element and has n elements as described above + firstelement = &_aircraft_sort[0]; + n = _num_aircraft_sort[0]; + } else { // nth element starts at the end of the previous one, and has n elements as described above + firstelement = &_aircraft_sort[_num_aircraft_sort[owner-1]]; + n = _num_aircraft_sort[owner] - _num_aircraft_sort[owner-1]; + } + + _internal_sort_type = _aircraft_sort_type[owner]; + _internal_sort_order = _aircraft_sort_order[owner]; + _internal_name_sorter_id = STR_SV_AIRCRAFT_NAME; + // only name sorting needs a different procedure, all others are handled by the general sorter + qsort(firstelement, n, sizeof(_aircraft_sort[0]), (_internal_sort_type == SORT_BY_NAME) ? VehicleNameSorter : GeneralVehicleSorter); - SetVScrollCount(w, num); + DEBUG(misc, 1) ("Resorting Aircraft list player %d...", owner+1); +} + +static void PlayerAircraftWndProc(Window *w, WindowEvent *e) +{ + switch(e->event) { + case WE_PAINT: { + uint32 i; + const byte window_number = (byte)w->window_number; + + if (_aircraft_sort_type[window_number] == SORT_BY_UNSORTED) // disable 'Sort By' tooltip on Unsorted sorting criteria + w->disabled_state |= (1 << 3); + + if (_aircraft_sort_dirty[window_number] || _vehicle_sort_dirty[VEHAIRCRAFT]) { + _aircraft_sort_dirty[window_number] = false; + MakeSortedAircraftList(window_number); + /* reset sorting timeout */ + w->custom[0] = DAY_TICKS; + w->custom[1] = PERIODIC_RESORT_DAYS; } + // aircraft are stored as a cummulative index, eg 25, 41, 43. This means + // Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2 aircraft + i = (window_number == 0) ? 0 : _num_aircraft_sort[window_number-1]; + SetVScrollCount(w, _num_aircraft_sort[window_number] - i); + /* draw the widgets */ { - Player *p = DEREF_PLAYER(w->window_number); + Player *p = DEREF_PLAYER(window_number); + /* Company Name -- (###) Aircraft */ SET_DPARAM16(0, p->name_1); SET_DPARAM32(1, p->name_2); + SET_DPARAM16(2, w->vscroll.count); + SET_DPARAM16(3, _vehicle_sort_listing[_aircraft_sort_type[window_number]]); DrawWindowWidgets(w); } + /* draw arrow pointing up/down for ascending/descending soring */ + DoDrawString(_aircraft_sort_order[window_number] & 1 ? "\xAA" : "\xA0", 85, 15, 0x10); /* draw the aircraft */ { Vehicle *v; - int pos = w->vscroll.pos; - byte owner = (byte)w->window_number; - int x = 2; - int y = 15; - - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_Aircraft && v->subtype <= 2 && v->owner == owner && - --pos < 0 && pos >= -4) { - StringID str; - - DrawAircraftImage(v, x + 19, y + 6, INVALID_VEHICLE); - DrawVehicleProfitButton(v, x, y+13); - - SET_DPARAM16(0, v->unitnumber); - if (IsAircraftHangarTile(v->tile)) { - str = STR_021F; - } else { - str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2; - } - DrawString(x, y+2, str, 0); + int n = 0; + const int x = 2; // offset from left side of widget + int y = PLY_WND_PRC__OFFSET_TOP_WIDGET; // offset from top of widget + i += w->vscroll.pos; // offset from sorted aircraft list of current player + + while (i < _num_aircraft_sort[window_number]) { + StringID str; + v = DEREF_VEHICLE(_aircraft_sort[i].index); + + DrawAircraftImage(v, x + 19, y + 6, INVALID_VEHICLE); + DrawVehicleProfitButton(v, x, y+13); + + SET_DPARAM16(0, v->unitnumber); + if (IsAircraftHangarTile(v->tile)) { + str = STR_021F; + } else { + str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2; + } + DrawString(x, y+2, str, 0); - SET_DPARAM32(0, v->profit_this_year); - SET_DPARAM32(1, v->profit_last_year); - DrawString(x+19, y + 28, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0); - if (v->string_id != STR_SV_AIRCRAFT_NAME) { - SET_DPARAM16(0, v->string_id); - DrawString(x+19, y, STR_01AB, 0); - } + SET_DPARAM32(0, v->profit_this_year); + SET_DPARAM32(1, v->profit_last_year); + DrawString(x+19, y + 28, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0); + + if (v->string_id != STR_SV_AIRCRAFT_NAME) { + SET_DPARAM16(0, v->string_id); + DrawString(x+19, y, STR_01AB, 0); + } - DrawSmallSchedule(v, x+136, y); + DrawSmallSchedule(v, x+136, y); - y += 36; - } + y += PLY_WND_PRC__SIZE_OF_ROW_BIG; + i++; // next aircraft + if (++n == w->vscroll.cap) { break;} // max number of aircraft in the window } } - break; + } break; - case WE_CLICK: + case WE_CLICK: { switch(e->click.widget) { - case 2: { /* click on aircraft */ - int sel; - Vehicle *v; - byte owner; + case 3: /* Flip sorting method ascending/descending */ + _aircraft_sort_order[(byte)w->window_number] ^= 1; + _aircraft_sort_dirty[(byte)w->window_number] = true; + SetWindowDirty(w); + break; + case 4: case 5:/* Select sorting criteria dropdown menu */ + ShowDropDownMenu(w, _vehicle_sort_listing, _aircraft_sort_type[(byte)w->window_number], 5, 0); // do it for widget 5 + return; + case 6: { /* Matrix to show vehicles */ + int id_v = (e->click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / PLY_WND_PRC__SIZE_OF_ROW_BIG; + + if ((uint)id_v >= w->vscroll.cap) { return;} // click out of bounds - sel = (e->click.pt.y - 14) / 36; + id_v += w->vscroll.pos; - if ((uint)sel >= 4) - break; - sel += w->vscroll.pos; - owner = (byte)w->window_number; - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_Aircraft && v->subtype <= 2 && v->owner == owner && - --sel < 0) { - ShowAircraftViewWindow(v); - break; - } + { + byte owner = (byte)w->window_number; + uint16 adder = (owner == 0) ? 0 : _num_aircraft_sort[owner - 1]; // first element in list + Vehicle *v; + + if (id_v + adder >= _num_aircraft_sort[owner]) { return;} // click out of vehicle bound + + v = DEREF_VEHICLE(_aircraft_sort[adder+id_v].index); // add the offset id_x to that + + assert(v->type == VEH_Aircraft && v->subtype <= 2 && v->owner == owner && v->owner == _aircraft_sort[adder+id_v].owner); + + ShowAircraftViewWindow(v); } - break; - } - case 4: { /* build new */ + } break; + + case 8: { /* Build new Vehicle */ uint tile; tile = _last_built_aircraft_depot_tile; do { - if (_map_owner[tile] == _local_player && - IsAircraftHangarTile(tile)) { + if (_map_owner[tile] == _local_player && IsAircraftHangarTile(tile)) { ShowAircraftDepotWindow(tile); ShowBuildAircraftWindow(tile); return; @@ -967,43 +1040,79 @@ static void PlayerAircraftWndProc(Window *w, WindowEvent *e) tile = TILE_MASK(tile + 1); } while(tile != _last_built_aircraft_depot_tile); + ShowBuildAircraftWindow(0); } break; } + } break; + + case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */ + if (_aircraft_sort_type[(byte)w->window_number] != e->dropdown.index) // if value hasn't changed, dont resort list + _aircraft_sort_dirty[(byte)w->window_number] = true; + + _aircraft_sort_type[(byte)w->window_number] = e->dropdown.index; + + if (_aircraft_sort_type[(byte)w->window_number] != SORT_BY_UNSORTED) // enable 'Sort By' if a sorter criteria is chosen + w->disabled_state &= ~(1 << 3); + + SetWindowDirty(w); + break; + case WE_CREATE: /* set up resort timer */ + w->custom[0] = DAY_TICKS; + w->custom[1] = PERIODIC_RESORT_DAYS; + break; + case WE_TICK: /* resort the list every 20 seconds orso (10 days) */ + if (--w->custom[0] == 0) { + w->custom[0] = DAY_TICKS; + if (--w->custom[1] == 0) { + w->custom[1] = PERIODIC_RESORT_DAYS; + _aircraft_sort_dirty[(byte)w->window_number] = true; + DEBUG(misc, 1) ("Periodic resort Aircraft list player %d...", w->window_number+1); + SetWindowDirty(w); + } + } break; } } static const Widget _player_aircraft_widgets[] = { -{ WWT_TEXTBTN, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, 14, 11, 259, 0, 13, STR_A009_AIRCRAFT, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_MATRIX, 14, 0, 248, 14, 157, 0x401, STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT}, -{ WWT_SCROLLBAR, 14, 249, 259, 14, 157, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, -{ WWT_PUSHTXTBTN, 14, 0, 129, 158, 169, STR_A003_NEW_AIRCRAFT, STR_A020_BUILD_NEW_AIRCRAFT_REQUIRES}, -{ WWT_IMGBTN, 14, 130, 259, 158, 169, 0x0, 0}, +{ WWT_CLOSEBOX, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, +{ WWT_CAPTION, 14, 11, 259, 0, 13, STR_A009_AIRCRAFT, STR_018C_WINDOW_TITLE_DRAG_THIS}, +{ WWT_PANEL, 14, 0, 15, 14, 25, 0x0, 0}, +{ WWT_PUSHTXTBTN, 14, 16, 96, 14, 25, SRT_SORT_BY, STR_SORT_TIP}, +{ WWT_TEXTBTN, 14, 97, 248, 14, 25, STR_02E7, 0}, +{ WWT_CLOSEBOX, 14, 249, 259, 14, 25, STR_0225, STR_SORT_TIP}, +{ WWT_MATRIX, 14, 0, 248, 26, 169, 0x401, STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT}, +{ WWT_SCROLLBAR, 14, 249, 259, 26, 169, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +{ WWT_PUSHTXTBTN, 14, 0, 129, 170, 181, STR_A003_NEW_AIRCRAFT, STR_A020_BUILD_NEW_AIRCRAFT_REQUIRES}, +{ WWT_PANEL, 14, 130, 259, 170, 181, 0x0, 0}, { WWT_LAST}, }; static const WindowDesc _player_aircraft_desc = { - -1, -1, 260, 170, + -1, -1, 260, 182, WC_AIRCRAFT_LIST,0, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, + WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM, _player_aircraft_widgets, PlayerAircraftWndProc }; static const Widget _other_player_aircraft_widgets[] = { -{ WWT_TEXTBTN, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, 14, 11, 259, 0, 13, STR_A009_AIRCRAFT, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_MATRIX, 14, 0, 248, 14, 157, 0x401, STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT}, -{ WWT_SCROLLBAR, 14, 249, 259, 14, 157, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +{ WWT_CLOSEBOX, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, +{ WWT_CAPTION, 14, 11, 259, 0, 13, STR_A009_AIRCRAFT, STR_018C_WINDOW_TITLE_DRAG_THIS}, +{ WWT_PANEL, 14, 0, 15, 14, 25, 0x0, 0}, +{ WWT_PUSHTXTBTN, 14, 16, 96, 14, 25, SRT_SORT_BY, STR_SORT_TIP}, +{ WWT_TEXTBTN, 14, 97, 248, 14, 25, STR_02E7, 0}, +{ WWT_CLOSEBOX, 14, 249, 259, 14, 25, STR_0225, STR_SORT_TIP}, +{ WWT_MATRIX, 14, 0, 248, 26, 169, 0x401, STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT}, +{ WWT_SCROLLBAR, 14, 249, 259, 26, 169, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, { WWT_LAST}, }; static const WindowDesc _other_player_aircraft_desc = { - -1, -1, 260, 158, + -1, -1, 260, 170, WC_AIRCRAFT_LIST,0, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, + WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM, _other_player_aircraft_widgets, PlayerAircraftWndProc }; @@ -1012,7 +1121,7 @@ void ShowPlayerAircraft(int player) { Window *w; - if ( player == _local_player) { + if (player == _local_player) { w = AllocateWindowDescFront(&_player_aircraft_desc, player); } else { w = AllocateWindowDescFront(&_other_player_aircraft_desc, player); @@ -1020,6 +1129,6 @@ void ShowPlayerAircraft(int player) if (w) { w->caption_color = w->window_number; - w->vscroll.cap = 4; + w->vscroll.cap = 4; // maximum number of vehicles shown } } @@ -108,8 +108,6 @@ void ShowBuildIndustryWindow(); void ShowQueryString(StringID str, StringID caption, int maxlen, int maxwidth, byte window_class, uint16 window_number); void ShowMusicWindow(); -void DrawVehicleProfitButton(Vehicle *v, int x, int y); - /* main_gui.c */ VARDEF byte _newspaper_flag; VARDEF byte _construct_mode; diff --git a/industry_gui.c b/industry_gui.c index de697f918..b67f2360f 100644 --- a/industry_gui.c +++ b/industry_gui.c @@ -388,45 +388,44 @@ static byte _last_industry_idx; static byte _industry_sort_order; -static int CDECL IndustrySorter(const void *a, const void *b) +static int CDECL GeneralIndustrySorter(const void *a, const void *b) { char buf1[96]; - Industry *i, *j; byte val; + Industry *i = DEREF_INDUSTRY(*(byte*)a); + Industry *j = DEREF_INDUSTRY(*(byte*)b); int r = 0; - i = DEREF_INDUSTRY(*(byte*)a); - j = DEREF_INDUSTRY(*(byte*)b); - switch (_industry_sort_order >> 1) { - case 0: - r = 0; - break; - case 1: /* Case 1, sort by type */ + /* case 0: Sort by Name (handled later) */ + case 1: /* Sort by Type */ r = i->type - j->type; break; - case 2: /* Case 2, sort by production */ - if (i->produced_cargo[0] != 0xFF && j->produced_cargo[0] != 0xFF) { //producing any cargo? - if (i->produced_cargo[1] == 0xFF) //producing one or two things? + // FIXME - Production & Transported sort need to be inversed...but, WTF it does not wanna! + // FIXME - And no simple --> "if (!(_industry_sort_order & 1)) r = -r;" hack at the bottom!! + case 2: { /* Sort by Production */ + if (i->produced_cargo[0] != 0xFF && j->produced_cargo[0] != 0xFF) { // both industries produce cargo? + if (i->produced_cargo[1] == 0xFF) // producing one or two things? r = j->total_production[0] - i->total_production[0]; else r = (j->total_production[0] + j->total_production[1]) / 2 - (i->total_production[0] + i->total_production[1]) / 2; - } else if (i->produced_cargo[0] == 0xFF && j->produced_cargo[0] == 0xFF) //None of them producing anything, let them go to the name-sorting + } else if (i->produced_cargo[0] == 0xFF && j->produced_cargo[0] == 0xFF) // none of them producing anything, let them go to the name-sorting r = 0; - else if (i->produced_cargo[0] == 0xFF) //Non-producers, end up last/first in list + else if (i->produced_cargo[0] == 0xFF) // end up the non-producer industry first/last in list r = 1; else r = -1; break; - case 3: /* Case 3, sort by transportation */ - if (i->produced_cargo[0] != 0xFF && j->produced_cargo[0] != 0xFF) { //producing any cargo? - if (i->produced_cargo[1] == 0xFF) //producing one or two things? + } + case 3: /* Sort by Transported amount */ + if (i->produced_cargo[0] != 0xFF && j->produced_cargo[0] != 0xFF) { // both industries produce cargo? + if (i->produced_cargo[1] == 0xFF) // producing one or two things? r = (j->pct_transported[0] * 100 >> 8) - (i->pct_transported[0] * 100 >> 8); else r = ((j->pct_transported[0] * 100 >> 8) + (j->pct_transported[1] * 100 >> 8)) / 2 - ((i->pct_transported[0] * 100 >> 8) + (i->pct_transported[1] * 100 >> 8)) / 2; - } else if (i->produced_cargo[0] == 0xFF && j->produced_cargo[0] == 0xFF) //None of them producing anything, let them go to the name-sorting + } else if (i->produced_cargo[0] == 0xFF && j->produced_cargo[0] == 0xFF) // none of them producing anything, let them go to the name-sorting r = 0; - else if (i->produced_cargo[0] == 0xFF) //Non-producers, end up last/first in list + else if (i->produced_cargo[0] == 0xFF) // end up the non-producer industry first/last in list r = 1; else r = -1; @@ -446,9 +445,7 @@ static int CDECL IndustrySorter(const void *a, const void *b) r = strcmp(buf1, _bufcache); } - if (_industry_sort_order & 1) - r = -r; - + if (_industry_sort_order & 1) r = -r; return r; } @@ -464,7 +461,7 @@ static void MakeSortedIndustryList() _num_industry_sort = n; _last_industry_idx = 255; // used for "cache" - qsort(_industry_sort, n, 1, IndustrySorter); + qsort(_industry_sort, n, 1, GeneralIndustrySorter); DEBUG(misc, 1) ("Resorting Industries list..."); } diff --git a/lang/english.txt b/lang/english.txt index 85369fc7a..9df37dee2 100644 --- a/lang/english.txt +++ b/lang/english.txt @@ -352,9 +352,24 @@ STR_015E_QUIT_GAME :Abandon game STR_015F_QUIT :Exit STR_0160_ARE_YOU_SURE_YOU_WANT_TO :{YELLOW}Are you sure you want to abandon this game? STR_0161_QUIT_GAME :{WHITE}Abandon Game -STR_SORT_TIP :{BLACK}Select sorting order -STR_SORT_BY_NAME :{BLACK}Name -STR_SORT_BY_DATE :{BLACK}Date +STR_SORT_TIP :{BLACK}Select sorting order descending/ascending +SRT_SORT_BY :{BLACK}Sort by + +STR_SORT_BY_POPULATION :{BLACK}Population +STR_SORT_BY_PRODUCTION :{BLACK}Production +STR_SORT_BY_TYPE :{BLACK}Type +STR_SORT_BY_TRANSPORTED :{BLACK}Transported +STR_SORT_BY_NAME :{BLACK}Name +STR_SORT_BY_DROPDOWN_NAME :Name +STR_SORT_BY_DATE :{BLACK}Date +STR_SORT_BY_UNSORTED :Unsorted +STR_SORT_BY_NUMBER :Number +STR_SORT_BY_PROFIT_LAST_YEAR :Profit last year +STR_SORT_BY_PROFIT_THIS_YEAR :Profit this year +STR_SORT_BY_AGE :Age +STR_SORT_BY_RELIABILITY :Reliability +STR_SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE :Total capacity per cargo type +STR_SORT_BY_MAX_SPEED :Maximum speed ############ range for months starts STR_0162_JAN :Jan @@ -843,8 +858,8 @@ STR_TOWNNAME_HUNGARIAN :Hungarian STR_TOWNNAME_AUSTRIAN :Austrian ############ end of townname region -STR_CURR_POUNDS :Pounds ({POUNDSIGN}) -STR_CURR_DOLLARS :Dollars ($) +STR_CURR_POUNDS :Pounds ({POUNDSIGN}) +STR_CURR_DOLLARS :Dollars ($) STR_CURR_FF :Franc (FF) STR_CURR_DM :Deutschmark (DM) STR_CURR_YEN :Yen ({YENSIGN}) @@ -1052,8 +1067,6 @@ STR_CHEAT_NO_JETCRASH :{LTBLUE}Jetplanes will not crash (frequently) on smal STR_CHEAT_SWITCH_CLIMATE :{LTBLUE}Switch climate: {ORANGE} {STRING} STR_CHEAT_CHANGE_DATE :{LTBLUE}Change date: {ORANGE} {DATE_SHORT} -STR_SORT_BY_POPULATION :{BLACK}Population - STR_HEADING_FOR_CHECKPOINT :{LTBLUE}Heading for {CHECKPOINT} STR_HEADING_FOR_CHECKPOINT_VEL :{LTBLUE}Heading for {CHECKPOINT}, {VELOCITY} @@ -1136,9 +1149,6 @@ STR_CONSTRUCT_BUBBLE_GENERATOR_TIP :{BLACK}Construct Bubble Generator STR_CONSTRUCT_TOFFEE_QUARRY_TIP :{BLACK}Fund Toffee Quarry STR_CONSTRUCT_SUGAR_MINE_TIP :{BLACK}Construct Sugar Mine -STR_SORT_BY_PRODUCTION :{BLACK}Production -STR_SORT_BY_TYPE :{BLACK}Type -STR_SORT_BY_TRANSPORTED :{BLACK}Transported STR_INDUSTRYDIR_CAPTION :{WHITE}Industries STR_INDUSTRYDIR_ITEM :{ORANGE}{TOWN} {STRING}{BLACK} ({STRING}){YELLOW} ({COMMA16}% transported) STR_INDUSTRYDIR_ITEM_TWO :{ORANGE}{TOWN} {STRING}{BLACK} ({STRING}/{STRING}){YELLOW} ({COMMA16}%/{COMMA16}% transported) @@ -2228,7 +2238,7 @@ STR_8817_COST_WEIGHT_T_SPEED_POWER :{BLACK}Cost: {GOLD}{CURRENCY}{BLACK} Weight STR_8818_INFORMATION :{BLACK}Information STR_8819_TRAIN_TOO_LONG :{WHITE}Train too long STR_881A_TRAINS_CAN_ONLY_BE_ALTERED :{WHITE}Trains can only be altered when stopped inside a depot -STR_881B_TRAINS :{WHITE}{STRING} - Trains +STR_881B_TRAINS :{WHITE}{STRING} - {COMMA16} Trains STR_881C_NEW_RAIL_VEHICLES :{WHITE}New Rail Vehicles STR_881D_NEW_MONORAIL_VEHICLES :{WHITE}New Monorail Vehicles STR_881E_NEW_MAGLEV_VEHICLES :{WHITE}New Maglev Vehicles @@ -2319,7 +2329,7 @@ STR_TRAIN_STOPPING_VEL :{RED}Stopping, {VELOCITY} ##id 0x9000 STR_9000_ROAD_VEHICLE_IN_THE_WAY :{WHITE}Road vehicle in the way -STR_9001_ROAD_VEHICLES :{WHITE}{STRING} - Road Vehicles +STR_9001_ROAD_VEHICLES :{WHITE}{STRING} - {COMMA16} Road Vehicles STR_9002 :{WHITE}{STRING} STR_9003_ROAD_VEHICLE_DEPOT :{WHITE}{TOWN} Road Vehicle Depot STR_9004_NEW_VEHICLES :{BLACK}New Vehicles @@ -2384,7 +2394,7 @@ STR_9801_DOCK_CONSTRUCTION :{WHITE}Dock construction STR_9802_CAN_T_BUILD_DOCK_HERE :{WHITE}Can't build dock here... STR_9803_SHIP_DEPOT :{WHITE}{TOWN} Ship Depot STR_9804_NEW_SHIPS :{BLACK}New Ships -STR_9805_SHIPS :{WHITE}{STRING} - Ships +STR_9805_SHIPS :{WHITE}{STRING} - {COMMA16} Ships STR_9806_CAN_T_BUILD_SHIPS :{WHITE}Can't build ships... STR_9807_MUST_BUILD_SHIP_DEPOT_FIRST :{WHITE}Must build ship depot first STR_9808_NEW_SHIPS :{WHITE}New Ships @@ -2460,7 +2470,7 @@ STR_A005_NEW_AIRCRAFT :{WHITE}New Aircraft STR_A006_BUILD_AIRCRAFT :{BLACK}Build Aircraft STR_A007_COST_SPEED_CAPACITY_PASSENGERS :{BLACK}Cost: {GOLD}{CURRENCY}{BLACK} Speed: {GOLD}{VELOCITY}{}{BLACK}Capacity: {GOLD}{COMMA16} passengers, {COMMA16} bags of mail{}{BLACK}Running Cost: {GOLD}{CURRENCY}/yr{}{BLACK}Designed: {GOLD}{NUMU16}{BLACK} Life: {GOLD}{COMMA16} years{}{BLACK}Max. Reliability: {GOLD}{COMMA8}% STR_A008_CAN_T_BUILD_AIRCRAFT :{WHITE}Can't build aircraft... -STR_A009_AIRCRAFT :{WHITE}{STRING} - Aircraft +STR_A009_AIRCRAFT :{WHITE}{STRING} - {COMMA16} Aircraft STR_A00A :{WHITE}{STRING} STR_A00B_ORDERS :{WHITE}{STRING} (Orders) STR_A00C_DETAILS :{WHITE}{STRING} (Details) @@ -121,6 +121,7 @@ void InitializeGame() InitializeIndustries(); InitializeNameMgr(); + InitializeVehiclesGuiList(); InitializeTrains(); InitializePlayers(); diff --git a/roadveh_cmd.c b/roadveh_cmd.c index cc8250ffc..9267b462b 100644 --- a/roadveh_cmd.c +++ b/roadveh_cmd.c @@ -271,6 +271,7 @@ int32 CmdBuildRoadVeh(int x, int y, uint32 flags, uint32 p1, uint32 p2) VehiclePositionChanged(v); InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); + _vehicle_sort_dirty[VEHROAD] = true; // build a new bus/truck InvalidateWindow(WC_ROADVEH_LIST, v->owner); InvalidateWindow(WC_COMPANY, v->owner); } @@ -314,6 +315,7 @@ int32 CmdSellRoadVeh(int x, int y, uint32 flags, uint32 p1, uint32 p2) if (flags & DC_EXEC) { // Invalidate depot InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); + _vehicle_sort_dirty[VEHROAD] = true; // sell a bus/truck InvalidateWindow(WC_ROADVEH_LIST, v->owner); InvalidateWindow(WC_COMPANY, v->owner); DeleteWindowById(WC_VEHICLE_VIEW, v->index); @@ -505,6 +507,7 @@ static void RoadVehDelete(Vehicle *v) DeleteWindowById(WC_VEHICLE_VIEW, v->index); InvalidateWindow(WC_VEHICLE_DETAILS, v->index); + _vehicle_sort_dirty[VEHROAD] = true; // delete bus/truck (eg. crash for example) InvalidateWindow(WC_ROADVEH_LIST, v->owner); InvalidateWindow(WC_COMPANY, v->owner); diff --git a/roadveh_gui.c b/roadveh_gui.c index 8c77b081c..a48b727ed 100644 --- a/roadveh_gui.c +++ b/roadveh_gui.c @@ -9,7 +9,6 @@ #include "station.h" #include "command.h" #include "player.h" -//#include "town.h" #include "engine.h" extern const byte _roadveh_price[88]; @@ -704,100 +703,172 @@ void ShowRoadDepotWindow(uint tile) } } +// used to get a sorted list of the vehicles +static SortStruct _road_sort[NUM_NORMAL_VEHICLES]; +static uint16 _num_road_sort[MAX_PLAYERS]; + +static void MakeSortedRoadList(byte owner) +{ + SortStruct *firstelement; + Vehicle *v; + uint32 n = 0; + uint16 *i; + + if (_vehicle_sort_dirty[VEHROAD]) { // only resort the whole array if vehicles have been added/removed + // reset to 0 just to be sure + for (i = _num_road_sort; i != endof(_num_road_sort); i++) {*i = 0;} + + FOR_ALL_VEHICLES(v) { + if(v->type == VEH_Road) { + _road_sort[n].index = v->index; + _road_sort[n++].owner = v->owner; + _num_road_sort[v->owner]++; // add number of roads of player + } + } + + // create cumulative road-ownage + // roads are stored as a cummulative index, eg 25, 41, 43. This means + // Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2 + for (i = &_num_road_sort[1]; i != endof(_num_road_sort); i++) {*i += *(i-1);} + + + // sort by owner, then only subsort the requested owner-vehicles + qsort(_road_sort, n, sizeof(_road_sort[0]), GeneralOwnerSorter); + + _last_vehicle_idx = 0; // used for "cache" in namesorting + _vehicle_sort_dirty[VEHROAD] = false; + } + + if (owner == 0) { // first element starts at 0th element and has n elements as described above + firstelement = &_road_sort[0]; + n = _num_road_sort[0]; + } else { // nth element starts at the end of the previous one, and has n elements as described above + firstelement = &_road_sort[_num_road_sort[owner-1]]; + n = _num_road_sort[owner] - _num_road_sort[owner-1]; + } + + _internal_sort_type = _road_sort_type[owner]; + _internal_sort_order = _road_sort_order[owner]; + _internal_name_sorter_id = STR_SV_ROADVEH_NAME; + // only name sorting needs a different procedure, all others are handled by the general sorter + qsort(firstelement, n, sizeof(_road_sort[0]), (_internal_sort_type == SORT_BY_NAME) ? VehicleNameSorter : GeneralVehicleSorter); + + DEBUG(misc, 1) ("Resorting Roadvehicles list player %d...", owner+1); +} + static void PlayerRoadVehWndProc(Window *w, WindowEvent *e) { switch(e->event) { - case WE_PAINT: - /* determine amount of items for scroller */ - { - Vehicle *v; - int num = 0; - byte owner = (byte)w->window_number; - - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_Road && v->owner == owner) - num++; - } - SetVScrollCount(w, num); + case WE_PAINT: { + uint32 i; + const byte window_number = (byte)w->window_number; + + if (_road_sort_type[window_number] == SORT_BY_UNSORTED) // disable 'Sort By' tooltip on Unsorted sorting criteria + w->disabled_state |= (1 << 3); + + if (_road_sort_dirty[window_number] || _vehicle_sort_dirty[VEHROAD]) { + _road_sort_dirty[window_number] = false; + MakeSortedRoadList(window_number); + /* reset sorting timeout */ + w->custom[0] = DAY_TICKS; + w->custom[1] = PERIODIC_RESORT_DAYS; } + // roads are stored as a cummulative index, eg 25, 41, 43. This means + // Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2 roads + i = (window_number == 0) ? 0 : _num_road_sort[window_number-1]; + SetVScrollCount(w, _num_road_sort[window_number] - i); + /* draw the widgets */ { - Player *p = DEREF_PLAYER(w->window_number); + Player *p = DEREF_PLAYER(window_number); + /* Company Name -- (###) Roadvehicles */ SET_DPARAM16(0, p->name_1); SET_DPARAM32(1, p->name_2); + SET_DPARAM16(2, w->vscroll.count); + SET_DPARAM16(3, _vehicle_sort_listing[_road_sort_type[window_number]]); DrawWindowWidgets(w); } + /* draw arrow pointing up/down for ascending/descending soring */ + DoDrawString(_road_sort_order[window_number] & 1 ? "\xAA" : "\xA0", 85, 15, 0x10); - /* draw the road vehicles */ + /* draw the roadvehicles */ { Vehicle *v; - int pos = w->vscroll.pos; - byte owner = (byte)w->window_number; - int x = 2; - int y = 15; - - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_Road && v->owner == owner && - --pos < 0 && pos >= -7) { - StringID str; - - DrawRoadVehImage(v, x + 22, y + 6, INVALID_VEHICLE); - DrawVehicleProfitButton(v, x, y+13); - - SET_DPARAM16(0, v->unitnumber); - if (IsRoadDepotTile(v->tile)) { - str = STR_021F; - } else { - str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2; - } - DrawString(x, y+2, str, 0); - - SET_DPARAM32(0, v->profit_this_year); - SET_DPARAM32(1, v->profit_last_year); - DrawString(x + 24, y + 18, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0); - - if (v->string_id != STR_SV_ROADVEH_NAME) { - SET_DPARAM16(0, v->string_id); - DrawString(x+24, y, STR_01AB, 0); - } - y += 26; - } + int n = 0; + const int x = 2; // offset from left side of widget + int y = PLY_WND_PRC__OFFSET_TOP_WIDGET; // offset from top of widget + i += w->vscroll.pos; // offset from sorted roads list of current player + + while (i < _num_road_sort[window_number]) { + StringID str; + v = DEREF_VEHICLE(_road_sort[i].index); + + DrawRoadVehImage(v, x + 22, y + 6, INVALID_VEHICLE); + DrawVehicleProfitButton(v, x, y+13); + + SET_DPARAM16(0, v->unitnumber); + if (IsRoadDepotTile(v->tile)) { + str = STR_021F; + } else { + str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2; + } + DrawString(x, y+2, str, 0); + + SET_DPARAM32(0, v->profit_this_year); + SET_DPARAM32(1, v->profit_last_year); + DrawString(x + 24, y + 18, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0); + + if (v->string_id != STR_SV_ROADVEH_NAME) { + SET_DPARAM16(0, v->string_id); + DrawString(x+24, y, STR_01AB, 0); + } + + y += PLY_WND_PRC__SIZE_OF_ROW_SMALL; + i++; // next road + if (++n == w->vscroll.cap) { break;} // max number of roads in the window } } - break; + } break; - case WE_CLICK: + case WE_CLICK: { switch(e->click.widget) { - case 2: { - int idx = (e->click.pt.y - 14) / 26; - Vehicle *v; - byte owner; + case 3: /* Flip sorting method ascending/descending */ + _road_sort_order[(byte)w->window_number] ^= 1; + _road_sort_dirty[(byte)w->window_number] = true; + SetWindowDirty(w); + break; + case 4: case 5:/* Select sorting criteria dropdown menu */ + ShowDropDownMenu(w, _vehicle_sort_listing, _road_sort_type[(byte)w->window_number], 5, 0); // do it for widget 5 + return; + case 6: { /* Matrix to show vehicles */ + int id_v = (e->click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / PLY_WND_PRC__SIZE_OF_ROW_SMALL; + + if ((uint)id_v >= w->vscroll.cap) { return;} // click out of bounds - if ((uint)idx >= 7) - break; + id_v += w->vscroll.pos; + + { + byte owner = (byte)w->window_number; + uint16 adder = (owner == 0) ? 0 : _num_road_sort[owner - 1]; // first element in list + Vehicle *v; - idx += w->vscroll.pos; + if (id_v + adder >= _num_road_sort[owner]) { return;} // click out of vehicle bound - owner = (byte)w->window_number; + v = DEREF_VEHICLE(_road_sort[adder+id_v].index); // add the offset id_x to that - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_Road && v->owner == owner && - --idx < 0) { - ShowRoadVehViewWindow(v); - break; - } + assert(v->type == VEH_Road && v->owner == owner && v->owner == _road_sort[adder+id_v].owner); + + ShowRoadVehViewWindow(v); } } break; - case 4: { + case 8: { /* Build new Vehicle */ uint tile; tile = _last_built_road_depot_tile; do { - if (_map_owner[tile] == _local_player && - IsRoadDepotTile(tile)) { - + if (_map_owner[tile] == _local_player && IsRoadDepotTile(tile)) { ShowRoadDepotWindow(tile); ShowBuildRoadVehWindow(tile); return; @@ -808,40 +879,76 @@ static void PlayerRoadVehWndProc(Window *w, WindowEvent *e) ShowBuildRoadVehWindow(0); } break; - } break; + } + } break; + + case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */ + if (_road_sort_type[(byte)w->window_number] != e->dropdown.index) // if value hasn't changed, dont resort list + _road_sort_dirty[(byte)w->window_number] = true; + + _road_sort_type[(byte)w->window_number] = e->dropdown.index; + + if (_road_sort_type[(byte)w->window_number] != SORT_BY_UNSORTED) // enable 'Sort By' if a sorter criteria is chosen + w->disabled_state &= ~(1 << 3); + + SetWindowDirty(w); + break; + case WE_CREATE: /* set up resort timer */ + w->custom[0] = DAY_TICKS; + w->custom[1] = PERIODIC_RESORT_DAYS; + break; + case WE_TICK: /* resort the list every 20 seconds orso (10 days) */ + if (--w->custom[0] == 0) { + w->custom[0] = DAY_TICKS; + if (--w->custom[1] == 0) { + w->custom[1] = PERIODIC_RESORT_DAYS; + _road_sort_dirty[(byte)w->window_number] = true; + DEBUG(misc, 1) ("Periodic resort Roadvehicles list player %d...", w->window_number+1); + SetWindowDirty(w); + } + } + break; } } static const Widget _player_roadveh_widgets[] = { -{ WWT_TEXTBTN, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, 14, 11, 259, 0, 13, STR_9001_ROAD_VEHICLES, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_MATRIX, 14, 0, 248, 14, 195, 0x701, STR_901A_ROAD_VEHICLES_CLICK_ON}, -{ WWT_SCROLLBAR, 14, 249, 259, 14, 195, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, -{ WWT_PUSHTXTBTN, 14, 0, 129, 196, 207, STR_9004_NEW_VEHICLES, STR_901B_BUILD_NEW_ROAD_VEHICLES}, -{ WWT_IMGBTN, 14, 130, 259, 196, 207, 0x0, 0}, +{ WWT_CLOSEBOX, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, +{ WWT_CAPTION, 14, 11, 259, 0, 13, STR_9001_ROAD_VEHICLES, STR_018C_WINDOW_TITLE_DRAG_THIS}, +{ WWT_PANEL, 14, 0, 15, 14, 25, 0x0, 0}, +{ WWT_PUSHTXTBTN, 14, 16, 96, 14, 25, SRT_SORT_BY, STR_SORT_TIP}, +{ WWT_TEXTBTN, 14, 97, 248, 14, 25, STR_02E7, 0}, +{ WWT_CLOSEBOX, 14, 249, 259, 14, 25, STR_0225, STR_SORT_TIP}, +{ WWT_MATRIX, 14, 0, 248, 26, 207, 0x701, STR_901A_ROAD_VEHICLES_CLICK_ON}, +{ WWT_SCROLLBAR, 14, 249, 259, 26, 207, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +{ WWT_PUSHTXTBTN, 14, 0, 129, 208, 219, STR_9004_NEW_VEHICLES, STR_901B_BUILD_NEW_ROAD_VEHICLES}, +{ WWT_IMGBTN, 14, 130, 259, 208, 219, 0x0, 0}, { WWT_LAST}, }; static const WindowDesc _player_roadveh_desc = { - -1, -1, 260, 208, + -1, -1, 260, 220, WC_ROADVEH_LIST,0, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, + WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM, _player_roadveh_widgets, PlayerRoadVehWndProc }; static const Widget _other_player_roadveh_widgets[] = { -{ WWT_TEXTBTN, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, 14, 11, 259, 0, 13, STR_9001_ROAD_VEHICLES, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_MATRIX, 14, 0, 248, 14, 195, 0x701, STR_901A_ROAD_VEHICLES_CLICK_ON}, -{ WWT_SCROLLBAR, 14, 249, 259, 14, 195, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +{ WWT_CLOSEBOX, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, +{ WWT_CAPTION, 14, 11, 259, 0, 13, STR_9001_ROAD_VEHICLES, STR_018C_WINDOW_TITLE_DRAG_THIS}, +{ WWT_PANEL, 14, 0, 15, 14, 25, 0x0, 0}, +{ WWT_PUSHTXTBTN, 14, 16, 96, 14, 25, SRT_SORT_BY, STR_SORT_TIP}, +{ WWT_TEXTBTN, 14, 97, 248, 14, 25, STR_02E7, 0}, +{ WWT_CLOSEBOX, 14, 249, 259, 14, 25, STR_0225, STR_SORT_TIP}, +{ WWT_MATRIX, 14, 0, 248, 26, 207, 0x701, STR_901A_ROAD_VEHICLES_CLICK_ON}, +{ WWT_SCROLLBAR, 14, 249, 259, 26, 207, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, { WWT_LAST}, }; static const WindowDesc _other_player_roadveh_desc = { - -1, -1, 260, 196, + -1, -1, 260, 208, WC_ROADVEH_LIST,0, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, + WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM, _other_player_roadveh_widgets, PlayerRoadVehWndProc }; diff --git a/settings_gui.c b/settings_gui.c index 58dea0b8e..6a6ddcd5a 100644 --- a/settings_gui.c +++ b/settings_gui.c @@ -3,198 +3,14 @@ #include "window.h" #include "gui.h" -#include "viewport.h" #include "gfx.h" #include "command.h" #include "engine.h" -static uint32 _dropdown_disabled; -static const StringID *_dropdown_items; -static int _dropdown_selindex; -static uint _dropdown_item_count; -static byte _dropdown_button; -static WindowClass _dropdown_windowclass; -static WindowNumber _dropdown_windownum; -static byte _dropdown_var1; -static byte _dropdown_var2; - static uint32 _difficulty_click_a; static uint32 _difficulty_click_b; static byte _difficulty_timeout; -static Widget _dropdown_menu_widgets[] = { -{ WWT_IMGBTN, 0, 0, 0, 0, 0, 0x0}, -{ WWT_LAST}, -}; - -static int GetDropdownItem(Window *w) -{ - uint item; - int y; - - if (GetWidgetFromPos(w, _cursor.pos.x - w->left, _cursor.pos.y - w->top) < 0) - return -1; - - y = _cursor.pos.y - w->top - 2; - - if (y < 0) - return - 1; - - item = y / 10; - if (item >= _dropdown_item_count || HASBIT(_dropdown_disabled,item) || _dropdown_items[item] == 0) - return - 1; - - return item; -} - -void DropdownMenuWndProc(Window *w, WindowEvent *e) -{ - int item; - - switch(e->event) { - case WE_PAINT: { - int x,y,i,sel; - uint32 dis; - - DrawWindowWidgets(w); - - x = 1; - y = 2; - sel = _dropdown_selindex; - dis = _dropdown_disabled; - - for(i=0; _dropdown_items[i] != INVALID_STRING_ID; i++) { - if (_dropdown_items[i] != 0) { - if (sel == 0) { - GfxFillRect(x+1, y, x+w->width-4, y + 9, 0); - } - DrawString(x+2, y, _dropdown_items[i], sel==0 ? 12 : 16); - - if (dis & 1) { - GfxFillRect(x, y, x+w->width-3, y + 9, 0x8000 + - _color_list[_dropdown_menu_widgets[0].color].window_color_bga); - } - } else { - int color_1 = _color_list[_dropdown_menu_widgets[0].color].window_color_1a; - int color_2 = _color_list[_dropdown_menu_widgets[0].color].window_color_2; - GfxFillRect(x+1, y+3, x+w->width-5, y+3, color_1); - GfxFillRect(x+1, y+4, x+w->width-5, y+4, color_2); - } - y += 10; - sel--; - dis>>=1; - } - } break; - - case WE_CLICK: { - item = GetDropdownItem(w); - if (item >= 0) { - _dropdown_var1 = 4; - _dropdown_selindex = item; - SetWindowDirty(w); - } - } break; - - case WE_MOUSELOOP: { - Window *w2 = FindWindowById(_dropdown_windowclass, _dropdown_windownum); - if (w2 == NULL) { - DeleteWindow(w); - return; - } - - if (_dropdown_var1 != 0 && --_dropdown_var1 == 0) { - WindowEvent e; - e.event = WE_DROPDOWN_SELECT; - e.dropdown.button = _dropdown_button; - e.dropdown.index = _dropdown_selindex; - w2->wndproc(w2, &e); - DeleteWindow(w); - return; - } - - if (_dropdown_var2 != 0) { - item = GetDropdownItem(w); - - if (!_left_button_clicked) { - _dropdown_var2 = 0; - if (item < 0) - return; - _dropdown_var1 = 2; - } else { - if (item < 0) - return; - } - - _dropdown_selindex = item; - SetWindowDirty(w); - } - } break; - - case WE_DESTROY: { - Window *w2 = FindWindowById(_dropdown_windowclass, _dropdown_windownum); - if (w2 != NULL) { - CLRBIT(w2->click_state, _dropdown_button); - InvalidateWidget(w2, _dropdown_button); - } - } break; - } -} - -void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask) -{ - WindowNumber num; - WindowClass cls; - int i,t1,t2; - const Widget *wi; - Window *w2; - uint32 old_click_state = w->click_state; - - _dropdown_disabled = disabled_mask; - - cls = w->window_class; - num = w->window_number; - DeleteWindowById(WC_DROPDOWN_MENU, 0); - w = FindWindowById(cls, num); - - if (HASBIT(old_click_state, button)) - return; - - SETBIT(w->click_state, button); - - InvalidateWidget(w, button); - - for(i=0;strings[i] != INVALID_STRING_ID;i++); - if (i == 0) - return; - - _dropdown_items = strings; - _dropdown_item_count = i; - _dropdown_selindex = selected; - - _dropdown_windowclass = w->window_class; - _dropdown_windownum = w->window_number; - _dropdown_button = button; - - _dropdown_var1 = 0; - _dropdown_var2 = 1; - - wi = &w->widget[button]; - - _dropdown_menu_widgets[0].color = wi->color; - - w2 = AllocateWindow( - w->left + wi[-1].left + 1, - w->top + wi->bottom + 2, - (_dropdown_menu_widgets[0].right=t1=wi->right - wi[-1].left, t1 + 1), - (_dropdown_menu_widgets[0].bottom=t2=i*10+3, t2+1), - DropdownMenuWndProc, - 0x3F, - _dropdown_menu_widgets); - - - w2->flags4 &= ~WF_WHITE_BORDER_MASK; -} - extern const StringID _currency_string_list[]; extern uint GetMaskOfAllowedCurrencies(); diff --git a/ship_cmd.c b/ship_cmd.c index de853968e..96ae9e25f 100644 --- a/ship_cmd.c +++ b/ship_cmd.c @@ -852,6 +852,7 @@ int32 CmdBuildShip(int x, int y, uint32 flags, uint32 p1, uint32 p2) VehiclePositionChanged(v); InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); + _vehicle_sort_dirty[VEHSHIP] = true; // build a ship InvalidateWindow(WC_SHIPS_LIST, v->owner); InvalidateWindow(WC_COMPANY, v->owner); } @@ -875,6 +876,7 @@ int32 CmdSellShip(int x, int y, uint32 flags, uint32 p1, uint32 p2) if (flags & DC_EXEC) { InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); + _vehicle_sort_dirty[VEHSHIP] = true; // sell a ship InvalidateWindow(WC_SHIPS_LIST, v->owner); InvalidateWindow(WC_COMPANY, v->owner); DeleteWindowById(WC_VEHICLE_VIEW, v->index); diff --git a/ship_gui.c b/ship_gui.c index 293132275..12f927b2b 100644 --- a/ship_gui.c +++ b/ship_gui.c @@ -9,7 +9,6 @@ #include "station.h" #include "command.h" #include "player.h" -//#include "town.h" #include "engine.h" @@ -868,100 +867,175 @@ static void DrawSmallShipSchedule(Vehicle *v, int x, int y) { } } +// used to get a sorted list of the vehicles +static SortStruct _ship_sort[NUM_NORMAL_VEHICLES]; +static uint16 _num_ship_sort[MAX_PLAYERS]; + +static void MakeSortedShiptList(byte owner) +{ + SortStruct *firstelement; + Vehicle *v; + uint32 n = 0; + uint16 *i; + + if (_vehicle_sort_dirty[VEHSHIP]) { // only resort the whole array if vehicles have been added/removed + // reset to 0 just to be sure + for (i = _num_ship_sort; i != endof(_num_ship_sort); i++) {*i = 0;} + + FOR_ALL_VEHICLES(v) { + if(v->type == VEH_Ship) { + _ship_sort[n].index = v->index; + _ship_sort[n++].owner = v->owner; + _num_ship_sort[v->owner]++; // add number of trains of player + } + } + + // create cumulative ship-ownage + // ships are stored as a cummulative index, eg 25, 41, 43. This means + // Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2 + for (i = &_num_ship_sort[1]; i != endof(_num_ship_sort); i++) {*i += *(i-1);} + + + // sort by owner, then only subsort the requested owner-vehicles + qsort(_ship_sort, n, sizeof(_ship_sort[0]), GeneralOwnerSorter); + + _last_vehicle_idx = 0; // used for "cache" in namesorting + _vehicle_sort_dirty[VEHSHIP] = false; + } + + if (owner == 0) { // first element starts at 0th element and has n elements as described above + firstelement = &_ship_sort[0]; + n = _num_ship_sort[0]; + } else { // nth element starts at the end of the previous one, and has n elements as described above + firstelement = &_ship_sort[_num_ship_sort[owner-1]]; + n = _num_ship_sort[owner] - _num_ship_sort[owner-1]; + } + + _internal_sort_type = _ship_sort_type[owner]; + _internal_sort_order = _ship_sort_order[owner]; + _internal_name_sorter_id = STR_SV_SHIP_NAME; + // only name sorting needs a different procedure, all others are handled by the general sorter + qsort(firstelement, n, sizeof(_ship_sort[0]), (_internal_sort_type == SORT_BY_NAME) ? VehicleNameSorter : GeneralVehicleSorter); + + DEBUG(misc, 1) ("Resorting Ships list player %d...", owner+1); +} + static void PlayerShipsWndProc(Window *w, WindowEvent *e) { switch(e->event) { - case WE_PAINT: - /* determine amount of items for scroller */ - { - Vehicle *v; - int num = 0; - byte owner = (byte)w->window_number; - - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_Ship && v->owner == owner) - num++; - } - SetVScrollCount(w, num); + case WE_PAINT: { + uint32 i; + const byte window_number = (byte)w->window_number; + + if (_ship_sort_type[window_number] == SORT_BY_UNSORTED) // disable 'Sort By' tooltip on Unsorted sorting criteria + w->disabled_state |= (1 << 3); + + if (_ship_sort_dirty[window_number] || _vehicle_sort_dirty[VEHSHIP]) { + _ship_sort_dirty[window_number] = false; + MakeSortedShiptList(window_number); + /* reset sorting timeout */ + w->custom[0] = DAY_TICKS; + w->custom[1] = PERIODIC_RESORT_DAYS; } + // ships are stored as a cummulative index, eg 25, 41, 43. This means + // Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2 ships + i = (window_number == 0) ? 0 : _num_ship_sort[window_number-1]; + SetVScrollCount(w, _num_ship_sort[window_number] - i); + /* draw the widgets */ { - Player *p = DEREF_PLAYER(w->window_number); + Player *p = DEREF_PLAYER(window_number); + /* Company Name -- (###) Ships */ SET_DPARAM16(0, p->name_1); SET_DPARAM32(1, p->name_2); + SET_DPARAM16(2, w->vscroll.count); + SET_DPARAM16(3, _vehicle_sort_listing[_ship_sort_type[window_number]]); DrawWindowWidgets(w); } + /* draw arrow pointing up/down for ascending/descending soring */ + DoDrawString(_ship_sort_order[window_number] & 1 ? "\xAA" : "\xA0", 85, 15, 0x10); - /* draw the ships vehicles */ + /* draw the ship vehicles */ { Vehicle *v; - int pos = w->vscroll.pos; - byte owner = (byte)w->window_number; - int x = 2; - int y = 15; - - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_Ship && v->owner == owner && - --pos < 0 && pos >= -4) { - StringID str; - - DrawShipImage(v, x + 19, y + 6, INVALID_VEHICLE); - DrawVehicleProfitButton(v, x, y+13); - - SET_DPARAM16(0, v->unitnumber); - if (IsShipDepotTile(v->tile)) { - str = STR_021F; - } else { - str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2; - } - DrawString(x, y+2, str, 0); + int n = 0; + const int x = 2; // offset from left side of widget + int y = PLY_WND_PRC__OFFSET_TOP_WIDGET; // offset from top of widget + i += w->vscroll.pos; // offset from sorted ship list of current player + + while (i < _num_ship_sort[window_number]) { + StringID str; + v = DEREF_VEHICLE(_ship_sort[i].index); + + DrawShipImage(v, x + 19, y + 6, INVALID_VEHICLE); + DrawVehicleProfitButton(v, x, y+13); + + SET_DPARAM16(0, v->unitnumber); + if (IsShipDepotTile(v->tile)) { + str = STR_021F; + } else { + str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2; + } + DrawString(x, y+2, str, 0); - SET_DPARAM32(0, v->profit_this_year); - SET_DPARAM32(1, v->profit_last_year); - DrawString(x + 12, y + 28, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0); - - if (v->string_id != STR_SV_SHIP_NAME) { - SET_DPARAM16(0, v->string_id); - DrawString(x+12, y, STR_01AB, 0); - } + SET_DPARAM32(0, v->profit_this_year); + SET_DPARAM32(1, v->profit_last_year); + DrawString(x + 12, y + 28, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0); + + if (v->string_id != STR_SV_SHIP_NAME) { + SET_DPARAM16(0, v->string_id); + DrawString(x+12, y, STR_01AB, 0); + } + + DrawSmallShipSchedule(v, x+138, y); - DrawSmallShipSchedule(v, x+138, y); - y += 36; - } + y += PLY_WND_PRC__SIZE_OF_ROW_BIG; + i++; // next ship + if (++n == w->vscroll.cap) { break;} // max number of ships in the window } } - break; - case WE_CLICK: + } break; + + case WE_CLICK: { switch(e->click.widget) { - case 2: { /* click ship */ - int sel; - Vehicle *v; - byte owner; - - sel = (e->click.pt.y - 14) / 36; + case 3: /* Flip sorting method ascending/descending */ + _ship_sort_order[(byte)w->window_number] ^= 1; + _ship_sort_dirty[(byte)w->window_number] = true; + SetWindowDirty(w); + break; + case 4: case 5:/* Select sorting criteria dropdown menu */ + ShowDropDownMenu(w, _vehicle_sort_listing, _ship_sort_type[(byte)w->window_number], 5, 0); // do it for widget 5 + return; + case 6: { /* Matrix to show vehicles */ + int id_v = (e->click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / PLY_WND_PRC__SIZE_OF_ROW_BIG; + + if ((uint)id_v >= w->vscroll.cap) { return;} // click out of bounds - if ((uint)sel >= 4) - break; - sel += w->vscroll.pos; - owner = (byte)w->window_number; - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_Ship && v->owner == owner && --sel < 0) { - ShowShipViewWindow(v); - break; - } + id_v += w->vscroll.pos; + + { + byte owner = (byte)w->window_number; + uint16 adder = (owner == 0) ? 0 : _num_ship_sort[owner - 1]; // first element in list + Vehicle *v; + + if (id_v + adder >= _num_ship_sort[owner]) { return;} // click out of vehicle bound + + v = DEREF_VEHICLE(_ship_sort[adder+id_v].index); // add the offset id_x to that + + assert(v->type == VEH_Ship && v->owner == owner && v->owner == _ship_sort[adder+id_v].owner); + + ShowShipViewWindow(v); } - break; - } - case 4: {/* click buy */ + } break; + + case 8: { /* Build new Vehicle */ uint tile; tile = _last_built_ship_depot_tile; do { - if (_map_owner[tile] == _local_player && - IsShipDepotTile(tile)) { - + if (_map_owner[tile] == _local_player && IsShipDepotTile(tile)) { ShowShipDepotWindow(tile); ShowBuildShipWindow(tile); return; @@ -973,38 +1047,74 @@ static void PlayerShipsWndProc(Window *w, WindowEvent *e) ShowBuildShipWindow(0); } break; } + } break; + + case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */ + if (_ship_sort_type[(byte)w->window_number] != e->dropdown.index) // if value hasn't changed, dont resort list + _ship_sort_dirty[(byte)w->window_number] = true; + + _ship_sort_type[(byte)w->window_number] = e->dropdown.index; + + if (_ship_sort_type[(byte)w->window_number] != SORT_BY_UNSORTED) // enable 'Sort By' if a sorter criteria is chosen + w->disabled_state &= ~(1 << 3); + + SetWindowDirty(w); + break; + case WE_CREATE: /* set up resort timer */ + w->custom[0] = DAY_TICKS; + w->custom[1] = PERIODIC_RESORT_DAYS; + break; + case WE_TICK: /* resort the list every 20 seconds orso (10 days) */ + if (--w->custom[0] == 0) { + w->custom[0] = DAY_TICKS; + if (--w->custom[1] == 0) { + w->custom[1] = PERIODIC_RESORT_DAYS; + _ship_sort_dirty[(byte)w->window_number] = true; + DEBUG(misc, 1) ("Periodic resort Ships list player %d...", w->window_number+1); + SetWindowDirty(w); + } + } break; } } - static const Widget _player_ships_widgets[] = { -{ WWT_TEXTBTN, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, 14, 11, 259, 0, 13, STR_9805_SHIPS, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_MATRIX, 14, 0, 248, 14, 157, 0x401, STR_9823_SHIPS_CLICK_ON_SHIP_FOR}, -{ WWT_SCROLLBAR, 14, 249, 259, 14, 157, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, -{ WWT_PUSHTXTBTN, 14, 0, 129, 158, 169, STR_9804_NEW_SHIPS, STR_9824_BUILD_NEW_SHIPS_REQUIRES}, -{ WWT_IMGBTN, 14, 130, 259, 158, 169, 0x0}, +{ WWT_CLOSEBOX, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, +{ WWT_CAPTION, 14, 11, 259, 0, 13, STR_9805_SHIPS, STR_018C_WINDOW_TITLE_DRAG_THIS}, +{ WWT_PANEL, 14, 0, 15, 14, 25, 0x0, 0}, +{ WWT_PUSHTXTBTN, 14, 16, 96, 14, 25, SRT_SORT_BY, STR_SORT_TIP}, +{ WWT_TEXTBTN, 14, 97, 248, 14, 25, STR_02E7, 0}, +{ WWT_CLOSEBOX, 14, 249, 259, 14, 25, STR_0225, STR_SORT_TIP}, +{ WWT_MATRIX, 14, 0, 248, 26, 169, 0x401, STR_9823_SHIPS_CLICK_ON_SHIP_FOR}, +{ WWT_SCROLLBAR, 14, 249, 259, 26, 169, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +{ WWT_PUSHTXTBTN, 14, 0, 129, 170, 181, STR_9804_NEW_SHIPS, STR_9824_BUILD_NEW_SHIPS_REQUIRES}, +{ WWT_PANEL, 14, 130, 259, 170, 181, 0x0, 0}, { WWT_LAST}, }; + static const WindowDesc _player_ships_desc = { - -1, -1, 260, 170, + -1, -1, 260, 182, WC_SHIPS_LIST,0, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, + WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM, _player_ships_widgets, PlayerShipsWndProc }; static const Widget _other_player_ships_widgets[] = { -{ WWT_TEXTBTN, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, 14, 11, 259, 0, 13, STR_9805_SHIPS, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_MATRIX, 14, 0, 248, 14, 157, 0x401, STR_9823_SHIPS_CLICK_ON_SHIP_FOR}, -{ WWT_SCROLLBAR, 14, 249, 259, 14, 157, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +{ WWT_CLOSEBOX, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, +{ WWT_CAPTION, 14, 11, 259, 0, 13, STR_9805_SHIPS, STR_018C_WINDOW_TITLE_DRAG_THIS}, +{ WWT_PANEL, 14, 0, 15, 14, 25, 0x0, 0}, +{ WWT_PUSHTXTBTN, 14, 16, 96, 14, 25, SRT_SORT_BY, STR_SORT_TIP}, +{ WWT_TEXTBTN, 14, 97, 248, 14, 25, STR_02E7, 0}, +{ WWT_CLOSEBOX, 14, 249, 259, 14, 25, STR_0225, STR_SORT_TIP}, +{ WWT_MATRIX, 14, 0, 248, 26, 169, 0x401, STR_9823_SHIPS_CLICK_ON_SHIP_FOR}, +{ WWT_SCROLLBAR, 14, 249, 259, 26, 169, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, { WWT_LAST}, }; + static const WindowDesc _other_player_ships_desc = { - -1, -1, 260, 158, + -1, -1, 260, 170, WC_SHIPS_LIST,0, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, + WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM, _other_player_ships_widgets, PlayerShipsWndProc }; diff --git a/station_gui.c b/station_gui.c index 17cb74240..be2cb28ca 100644 --- a/station_gui.c +++ b/station_gui.c @@ -44,47 +44,37 @@ static void StationsWndShowStationRating(int x, int y, int type, uint acceptance } } -// used to get a sorted list of the stations -typedef struct StationSort { - uint16 index; - byte owner; -} StationSort; - -static StationSort _station_sort[lengthof(_stations)]; +static SortStruct _station_sort[lengthof(_stations)]; static uint16 _num_station_sort[MAX_PLAYERS]; static char _bufcache[64]; static uint16 _last_station_idx; -static int CDECL StationSorterByName(const void *a, const void *b) +static int CDECL StationNameSorter(const void *a, const void *b) { char buf1[64]; Station *st; - StationSort *cmp1, *cmp2; - cmp1 = (StationSort*)a; - cmp2 = (StationSort*)b; - - // sort stations by owner, and inside owner by name - if (cmp1->owner == cmp2->owner) { // if same owner, sort by name - st = DEREF_STATION(cmp1->index); + SortStruct *cmp1, *cmp2; + cmp1 = (SortStruct*)a; + cmp2 = (SortStruct*)b; + + st = DEREF_STATION(cmp1->index); + SET_DPARAM16(0, st->town->townnametype); + SET_DPARAM32(1, st->town->townnameparts); + GetString(buf1, st->string_id); + + if ( cmp2->index != _last_station_idx) { + _last_station_idx = cmp2->index; + st = DEREF_STATION(cmp2->index); SET_DPARAM16(0, st->town->townnametype); SET_DPARAM32(1, st->town->townnameparts); - GetString(buf1, st->string_id); - - if ( cmp2->index != _last_station_idx) { - _last_station_idx = cmp2->index; - st = DEREF_STATION(cmp2->index); - SET_DPARAM16(0, st->town->townnametype); - SET_DPARAM32(1, st->town->townnameparts); - GetString(_bufcache, st->string_id); - } - - return strcmp(buf1, _bufcache); // sort by name + GetString(_bufcache, st->string_id); } - return cmp1->owner - cmp2->owner; // sort by owner + + return strcmp(buf1, _bufcache); // sort by name } -static void MakeSortedStationList(Window *w) +static void MakeSortedStationList() { Station *st; uint16 n = 0; @@ -106,7 +96,11 @@ static void MakeSortedStationList(Window *w) for (i = &_num_station_sort[1]; i != endof(_num_station_sort); i++) {*i += *(i-1);} _last_station_idx = 255; // used for "cache" - qsort(_station_sort, n, sizeof(_station_sort[0]), StationSorterByName); + + // sort by owner, then only subsort the requested owner-vehicles + qsort(_station_sort, n, sizeof(_station_sort[0]), GeneralOwnerSorter); + + qsort(_station_sort, n, sizeof(_station_sort[0]), StationNameSorter); DEBUG(misc, 1) ("Resorting Stations list..."); } @@ -118,12 +112,12 @@ static void PlayerStationsWndProc(Window *w, WindowEvent *e) byte i; if (_station_sort_dirty) { _station_sort_dirty = false; - MakeSortedStationList(w); + MakeSortedStationList(); } // stations are stored as a cummulative index, eg 25, 41, 43. This means // Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2 stations - i = (byte)(w->window_number == 0) ? 0 : _num_station_sort[w->window_number-1];; + i = (byte)(w->window_number == 0) ? 0 : _num_station_sort[w->window_number-1]; SetVScrollCount(w, _num_station_sort[w->window_number] - i); /* draw widgets, with player's name in the caption */ @@ -167,7 +161,7 @@ static void PlayerStationsWndProc(Window *w, WindowEvent *e) } y += 10; i++; // next station - if (++p == 12) { break;} // max number of stations in 1 window + if (++p == w->vscroll.cap) { break;} // max number of stations in 1 window } } } break; diff --git a/town_gui.c b/town_gui.c index 3a8bf8726..8d6cfbd5d 100644 --- a/town_gui.c +++ b/town_gui.c @@ -351,7 +351,7 @@ static uint _num_town_sort; static char _bufcache[64]; static byte _last_town_idx; -static int CDECL TownSorterByName(const void *a, const void *b) +static int CDECL TownNameSorter(const void *a, const void *b) { char buf1[64]; Town *t; @@ -374,11 +374,11 @@ static int CDECL TownSorterByName(const void *a, const void *b) return r; } -static int CDECL TownSorterByPop(const void *a, const void *b) +static int CDECL TownPopSorter(const void *a, const void *b) { Town *ta = DEREF_TOWN(*(byte*)a); Town *tb = DEREF_TOWN(*(byte*)b); - int r = tb->population - ta->population; + int r = ta->population - tb->population; if (_town_sort_order & 1) r = -r; return r; } @@ -391,7 +391,7 @@ static void MakeSortedTownList() _num_town_sort = n; _last_town_idx = 255; // used for "cache" - qsort(_town_sort, n, 1, _town_sort_order & 2 ? TownSorterByPop : TownSorterByName); + qsort(_town_sort, n, sizeof(_town_sort[0]), _town_sort_order & 2 ? TownPopSorter : TownNameSorter); DEBUG(misc, 1) ("Resorting Towns list..."); } diff --git a/train_cmd.c b/train_cmd.c index cb6306d91..342883e1f 100644 --- a/train_cmd.c +++ b/train_cmd.c @@ -496,6 +496,7 @@ int32 CmdBuildRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) NormalizeTrainVehInDepot(v); InvalidateWindow(WC_VEHICLE_DEPOT, tile); + _vehicle_sort_dirty[VEHTRAIN] = true; // build a trainengine InvalidateWindow(WC_TRAINS_LIST, v->owner); InvalidateWindow(WC_COMPANY, v->owner); } @@ -793,7 +794,10 @@ int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2) if (flags & DC_EXEC) { // always redraw the depot. maybe redraw train list InvalidateWindow(WC_VEHICLE_DEPOT, first->tile); - if (first->subtype == 0) InvalidateWindow(WC_TRAINS_LIST, first->owner); + if (first->subtype == 0) { + _vehicle_sort_dirty[VEHTRAIN] = true; // sell a wagon / locomotive + InvalidateWindow(WC_TRAINS_LIST, first->owner); + } // when selling an attached locomotive. we need to delete its window. if (v->subtype == 0) { DeleteWindowById(WC_VEHICLE_VIEW, v->index); @@ -2198,6 +2202,7 @@ static void DeleteLastWagon(Vehicle *v) InvalidateWindow(WC_VEHICLE_DETAILS, v->index); DeleteWindowById(WC_VEHICLE_VIEW, v->index); + _vehicle_sort_dirty[VEHTRAIN] = true; // remove crashed train InvalidateWindow(WC_TRAINS_LIST, v->owner); InvalidateWindow(WC_COMPANY, v->owner); diff --git a/train_gui.c b/train_gui.c index d11832ff8..551b0e8e7 100644 --- a/train_gui.c +++ b/train_gui.c @@ -9,7 +9,6 @@ #include "station.h" #include "command.h" #include "player.h" -//#include "town.h" #include "engine.h" static Engine * const _rail_engines[3] = { @@ -54,6 +53,7 @@ static void CcBuildWagon(bool success, uint tile, uint32 p1, uint32 p2) found = GetLastVehicleInChain(found); // put the new wagon at the end of the loco. DoCommandP(0, _new_wagon_id | (found->index<<16), 0, NULL, CMD_MOVE_RAIL_VEHICLE); + _vehicle_sort_dirty[VEHTRAIN] = true; } } @@ -562,7 +562,6 @@ static const Widget _train_depot_widgets[] = { { WWT_CLOSEBOX, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, { WWT_CAPTION, 14, 11, 348, 0, 13, STR_8800_TRAIN_DEPOT, STR_018C_WINDOW_TITLE_DRAG_THIS}, { WWT_MATRIX, 14, 0, 313, 14, 97, 0x601, STR_883F_TRAINS_CLICK_ON_TRAIN_FOR}, -//{ WWT_PANEL, 14, 314, 337, 14, 108, 0x2A9, STR_8841_DRAG_TRAIN_VEHICLE_TO_HERE}, { WWT_PANEL, 14, 314, 337, 14, 54, 0x2A9, STR_8841_DRAG_TRAIN_VEHICLE_TO_HERE}, { WWT_PANEL, 14, 314, 337, 55, 108, 0x2BF, STR_DRAG_WHOLE_TRAIN_TO_SELL_TIP}, @@ -700,9 +699,6 @@ static void ShowRailVehicleRefitWindow(Vehicle *v) WP(w,refit_d).sel = -1; } - - - static Widget _train_view_widgets[] = { { WWT_CLOSEBOX, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, { WWT_CAPTION, 14, 11, 249, 0, 13, STR_882E, STR_018C_WINDOW_TITLE_DRAG_THIS}, @@ -1140,129 +1136,172 @@ void ShowTrainDetailsWindow(Vehicle *v) WP(w,traindetails_d).tab = 0; } -// draw the vehicle profit button in the vehicle list window. -void DrawVehicleProfitButton(Vehicle *v, int x, int y) +// used to get a sorted list of the vehicles +static SortStruct _train_sort[NUM_NORMAL_VEHICLES]; +static uint16 _num_train_sort[MAX_PLAYERS]; + +static void MakeSortedTrainList(byte owner) { - uint32 ormod; - - // draw profit-based colored icons - if(v->age <= 365 * 2) - ormod = 0x3158000; // grey - else if(v->profit_last_year < 0) - ormod = 0x30b8000; //red - else if(v->profit_last_year < 10000) - ormod = 0x30a8000; // yellow - else - ormod = 0x30d8000; // green - DrawSprite((SPR_OPENTTD_BASE + 10) | ormod, x, y); -} + SortStruct *firstelement; + Vehicle *v; + uint32 n = 0; + uint16 *i; + + if (_vehicle_sort_dirty[VEHTRAIN]) { // only resort the whole array if vehicles have been added/removed + // reset to 0 just to be sure + for (i = _num_train_sort; i != endof(_num_train_sort); i++) {*i = 0;} + + FOR_ALL_VEHICLES(v) { + if(v->type == VEH_Train && v->subtype == 0) { + _train_sort[n].index = v->index; + _train_sort[n++].owner = v->owner; + _num_train_sort[v->owner]++; // add number of trains of player + } + } + + // create cumulative train-ownage + // trains are stored as a cummulative index, eg 25, 41, 43. This means + // Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2 + for (i = &_num_train_sort[1]; i != endof(_num_train_sort); i++) {*i += *(i-1);} + + // sort by owner, then only subsort the requested owner-vehicles + qsort(_train_sort, n, sizeof(_train_sort[0]), GeneralOwnerSorter); -static const StringID _player_trains_tooltips[] = { - STR_018B_CLOSE_WINDOW, - STR_018C_WINDOW_TITLE_DRAG_THIS, - STR_883D_TRAINS_CLICK_ON_TRAIN_FOR, - STR_0190_SCROLL_BAR_SCROLLS_LIST, - STR_883E_BUILD_NEW_TRAINS_REQUIRES, - 0, -}; + _last_vehicle_idx = 0; // used for "cache" in namesorting + _vehicle_sort_dirty[VEHTRAIN] = false; + } + + if (owner == 0) { // first element starts at 0th element and has n elements as described above + firstelement = &_train_sort[0]; + n = _num_train_sort[0]; + } else { // nth element starts at the end of the previous one, and has n elements as described above + firstelement = &_train_sort[_num_train_sort[owner-1]]; + n = _num_train_sort[owner] - _num_train_sort[owner-1]; + } + + _internal_sort_type = _train_sort_type[owner]; + _internal_sort_order = _train_sort_order[owner]; + _internal_name_sorter_id = STR_SV_TRAIN_NAME; + // only name sorting needs a different procedure, all others are handled by the general sorter + qsort(firstelement, n, sizeof(_train_sort[0]), (_internal_sort_type == SORT_BY_NAME) ? VehicleNameSorter : GeneralVehicleSorter); + + DEBUG(misc, 1) ("Resorting Trains list player %d...", owner+1); +} static void PlayerTrainsWndProc(Window *w, WindowEvent *e) { switch(e->event) { - case WE_PAINT: - /* determine amount of items for scroller */ - { - Vehicle *v; - int num = 0; - byte owner = (byte)w->window_number; - - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_Train && v->subtype == 0 && v->owner == owner) - num++; - } - - SetVScrollCount(w, num); + case WE_PAINT: { + uint32 i; + const byte window_number = (byte)w->window_number; + + if (_train_sort_type[window_number] == SORT_BY_UNSORTED) // disable 'Sort By' tooltip on Unsorted sorting criteria + w->disabled_state |= (1 << 3); + + if (_train_sort_dirty[window_number] || _vehicle_sort_dirty[VEHTRAIN]) { + _train_sort_dirty[window_number] = false; + MakeSortedTrainList(window_number); + /* reset sorting timeout */ + w->custom[0] = DAY_TICKS; + w->custom[1] = PERIODIC_RESORT_DAYS; } + + // Trains are stored as a cummulative index, eg 25, 41, 43. This means + // Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2 trains + i = (window_number == 0) ? 0 : _num_train_sort[window_number-1]; + SetVScrollCount(w, _num_train_sort[window_number] - i); /* draw the widgets */ { - Player *p = DEREF_PLAYER(w->window_number); + Player *p = DEREF_PLAYER(window_number); + /* Company Name -- (###) Trains */ SET_DPARAM16(0, p->name_1); SET_DPARAM32(1, p->name_2); + SET_DPARAM16(2, w->vscroll.count); + SET_DPARAM16(3, _vehicle_sort_listing[_train_sort_type[window_number]]); DrawWindowWidgets(w); } + /* draw arrow pointing up/down for ascending/descending soring */ + DoDrawString(_train_sort_order[window_number] & 1 ? "\xAA" : "\xA0", 150, 15, 0x10); /* draw the trains */ { Vehicle *v; - int pos = w->vscroll.pos; - byte owner = (byte)w->window_number; - int x = 2; - int y = 15; - - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_Train && v->subtype == 0 && v->owner == owner && - --pos < 0 && pos >= -7) { - StringID str; - - DrawTrainImage(v, x + 21, y + 6, 10, 0, INVALID_VEHICLE); - DrawVehicleProfitButton(v, x, y+13); - - SET_DPARAM16(0, v->unitnumber); - if (IsTrainDepotTile(v->tile)) { - str = STR_021F; - } else { - str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2; - } - DrawString(x, y+2, str, 0); + int n = 0; + const int x = 2; // offset from left side of widget + int y = PLY_WND_PRC__OFFSET_TOP_WIDGET; // offset from top of widget + i += w->vscroll.pos; // offset from sorted trains list of current player - SET_DPARAM32(0, v->profit_this_year); - SET_DPARAM32(1, v->profit_last_year); - DrawString(x + 21, y + 18, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0); - - if (v->string_id != STR_SV_TRAIN_NAME) { - SET_DPARAM16(0, v->string_id); - DrawString(x+21, y, STR_01AB, 0); - } + while (i < _num_train_sort[window_number]) { + StringID str; + v = DEREF_VEHICLE(_train_sort[i].index); + + DrawTrainImage(v, x + 21, y + 6, 10, 0, INVALID_VEHICLE); + DrawVehicleProfitButton(v, x, y+13); - y += 26; - } + SET_DPARAM16(0, v->unitnumber); + if (IsTrainDepotTile(v->tile)) { + str = STR_021F; + } else { + str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2; + } + DrawString(x, y+2, str, 0); + + SET_DPARAM32(0, v->profit_this_year); + SET_DPARAM32(1, v->profit_last_year); + DrawString(x + 21, y + 18, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0); + + if (v->string_id != STR_SV_TRAIN_NAME) { + SET_DPARAM16(0, v->string_id); + DrawString(x+21, y, STR_01AB, 0); + } + + y += PLY_WND_PRC__SIZE_OF_ROW_SMALL; + i++; // next train + if (++n == w->vscroll.cap) { break;} // max number of trains in the window } } - break; + } break; case WE_CLICK: { switch(e->click.widget) { - case 2: { - int idx = (e->click.pt.y - 0xE) / 26; - Vehicle *v; - byte owner; + case 3: /* Flip sorting method ascending/descending */ + _train_sort_order[(byte)w->window_number] ^= 1; + _train_sort_dirty[(byte)w->window_number] = true; + SetWindowDirty(w); + break; + case 4: case 5:/* Select sorting criteria dropdown menu */ + ShowDropDownMenu(w, _vehicle_sort_listing, _train_sort_type[(byte)w->window_number], 5, 0); // do it for widget 5 + return; + case 6: { /* Matrix to show vehicles */ + int id_v = (e->click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / PLY_WND_PRC__SIZE_OF_ROW_SMALL; + + if ((uint)id_v >= w->vscroll.cap) { return;} // click out of bounds - if ((uint)idx >= 7) - break; + id_v += w->vscroll.pos; - idx += w->vscroll.pos; + { + byte owner = (byte)w->window_number; + uint16 adder = (owner == 0) ? 0 : _num_train_sort[owner - 1]; // first element in list + Vehicle *v; - owner = (byte)w->window_number; + if (id_v + adder >= _num_train_sort[owner]) { return;} // click out of vehicle bound - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_Train && v->subtype == 0 && v->owner == owner && - --idx < 0) { - ShowTrainViewWindow(v); - break; - } + v = DEREF_VEHICLE(_train_sort[adder+id_v].index); // add the offset id_x to that + + assert(v->type == VEH_Train && v->subtype == 0 && v->owner == owner && v->owner == _train_sort[adder+id_v].owner); + + ShowTrainViewWindow(v); } } break; - case 4: { + case 8: { /* Build new Vehicle */ uint tile; tile = _last_built_train_depot_tile; do { - if (_map_owner[tile] == _local_player && - IsTrainDepotTile(tile)) { - + if (_map_owner[tile] == _local_player && IsTrainDepotTile(tile)) { ShowTrainDepotWindow(tile); ShowBuildTrainWindow(tile); return; @@ -1276,39 +1315,73 @@ static void PlayerTrainsWndProc(Window *w, WindowEvent *e) } } break; + case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */ + if (_train_sort_type[(byte)w->window_number] != e->dropdown.index) // if value hasn't changed, dont resort list + _train_sort_dirty[(byte)w->window_number] = true; + + _train_sort_type[(byte)w->window_number] = e->dropdown.index; + + if (_train_sort_type[(byte)w->window_number] != SORT_BY_UNSORTED) // enable 'Sort By' if a sorter criteria is chosen + w->disabled_state &= ~(1 << 3); + + SetWindowDirty(w); + break; + case WE_CREATE: /* set up resort timer */ + w->custom[0] = DAY_TICKS; + w->custom[1] = PERIODIC_RESORT_DAYS; + break; + case WE_TICK: /* resort the list every 20 seconds orso (10 days) */ + if (--w->custom[0] == 0) { + w->custom[0] = DAY_TICKS; + if (--w->custom[1] == 0) { + w->custom[1] = PERIODIC_RESORT_DAYS; + _train_sort_dirty[(byte)w->window_number] = true; + DEBUG(misc, 1) ("Periodic resort Trains list player %d...", w->window_number+1); + SetWindowDirty(w); + } + } + break; } } static const Widget _player_trains_widgets[] = { { WWT_CLOSEBOX, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, { WWT_CAPTION, 14, 11, 324, 0, 13, STR_881B_TRAINS, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_MATRIX, 14, 0, 313, 14, 195, 0x701, STR_883D_TRAINS_CLICK_ON_TRAIN_FOR}, -{ WWT_SCROLLBAR, 14, 314, 324, 14, 195, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, -{ WWT_PUSHTXTBTN, 14, 0, 161, 196, 207, STR_8815_NEW_VEHICLES, STR_883E_BUILD_NEW_TRAINS_REQUIRES}, -{ WWT_PANEL, 14, 162, 324, 196, 207, 0x0, 0}, +{ WWT_PANEL, 14, 0, 80, 14, 25, 0x0, 0}, +{ WWT_PUSHTXTBTN, 14, 81, 161, 14, 25, SRT_SORT_BY, STR_SORT_TIP}, +{ WWT_TEXTBTN, 14, 162, 313, 14, 25, STR_02E7, 0}, +{ WWT_CLOSEBOX, 14, 314, 324, 14, 25, STR_0225, STR_SORT_TIP}, +{ WWT_MATRIX, 14, 0, 313, 26, 207, 0x701, STR_883D_TRAINS_CLICK_ON_TRAIN_FOR}, +{ WWT_SCROLLBAR, 14, 314, 324, 26, 207, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +{ WWT_PUSHTXTBTN, 14, 0, 161, 208, 219, STR_8815_NEW_VEHICLES, STR_883E_BUILD_NEW_TRAINS_REQUIRES}, +{ WWT_PANEL, 14, 162, 324, 208, 219, 0x0, 0}, { WWT_LAST}, }; static const WindowDesc _player_trains_desc = { - -1, -1, 325, 208, + -1, -1, 325, 220, WC_TRAINS_LIST,0, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, + WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM, _player_trains_widgets, PlayerTrainsWndProc }; static const Widget _other_player_trains_widgets[] = { -{ WWT_CLOSEBOX, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, 14, 11, 324, 0, 13, STR_881B_TRAINS, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_MATRIX, 14, 0, 313, 14, 195, 0x701, STR_883D_TRAINS_CLICK_ON_TRAIN_FOR}, -{ WWT_SCROLLBAR, 14, 314, 324, 14, 195, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +{ WWT_CLOSEBOX, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, +{ WWT_CAPTION, 14, 11, 324, 0, 13, STR_881B_TRAINS, STR_018C_WINDOW_TITLE_DRAG_THIS}, +{ WWT_PANEL, 14, 0, 80, 14, 25, 0x0, 0}, +{ WWT_PUSHTXTBTN, 14, 81, 161, 14, 25, SRT_SORT_BY, STR_SORT_TIP}, +{ WWT_TEXTBTN, 14, 162, 313, 14, 25, STR_02E7, 0}, +{ WWT_CLOSEBOX, 14, 314, 324, 14, 25, STR_0225, STR_SORT_TIP}, +{ WWT_MATRIX, 14, 0, 313, 26, 207, 0x701, STR_883D_TRAINS_CLICK_ON_TRAIN_FOR}, +{ WWT_SCROLLBAR, 14, 314, 324, 26, 207, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, { WWT_LAST}, }; static const WindowDesc _other_player_trains_desc = { - -1, -1, 325, 196, + -1, -1, 325, 208, WC_TRAINS_LIST,0, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, + WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM, _other_player_trains_widgets, PlayerTrainsWndProc }; @@ -1317,13 +1390,13 @@ void ShowPlayerTrains(int player) { Window *w; - if ( player == _local_player) { + if (player == _local_player) { w = AllocateWindowDescFront(&_player_trains_desc, player); - } else { + } else { w = AllocateWindowDescFront(&_other_player_trains_desc, player); } if (w) { w->caption_color = w->window_number; - w->vscroll.cap = 7; + w->vscroll.cap = 7; // maximum number of vehicles shown } } @@ -358,6 +358,10 @@ SOURCE=.\vehicle.c # End Source File
# Begin Source File
+SOURCE=.\vehicle_gui.c
+# End Source File
+# Begin Source File
+
SOURCE=.\viewport.c
# End Source File
# Begin Source File
@@ -502,6 +506,10 @@ SOURCE=.\vehicle.h # End Source File
# Begin Source File
+SOURCE=.\vehicle_gui.h
+# End Source File
+# Begin Source File
+
SOURCE=.\viewport.h
# End Source File
# Begin Source File
@@ -32,6 +32,16 @@ typedef struct YearMonthDay { int year, month, day; } YearMonthDay; +/* --- 1 Day is 74 ticks --- +* The game's internal structure is dictated by ticks. The date counter (date_fract) is an integer of +* uint16 type, so it can have a max value of 65536. Every tick this variable (date_fract) is +* increased by 885. When it overflows, the new day loop is called. +* * this that means 1 day is : 65536 / 885 = 74 ticks +* * 1 tick is approximately 27ms. +* * 1 day is thus about 2 seconds (74*27 = 1998) on a machine that can run OpenTTD normally +*/ +#define DAY_TICKS 74 + #include "macros.h" // Forward declarations of structs. diff --git a/ttd.vcproj b/ttd.vcproj index 59f19d41c..b3b0a0a09 100644 --- a/ttd.vcproj +++ b/ttd.vcproj @@ -1168,6 +1168,9 @@ RelativePath="vehicle.h">
</File>
<File
+ RelativePath=".\vehicle_gui.h">
+ </File>
+ <File
RelativePath="viewport.h">
</File>
<File
@@ -2015,6 +2018,9 @@ BasicRuntimeChecks="3"/>
</FileConfiguration>
</File>
+ <File
+ RelativePath=".\vehicle_gui.c">
+ </File>
</Filter>
<Filter
Name="Landscape"
@@ -43,7 +43,7 @@ int compare_FiosItems (const void *a, const void *b) { int r; if (_savegame_sort_order < 2) // sort by date - r = da->mtime < db->mtime ? 1 : -1; + r = da->mtime < db->mtime ? -1 : 1; else r = strcmp(da->title[0] ? da->title : da->name, db->title[0] ? db->title : db->name); @@ -1399,6 +1399,10 @@ int32 CmdNameVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) StringID old_str = v->string_id; v->string_id = str; DeleteName(old_str); + _train_sort_dirty[v->owner] = true; + _aircraft_sort_dirty[v->owner] = true; + _ship_sort_dirty[v->owner] = true; + _road_sort_dirty[v->owner] = true; MarkWholeScreenDirty(); } else { DeleteName(str); @@ -1,6 +1,8 @@ #ifndef VEHICLE_H #define VEHICLE_H +#include "vehicle_gui.h" + typedef struct VehicleRail { uint16 last_speed; // NOSAVE: only used in UI uint16 crash_anim_pos; @@ -369,6 +371,7 @@ byte GetDirectionTowards(Vehicle *v, int x, int y); #define BEGIN_ENUM_WAGONS(v) do { #define END_ENUM_WAGONS(v) } while ( (v=v->next) != NULL); +#define DEREF_VEHICLE(i) (&_vehicles[i]) #define FOR_ALL_VEHICLES(v) for(v=_vehicles; v != endof(_vehicles); v++) /* vehicle.c */ diff --git a/vehicle_gui.c b/vehicle_gui.c new file mode 100644 index 000000000..751e3d230 --- /dev/null +++ b/vehicle_gui.c @@ -0,0 +1,147 @@ +#include "stdafx.h" +#include "ttd.h" + +#include "vehicle.h" + +/* General Vehicle GUI based procedures that are independent of vehicle types */ +void InitializeVehiclesGuiList() +{ + bool *i; + for (i = _train_sort_dirty; i != endof(_train_sort_dirty); i++) + *i = true; + + for (i = _aircraft_sort_dirty; i != endof(_aircraft_sort_dirty); i++) + *i = true; + + for (i = _ship_sort_dirty; i != endof(_ship_sort_dirty); i++) + *i = true; + + for (i = _road_sort_dirty; i != endof(_road_sort_dirty); i++) + *i = true; + + for (i = _vehicle_sort_dirty; i != endof(_vehicle_sort_dirty); i++) + *i = true; + + //memset(_train_sort_dirty, true, sizeof(_train_sort_dirty)); +} + +// draw the vehicle profit button in the vehicle list window. +void DrawVehicleProfitButton(Vehicle *v, int x, int y) +{ + uint32 ormod; + + // draw profit-based colored icons + if(v->age <= 365 * 2) + ormod = 0x3158000; // grey + else if(v->profit_last_year < 0) + ormod = 0x30b8000; //red + else if(v->profit_last_year < 10000) + ormod = 0x30a8000; // yellow + else + ormod = 0x30d8000; // green + DrawSprite((SPR_OPENTTD_BASE + 10) | ormod, x, y); +} + +/************ Sorter functions *****************/ +int CDECL GeneralOwnerSorter(const void *a, const void *b) +{ + return (*(SortStruct*)a).owner - (*(SortStruct*)b).owner; +} + +static char _bufcache[64]; // used together with _last_vehicle_idx to hopefully speed up stringsorting + +/* Variables you need to set before calling this function! +* 1. (uint32)_internal_name_sorter_id: default StringID of the vehicle when no name is set. eg +* STR_SV_TRAIN_NAME for trains or STR_SV_AIRCRAFT_NAME for aircraft +* 2. (bool)_internal_sort_order: sorting order, descending/ascending +*/ +int CDECL VehicleNameSorter(const void *a, const void *b) +{ + SortStruct *cmp1 = (SortStruct*)a; + SortStruct *cmp2 = (SortStruct*)b; + Vehicle *v; + char buf1[64] = "\0"; + int r; + + v = DEREF_VEHICLE(cmp1->index); + if (v->string_id != _internal_name_sorter_id) { + SET_DPARAM16(0, v->string_id); + GetString(buf1, STR_0315); + } + + if ( cmp2->index != _last_vehicle_idx) { + _last_vehicle_idx = cmp2->index; + v = DEREF_VEHICLE(cmp2->index); + _bufcache[0] = '\0'; + if (v->string_id != _internal_name_sorter_id) { + SET_DPARAM16(0, v->string_id); + GetString(_bufcache, STR_0315); + } + } + + r = strcmp(buf1, _bufcache); // sort by name + if (_internal_sort_order & 1) r = -r; + return r; +} + +/* Variables you need to set before calling this function! +* 1. (byte)_internal_sort_type: the criteria by which to sort these vehicles (number, age, etc) +* 2. (bool)_internal_sort_order: sorting order, descending/ascending +*/ +int CDECL GeneralVehicleSorter(const void *a, const void *b) +{ + SortStruct *cmp1 = (SortStruct*)a; + SortStruct *cmp2 = (SortStruct*)b; + Vehicle *va = DEREF_VEHICLE(cmp1->index); + Vehicle *vb = DEREF_VEHICLE(cmp2->index); + int r; + + switch (_internal_sort_type) { + case SORT_BY_UNSORTED: /* Sort unsorted */ + return va->index - vb->index; + case SORT_BY_NUMBER: /* Sort by Number */ + r = va->unitnumber - vb->unitnumber; + break; + /* case SORT_BY_NAME: Sort by Name (VehicleNameSorter)*/ + case SORT_BY_AGE: /* Sort by Age */ + r = va->age - vb->age; + break; + case SORT_BY_PROFIT_THIS_YEAR: /* Sort by Profit this year */ + r = va->profit_this_year - vb->profit_this_year; + break; + case SORT_BY_PROFIT_LAST_YEAR: /* Sort by Profit last year */ + r = va->profit_last_year - vb->profit_last_year; + break; + case SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE: { /* Sort by Total capacity per cargotype */ + // FIXME - someone write a normal cargo sorter that also works by cargo_cap, + // FIXME - since I seem to be unable to do so :S + Vehicle *ua = va; + Vehicle *ub = vb; + int i; + byte _cargo_counta[NUM_CARGO]; + byte _cargo_countb[NUM_CARGO]; + do { + _cargo_counta[ua->cargo_type]++; + } while ( (ua = ua->next) != NULL); + do { + _cargo_countb[ub->cargo_type]++; + } while ( (ub = ub->next) != NULL); + + for (i = 0; i < NUM_CARGO; i++) { + r = _cargo_counta[i] - _cargo_countb[i]; + if (r != 0) + break; + } + } break; + case SORT_BY_RELIABILITY: /* Sort by Reliability */ + r = va->reliability - vb->reliability; + break; + case SORT_BY_MAX_SPEED: /* Sort by Max speed */ + r = va->max_speed - vb->max_speed; + break; + default: NOT_REACHED(); + } + + if (_internal_sort_order & 1) r = -r; + return r; +} diff --git a/vehicle_gui.h b/vehicle_gui.h new file mode 100644 index 000000000..b6da5085f --- /dev/null +++ b/vehicle_gui.h @@ -0,0 +1,79 @@ +#ifndef VEHICLE_GUI_H +#define VEHICLE_GUI_H + +void DrawVehicleProfitButton(Vehicle *v, int x, int y); +void InitializeVehiclesGuiList(); + +/* sorter stuff */ +int CDECL GeneralOwnerSorter (const void *a, const void *b); +int CDECL VehicleNameSorter (const void *a, const void *b); +int CDECL GeneralVehicleSorter(const void *a, const void *b); +VARDEF uint32 _internal_name_sorter_id; // internal StringID for default vehicle-names +VARDEF uint32 _last_vehicle_idx; // cached index to hopefully speed up name-sorting +VARDEF bool _internal_sort_order; // descending/ascending +VARDEF byte _internal_sort_type; // Miscalleneous sorting criteria + +typedef struct SortStruct { // store owner through sorting process + uint32 index; + byte owner; +} SortStruct; + +#define PERIODIC_RESORT_DAYS 10 + +enum VehicleSortListingTypes { + SORT_BY_UNSORTED = 0, + SORT_BY_NUMBER = 1, + SORT_BY_NAME = 2, + SORT_BY_AGE = 3, + SORT_BY_PROFIT_THIS_YEAR = 4, + SORT_BY_PROFIT_LAST_YEAR = 5, + SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE = 6, + SORT_BY_RELIABILITY = 7, + SORT_BY_MAX_SPEED = 8 +}; + +static const uint16 _vehicle_sort_listing[] = { + STR_SORT_BY_UNSORTED, + STR_SORT_BY_NUMBER, + STR_SORT_BY_DROPDOWN_NAME, + STR_SORT_BY_AGE, + STR_SORT_BY_PROFIT_THIS_YEAR, + STR_SORT_BY_PROFIT_LAST_YEAR, + STR_SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE, + STR_SORT_BY_RELIABILITY, + STR_SORT_BY_MAX_SPEED, + INVALID_STRING_ID +}; + +enum VehicleSortTypes { + VEHTRAIN = 0, + VEHROAD = 1, + VEHSHIP = 2, + VEHAIRCRAFT = 3 +}; + +VARDEF bool _vehicle_sort_dirty[4]; // global sort, vehicles added/removed (4 types of vehicles) + +VARDEF bool _train_sort_dirty[MAX_PLAYERS]; // vehicles for a given player needs to be resorted (new criteria) +VARDEF byte _train_sort_type[MAX_PLAYERS]; // different criteria for sorting +VARDEF bool _train_sort_order[MAX_PLAYERS]; // sort descending/ascending + +VARDEF bool _aircraft_sort_dirty[MAX_PLAYERS]; // vehicles for a given player needs to be resorted (new criteria) +VARDEF byte _aircraft_sort_type[MAX_PLAYERS]; // different criteria for sorting +VARDEF bool _aircraft_sort_order[MAX_PLAYERS]; // sort descending/ascending + +VARDEF bool _ship_sort_dirty[MAX_PLAYERS]; // vehicles for a given player needs to be resorted (new criteria) +VARDEF byte _ship_sort_type[MAX_PLAYERS]; // different criteria for sorting +VARDEF bool _ship_sort_order[MAX_PLAYERS]; // sort descending/ascending + +VARDEF bool _road_sort_dirty[MAX_PLAYERS]; // vehicles for a given player needs to be resorted (new criteria) +VARDEF byte _road_sort_type[MAX_PLAYERS]; // different criteria for sorting +VARDEF bool _road_sort_order[MAX_PLAYERS]; // sort descending/ascending + +enum { + PLY_WND_PRC__OFFSET_TOP_WIDGET = 26, + PLY_WND_PRC__SIZE_OF_ROW_SMALL = 26, + PLY_WND_PRC__SIZE_OF_ROW_BIG = 36, +}; + +#endif /* VEHICLE_GUI_H */ diff --git a/water_cmd.c b/water_cmd.c index fa22d5a1e..789ceb446 100644 --- a/water_cmd.c +++ b/water_cmd.c @@ -531,6 +531,7 @@ static void FloodVehicle(Vehicle *v) v->vehstatus |= VS_CRASHED; v->u.road.crashed_ctr = 2000; // max 2220, disappear pretty fast + _vehicle_sort_dirty[VEHROAD] = true; InvalidateWindow(WC_ROADVEH_LIST, v->owner); } @@ -547,6 +548,7 @@ static void FloodVehicle(Vehicle *v) v = u; v->u.rail.crash_anim_pos = 4000; // max 4440, disappear pretty fast + _vehicle_sort_dirty[VEHTRAIN] = true; InvalidateWindow(WC_TRAINS_LIST, v->owner); } @@ -373,3 +373,186 @@ draw_default:; } } + +static uint _dropdown_item_count; +static uint32 _dropdown_disabled; +static const StringID *_dropdown_items; +static int _dropdown_selindex; +static byte _dropdown_button; +static WindowClass _dropdown_windowclass; +static WindowNumber _dropdown_windownum; +static byte _dropdown_var1; +static byte _dropdown_var2; + +static Widget _dropdown_menu_widgets[] = { +{ WWT_IMGBTN, 0, 0, 0, 0, 0, 0x0}, +{ WWT_LAST}, +}; + +static int GetDropdownItem(Window *w) +{ + uint item; + int y; + + if (GetWidgetFromPos(w, _cursor.pos.x - w->left, _cursor.pos.y - w->top) < 0) + return -1; + + y = _cursor.pos.y - w->top - 2; + + if (y < 0) + return - 1; + + item = y / 10; + if (item >= _dropdown_item_count || HASBIT(_dropdown_disabled,item) || _dropdown_items[item] == 0) + return - 1; + + return item; +} + +void DropdownMenuWndProc(Window *w, WindowEvent *e) +{ + int item; + + switch(e->event) { + case WE_PAINT: { + int x,y,i,sel; + uint32 dis; + + DrawWindowWidgets(w); + + x = 1; + y = 2; + sel = _dropdown_selindex; + dis = _dropdown_disabled; + + for(i=0; _dropdown_items[i] != INVALID_STRING_ID; i++) { + if (_dropdown_items[i] != 0) { + if (sel == 0) { + GfxFillRect(x+1, y, x+w->width-4, y + 9, 0); + } + DrawString(x+2, y, _dropdown_items[i], sel==0 ? 12 : 16); + + if (dis & 1) { + GfxFillRect(x, y, x+w->width-3, y + 9, 0x8000 + + _color_list[_dropdown_menu_widgets[0].color].window_color_bga); + } + } else { + int color_1 = _color_list[_dropdown_menu_widgets[0].color].window_color_1a; + int color_2 = _color_list[_dropdown_menu_widgets[0].color].window_color_2; + GfxFillRect(x+1, y+3, x+w->width-5, y+3, color_1); + GfxFillRect(x+1, y+4, x+w->width-5, y+4, color_2); + } + y += 10; + sel--; + dis>>=1; + } + } break; + + case WE_CLICK: { + item = GetDropdownItem(w); + if (item >= 0) { + _dropdown_var1 = 4; + _dropdown_selindex = item; + SetWindowDirty(w); + } + } break; + + case WE_MOUSELOOP: { + Window *w2 = FindWindowById(_dropdown_windowclass, _dropdown_windownum); + if (w2 == NULL) { + DeleteWindow(w); + return; + } + + if (_dropdown_var1 != 0 && --_dropdown_var1 == 0) { + WindowEvent e; + e.event = WE_DROPDOWN_SELECT; + e.dropdown.button = _dropdown_button; + e.dropdown.index = _dropdown_selindex; + w2->wndproc(w2, &e); + DeleteWindow(w); + return; + } + + if (_dropdown_var2 != 0) { + item = GetDropdownItem(w); + + if (!_left_button_clicked) { + _dropdown_var2 = 0; + if (item < 0) + return; + _dropdown_var1 = 2; + } else { + if (item < 0) + return; + } + + _dropdown_selindex = item; + SetWindowDirty(w); + } + } break; + + case WE_DESTROY: { + Window *w2 = FindWindowById(_dropdown_windowclass, _dropdown_windownum); + if (w2 != NULL) { + CLRBIT(w2->click_state, _dropdown_button); + InvalidateWidget(w2, _dropdown_button); + } + } break; + } +} + +void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask) +{ + WindowNumber num; + WindowClass cls; + int i,t1,t2; + const Widget *wi; + Window *w2; + uint32 old_click_state = w->click_state; + + _dropdown_disabled = disabled_mask; + + cls = w->window_class; + num = w->window_number; + DeleteWindowById(WC_DROPDOWN_MENU, 0); + w = FindWindowById(cls, num); + + if (HASBIT(old_click_state, button)) + return; + + SETBIT(w->click_state, button); + + InvalidateWidget(w, button); + + for(i=0;strings[i] != INVALID_STRING_ID;i++); + if (i == 0) + return; + + _dropdown_items = strings; + _dropdown_item_count = i; + _dropdown_selindex = selected; + + _dropdown_windowclass = w->window_class; + _dropdown_windownum = w->window_number; + _dropdown_button = button; + + _dropdown_var1 = 0; + _dropdown_var2 = 1; + + wi = &w->widget[button]; + + _dropdown_menu_widgets[0].color = wi->color; + + w2 = AllocateWindow( + w->left + wi[-1].left + 1, + w->top + wi->bottom + 2, + (_dropdown_menu_widgets[0].right=t1=wi->right - wi[-1].left, t1 + 1), + (_dropdown_menu_widgets[0].bottom=t2=i*10+3, t2+1), + DropdownMenuWndProc, + 0x3F, + _dropdown_menu_widgets); + + + w2->flags4 &= ~WF_WHITE_BORDER_MASK; +} @@ -1470,7 +1470,7 @@ int CDECL compare_FiosItems (const void *a, const void *b) { int r; if (_savegame_sort_order < 2) // sort by date - r = da->mtime < db->mtime ? 1 : -1; + r = da->mtime < db->mtime ? -1 : 1; else r = stricmp(da->title[0] ? da->title : da->name, db->title[0] ? db->title : db->name); @@ -422,6 +422,7 @@ int32 PositionMainToolbar(Window *w); /* widget.c */ int GetWidgetFromPos(Window *w, int x, int y); void DrawWindowWidgets(Window *w); +void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask); void HandleButtonClick(Window *w, byte widget); |