(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.
This commit is contained in:
darkvater 2004-09-06 18:15:13 +00:00
parent df1397a47e
commit bf0652d3fc
28 changed files with 1282 additions and 612 deletions

View File

@ -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

View File

@ -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);

View File

@ -864,102 +864,175 @@ static void DrawSmallSchedule(Vehicle *v, int x, int y) {
}
}
// used to get a sorted list of the vehicles
static SortStruct _aircraft_sort[NUM_NORMAL_VEHICLES];
static uint16 _num_aircraft_sort[MAX_PLAYERS];
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);
DEBUG(misc, 1) ("Resorting Aircraft list player %d...", owner+1);
}
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;
case WE_PAINT: {
uint32 i;
const byte window_number = (byte)w->window_number;
FOR_ALL_VEHICLES(v) {
if (v->type == VEH_Aircraft && v->subtype <= 2 && v->owner == owner)
num++;
}
if (_aircraft_sort_type[window_number] == SORT_BY_UNSORTED) // disable 'Sort By' tooltip on Unsorted sorting criteria
w->disabled_state |= (1 << 3);
SetVScrollCount(w, num);
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;
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
FOR_ALL_VEHICLES(v) {
if (v->type == VEH_Aircraft && v->subtype <= 2 && v->owner == owner &&
--pos < 0 && pos >= -4) {
StringID str;
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);
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);
}
DrawSmallSchedule(v, x+136, y);
y += 36;
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);
}
DrawSmallSchedule(v, x+136, y);
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;
sel = (e->click.pt.y - 14) / 36;
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;
}
}
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: { /* build new */
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
id_v += w->vscroll.pos;
{
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 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
}
}

2
gui.h
View File

@ -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;

View File

@ -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...");
}

View File

@ -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)

1
misc.c
View File

@ -121,6 +121,7 @@ void InitializeGame()
InitializeIndustries();
InitializeNameMgr();
InitializeVehiclesGuiList();
InitializeTrains();
InitializePlayers();

View File

@ -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);

View File

@ -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;
case WE_PAINT: {
uint32 i;
const byte window_number = (byte)w->window_number;
FOR_ALL_VEHICLES(v) {
if (v->type == VEH_Road && v->owner == owner)
num++;
}
SetVScrollCount(w, num);
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);
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
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;
}
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;
idx += 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;
owner = (byte)w->window_number;
if (id_v + adder >= _num_road_sort[owner]) { return;} // click out of vehicle bound
FOR_ALL_VEHICLES(v) {
if (v->type == VEH_Road && v->owner == owner &&
--idx < 0) {
ShowRoadVehViewWindow(v);
break;
}
v = DEREF_VEHICLE(_road_sort[adder+id_v].index); // add the offset id_x to that
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
};

View File

@ -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();

View File

@ -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);

View File

@ -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;
case WE_PAINT: {
uint32 i;
const byte window_number = (byte)w->window_number;
FOR_ALL_VEHICLES(v) {
if (v->type == VEH_Ship && v->owner == owner)
num++;
}
SetVScrollCount(w, num);
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
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);
}
while (i < _num_ship_sort[window_number]) {
StringID str;
v = DEREF_VEHICLE(_ship_sort[i].index);
DrawSmallShipSchedule(v, x+138, y);
DrawShipImage(v, x + 19, y + 6, INVALID_VEHICLE);
DrawVehicleProfitButton(v, x, y+13);
y += 36;
}
}
}
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;
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;
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);
}
DrawSmallShipSchedule(v, x+138, y);
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 4: {/* click buy */
} break;
case WE_CLICK: {
switch(e->click.widget) {
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
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 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
};

View File

@ -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;
SortStruct *cmp1, *cmp2;
cmp1 = (SortStruct*)a;
cmp2 = (SortStruct*)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);
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;

View File

@ -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...");
}

View File

@ -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);

View File

@ -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;
SortStruct *firstelement;
Vehicle *v;
uint32 n = 0;
uint16 *i;
// 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);
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);
_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 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,
};
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;
case WE_PAINT: {
uint32 i;
const byte window_number = (byte)w->window_number;
FOR_ALL_VEHICLES(v) {
if (v->type == VEH_Train && v->subtype == 0 && v->owner == owner)
num++;
}
if (_train_sort_type[window_number] == SORT_BY_UNSORTED) // disable 'Sort By' tooltip on Unsorted sorting criteria
w->disabled_state |= (1 << 3);
SetVScrollCount(w, num);
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);
y += 26;
}
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);
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
}
}

View File

@ -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

10
ttd.h
View 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.

View File

@ -1167,6 +1167,9 @@
<File
RelativePath="vehicle.h">
</File>
<File
RelativePath=".\vehicle_gui.h">
</File>
<File
RelativePath="viewport.h">
</File>
@ -2015,6 +2018,9 @@
BasicRuntimeChecks="3"/>
</FileConfiguration>
</File>
<File
RelativePath=".\vehicle_gui.c">
</File>
</Filter>
<Filter
Name="Landscape"

2
unix.c
View File

@ -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);

View File

@ -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);

View File

@ -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 */

147
vehicle_gui.c Normal file
View File

@ -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;
}

79
vehicle_gui.h Normal file
View File

@ -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 */

View File

@ -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);
}

183
widget.c
View File

@ -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;
}

View File

@ -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);

View File

@ -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);