diff options
-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); |