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