diff options
Diffstat (limited to 'network_gui.c')
-rw-r--r-- | network_gui.c | 1199 |
1 files changed, 980 insertions, 219 deletions
diff --git a/network_gui.c b/network_gui.c index e924e8b72..5395378c7 100644 --- a/network_gui.c +++ b/network_gui.c @@ -1,20 +1,29 @@ #include "stdafx.h" #include "ttd.h" +#include "network.h" +#include "saveload.h" + +#include "hal.h" // for file list + +#ifdef ENABLE_NETWORK + #include "table/strings.h" +#include "network_data.h" #include "window.h" #include "gui.h" #include "gfx.h" #include "command.h" -#include "network.h" +#include "functions.h" +#include "variables.h" +#include "network_server.h" +#include "network_udp.h" #define BGC 5 #define BTC 15 #define MAX_QUERYSTR_LEN 64 static byte _edit_str_buf[MAX_QUERYSTR_LEN*2]; -static void ShowNetworkStartServerWindow(); -#if 0 -static void ShowNetworkLobbyWindow(); -#endif +static void ShowNetworkStartServerWindow(void); +static void ShowNetworkLobbyWindow(void); static byte _selected_field; @@ -24,148 +33,293 @@ static const StringID _connection_types_dropdown[] = { INVALID_STRING_ID }; -/* Should be _network_game->players_max but since network is not yet really done -* we'll just use some dummy here -* network.c -->> static NetworkGameInfo _network_game; -*/ -static byte _players_max; -/* Should be ??????????? (something) but since network is not yet really done -* we'll just use some dummy here -*/ static byte _network_connection; -static uint16 _network_game_count_last; + +static StringID _str_map_name, _str_game_name, _str_server_version, _str_server_address; enum { - NET_PRC__OFFSET_TOP_WIDGET = 93, - NET_PRC__SIZE_OF_ROW = 14, + NET_PRC__OFFSET_TOP_WIDGET = 74, + NET_PRC__OFFSET_TOP_WIDGET_COMPANY = 42, + NET_PRC__SIZE_OF_ROW = 14, + NET_PRC__SIZE_OF_ROW_COMPANY = 12, }; -static NetworkGameList *selected_item = NULL; +static NetworkGameList *_selected_item = NULL; +static int8 _selected_company_item = -1; + +#ifdef WITH_REV +extern char _openttd_revision[]; +#endif + +// Truncates a string to max_width (via GetStringWidth) and adds 3 dots +// at the end of the name. +static void NetworkTruncateString(char *name, const int max_width) +{ + char temp[NETWORK_NAME_LENGTH]; + char internal_name[NETWORK_NAME_LENGTH]; + + ttd_strlcpy(internal_name, name, sizeof(internal_name)); + + if (GetStringWidth(internal_name) > max_width) { + // Servername is too long, trunc it! + snprintf(temp, sizeof(temp), "%s...", internal_name); + // Continue to delete 1 char of the string till it is in range + while (GetStringWidth(temp) > max_width) { + internal_name[strlen(internal_name) - 1] = '\0'; + snprintf(temp, sizeof(temp), "%s...", internal_name); + } + ttd_strlcpy(name, temp, sizeof(temp)); + } +} static void NetworkGameWindowWndProc(Window *w, WindowEvent *e) { switch(e->event) { case WE_PAINT: { + if (_selected_item == NULL) + w->disabled_state = (1<<17) | (1<<18); + else if (!_selected_item->online) + w->disabled_state = (1<<17); // Server offline, join button disabled + else if (_selected_item->info.clients_on == _selected_item->info.clients_max) + w->disabled_state = (1<<17); // Server full, join button disabled + else + w->disabled_state = 0; SetDParam(0, 0x00); SetDParam(2, STR_NETWORK_LAN + _network_connection); DrawWindowWidgets(w); - DrawEditBox(w, 6); + DrawEditBox(w, 3); - DrawString(9, 43, STR_NETWORK_PLAYER_NAME, 2); - DrawString(9, 63, STR_NETWORK_SELECT_CONNECTION, 2); + DrawString(9, 23, STR_NETWORK_PLAYER_NAME, 2); + DrawString(9, 43, STR_NETWORK_CONNECTION, 2); - DrawString(15, 82, STR_NETWORK_GAME_NAME, 2); - DrawString(238, 82, STR_NETWORK_PLAYERS, 2); - DrawString(288, 82, STR_NETWORK_MAP_SIZE, 2); + DrawString(15, 63, STR_NETWORK_GAME_NAME, 2); + DrawString(135, 63, STR_NETWORK_CLIENTS_CAPTION, 2); { // draw list of games uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3; int32 n = 0; - NetworkGameList *cur_item = _network_game_list; + char servername[NETWORK_NAME_LENGTH]; + const NetworkGameList *cur_item = _network_game_list; while (cur_item != NULL) { - if (cur_item == selected_item) - GfxFillRect(11, y - 2, 380, y + 9, 10); // show highlighted item with a different colour +#ifdef WITH_REV + bool compatible = (strncmp(cur_item->info.server_revision, _openttd_revision, 10) == 0); +#else + bool compatible = true; // We have no idea if we are compatible... +#endif + if (strncmp(cur_item->info.server_revision, "norev000", sizeof(cur_item->info.server_revision)) == 0) + compatible = true; + + if (cur_item == _selected_item) + GfxFillRect(11, y - 2, 218, y + 9, 10); // show highlighted item with a different colour - DoDrawString(cur_item->item.server_name, 15, y, 16); // server name + snprintf(servername, sizeof(servername), "%s", cur_item->info.server_name); + NetworkTruncateString(servername, 110); + DoDrawString(servername, 15, y, 16); // server name - SetDParam(0, cur_item->item.players_on); - SetDParam(1, cur_item->item.players_max); - DrawString(238, y, STR_NETWORK_PLAYERS_VAL, 2); // #/# + SetDParam(0, cur_item->info.clients_on); + SetDParam(1, cur_item->info.clients_max); + DrawString(135, y, STR_NETWORK_CLIENTS_ONLINE, 2); - DoDrawString(cur_item->item.map_name, 288, y, 16); // map size - cur_item = cur_item->_next; + // draw red or green icon, depending on compatibility with server. TODO: needs new icons + DrawSprite((SPR_OPENTTD_BASE + 10) | (compatible?0x30d8000:0x30b8000), 185, y); + // draw red or green flag, to show if the server is password protected. TODO: needs new icons + DrawSprite((cur_item->info.use_password)? 0xC12 : 0xC13, 195, y); + + cur_item = cur_item->next; y += NET_PRC__SIZE_OF_ROW; if (++n == w->vscroll.cap) { break;} // max number of games in the window } } + + // right menu + GfxFillRect(252, 23, 468, 65, 157); + if (_selected_item == NULL) { + DrawStringMultiCenter(360, 40, STR_NETWORK_GAME_INFO, 0); + } else if (!_selected_item->online) { + SetDParam(0, _str_game_name); + DrawStringMultiCenter(360, 42, STR_ORANGE, 2); // game name + + DrawStringMultiCenter(360, 110, STR_NETWORK_SERVER_OFFLINE, 2); // server offline + } else { // show game info + uint16 y = 70; + + DrawStringMultiCenter(360, 30, STR_NETWORK_GAME_INFO, 0); + + SetDParam(0, _str_game_name); + DrawStringMultiCenter(360, 42, STR_ORANGE, 2); // game name + + SetDParam(0, _str_map_name); + DrawStringMultiCenter(360, 54, STR_02BD, 2); // map name + + SetDParam(0, _selected_item->info.clients_on); + SetDParam(1, _selected_item->info.clients_max); + DrawString(260, y, STR_NETWORK_CLIENTS, 2); // clients on the server / maximum slots + y+=10; + + SetDParam(0, STR_NETWORK_LANG_ANY+_selected_item->info.server_lang); + DrawString(260, y, STR_NETWORK_LANGUAGE, 2); // server language + y+=10; + + SetDParam(0, STR_TEMPERATE_LANDSCAPE+_selected_item->info.map_set); + DrawString(260, y, STR_NETWORK_TILESET, 2); // tileset + y+=10; + + SetDParam(0, _selected_item->info.map_width); + SetDParam(1, _selected_item->info.map_height); + DrawString(260, y, STR_NETWORK_MAP_SIZE, 2); // map size + y+=10; + + SetDParam(0, _str_server_version); + DrawString(260, y, STR_NETWORK_SERVER_VERSION, 2); // server version + y+=10; + + SetDParam(0, _str_server_address); + DrawString(260, y, STR_NETWORK_SERVER_ADDRESS, 2); // server address + y+=10; + + SetDParam(0, _selected_item->info.start_date); + DrawString(260, y, STR_NETWORK_START_DATE, 2); // start date + y+=10; + + SetDParam(0, _selected_item->info.game_date); + DrawString(260, y, STR_NETWORK_CURRENT_DATE, 2); // current date + y+=10; + + if (_selected_item->info.clients_on == _selected_item->info.clients_max) + // Show: server full, when clients_on == clients_max + DrawStringMultiCenter(360, y, STR_NETWORK_SERVER_FULL, 2); // server full + else if (_selected_item->info.use_password) + DrawStringMultiCenter(360, y, STR_NETWORK_PASSWORD, 2); // password warning + y+=10; + } } break; case WE_CLICK: _selected_field = e->click.widget; switch(e->click.widget) { - case 0: case 15: /* Close 'X' | Cancel button */ + case 0: case 14: /* Close 'X' | Cancel button */ DeleteWindowById(WC_NETWORK_WINDOW, 0); - NetworkLobbyShutdown(); break; - case 3: { /* Find server automaticaly */ - NetworkCoreConnectGame("auto", _network_server_port); - } break; - case 4: { /* Connect via direct ip */ - StringID str; - str = AllocateName((byte*)_decode_parameters, 0); - - ShowQueryString( - str, - STR_NETWORK_ENTER_IP, - 50, // maximum 50 characters OR - 250, // characters up to width 250 pixels, whichever is satisfied first - w->window_class, - w->window_number); - DeleteName(str); - } break; - case 5: /* Start server */ - ShowNetworkStartServerWindow(); - break; - case 7: case 8: /* Connection type */ - ShowDropDownMenu(w, _connection_types_dropdown, _network_connection, 8, 0); // do it for widget 8 + case 4: case 5: /* Connection type */ + ShowDropDownMenu(w, _connection_types_dropdown, _network_connection, 5, 0); // do it for widget 5 return; - case 14: { /* Matrix to show networkgames */ + case 10: { /* Matrix to show networkgames */ uint32 id_v = (e->click.pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW; if (id_v >= w->vscroll.cap) { return;} // click out of bounds - id_v += w->vscroll.pos; { NetworkGameList *cur_item = _network_game_list; for (; id_v > 0 && cur_item != NULL; id_v--) - cur_item = cur_item->_next; - - if (cur_item == NULL) { return;} // click out of vehicle bounds - - selected_item = cur_item; + cur_item = cur_item->next; + + if (cur_item == NULL) { + // click out of vehicle bounds + _selected_item = NULL; + SetWindowDirty(w); + return; + } + _selected_item = cur_item; + + DeleteName(_str_game_name); + DeleteName(_str_map_name); + DeleteName(_str_server_version); + DeleteName(_str_server_address); + if (_selected_item->info.server_name[0] != '\0') + _str_game_name = AllocateName((byte*) _selected_item->info.server_name, 0); + else + _str_game_name = STR_EMPTY; + + if (_selected_item->info.map_name[0] != '\0') + _str_map_name = AllocateName((byte*) _selected_item->info.map_name, 0); + else + _str_map_name = STR_EMPTY; + + if (_selected_item->info.server_revision[0] != '\0') + _str_server_version = AllocateName((byte*) _selected_item->info.server_revision, 0); + else + _str_server_version = STR_EMPTY; + + if (_selected_item->info.hostname[0] != '\0') + _str_server_address = AllocateName((byte*) _selected_item->info.hostname, 0); + else + _str_server_address = STR_EMPTY; } + SetWindowDirty(w); } break; - case 16: /* Join Game */ - if (selected_item != NULL) - NetworkCoreConnectGameStruct(selected_item); + case 11: /* Find server automatically */ + NetworkUDPSearchGame(); break; - } - break; + case 12: { // Add a server + StringID str = AllocateName((byte*)_network_default_ip, 0); + + ShowQueryString( + str, + STR_NETWORK_ENTER_IP, + 31 | 0x1000, // maximum number of characters OR + 250, // characters up to this width pixels, whichever is satisfied first + w->window_class, + w->window_number); + DeleteName(str); + } break; + case 13: /* Start server */ + ShowNetworkStartServerWindow(); + break; + case 17: /* Join Game */ + if (_selected_item != NULL) { + memcpy(&_network_game_info, &_selected_item->info, sizeof(NetworkGameInfo)); + snprintf(_network_last_host, sizeof(_network_last_host), "%s", inet_ntoa(*(struct in_addr *)&_selected_item->ip)); + _network_last_port = _selected_item->port; + ShowNetworkLobbyWindow(); + } + break; + case 18: // Refresh + if (_selected_item != NULL) { + NetworkQueryServer(_selected_item->info.hostname, _selected_item->port, true); + } + break; + + } break; case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */ _network_connection = e->dropdown.index; switch (_network_connection) { case 0: /* LAN */ - NetworkGameListFromLAN(); +// NetworkGameListFromLAN(); break; case 1: /* Internet */ - NetworkGameListFromInternet(); +// NetworkGameListFromInternet(); break; } - _network_game_count_last = _network_game_count; SetWindowDirty(w); - break; case WE_MOUSELOOP: - if (_selected_field == 6) - HandleEditBox(w, 6); + if (_selected_field == 3) + HandleEditBox(w, 3); break; case WE_KEYPRESS: - if (_selected_field != 6) + if (_selected_field != 3) break; - switch (HandleEditBoxKey(w, 6, e)) { + switch (HandleEditBoxKey(w, 3, e)) { case 1: - HandleButtonClick(w, 9); + HandleButtonClick(w, 10); break; } + + // The name is only allowed when it starts with a letter! + if (_edit_str_buf[0] != '\0' && _edit_str_buf[0] != ' ') + ttd_strlcpy(_network_player_name, _edit_str_buf, lengthof(_network_player_name)); + else + ttd_strlcpy(_network_player_name, "Player", lengthof(_network_player_name)); + break; case WE_ON_EDIT_TEXT: { @@ -175,147 +329,225 @@ static void NetworkGameWindowWndProc(Window *w, WindowEvent *e) const byte *player = NULL; uint16 rport; - rport = _network_server_port; + ttd_strlcpy(_network_default_ip, b, lengthof(_network_default_ip)); + rport = NETWORK_DEFAULT_PORT; ParseConnectionString(&player, &port, b); - if (player!=NULL) _network_playas = atoi(player); - if (port!=NULL) rport = atoi(port); + if (player != NULL) _network_playas = atoi(player); + if (port != NULL) rport = atoi(port); - NetworkCoreConnectGame(b, rport); + NetworkQueryServer(b, rport, true); } } break; - case WE_TICK: { - if (_network_game_count_last != _network_game_count) - SetWindowDirty(w); + case WE_CREATE: { + _selected_item = NULL; } break; - } } static const Widget _network_game_window_widgets[] = { { WWT_CLOSEBOX, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, BGC, 10, 399, 0, 13, STR_NETWORK_MULTIPLAYER, STR_NULL}, -{ WWT_IMGBTN, BGC, 0, 399, 14, 199, 0x0, STR_NULL}, +{ WWT_CAPTION, BGC, 11, 479, 0, 13, STR_NETWORK_MULTIPLAYER, STR_NULL}, +{ WWT_IMGBTN, BGC, 0, 479, 14, 214, 0x0, STR_NULL}, + +/* LEFT SIDE */ +{ WWT_IMGBTN, BGC, 90, 230, 22, 33, 0x0, STR_NETWORK_ENTER_NAME_TIP}, -{ WWT_PUSHTXTBTN, BTC, 20, 130, 22, 33, STR_NETWORK_FIND_SERVER, STR_NETWORK_FIND_SERVER_TIP}, -{ WWT_PUSHTXTBTN, BTC, 145, 255, 22, 33, STR_NETWORK_DIRECT_CONNECT, STR_NETWORK_DIRECT_CONNECT_TIP}, -{ WWT_PUSHTXTBTN, BTC, 270, 380, 22, 33, STR_NETWORK_START_SERVER, STR_NETWORK_START_SERVER_TIP}, +{ WWT_6, BGC, 90, 230, 42, 53, STR_NETWORK_COMBO1, STR_NETWORK_CONNECTION_TIP}, +{ WWT_CLOSEBOX, BGC, 219, 229, 43, 52, STR_0225, STR_NETWORK_CONNECTION_TIP}, -{ WWT_IMGBTN, BGC, 250, 394, 42, 53, 0x0, STR_NETWORK_ENTER_NAME_TIP}, +{ WWT_SCROLLBAR, BGC, 220, 230, 62, 185, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, -{ WWT_6, BGC, 250, 393, 62, 73, STR_NETWORK_COMBO1, STR_NETWORK_CONNECTION_TYPE_TIP}, -{ WWT_CLOSEBOX, BGC, 382, 392, 63, 72, STR_0225, STR_NETWORK_CONNECTION_TYPE_TIP}, +{ WWT_IMGBTN, BTC, 10, 130, 62, 73, 0x0, STR_NETWORK_GAME_NAME_TIP }, +{ WWT_IMGBTN, BTC, 131, 180, 62, 73, 0x0, STR_NETWORK_CLIENTS_CAPTION_TIP }, +{ WWT_IMGBTN, BTC, 181, 219, 62, 73, 0x0, STR_NETWORK_INFO_ICONS_TIP }, -{ WWT_SCROLLBAR, BGC, 382, 392, 81, 176, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +{ WWT_MATRIX, BGC, 10, 219, 74, 185, 0x801, STR_NETWORK_CLICK_GAME_TO_SELECT}, -{ WWT_IMGBTN, BTC, 10, 231, 81, 92, 0x0, STR_NETWORK_GAME_NAME_TIP }, -{ WWT_IMGBTN, BTC, 232, 281, 81, 92, 0x0, STR_NETWORK_PLAYERS_TIP }, -{ WWT_IMGBTN, BTC, 282, 331, 81, 92, 0x0, STR_NETWORK_MAP_SIZE_TIP }, -{ WWT_IMGBTN, BTC, 332, 381, 81, 92, 0x0, STR_NETWORK_INFO_ICONS_TIP }, +{ WWT_PUSHTXTBTN, BTC, 10, 115, 195, 206, STR_NETWORK_FIND_SERVER, STR_NETWORK_FIND_SERVER_TIP}, +{ WWT_PUSHTXTBTN, BTC, 125, 230, 195, 206, STR_NETWORK_ADD_SERVER, STR_NETWORK_ADD_SERVER_TIP}, +{ WWT_PUSHTXTBTN, BTC, 250, 355, 195, 206, STR_NETWORK_START_SERVER, STR_NETWORK_START_SERVER_TIP}, +{ WWT_PUSHTXTBTN, BTC, 365, 470, 195, 206, STR_012E_CANCEL, STR_NULL}, -{ WWT_MATRIX, BGC, 10, 381, 93, 176, 0x601, STR_NETWORK_CLICK_GAME_TO_SELECT}, +/* RIGHT SIDE */ +{ WWT_IMGBTN, BGC, 250, 470, 22, 185, 0x0, STR_NULL}, +{ WWT_6, BGC, 251, 469, 23, 184, 0x0, STR_NULL}, + +{ WWT_PUSHTXTBTN, BTC, 260, 355, 164, 175, STR_NETWORK_JOIN_GAME, STR_NULL}, +{ WWT_PUSHTXTBTN, BTC, 365, 460, 164, 175, STR_NETWORK_REFRESH, STR_NETWORK_REFRESH_TIP}, -{ WWT_PUSHTXTBTN, BTC, 145, 255, 180, 191, STR_012E_CANCEL, STR_NULL}, -{ WWT_PUSHTXTBTN, BTC, 270, 392, 180, 191, STR_NETWORK_JOIN_GAME, STR_NULL}, { WIDGETS_END}, }; static const WindowDesc _network_game_window_desc = { - WDP_CENTER, WDP_CENTER, 400, 200, + WDP_CENTER, WDP_CENTER, 480, 215, WC_NETWORK_WINDOW,0, - WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, + WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM, _network_game_window_widgets, NetworkGameWindowWndProc, }; +static FiosItem *selected_map = NULL; // to highlight slected map void ShowNetworkGameWindow() { Window *w; DeleteWindowById(WC_NETWORK_WINDOW, 0); - NetworkLobbyInit(); - +// NetworkLobbyInit(); w = AllocateWindowDesc(&_network_game_window_desc); - strcpy(_edit_str_buf, "Your name"); - w->vscroll.cap = 6; - w->disabled_state = (1<<6) | (1<<7) | (1<<8); // disable buttons not yet working - NetworkGameListFromLAN(); // default dropdown item is LAN, so fill that array - - _network_game_count_last = _network_game_count; + ttd_strlcpy(_edit_str_buf, _network_player_name, MAX_QUERYSTR_LEN); + w->vscroll.cap = 8; + w->disabled_state = (1<<4) | (1<<5); // disable buttons not yet working +// NetworkGameListFromLAN(); // default dropdown item is LAN, so fill that array WP(w,querystr_d).caret = 1; WP(w,querystr_d).maxlen = MAX_QUERYSTR_LEN; - WP(w,querystr_d).maxwidth = 240; + WP(w,querystr_d).maxwidth = 120; WP(w,querystr_d).buf = _edit_str_buf; } +// called when a new server is found on the network +void UpdateNetworkGameWindow(bool unselect) +{ + Window *w; + w = FindWindowById(WC_NETWORK_WINDOW, 0); + if (w != NULL) { + if (unselect) + _selected_item = NULL; + w->vscroll.count = _network_game_count; + SetWindowDirty(w); + } +} + static const StringID _players_dropdown[] = { - STR_NETWORK_2_PLAYERS, - STR_NETWORK_3_PLAYERS, - STR_NETWORK_4_PLAYERS, - STR_NETWORK_5_PLAYERS, - STR_NETWORK_6_PLAYERS, - STR_NETWORK_7_PLAYERS, - STR_NETWORK_8_PLAYERS, + STR_NETWORK_2_CLIENTS, + STR_NETWORK_3_CLIENTS, + STR_NETWORK_4_CLIENTS, + STR_NETWORK_5_CLIENTS, + STR_NETWORK_6_CLIENTS, + STR_NETWORK_7_CLIENTS, + STR_NETWORK_8_CLIENTS, + STR_NETWORK_9_CLIENTS, + STR_NETWORK_10_CLIENTS, INVALID_STRING_ID }; +static const StringID _language_dropdown[] = { + STR_NETWORK_LANG_ANY, + STR_NETWORK_LANG_ENGLISH, + STR_NETWORK_LANG_GERMAN, + STR_NETWORK_LANG_FRENCH, + INVALID_STRING_ID +}; + +enum { + NSSWND_START = 64, + NSSWND_ROWSIZE = 12 +}; + static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e) { switch(e->event) { case WE_PAINT: { + int y = NSSWND_START, pos; + const FiosItem *item; - SetDParam(7, STR_NETWORK_2_PLAYERS + _players_max); + SetDParam(7, STR_NETWORK_2_CLIENTS + _network_game_info.clients_max - 2); + SetDParam(9, STR_NETWORK_LANG_ANY + _network_game_info.server_lang); DrawWindowWidgets(w); - GfxFillRect(11, 63, 237, 168, 0xD7); + GfxFillRect(11, 63, 239, 165, 0xD7); DrawEditBox(w, 3); - DrawEditBox(w, 4); DrawString(10, 22, STR_NETWORK_NEW_GAME_NAME, 2); - DrawString(210, 22, STR_NETWORK_PASSWORD, 2); DrawString(10, 43, STR_NETWORK_SELECT_MAP, 2); - DrawString(260, 63, STR_NETWORK_NUMBER_OF_PLAYERS, 2); - + DrawString(260, 63, STR_NETWORK_NUMBER_OF_CLIENTS, 2); + DrawString(260, 105, STR_NETWORK_LANGUAGE_SPOKEN, 2); + + // draw list of maps + pos = w->vscroll.pos; + while (pos < _fios_num + 1) { + item = _fios_list + pos - 1; + if (item == selected_map || (pos == 0 && selected_map == NULL)) + GfxFillRect(11, y - 1, 239, y + 10, 155); // show highlighted item with a different colour + + if (pos == 0) DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, 9); + else DoDrawString(item->title[0] ? item->title : item->name, 14, y, _fios_colors[item->type] ); + pos++; + y += NSSWND_ROWSIZE; + + if (y >= w->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break; + } } break; case WE_CLICK: _selected_field = e->click.widget; switch(e->click.widget) { - case 0: case 12: /* Close 'X' | Cancel button */ + case 0: case 13: /* Close 'X' | Cancel button */ ShowNetworkGameWindow(); break; + case 4: { /* Set password button */ + StringID str; + str = AllocateName(_network_game_info.server_password, 0); + ShowQueryString(str, STR_NETWORK_SET_PASSWORD, 20, 250, w->window_class, w->window_number); + DeleteName(str); + } break; + case 5: { /* Select map */ + int y = (e->click.pt.y - NSSWND_START) / NSSWND_ROWSIZE; + if ((y += w->vscroll.pos) >= w->vscroll.count) + return; + if (y == 0) selected_map = NULL; + else selected_map = _fios_list + y-1; + SetWindowDirty(w); + } break; case 7: case 8: /* Number of Players */ - ShowDropDownMenu(w, _players_dropdown, _players_max, 8, 0); // do it for widget 8 + ShowDropDownMenu(w, _players_dropdown, _network_game_info.clients_max - 2, 8, 0); // do it for widget 8 return; - case 9: /* Start game */ - NetworkCoreStartGame(); - strcpy(_network_game.server_name, WP(w,querystr_d).buf); - //ShowNetworkLobbyWindow(); - DoCommandP(0, 0, 0, NULL, CMD_START_NEW_GAME); + case 9: case 10: /* Language */ + ShowDropDownMenu(w, _language_dropdown, _network_game_info.server_lang, 10, 0); // do it for widget 10 + return; + case 11: /* Start game */ + _is_network_server = true; + ttd_strlcpy(_network_server_name, WP(w,querystr_d).buf, sizeof(_network_server_name)); + if(selected_map==NULL) { // start random new game + DoCommandP(0, Random(), InteractiveRandom(), NULL, CMD_GEN_RANDOM_NEW_GAME); + } else { // load a scenario + char *name; + if ((name = FiosBrowseTo(selected_map)) != NULL) { + SetFiosType(selected_map->type); + strcpy(_file_to_saveload.name, name); + snprintf(_network_game_info.map_name, sizeof(_network_game_info.map_name), "Loaded scenario"); + DeleteWindow(w); + DoCommandP(0, Random(), InteractiveRandom(), NULL, CMD_START_SCENARIO); + } + } break; - case 10: /* Load game */ - NetworkCoreStartGame(); - strcpy(_network_game.server_name, WP(w,querystr_d).buf); - //ShowNetworkLobbyWindow(); + case 12: /* Load game */ + _is_network_server = true; + ttd_strlcpy(_network_server_name, WP(w,querystr_d).buf, sizeof(_network_server_name)); + snprintf(_network_game_info.map_name, sizeof(_network_game_info.map_name), "Loaded game"); + /* XXX - WC_NETWORK_WINDOW should stay, but if it stays, it gets + * copied all the elements of 'load game' and upon closing that, it segfaults */ + DeleteWindowById(WC_NETWORK_WINDOW, 0); ShowSaveLoadDialog(SLD_LOAD_GAME); break; - case 11: /* Load scenario */ - NetworkCoreStartGame(); - strcpy(_network_game.server_name, WP(w,querystr_d).buf); - //ShowNetworkLobbyWindow(); - ShowSaveLoadDialog(SLD_LOAD_SCENARIO); - break; } break; case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */ - _players_max = e->dropdown.index; + switch(e->dropdown.button) { + case 8: + _network_game_info.clients_max = e->dropdown.index + 2; + break; + case 10: + _network_game_info.server_lang = e->dropdown.index; + break; + } SetWindowDirty(w); break; @@ -327,7 +559,7 @@ static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e) break; case WE_KEYPRESS: - if(_selected_field != 3 && _selected_field != 4) + if(_selected_field != 3) break; switch (HandleEditBoxKey(w, _selected_field, e)) { case 1: @@ -336,142 +568,230 @@ static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e) } break; + case WE_ON_EDIT_TEXT: { + byte *b = e->edittext.str; + ttd_strlcpy(_network_game_info.server_password, b, sizeof(_network_game_info.server_password)); + if (_network_game_info.server_password[0] == '\0') { + _network_game_info.use_password = 0; + } else { + _network_game_info.use_password = 1; + } + } break; } } static const Widget _network_start_server_window_widgets[] = { { WWT_CLOSEBOX, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, -{ WWT_CAPTION, BGC, 10, 399, 0, 13, STR_NETWORK_START_GAME_WINDOW, STR_NULL}, +{ WWT_CAPTION, BGC, 11, 399, 0, 13, STR_NETWORK_START_GAME_WINDOW, STR_NULL}, { WWT_IMGBTN, BGC, 0, 399, 14, 199, 0x0, STR_NULL}, -{ WWT_IMGBTN, BGC, 80, 190, 22, 33, 0x0, STR_NETWORK_NEW_GAME_NAME_TIP}, -{ WWT_IMGBTN, BGC, 280, 390, 22, 33, 0x0, STR_NETWORK_PASSWORD_TIP}, +{ WWT_IMGBTN, BGC, 80, 251, 22, 33, 0x0, STR_NETWORK_NEW_GAME_NAME_TIP}, +{ WWT_PUSHTXTBTN, BTC, 270, 380, 22, 33, STR_NETWORK_SET_PASSWORD, STR_NETWORK_PASSWORD_TIP}, + +{ WWT_6, BGC, 10, 251, 62, 166, 0x0, STR_NETWORK_SELECT_MAP_TIP}, +{ WWT_SCROLLBAR, BGC, 240, 250, 63, 165, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, -{ WWT_IMGBTN, BGC, 10, 240, 62, 170, 0x0, STR_NETWORK_SELECT_MAP_TIP}, -{ WWT_SCROLLBAR, BGC, 241, 251, 62, 170, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +{ WWT_6, BGC, 260, 390, 77, 88, STR_NETWORK_COMBO2, STR_NETWORK_NUMBER_OF_CLIENTS_TIP}, +{ WWT_CLOSEBOX, BGC, 379, 389, 78, 87, STR_0225, STR_NETWORK_NUMBER_OF_CLIENTS_TIP}, -{ WWT_6, BGC, 260, 390, 81, 92, STR_NETWORK_COMBO2, STR_NETWORK_NUMBER_OF_PLAYERS_TIP}, -{ WWT_CLOSEBOX, BGC, 379, 389, 82, 91, STR_0225, STR_NETWORK_NUMBER_OF_PLAYERS_TIP}, +{ WWT_6, BGC, 260, 390, 119, 130, STR_NETWORK_COMBO3, STR_NETWORK_LANGUAGE_TIP}, +{ WWT_CLOSEBOX, BGC, 379, 389, 120, 129, STR_0225, STR_NETWORK_LANGUAGE_TIP}, -{ WWT_PUSHTXTBTN, BTC, 10, 100, 180, 191, STR_NETWORK_START_GAME, STR_NETWORK_START_GAME_TIP}, -{ WWT_PUSHTXTBTN, BTC, 110, 200, 180, 191, STR_NETWORK_LOAD_GAME, STR_NETWORK_LOAD_GAME_TIP}, -{ WWT_PUSHTXTBTN, BTC, 210, 300, 180, 191, STR_NETWORK_LOAD_SCENARIO, STR_NETWORK_LOAD_SCENARIO_TIP}, -{ WWT_PUSHTXTBTN, BTC, 310, 390, 180, 191, STR_012E_CANCEL, STR_NULL}, +{ WWT_PUSHTXTBTN, BTC, 55, 145, 180, 191, STR_NETWORK_START_GAME, STR_NETWORK_START_GAME_TIP}, +{ WWT_PUSHTXTBTN, BTC, 155, 245, 180, 191, STR_NETWORK_LOAD_GAME, STR_NETWORK_LOAD_GAME_TIP}, +{ WWT_PUSHTXTBTN, BTC, 255, 345, 180, 191, STR_012E_CANCEL, STR_NULL}, { WIDGETS_END}, }; static const WindowDesc _network_start_server_window_desc = { WDP_CENTER, WDP_CENTER, 400, 200, WC_NETWORK_WINDOW,0, - WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, + WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM, _network_start_server_window_widgets, NetworkStartServerWindowWndProc, }; -static void ShowNetworkStartServerWindow() +static void ShowNetworkStartServerWindow(void) { Window *w; DeleteWindowById(WC_NETWORK_WINDOW, 0); w = AllocateWindowDesc(&_network_start_server_window_desc); - strcpy(_edit_str_buf, ""); - w->disabled_state = (1<<4) | (1<<5) | (1<<6) | (1<<7) | (1<<8); // disable buttons not yet working + ttd_strlcpy(_edit_str_buf, _network_server_name, MAX_QUERYSTR_LEN); + + _saveload_mode = SLD_NEW_GAME; + BuildFileList(); + w->vscroll.cap = 10; + w->vscroll.count = _fios_num+1; WP(w,querystr_d).caret = 1; WP(w,querystr_d).maxlen = MAX_QUERYSTR_LEN; - WP(w,querystr_d).maxwidth = 240; + WP(w,querystr_d).maxwidth = 160; WP(w,querystr_d).buf = _edit_str_buf; } -#if 0 static void NetworkLobbyWindowWndProc(Window *w, WindowEvent *e) { switch(e->event) { case WE_PAINT: { + int y = NET_PRC__OFFSET_TOP_WIDGET_COMPANY, pos; + StringID str; + + if (_selected_company_item == -1) { + w->disabled_state = (1<<7); + } else + w->disabled_state = 0; - SetDParam(7, STR_NETWORK_2_PLAYERS + _opt_mod_ptr->road_side); DrawWindowWidgets(w); - GfxFillRect( 11, 31, 239, 239, 0xD7); - GfxFillRect(261, 31, 378, 220, 0xD7); + SetDParam(0, _str_game_name); + DrawString(10, 22, STR_NETWORK_PREPARE_TO_JOIN, 2); - DrawEditBox(w, 5); - DrawEditBox(w, 7); + // draw company list + GfxFillRect(11, 41, 139, 165, 0xD7); + pos = w->vscroll.pos; + while (pos < _network_lobby_company_count) { + if (_selected_company_item == pos) + GfxFillRect(11, y - 1, 139, y + 10, 155); // show highlighted item with a different colour - DrawString(10, 255, STR_NETWORK_COMPANY_NAME, 2); + DoDrawString(_network_player_info[pos].company_name, 13, y, 2); - break; - } + pos++; + y += NET_PRC__SIZE_OF_ROW_COMPANY; + if (pos >= w->vscroll.cap) + break; + } + + // draw info about selected company + DrawStringMultiCenter(270, 48, STR_NETWORK_COMPANY_INFO, 0); + if (_selected_company_item != -1) { // if a company is selected... + // show company info + const uint x = 168; + uint xm; + y = 65; + + str = AllocateName(_network_player_info[_selected_company_item].company_name, 0); + SetDParam(0, str); + DrawString(x, y, STR_NETWORK_COMPANY_NAME, 2); + DeleteName(str); + y += 10; + + SetDParam(0, _network_player_info[_selected_company_item].inaugurated_year + 1920); + DrawString(x, y, STR_NETWORK_INAUGURATION_YEAR, 2); // inauguration year + y += 10; + + SetDParam64(0, _network_player_info[_selected_company_item].company_value); + DrawString(x, y, STR_NETWORK_VALUE, 2); // company value + y += 10; + + SetDParam64(0, _network_player_info[_selected_company_item].money); + DrawString(x, y, STR_NETWORK_CURRENT_BALANCE, 2); // current balance + y += 10; + + SetDParam64(0, _network_player_info[_selected_company_item].income); + DrawString(x, y, STR_NETWORK_LAST_YEARS_INCOME, 2); // last year's income + y += 10; + + SetDParam(0, _network_player_info[_selected_company_item].performance); + DrawString(x, y, STR_NETWORK_PERFORMANCE, 2); // performance + y += 10; + + SetDParam(0, _network_player_info[_selected_company_item].num_vehicle[0]); + SetDParam(1, _network_player_info[_selected_company_item].num_vehicle[1]); + SetDParam(2, _network_player_info[_selected_company_item].num_vehicle[2]); + SetDParam(3, _network_player_info[_selected_company_item].num_vehicle[3]); + SetDParam(4, _network_player_info[_selected_company_item].num_vehicle[4]); + DrawString(x, y, STR_NETWORK_VEHICLES, 2); // vehicles + y += 10; + + SetDParam(0, _network_player_info[_selected_company_item].num_station[0]); + SetDParam(1, _network_player_info[_selected_company_item].num_station[1]); + SetDParam(2, _network_player_info[_selected_company_item].num_station[2]); + SetDParam(3, _network_player_info[_selected_company_item].num_station[3]); + SetDParam(4, _network_player_info[_selected_company_item].num_station[4]); + DrawString(x, y, STR_NETWORK_STATIONS, 2); // stations + y += 10; + + str = AllocateName(_network_player_info[_selected_company_item].players, 0); + SetDParam(0, str); + xm = DrawString(x, y, STR_NETWORK_PLAYERS, 2); // players + DeleteName(str); + y += 10; + } + } break; case WE_CLICK: - _selected_field = e->click.widget; switch(e->click.widget) { - - case 0: // close X - case 13: // cancel button + case 0: case 11: /* Close 'X' | Cancel button */ ShowNetworkGameWindow(); break; + case 3: /* Company list */ + _selected_company_item = (e->click.pt.y - NET_PRC__OFFSET_TOP_WIDGET_COMPANY) / NET_PRC__SIZE_OF_ROW_COMPANY; + + if (_selected_company_item >= w->vscroll.cap) { + // click out of bounds + _selected_company_item = -1; + SetWindowDirty(w); + return; + } + _selected_company_item += w->vscroll.pos; + if (_selected_company_item >= _network_lobby_company_count) { + _selected_company_item = -1; + SetWindowDirty(w); + return; + } - } - - case WE_MOUSELOOP: - if(_selected_field == 5) - { - HandleEditBox(w, 5); + SetWindowDirty(w); break; - } - if(_selected_field == 7) - { - HandleEditBox(w, 7); + case 7: /* Join company */ + if (_selected_company_item != -1) { + _network_playas = _selected_company_item + 1; + NetworkClientConnectGame(_network_last_host, _network_last_port); + } break; - } - - break; - - case WE_KEYPRESS: - if(_selected_field != 5 && _selected_field != 7) + case 8: /* New company */ + _network_playas = 0; + NetworkClientConnectGame(_network_last_host, _network_last_port); break; - switch (HandleEditBoxKey(w, _selected_field, e)) { - case 1: - HandleButtonClick(w, 12); + case 9: /* Spectate game */ + _network_playas = OWNER_SPECTATOR; + NetworkClientConnectGame(_network_last_host, _network_last_port); break; - } - break; + case 10: /* Refresh */ + NetworkQueryServer(_network_last_host, _network_last_port, false); + break; + } break; + case WE_CREATE: + _selected_company_item = -1; } } static const Widget _network_lobby_window_widgets[] = { { WWT_CLOSEBOX, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, -{ WWT_CAPTION, BGC, 10, 399, 0, 13, STR_NETWORK_GAME_LOBBY, STR_NULL}, -{ WWT_IMGBTN, BGC, 0, 399, 14, 299, 0x0, STR_NULL}, - -// chat widget -{ WWT_IMGBTN, BGC, 10, 240, 30, 240, 0x0, STR_NULL}, -{ WWT_SCROLLBAR, BGC, 241, 251, 30, 240, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +{ WWT_CAPTION, BGC, 11, 399, 0, 13, STR_NETWORK_GAME_LOBBY, STR_NULL}, +{ WWT_IMGBTN, BGC, 0, 399, 14, 209, 0x0, STR_NULL}, -// send message prompt -{ WWT_IMGBTN, BGC, 10, 200, 241, 252, 0x0, STR_NETWORK_ENTER_NAME_TIP}, -{ WWT_PUSHTXTBTN, BTC, 201, 251, 241, 252, STR_NETWORK_SEND, STR_NETWORK_SEND_TIP}, +// company list +{ WWT_6, BGC, 10, 151, 40, 166, 0x0, STR_NETWORK_COMPANY_LIST_TIP}, +{ WWT_SCROLLBAR, BGC, 140, 150, 41, 165, 0x1, STR_0190_SCROLL_BAR_SCROLLS_LIST}, -// company name -{ WWT_IMGBTN, BGC, 100, 251, 254, 265, 0x0, STR_NETWORK_COMPANY_NAME_TIP}, - -// player information -{ WWT_IMGBTN, BGC, 260, 379, 30, 221, 0x0, STR_NULL}, -{ WWT_SCROLLBAR, BGC, 380, 390, 30, 221, 0x1, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +// company/player info +{ WWT_IMGBTN, BGC, 158, 389, 38, 165, 0x0, STR_NULL}, +{ WWT_6, BGC, 159, 388, 39, 164, 0x0, STR_NULL}, // buttons -{ WWT_PUSHTXTBTN, BTC, 260, 390, 233, 244, STR_NETWORK_NEW_COMPANY, STR_NETWORK_NEW_COMPANY_TIP}, -{ WWT_PUSHTXTBTN, BTC, 260, 390, 254, 265, STR_NETWORK_SPECTATE_GAME, STR_NETWORK_SPECTATE_GAME_TIP}, +{ WWT_PUSHTXTBTN, BTC, 10, 150, 175, 186, STR_NETWORK_JOIN_COMPANY, STR_NETWORK_JOIN_COMPANY_TIP}, +{ WWT_PUSHTXTBTN, BTC, 10, 150, 190, 201, STR_NETWORK_NEW_COMPANY, STR_NETWORK_NEW_COMPANY_TIP}, +{ WWT_PUSHTXTBTN, BTC, 158, 268, 175, 186, STR_NETWORK_SPECTATE_GAME, STR_NETWORK_SPECTATE_GAME_TIP}, +{ WWT_PUSHTXTBTN, BTC, 158, 268, 190, 201, STR_NETWORK_REFRESH, STR_NETWORK_REFRESH_TIP}, +{ WWT_PUSHTXTBTN, BTC, 278, 388, 175, 186, STR_012E_CANCEL, STR_NULL}, -{ WWT_PUSHTXTBTN, BTC, 80, 180, 280, 291, STR_NETWORK_READY, STR_NULL}, -{ WWT_PUSHTXTBTN, BTC, 220, 320, 280, 291, STR_012E_CANCEL, STR_NULL}, { WIDGETS_END}, }; static const WindowDesc _network_lobby_window_desc = { - WDP_CENTER, WDP_CENTER, 400, 300, + WDP_CENTER, WDP_CENTER, 400, 210, WC_NETWORK_WINDOW,0, WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, _network_lobby_window_widgets, @@ -479,18 +799,459 @@ static const WindowDesc _network_lobby_window_desc = { }; -static void ShowNetworkLobbyWindow() +static void ShowNetworkLobbyWindow(void) { Window *w; DeleteWindowById(WC_NETWORK_WINDOW, 0); + _network_lobby_company_count = 0; + + NetworkQueryServer(_network_last_host, _network_last_port, false); + w = AllocateWindowDesc(&_network_lobby_window_desc); strcpy(_edit_str_buf, ""); + w->vscroll.pos = 0; + w->vscroll.cap = 8; +} - WP(w,querystr_d).caret = 1; - WP(w,querystr_d).maxlen = MAX_QUERYSTR_LEN; - WP(w,querystr_d).maxwidth = 240; - WP(w,querystr_d).buf = _edit_str_buf; + + +// The window below gives information about the connected clients +// and also makes able to give money to them, kick them (if server) +// and stuff like that. + +extern void DrawPlayerIcon(int p, int x, int y); + +// Every action must be of this form +typedef void ClientList_Action_Proc(byte client_no); + +// Max 10 actions per client +#define MAX_CLIENTLIST_ACTION 10 + +// Some standard bullshit.. defines variables ;) +static void ClientListWndProc(Window *w, WindowEvent *e); +static void ClientListPopupWndProc(Window *w, WindowEvent *e); +static byte _selected_clientlist_item = 255; +static byte _selected_clientlist_y = 0; +static uint16 _client_list_popup_height = 0; +static char _clientlist_action[MAX_CLIENTLIST_ACTION][50]; +static ClientList_Action_Proc *_clientlist_proc[MAX_CLIENTLIST_ACTION]; + +enum { + CLNWND_OFFSET = 16, + CLNWND_ROWSIZE = 10 +}; + +static Widget _client_list_widgets[] = { +{ WWT_TEXTBTN, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, +{ WWT_CAPTION, 14, 11, 249, 0, 13, STR_NETWORK_CLIENT_LIST, STR_018C_WINDOW_TITLE_DRAG_THIS}, + +{ WWT_IMGBTN, 14, 0, 249, 14, 14 + CLNWND_ROWSIZE + 1, 0x0, STR_NULL}, +{ WIDGETS_END}, +}; + +static Widget _client_list_popup_widgets[] = { +{ WWT_PANEL, 14, 0, 99, 0, 0, 0, STR_NULL}, +{ WIDGETS_END}, +}; + +static WindowDesc _client_list_desc = { + -1, -1, 250, 1, + WC_CLIENT_LIST,0, + WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, + _client_list_widgets, + ClientListWndProc +}; + +// Finds the Xth client-info that is active +static NetworkClientInfo *NetworkFindClientInfo(byte client_no) +{ + NetworkClientInfo *ci; + for (ci = _network_client_info; ci != &_network_client_info[MAX_CLIENT_INFO]; ci++) { + // Skip non-active items + if (ci->client_index == NETWORK_EMPTY_INDEX) continue; + if (client_no == 0) return ci; + client_no--; + } + + return NULL; } -#endif + +// Here we start to define the options out of the menu +static void ClientList_Kick(byte client_no) +{ + if (client_no < MAX_PLAYERS) + SEND_COMMAND(PACKET_SERVER_ERROR)(&_clients[client_no], NETWORK_ERROR_KICKED); +} + +/*static void ClientList_Ban(byte client_no) +{ +// TODO +}*/ + +static void ClientList_GiveMoney(byte client_no) +{ + if (NetworkFindClientInfo(client_no) != NULL) + ShowNetworkGiveMoneyWindow(NetworkFindClientInfo(client_no)->client_playas - 1); +} + +static void ClientList_SpeakToClient(byte client_no) +{ + if (NetworkFindClientInfo(client_no) != NULL) + ShowNetworkChatQueryWindow(DESTTYPE_CLIENT, NetworkFindClientInfo(client_no)->client_index); +} + +static void ClientList_SpeakToPlayer(byte client_no) +{ + if (NetworkFindClientInfo(client_no) != NULL) + ShowNetworkChatQueryWindow(DESTTYPE_PLAYER, NetworkFindClientInfo(client_no)->client_playas); +} + +static void ClientList_SpeakToAll(byte client_no) +{ + ShowNetworkChatQueryWindow(DESTTYPE_BROADCAST, 0); +} + +static void ClientList_None(byte client_no) +{ + // No action ;) +} + + + +// Help, a action is clicked! What do we do? +static void HandleClientListPopupClick(byte index, byte clientno) { + // A click on the Popup of the ClientList.. handle the command + if (index < MAX_CLIENTLIST_ACTION && _clientlist_proc[index] != NULL) { + _clientlist_proc[index](clientno); + } +} + +// Finds the amount of clients and set the height correct +static bool CheckClientListHeight(Window *w) +{ + int num = 0; + NetworkClientInfo *ci; + + // Should be replaced with a loop through all clients + for (ci = _network_client_info; ci != &_network_client_info[MAX_CLIENT_INFO]; ci++) { + // Skip non-active items + if (ci->client_index == NETWORK_EMPTY_INDEX) continue; + num++; + } + + num *= CLNWND_ROWSIZE; + + // If height is changed + if (_client_list_desc.height != CLNWND_OFFSET + num + 1) { + // XXX - magic unfortunately; (num + 2) has to be one bigger than heigh (num + 1) + _client_list_widgets[2].bottom = _client_list_widgets[2].top + num + 2; + _client_list_desc.height = CLNWND_OFFSET + num + 1; + _client_list_desc.left = w->left; + _client_list_desc.top = w->top; + // Delete the window and reallocate.. else we can not change the height ;) + DeleteWindow(w); + w = AllocateWindowDescFront(&_client_list_desc, 0); + return false; + } + return true; +} + +// Finds the amount of actions in the popup and set the height correct +static void UpdateClientListPopupHeigth(void) { + int i, num = 0; + + // Find the amount of actions + for (i = 0; i < MAX_CLIENTLIST_ACTION; i++) { + if (_clientlist_action[i][0] == '\0') continue; + if (_clientlist_proc[i] == NULL) continue; + num++; + } + + num *= CLNWND_ROWSIZE; + // Set the height + _client_list_popup_height = num + 2; // XXX - magic, has to be one more than the value below (num + 1) + _client_list_popup_widgets[0].bottom = _client_list_popup_widgets[0].top + num + 1; +} + +// Show the popup (action list) +static Window *PopupClientList(Window *w, int client_no, int x, int y) +{ + int i; + NetworkClientInfo *ci; + DeleteWindowById(WC_TOOLBAR_MENU, 0); + + // Clean the current actions + for (i = 0; i < MAX_CLIENTLIST_ACTION; i++) { + _clientlist_action[i][0] = '\0'; + _clientlist_proc[i] = NULL; + } + + // Fill the actions this client has + // Watch is, max 50 chars long! + + ci = NetworkFindClientInfo(client_no); + if (ci == NULL) return NULL; + + i = 0; + if (_network_own_client_index != ci->client_index) { + sprintf(_clientlist_action[i],"Private message"); + _clientlist_proc[i++] = &ClientList_SpeakToClient; + } + + if (ci->client_playas >= 1 && ci->client_playas <= MAX_PLAYERS) { + sprintf(_clientlist_action[i],"Speak to company"); + _clientlist_proc[i++] = &ClientList_SpeakToPlayer; + } + sprintf(_clientlist_action[i],"Speak to all"); + _clientlist_proc[i++] = &ClientList_SpeakToAll; + + if (_network_own_client_index != ci->client_index) { + if (_network_playas >= 1 && _network_playas <= MAX_PLAYERS) { + // We are no spectator + if (ci->client_playas >= 1 && ci->client_playas <= MAX_PLAYERS) { + sprintf(_clientlist_action[i],"Give money"); + _clientlist_proc[i++] = &ClientList_GiveMoney; + } + } + } + + // A server can kick clients (but not hisself) + if (_network_server && _network_own_client_index != ci->client_index) { + sprintf(_clientlist_action[i],"Kick"); + _clientlist_proc[i++] = &ClientList_Kick; + +/* sprintf(clientlist_action[i],"Ban"); + clientlist_proc[i++] = &ClientList_Ban;*/ + } + + if (i == 0) { + sprintf(_clientlist_action[i],"(none)"); + _clientlist_proc[i++] = &ClientList_None; + } + + + // Find the right height for the popup + UpdateClientListPopupHeigth(); + + // Allocate the popup + w = AllocateWindow(x, y, 100, _client_list_popup_height, ClientListPopupWndProc, WC_TOOLBAR_MENU, _client_list_popup_widgets); + w->flags4 &= ~WF_WHITE_BORDER_MASK; + WP(w,menu_d).item_count = 0; + // Save our client + WP(w,menu_d).main_button = client_no; + WP(w,menu_d).sel_index = 0; + // We are a popup + _popup_menu_active = true; + + return w; +} + +// Main handle for the popup +static void ClientListPopupWndProc(Window *w, WindowEvent *e) +{ + switch(e->event) { + case WE_PAINT: { + int i, y, sel; + byte colour; + DrawWindowWidgets(w); + + // Draw the actions + sel = WP(w,menu_d).sel_index; + y = 1; + for (i = 0; i < MAX_CLIENTLIST_ACTION; i++, y += CLNWND_ROWSIZE) { + if (_clientlist_action[i][0] == '\0') continue; + if (_clientlist_proc[i] == NULL) continue; + + if (sel-- == 0) { // Selected item, highlight it + GfxFillRect(1, y, 98, y + CLNWND_ROWSIZE - 1, 0); + colour = 0xC; + } else colour = 0x10; + + DoDrawString(_clientlist_action[i], 4, y, colour); + } + } break; + + case WE_POPUPMENU_SELECT: { + // We selected an action + int index = (e->popupmenu.pt.y - w->top) / CLNWND_ROWSIZE; + + if (index >= 0 && e->popupmenu.pt.y >= w->top) + HandleClientListPopupClick(index, WP(w,menu_d).main_button); + + // Sometimes, because of the bad DeleteWindow-proc, the 'w' pointer is + // invalid after the last functions (mostly because it kills a window + // that is in front of 'w', and because of a silly memmove, the address + // 'w' was pointing to becomes invalid), so we need to refetch + // the right address... + DeleteWindowById(WC_TOOLBAR_MENU, 0); + } break; + + case WE_POPUPMENU_OVER: { + // Our mouse hoovers over an action? Select it! + int index = (e->popupmenu.pt.y - w->top) / CLNWND_ROWSIZE; + + if (index == -1 || index == WP(w,menu_d).sel_index) + return; + + WP(w,menu_d).sel_index = index; + SetWindowDirty(w); + } break; + + } +} + +// Main handle for clientlist +static void ClientListWndProc(Window *w, WindowEvent *e) +{ + switch(e->event) { + case WE_PAINT: { + NetworkClientInfo *ci; + int y, i = 0; + byte colour; + + // Check if we need to reset the height + if (!CheckClientListHeight(w)) break; + + DrawWindowWidgets(w); + + y = CLNWND_OFFSET; + + for (ci = _network_client_info; ci != &_network_client_info[MAX_CLIENT_INFO]; ci++) { + // Skip non-active items + if (ci->client_index == NETWORK_EMPTY_INDEX) continue; + + if (_selected_clientlist_item == i++) { // Selected item, highlight it + GfxFillRect(1, y, 248, y + CLNWND_ROWSIZE - 1, 0); + colour = 0xC; + } else + colour = 0x10; + + if (ci->client_index == NETWORK_SERVER_INDEX) { + DoDrawString("Server", 4, y, colour); + } else + DoDrawString("Client", 4, y, colour); + + // Filter out spectators + if (ci->client_playas > 0 && ci->client_playas <= MAX_PLAYERS) + DrawPlayerIcon(ci->client_playas - 1, 44, y + 1); + + DoDrawString(ci->client_name, 61, y, colour); + + y += CLNWND_ROWSIZE; + } + } break; + + case WE_CLICK: + // Show the popup with option + if (_selected_clientlist_item != 255) { + PopupClientList(w, _selected_clientlist_item, e->click.pt.x + w->left, e->click.pt.y + w->top); + } + + break; + + case WE_MOUSEOVER: + // -1 means we left the current window + if (e->mouseover.pt.y == -1) { + _selected_clientlist_y = 0; + _selected_clientlist_item = 255; + SetWindowDirty(w); + break; + } + // It did not change.. no update! + if (e->mouseover.pt.y == _selected_clientlist_y) break; + + // Find the new selected item (if any) + _selected_clientlist_y = e->mouseover.pt.y; + if (e->mouseover.pt.y > CLNWND_OFFSET) { + _selected_clientlist_item = (e->mouseover.pt.y - CLNWND_OFFSET) / CLNWND_ROWSIZE; + } else + _selected_clientlist_item = 255; + + // Repaint + SetWindowDirty(w); + break; + + case WE_DESTROY: case WE_CREATE: + // When created or destroyed, data is reset + _selected_clientlist_item = 255; + _selected_clientlist_y = 0; + break; + } +} + +void ShowClientList() +{ + AllocateWindowDesc(&_client_list_desc); +} + +static void NetworkJoinStatusWindowWndProc(Window *w, WindowEvent *e) +{ + switch(e->event) { + case WE_PAINT: { + uint8 progress; // used for progress bar + DrawWindowWidgets(w); + + DrawStringCentered(125, 35, STR_NETWORK_CONNECTING_1 + _network_join_status, 14); + switch (_network_join_status) { + case NETWORK_JOIN_STATUS_CONNECTING: case NETWORK_JOIN_STATUS_AUTHORIZING: + case NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO: + progress = 10; // first two stages 10% + break; + case NETWORK_JOIN_STATUS_WAITING: + SetDParam(0, _network_join_waiting); + DrawStringCentered(125, 46, STR_NETWORK_CONNECTING_WAITING, 14); + progress = 15; // third stage is 15% + break; + case NETWORK_JOIN_STATUS_DOWNLOADING: + SetDParam(0, _network_join_kbytes); + SetDParam(1, _network_join_kbytes_total); + DrawStringCentered(125, 46, STR_NETWORK_CONNECTING_DOWNLOADING, 14); + /* Fallthrough */ + default: /* Waiting is 15%, so the resting receivement of map is maximum 70% */ + progress = 15 + _network_join_kbytes * (100 - 15) / _network_join_kbytes_total; + } + + /* Draw nice progress bar :) */ + DrawFrameRect(20, 18, (int)((w->width - 20) * progress / 100), 28, 10, 0); + } break; + + case WE_CLICK: + switch(e->click.widget) { + case 0: case 3: /* Close 'X' | Disconnect button */ + NetworkDisconnect(); + ShowNetworkGameWindow(); + DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0); + break; + } + break; + + } +} + +static const Widget _network_join_status_window_widget[] = { +{ WWT_TEXTBTN, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, +{ WWT_CAPTION, 14, 11, 249, 0, 13, STR_NETWORK_CONNECTING, STR_018C_WINDOW_TITLE_DRAG_THIS}, +{ WWT_IMGBTN, 14, 0, 249, 14, 84, 0x0,STR_NULL}, +{ WWT_PUSHTXTBTN, BTC, 75, 175, 69, 80, STR_NETWORK_DISCONNECT, STR_NULL}, +{ WIDGETS_END}, +}; + +static const WindowDesc _network_join_status_window_desc = { + WDP_CENTER, WDP_CENTER, 250, 85, + WC_NETWORK_STATUS_WINDOW, 0, + WDF_STD_TOOLTIPS | WDF_DEF_WIDGET, + _network_join_status_window_widget, + NetworkJoinStatusWindowWndProc, +}; + +void ShowJoinStatusWindow() +{ + DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0); + _network_join_status = NETWORK_JOIN_STATUS_CONNECTING; + AllocateWindowDesc(&_network_join_status_window_desc); +} + + +#endif /* ENABLE_NETWORK */ |