tron@2186: /* $Id$ */ tron@2186: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@1317: #include "string.h" tron@1309: #include "strings.h" tron@1363: #include "table/sprites.h" truelight@543: #include "network.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" tron@2163: #include "functions.h" truelight@543: #include "network_data.h" dominik@738: #include "network_gamelist.h" truelight@0: #include "window.h" truelight@0: #include "gui.h" truelight@0: #include "gfx.h" truelight@0: #include "command.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 tron@1323: static char _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@764: static bool _first_time_show_network_game_window = true; truelight@0: truelight@0: static const StringID _connection_types_dropdown[] = { truelight@675: STR_NETWORK_LAN_INTERNET, truelight@675: STR_NETWORK_INTERNET_ADVERTISE, truelight@0: INVALID_STRING_ID truelight@0: }; truelight@0: truelight@764: static const StringID _lan_internet_types_dropdown[] = { truelight@764: STR_NETWORK_LAN, truelight@764: STR_NETWORK_INTERNET, truelight@764: INVALID_STRING_ID truelight@764: }; truelight@764: 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: darkvater@659: extern const char _openttd_revision[]; darkvater@659: Darkvater@2100: static FiosItem *_selected_map = NULL; // to highlight slected map dominik@897: dominik@897: // called when a new server is found on the network dominik@897: void UpdateNetworkGameWindow(bool unselect) dominik@897: { dominik@897: Window *w; dominik@897: w = FindWindowById(WC_NETWORK_WINDOW, 0); dominik@897: if (w != NULL) { dominik@897: if (unselect) dominik@897: _selected_item = NULL; dominik@897: w->vscroll.count = _network_game_count; dominik@897: SetWindowDirty(w); dominik@897: } dominik@897: } dominik@897: truelight@0: static void NetworkGameWindowWndProc(Window *w, WindowEvent *e) truelight@0: { Darkvater@1653: switch (e->event) { Darkvater@1653: case WE_CREATE: /* focus input box */ Darkvater@1653: _selected_field = 3; Darkvater@1653: _selected_item = NULL; Darkvater@1653: break; truelight@0: case WE_PAINT: { ludde@2071: NetworkGameList *sel = _selected_item; ludde@2071: darkvater@659: w->disabled_state = 0; darkvater@659: ludde@2071: if (sel == NULL) { truelight@764: SETBIT(w->disabled_state, 17); SETBIT(w->disabled_state, 18); ludde@2071: } else if (!sel->online) { truelight@764: SETBIT(w->disabled_state, 17); // Server offline, join button disabled ludde@2071: } else if (sel->info.clients_on == sel->info.clients_max) { truelight@764: SETBIT(w->disabled_state, 17); // Server full, join button disabled darkvater@659: darkvater@659: // revisions don't match, check if server has no revision; then allow connection ludde@2071: } else if (strncmp(sel->info.server_revision, _openttd_revision, NETWORK_REVISION_LENGTH - 1) != 0) { ludde@2071: if (strncmp(sel->info.server_revision, NOREV_STRING, sizeof(sel->info.server_revision)) != 0) truelight@764: SETBIT(w->disabled_state, 17); // Revision mismatch, join button disabled truelight@622: } truelight@193: tron@534: SetDParam(0, 0x00); truelight@764: SetDParam(7, _lan_internet_types_dropdown[_network_lan_internet]); truelight@0: DrawWindowWidgets(w); truelight@193: truelight@543: DrawEditBox(w, 3); truelight@193: truelight@543: DrawString(9, 23, STR_NETWORK_PLAYER_NAME, 2); truelight@764: 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@809: int32 pos = w->vscroll.pos; truelight@543: const NetworkGameList *cur_item = _network_game_list; truelight@809: truelight@809: while (pos > 0 && cur_item != NULL) { truelight@809: pos--; bjarni@808: cur_item = cur_item->next; bjarni@808: } truelight@809: darkvater@211: while (cur_item != NULL) { ludde@2071: bool compatible = ludde@2071: !strncmp(cur_item->info.server_revision, _openttd_revision, NETWORK_REVISION_LENGTH - 1) || ludde@2071: !strncmp(cur_item->info.server_revision, NOREV_STRING, sizeof(cur_item->info.server_revision)); darkvater@211: ludde@2071: if (cur_item == sel) truelight@543: GfxFillRect(11, y - 2, 218, y + 9, 10); // show highlighted item with a different colour darkvater@211: Darkvater@2100: SetDParamStr(0, cur_item->info.server_name); Darkvater@2100: DrawStringTruncated(15, y, STR_02BD, 16, 110); 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: dominik@579: // only draw icons if the server is online truelight@591: if (cur_item->online) { truelight@543: dominik@579: // draw a lock if the server is password protected. dominik@579: if(cur_item->info.use_password) dominik@579: DrawSprite(SPR_LOCK, 186, y-1); truelight@591: dominik@579: // draw red or green icon, depending on compatibility with server. Darkvater@1916: DrawSprite(SPR_BLOT | (compatible ? PALETTE_TO_GREEN : PALETTE_TO_RED), 195, y); truelight@591: dominik@579: // draw flag according to server language dominik@579: DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, 206, y); dominik@579: } truelight@591: truelight@591: cur_item = cur_item->next; truelight@591: y += NET_PRC__SIZE_OF_ROW; tron@2549: if (++n == w->vscroll.cap) break; // max number of games in the window darkvater@211: } darkvater@211: } truelight@543: truelight@543: // right menu dominik@1110: GfxFillRect(252, 23, 478, 65, 157); ludde@2071: if (sel == NULL) { dominik@1110: DrawStringMultiCenter(365, 40, STR_NETWORK_GAME_INFO, 0); ludde@2071: } else if (!sel->online) { ludde@2071: SetDParamStr(0, sel->info.server_name); dominik@1110: DrawStringMultiCenter(365, 42, STR_ORANGE, 2); // game name truelight@543: dominik@1110: DrawStringMultiCenter(365, 110, STR_NETWORK_SERVER_OFFLINE, 2); // server offline truelight@543: } else { // show game info truelight@543: uint16 y = 70; truelight@543: dominik@1110: DrawStringMultiCenter(365, 30, STR_NETWORK_GAME_INFO, 0); truelight@543: ludde@2055: ludde@2071: SetDParamStr(0, sel->info.server_name); Darkvater@2114: DrawStringCenteredTruncated(w->widget[16].left, w->widget[16].right, 42, STR_ORANGE, 16); // game name truelight@543: ludde@2071: SetDParamStr(0, sel->info.map_name); Darkvater@2114: DrawStringCenteredTruncated(w->widget[16].left, w->widget[16].right, 54, STR_02BD, 16); // map name truelight@543: ludde@2071: SetDParam(0, sel->info.clients_on); ludde@2071: SetDParam(1, sel->info.clients_max); truelight@543: DrawString(260, y, STR_NETWORK_CLIENTS, 2); // clients on the server / maximum slots truelight@543: y+=10; truelight@543: ludde@2071: if (sel->info.server_lang < NETWORK_NUM_LANGUAGES) { ludde@2071: SetDParam(0, STR_NETWORK_LANG_ANY+sel->info.server_lang); ludde@2071: DrawString(260, y, STR_NETWORK_LANGUAGE, 2); // server language ludde@2071: } truelight@543: y+=10; truelight@543: ludde@2071: if (sel->info.map_set < NUM_LANDSCAPE ) { ludde@2071: SetDParam(0, STR_TEMPERATE_LANDSCAPE+sel->info.map_set); ludde@2071: DrawString(260, y, STR_NETWORK_TILESET, 2); // tileset ludde@2071: } truelight@543: y+=10; truelight@543: ludde@2071: SetDParam(0, sel->info.map_width); ludde@2071: SetDParam(1, sel->info.map_height); truelight@543: DrawString(260, y, STR_NETWORK_MAP_SIZE, 2); // map size truelight@543: y+=10; truelight@543: ludde@2071: SetDParamStr(0, sel->info.server_revision); truelight@543: DrawString(260, y, STR_NETWORK_SERVER_VERSION, 2); // server version truelight@543: y+=10; truelight@543: ludde@2071: SetDParamStr(0, sel->info.hostname); ludde@2071: SetDParam(1, sel->port); truelight@543: DrawString(260, y, STR_NETWORK_SERVER_ADDRESS, 2); // server address truelight@543: y+=10; truelight@543: ludde@2071: SetDParam(0, sel->info.start_date); truelight@543: DrawString(260, y, STR_NETWORK_START_DATE, 2); // start date truelight@543: y+=10; truelight@543: ludde@2071: SetDParam(0, sel->info.game_date); truelight@543: DrawString(260, y, STR_NETWORK_CURRENT_DATE, 2); // current date truelight@543: y+=10; truelight@543: truelight@622: y+=2; truelight@622: ludde@2071: if (strncmp(sel->info.server_revision, _openttd_revision, NETWORK_REVISION_LENGTH - 1) != 0) { ludde@2071: if (strncmp(sel->info.server_revision, NOREV_STRING, sizeof(sel->info.server_revision)) != 0) dominik@1110: DrawStringMultiCenter(365, y, STR_NETWORK_VERSION_MISMATCH, 2); // server mismatch ludde@2071: } else if (sel->info.clients_on == sel->info.clients_max) { truelight@543: // Show: server full, when clients_on == clients_max dominik@1110: DrawStringMultiCenter(365, y, STR_NETWORK_SERVER_FULL, 2); // server full ludde@2071: } else if (sel->info.use_password) dominik@1110: DrawStringMultiCenter(365, y, STR_NETWORK_PASSWORD, 2); // password warning darkvater@659: 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@764: case 0: case 14: /* Close 'X' | Cancel button */ truelight@0: DeleteWindowById(WC_NETWORK_WINDOW, 0); truelight@0: break; truelight@764: case 4: case 5: bjarni@842: ShowDropDownMenu(w, _lan_internet_types_dropdown, _network_lan_internet, 5, 0, 0); // do it for widget 5 truelight@764: break; darkvater@982: case 9: { /* 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: tron@2549: 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: SetWindowDirty(w); darkvater@211: } break; truelight@764: case 11: /* Find server automatically */ truelight@764: switch (_network_lan_internet) { truelight@764: case 0: NetworkUDPSearchGame(); break; truelight@764: case 1: NetworkUDPQueryMasterServer(); break; truelight@764: } darkvater@211: break; truelight@764: case 12: { // Add a server truelight@543: ShowQueryString( ludde@2055: BindCString(_network_default_ip), 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: } break; truelight@764: case 13: /* Start server */ truelight@543: ShowNetworkStartServerWindow(); truelight@543: break; truelight@764: 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@764: 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: truelight@764: case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */ truelight@764: switch(e->dropdown.button) { truelight@764: case 5: truelight@764: _network_lan_internet = e->dropdown.index; truelight@764: break; truelight@764: } truelight@764: truelight@764: SetWindowDirty(w); truelight@764: break; truelight@764: 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: dominik@738: if (_selected_field != 3) { dominik@738: if ( e->keypress.keycode == WKC_DELETE ) { // press 'delete' to remove servers dominik@897: if (_selected_item != NULL) { dominik@738: NetworkGameListRemoveItem(_selected_item); dominik@738: NetworkRebuildHostList(); dominik@738: SetWindowDirty(w); dominik@897: _network_game_count--; dominik@897: // reposition scrollbar dominik@897: if (_network_game_count >= w->vscroll.cap && w->vscroll.pos > _network_game_count-w->vscroll.cap) w->vscroll.pos--; dominik@897: UpdateNetworkGameWindow(false); dominik@738: _selected_item = NULL; dominik@738: } dominik@738: } truelight@741: break; truelight@741: } truelight@741: darkvater@982: if (HandleEditBoxKey(w, 3, e) == 1) break; // enter pressed 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: { truelight@716: NetworkAddServer(e->edittext.str); dominik@738: NetworkRebuildHostList(); darkvater@211: } break; truelight@0: } truelight@0: } truelight@0: truelight@0: static const Widget _network_game_window_widgets[] = { truelight@867: { WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, dominik@1110: { WWT_CAPTION, RESIZE_NONE, BGC, 11, 489, 0, 13, STR_NETWORK_MULTIPLAYER, STR_NULL}, dominik@1110: { WWT_IMGBTN, RESIZE_NONE, BGC, 0, 489, 14, 214, 0x0, STR_NULL}, truelight@0: truelight@543: /* LEFT SIDE */ darkvater@893: { WWT_IMGBTN, RESIZE_NONE, BGC, 90, 231, 22, 33, 0x0, STR_NETWORK_ENTER_NAME_TIP}, darkvater@172: darkvater@893: { WWT_6, RESIZE_NONE, BGC, 90, 231, 42, 53, STR_NETWORK_COMBO1, STR_NETWORK_CONNECTION_TIP}, darkvater@893: { WWT_CLOSEBOX, RESIZE_NONE, BGC, 220, 230, 43, 52, STR_0225, STR_NETWORK_CONNECTION_TIP}, truelight@543: truelight@867: { WWT_IMGBTN, RESIZE_NONE, BTC, 10, 130, 62, 73, 0x0, STR_NETWORK_GAME_NAME_TIP }, truelight@867: { WWT_IMGBTN, RESIZE_NONE, BTC, 131, 180, 62, 73, 0x0, STR_NETWORK_CLIENTS_CAPTION_TIP }, truelight@867: { WWT_IMGBTN, RESIZE_NONE, BTC, 181, 219, 62, 73, 0x0, STR_NETWORK_INFO_ICONS_TIP }, truelight@867: truelight@867: { WWT_MATRIX, RESIZE_NONE, BGC, 10, 219, 74, 185, 0x801, STR_NETWORK_CLICK_GAME_TO_SELECT}, darkvater@982: { WWT_SCROLLBAR, RESIZE_NONE, BGC, 220, 231, 62, 185, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, truelight@867: truelight@867: { WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 10, 115, 195, 206, STR_NETWORK_FIND_SERVER, STR_NETWORK_FIND_SERVER_TIP}, darkvater@893: { WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 125, 231, 195, 206, STR_NETWORK_ADD_SERVER, STR_NETWORK_ADD_SERVER_TIP}, dominik@1110: { WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 250, 360, 195, 206, STR_NETWORK_START_SERVER, STR_NETWORK_START_SERVER_TIP}, dominik@1110: { WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 370, 480, 195, 206, STR_012E_CANCEL, STR_NULL}, truelight@543: truelight@543: /* RIGHT SIDE */ dominik@1110: { WWT_IMGBTN, RESIZE_NONE, BGC, 250, 480, 22, 185, 0x0, STR_NULL}, dominik@1110: { WWT_6, RESIZE_NONE, BGC, 251, 479, 23, 184, 0x0, STR_NULL}, truelight@543: dominik@1110: { WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 257, 360, 164, 175, STR_NETWORK_JOIN_GAME, STR_NULL}, dominik@1110: { WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 370, 473, 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 = { dominik@1110: WDP_CENTER, WDP_CENTER, 490, 215, truelight@0: WC_NETWORK_WINDOW,0, ludde@2064: WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, truelight@0: _network_game_window_widgets, truelight@0: NetworkGameWindowWndProc, truelight@0: }; truelight@0: tron@1093: void ShowNetworkGameWindow(void) truelight@0: { truelight@764: uint i; truelight@0: Window *w; truelight@0: DeleteWindowById(WC_NETWORK_WINDOW, 0); dominik@105: truelight@764: /* Only show once */ truelight@764: if (_first_time_show_network_game_window) { truelight@764: _first_time_show_network_game_window = false; truelight@764: // add all servers from the config file to our list truelight@764: for (i=0; i != lengthof(_network_host_list); i++) { truelight@764: if (_network_host_list[i] == NULL) break; truelight@764: NetworkAddServer(_network_host_list[i]); truelight@764: } truelight@764: } truelight@764: 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@193: Darkvater@1390: WP(w, querystr_d).text.caret = true; Darkvater@1390: WP(w, querystr_d).text.maxlength = MAX_QUERYSTR_LEN - 1; Darkvater@1390: WP(w, querystr_d).text.maxwidth = 120; Darkvater@1390: WP(w, querystr_d).text.buf = _edit_str_buf; Darkvater@1390: UpdateTextBufferSize(&WP(w, querystr_d).text); truelight@0: truelight@827: UpdateNetworkGameWindow(true); 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: { Darkvater@1653: switch (e->event) { Darkvater@1653: case WE_CREATE: /* focus input box */ Darkvater@1653: _selected_field = 3; tron@1899: _network_game_info.use_password = (_network_server_password[0] != '\0'); Darkvater@1653: break; Darkvater@1653: truelight@0: case WE_PAINT: { truelight@543: int y = NSSWND_START, pos; truelight@543: const FiosItem *item; truelight@193: truelight@675: SetDParam(7, STR_NETWORK_LAN_INTERNET + _network_advertise); truelight@675: SetDParam(9, STR_NETWORK_2_CLIENTS + _network_game_info.clients_max - 2); truelight@675: SetDParam(11, STR_NETWORK_LANG_ANY + _network_game_info.server_lang); truelight@0: DrawWindowWidgets(w); truelight@0: dominik@853: GfxFillRect(11, 63, 259, 171, 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); dominik@749: DrawString(280, 63, STR_NETWORK_CONNECTION, 2); dominik@749: DrawString(280, 95, STR_NETWORK_NUMBER_OF_CLIENTS, 2); dominik@749: DrawString(280, 127, STR_NETWORK_LANGUAGE_SPOKEN, 2); truelight@193: Darkvater@1653: if (_network_game_info.use_password) DoDrawString("*", 408, 23, 3); Darkvater@1653: 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; Darkvater@2100: if (item == _selected_map || (pos == 0 && _selected_map == NULL)) dominik@749: GfxFillRect(11, y - 1, 259, 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); Darkvater@2100: else DoDrawString(item->title, 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@675: case 0: case 15: /* Close 'X' | Cancel button */ truelight@0: ShowNetworkGameWindow(); truelight@0: break; truelight@543: case 4: { /* Set password button */ ludde@2055: ShowQueryString(BindCString(_network_server_password), ludde@2055: STR_NETWORK_SET_PASSWORD, 20, 250, w->window_class, w->window_number); 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; Darkvater@2100: Darkvater@2100: _selected_map = (y == 0) ? NULL : _fios_list + y - 1; truelight@543: SetWindowDirty(w); truelight@543: } break; truelight@675: case 7: case 8: /* Connection type */ bjarni@842: ShowDropDownMenu(w, _connection_types_dropdown, _network_advertise, 8, 0, 0); // do it for widget 8 truelight@675: break; truelight@675: case 9: case 10: /* Number of Players */ bjarni@842: ShowDropDownMenu(w, _players_dropdown, _network_game_info.clients_max - 2, 10, 0, 0); // do it for widget 10 truelight@0: return; truelight@675: case 11: case 12: /* Language */ bjarni@842: ShowDropDownMenu(w, _language_dropdown, _network_game_info.server_lang, 12, 0, 0); // do it for widget 12 truelight@543: return; truelight@675: case 13: /* Start game */ truelight@543: _is_network_server = true; Darkvater@1390: ttd_strlcpy(_network_server_name, WP(w, querystr_d).text.buf, sizeof(_network_server_name)); Darkvater@1390: UpdateTextBufferSize(&WP(w, querystr_d).text); Darkvater@2100: if (_selected_map == NULL) { // start random new game Darkvater@1794: GenRandomNewGame(Random(), InteractiveRandom()); truelight@543: } else { // load a scenario Darkvater@2100: char *name = FiosBrowseTo(_selected_map); Darkvater@2100: if (name != NULL) { Darkvater@2100: SetFiosType(_selected_map->type); Darkvater@2100: ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name)); Darkvater@2100: ttd_strlcpy(_file_to_saveload.title, _selected_map->title, sizeof(_file_to_saveload.title)); Darkvater@2100: truelight@543: DeleteWindow(w); Darkvater@1796: StartScenarioEditor(Random(), InteractiveRandom()); truelight@543: } truelight@543: } truelight@0: break; truelight@675: case 14: /* Load game */ truelight@543: _is_network_server = true; Darkvater@1390: ttd_strlcpy(_network_server_name, WP(w, querystr_d).text.buf, sizeof(_network_server_name)); Darkvater@1390: UpdateTextBufferSize(&WP(w, querystr_d).text); 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: tron@1899: _network_advertise = (e->dropdown.index != 0); truelight@675: break; truelight@675: case 10: truelight@543: _network_game_info.clients_max = e->dropdown.index + 2; truelight@543: break; truelight@675: case 12: 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@1653: if (_selected_field == 3) Darkvater@1653: HandleEditBox(w, 3); truelight@0: break; truelight@0: truelight@0: case WE_KEYPRESS: Darkvater@1653: if (_selected_field == 3) Darkvater@1653: HandleEditBoxKey(w, 3, e); truelight@0: break; truelight@193: truelight@543: case WE_ON_EDIT_TEXT: { tron@1323: const char *b = e->edittext.str; truelight@1142: ttd_strlcpy(_network_server_password, b, sizeof(_network_server_password)); tron@1899: _network_game_info.use_password = (_network_server_password[0] != '\0'); Darkvater@1653: SetWindowDirty(w); truelight@543: } break; truelight@0: } truelight@0: } truelight@0: truelight@0: static const Widget _network_start_server_window_widgets[] = { truelight@867: { WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, truelight@867: { WWT_CAPTION, RESIZE_NONE, BGC, 11, 419, 0, 13, STR_NETWORK_START_GAME_WINDOW, STR_NULL}, truelight@867: { WWT_IMGBTN, RESIZE_NONE, BGC, 0, 419, 14, 199, 0x0, STR_NULL}, truelight@543: darkvater@893: { WWT_IMGBTN, RESIZE_NONE, BGC, 100, 272, 22, 33, 0x0, STR_NETWORK_NEW_GAME_NAME_TIP}, truelight@867: { WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 285, 405, 22, 33, STR_NETWORK_SET_PASSWORD, STR_NETWORK_PASSWORD_TIP}, dominik@749: darkvater@893: { WWT_6, RESIZE_NONE, BGC, 10, 271, 62, 172, 0x0, STR_NETWORK_SELECT_MAP_TIP}, darkvater@893: { WWT_SCROLLBAR, RESIZE_NONE, BGC, 260, 271, 63, 171, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, dominik@749: truelight@867: { WWT_6, RESIZE_NONE, BGC, 280, 410, 77, 88, STR_NETWORK_COMBO1, STR_NETWORK_CONNECTION_TIP}, truelight@867: { WWT_CLOSEBOX, RESIZE_NONE, BGC, 399, 409, 78, 87, STR_0225, STR_NETWORK_CONNECTION_TIP}, truelight@867: truelight@867: { WWT_6, RESIZE_NONE, BGC, 280, 410, 109, 120, STR_NETWORK_COMBO2, STR_NETWORK_NUMBER_OF_CLIENTS_TIP}, truelight@867: { WWT_CLOSEBOX, RESIZE_NONE, BGC, 399, 409, 110, 119, STR_0225, STR_NETWORK_NUMBER_OF_CLIENTS_TIP}, truelight@867: truelight@867: { WWT_6, RESIZE_NONE, BGC, 280, 410, 141, 152, STR_NETWORK_COMBO3, STR_NETWORK_LANGUAGE_TIP}, truelight@867: { WWT_CLOSEBOX, RESIZE_NONE, BGC, 399, 409, 142, 151, STR_0225, STR_NETWORK_LANGUAGE_TIP}, truelight@867: truelight@867: { WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 40, 140, 180, 191, STR_NETWORK_START_GAME, STR_NETWORK_START_GAME_TIP}, truelight@867: { WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 150, 250, 180, 191, STR_NETWORK_LOAD_GAME, STR_NETWORK_LOAD_GAME_TIP}, truelight@867: { WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 260, 360, 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 = { dominik@749: WDP_CENTER, WDP_CENTER, 420, 200, truelight@0: WC_NETWORK_WINDOW,0, ludde@2064: WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, 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(); dominik@853: w->vscroll.cap = 9; truelight@543: w->vscroll.count = _fios_num+1; truelight@193: Darkvater@1390: WP(w, querystr_d).text.caret = true; Darkvater@1390: WP(w, querystr_d).text.maxlength = MAX_QUERYSTR_LEN - 1; Darkvater@1390: WP(w, querystr_d).text.maxwidth = 160; Darkvater@1390: WP(w, querystr_d).text.buf = _edit_str_buf; Darkvater@1390: UpdateTextBufferSize(&WP(w, querystr_d).text); truelight@0: } truelight@0: truelight@734: static byte NetworkLobbyFindCompanyIndex(byte pos) truelight@734: { truelight@734: byte i; truelight@734: /* Scroll through all _network_player_info and get the 'pos' item truelight@734: that is not empty */ truelight@734: for (i = 0; i < MAX_PLAYERS; i++) { truelight@734: if (_network_player_info[i].company_name[0] != '\0') { truelight@734: if (pos-- == 0) truelight@734: return i; truelight@734: } truelight@734: } truelight@734: truelight@734: return 0; truelight@734: } truelight@734: 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@193: darkvater@1025: w->disabled_state = (_selected_company_item == -1) ? 1 << 7 : 0; truelight@543: truelight@621: if (_network_lobby_company_count == MAX_PLAYERS) darkvater@1025: SETBIT(w->disabled_state, 8); truelight@735: /* You can not join a server as spectator when it has no companies active.. truelight@735: it causes some nasty crashes */ truelight@735: if (_network_lobby_company_count == 0) darkvater@1025: SETBIT(w->disabled_state, 9); truelight@621: truelight@0: DrawWindowWidgets(w); truelight@0: ludde@2064: SetDParamStr(0, _selected_item->info.server_name); truelight@543: DrawString(10, 22, STR_NETWORK_PREPARE_TO_JOIN, 2); truelight@0: truelight@543: // draw company list darkvater@1025: GfxFillRect(11, 41, 154, 165, 0xD7); truelight@543: pos = w->vscroll.pos; truelight@543: while (pos < _network_lobby_company_count) { truelight@734: byte index = NetworkLobbyFindCompanyIndex(pos); truelight@1011: bool income = false; truelight@734: if (_selected_company_item == index) darkvater@1025: GfxFillRect(11, y - 1, 154, y + 10, 155); // show highlighted item with a different colour truelight@193: truelight@734: DoDrawString(_network_player_info[index].company_name, 13, y, 2); truelight@1011: if(_network_player_info[index].use_password != 0) darkvater@1025: DrawSprite(SPR_LOCK, 135, y); truelight@1011: truelight@1011: /* If the company's income was positive puts a green dot else a red dot */ darkvater@1025: if ((_network_player_info[index].income) >= 0) truelight@1011: income = true; darkvater@1025: DrawSprite(SPR_BLOT | (income ? PALETTE_TO_GREEN : PALETTE_TO_RED), 145, y); 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 darkvater@1025: DrawStringMultiCenter(290, 48, STR_NETWORK_COMPANY_INFO, 0); truelight@543: if (_selected_company_item != -1) { // if a company is selected... truelight@543: // show company info darkvater@1025: const uint x = 183; truelight@543: uint xm; truelight@543: y = 65; truelight@543: ludde@2055: SetDParamStr(0, _network_player_info[_selected_company_item].company_name); truelight@543: DrawString(x, y, STR_NETWORK_COMPANY_NAME, 2); truelight@543: y += 10; truelight@543: darkvater@970: SetDParam(0, _network_player_info[_selected_company_item].inaugurated_year + MAX_YEAR_BEGIN_REAL); 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: ludde@2055: SetDParamStr(0, _network_player_info[_selected_company_item].players); truelight@543: xm = DrawString(x, y, STR_NETWORK_PLAYERS, 2); // players 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@734: _selected_company_item = NetworkLobbyFindCompanyIndex(_selected_company_item); truelight@734: 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@867: { WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, truelight@867: { WWT_CAPTION, RESIZE_NONE, BGC, 11, 419, 0, 13, STR_NETWORK_GAME_LOBBY, STR_NULL}, truelight@867: { WWT_IMGBTN, RESIZE_NONE, BGC, 0, 419, 14, 209, 0x0, STR_NULL}, truelight@543: truelight@543: // company list darkvater@1025: { WWT_6, RESIZE_NONE, BGC, 10, 167, 40, 166, 0x0, STR_NETWORK_COMPANY_LIST_TIP}, darkvater@1025: { WWT_SCROLLBAR, RESIZE_NONE, BGC, 155, 166, 41, 165, 0x1, STR_0190_SCROLL_BAR_SCROLLS_LIST}, truelight@543: truelight@543: // company/player info darkvater@1025: { WWT_IMGBTN, RESIZE_NONE, BGC, 173, 404, 38, 166, 0x0, STR_NULL}, darkvater@1025: { WWT_6, RESIZE_NONE, BGC, 174, 403, 39, 165, 0x0, STR_NULL}, truelight@543: truelight@543: // buttons darkvater@893: { WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 10, 151, 175, 186, STR_NETWORK_JOIN_COMPANY, STR_NETWORK_JOIN_COMPANY_TIP}, darkvater@893: { WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 10, 151, 190, 201, STR_NETWORK_NEW_COMPANY, STR_NETWORK_NEW_COMPANY_TIP}, truelight@867: { WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 158, 268, 175, 186, STR_NETWORK_SPECTATE_GAME, STR_NETWORK_SPECTATE_GAME_TIP}, truelight@867: { WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 158, 268, 190, 201, STR_NETWORK_REFRESH, STR_NETWORK_REFRESH_TIP}, truelight@867: { WWT_PUSHTXTBTN, RESIZE_NONE, 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 = { dominik@749: WDP_CENTER, WDP_CENTER, 420, 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 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@867: static const Widget _client_list_widgets[] = { truelight@867: { WWT_TEXTBTN, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, truelight@867: { WWT_CAPTION, RESIZE_NONE, 14, 11, 249, 0, 13, STR_NETWORK_CLIENT_LIST, STR_018C_WINDOW_TITLE_DRAG_THIS}, truelight@543: truelight@867: { WWT_IMGBTN, RESIZE_NONE, 14, 0, 249, 14, 14 + CLNWND_ROWSIZE + 1, 0x0, STR_NULL}, truelight@543: { WIDGETS_END}, truelight@543: }; truelight@543: truelight@867: static const Widget _client_list_popup_widgets[] = { truelight@867: { WWT_PANEL, RESIZE_NONE, 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@841: static void ClientList_Ban(byte client_no) truelight@543: { truelight@841: uint i; truelight@841: uint32 ip = NetworkFindClientInfo(client_no)->client_ip; truelight@841: truelight@841: for (i = 0; i < lengthof(_network_ban_list); i++) { truelight@841: if (_network_ban_list[i] == NULL || _network_ban_list[i][0] == '\0') { truelight@841: _network_ban_list[i] = strdup(inet_ntoa(*(struct in_addr *)&ip)); truelight@841: break; truelight@841: } truelight@841: } truelight@841: truelight@841: if (client_no < MAX_PLAYERS) truelight@841: SEND_COMMAND(PACKET_SERVER_ERROR)(&_clients[client_no], NETWORK_ERROR_KICKED); truelight@841: } 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@867: if (w->height != CLNWND_OFFSET + num + 1) { truelight@543: // XXX - magic unfortunately; (num + 2) has to be one bigger than heigh (num + 1) truelight@867: SetWindowDirty(w); truelight@867: w->widget[2].bottom = w->widget[2].top + num + 2; truelight@867: w->height = CLNWND_OFFSET + num + 1; truelight@867: SetWindowDirty(w); 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@867: static uint ClientListPopupHeigth(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@867: truelight@867: return 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@867: int i, h; 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@722: GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT); truelight@543: _clientlist_proc[i++] = &ClientList_SpeakToClient; truelight@543: } truelight@543: truelight@543: if (ci->client_playas >= 1 && ci->client_playas <= MAX_PLAYERS) { truelight@722: GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY); truelight@543: _clientlist_proc[i++] = &ClientList_SpeakToPlayer; truelight@543: } truelight@722: GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_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@722: GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_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@722: GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_KICK); truelight@543: _clientlist_proc[i++] = &ClientList_Kick; truelight@543: truelight@841: sprintf(_clientlist_action[i],"Ban"); truelight@841: _clientlist_proc[i++] = &ClientList_Ban; truelight@543: } truelight@543: truelight@543: if (i == 0) { truelight@722: GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_NONE); truelight@543: _clientlist_proc[i++] = &ClientList_None; truelight@543: } truelight@543: truelight@867: /* Calculate the height */ truelight@867: h = ClientListPopupHeigth(); truelight@543: truelight@543: // Allocate the popup truelight@867: w = AllocateWindow(x, y, 100, h + 1, ClientListPopupWndProc, WC_TOOLBAR_MENU, _client_list_popup_widgets); truelight@867: w->widget[0].bottom = w->widget[0].top + h; truelight@867: 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@722: DrawString(4, y, STR_NETWORK_SERVER, colour); truelight@543: } else truelight@722: DrawString(4, y, STR_NETWORK_CLIENT, 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: darkvater@1003: void ShowClientList(void) truelight@543: { darkvater@1003: Window *w = AllocateWindowDescFront(&_client_list_desc, 0); darkvater@1003: if (w) darkvater@1003: w->window_number = 0; truelight@543: } truelight@543: truelight@685: extern void SwitchMode(int new_mode); truelight@685: 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@685: DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0); truelight@685: SwitchMode(SM_MENU); truelight@543: ShowNetworkGameWindow(); 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@867: { WWT_TEXTBTN, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, truelight@867: { WWT_CAPTION, RESIZE_NONE, 14, 11, 249, 0, 13, STR_NETWORK_CONNECTING, STR_018C_WINDOW_TITLE_DRAG_THIS}, truelight@867: { WWT_IMGBTN, RESIZE_NONE, 14, 0, 249, 14, 84, 0x0,STR_NULL}, truelight@867: { WWT_PUSHTXTBTN, RESIZE_NONE, 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: tron@1093: void ShowJoinStatusWindow(void) 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: darkvater@774: void ShowJoinStatusWindowAfterJoin(void) truelight@670: { truelight@670: /* This is a special instant of ShowJoinStatusWindow, because truelight@670: it is opened after the map is loaded, but the client maybe is not truelight@670: done registering itself to the server */ truelight@670: DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0); truelight@670: _network_join_status = NETWORK_JOIN_STATUS_REGISTERING; truelight@670: AllocateWindowDesc(&_network_join_status_window_desc); truelight@670: } truelight@0: dominik@649: dominik@649: dominik@649: #define MAX_QUERYSTR_LEN 64 dominik@649: dominik@649: static void ChatWindowWndProc(Window *w, WindowEvent *e) dominik@649: { dominik@649: static bool closed = false; darkvater@1648: switch (e->event) { darkvater@1648: case WE_CREATE: darkvater@1648: SendWindowMessage(WC_NEWS_WINDOW, 0, WE_CREATE, w->height, 0); Darkvater@1843: SETBIT(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys darkvater@1648: closed = false; darkvater@1648: break; dominik@649: darkvater@1648: case WE_PAINT: dominik@649: DrawWindowWidgets(w); Darkvater@1748: DrawEditBox(w, 1); darkvater@1648: break; dominik@649: dominik@649: case WE_CLICK: Darkvater@1748: switch(e->click.widget) { Darkvater@1748: case 3: DeleteWindow(w); break; // Cancel Darkvater@1748: case 2: // Send dominik@649: press_ok:; Darkvater@1390: if (strcmp(WP(w, querystr_d).text.buf, WP(w, querystr_d).text.buf + MAX_QUERYSTR_LEN) == 0) { dominik@649: DeleteWindow(w); dominik@649: } else { Darkvater@1390: char *buf = WP(w, querystr_d).text.buf; Darkvater@1390: WindowClass wnd_class = WP(w, querystr_d).wnd_class; Darkvater@1390: WindowNumber wnd_num = WP(w, querystr_d).wnd_num; dominik@649: Window *parent; dominik@649: dominik@649: // Mask the edit-box as closed, so we don't send out a CANCEL dominik@649: closed = true; dominik@649: dominik@649: DeleteWindow(w); dominik@649: dominik@649: parent = FindWindowById(wnd_class, wnd_num); dominik@649: if (parent != NULL) { dominik@649: WindowEvent e; dominik@649: e.event = WE_ON_EDIT_TEXT; dominik@649: e.edittext.str = buf; dominik@649: parent->wndproc(parent, &e); dominik@649: } dominik@649: } dominik@649: break; dominik@649: } dominik@649: break; dominik@649: dominik@649: case WE_MOUSELOOP: { dominik@649: if (!FindWindowById(WP(w,querystr_d).wnd_class, WP(w,querystr_d).wnd_num)) { dominik@649: DeleteWindow(w); dominik@649: return; dominik@649: } Darkvater@1748: HandleEditBox(w, 1); dominik@649: } break; dominik@649: dominik@649: case WE_KEYPRESS: { Darkvater@1748: switch(HandleEditBoxKey(w, 1, e)) { dominik@649: case 1: // Return dominik@649: goto press_ok; dominik@649: case 2: // Escape dominik@649: DeleteWindow(w); dominik@649: break; dominik@649: } dominik@649: } break; dominik@649: dominik@649: case WE_DESTROY: darkvater@1648: SendWindowMessage(WC_NEWS_WINDOW, 0, WE_DESTROY, 0, 0); Darkvater@1843: CLRBIT(_no_scroll, SCROLL_CHAT); dominik@649: // If the window is not closed yet, it means it still needs to send a CANCEL dominik@649: if (!closed) { dominik@649: Window *parent = FindWindowById(WP(w,querystr_d).wnd_class, WP(w,querystr_d).wnd_num); dominik@649: if (parent != NULL) { dominik@649: WindowEvent e; dominik@649: e.event = WE_ON_EDIT_TEXT_CANCEL; dominik@649: parent->wndproc(parent, &e); dominik@649: } dominik@649: } dominik@649: break; dominik@649: } dominik@649: } dominik@649: dominik@649: static const Widget _chat_window_widgets[] = { Darkvater@1747: { WWT_IMGBTN, RESIZE_NONE, 14, 0, 639, 0, 13, STR_NULL, STR_NULL}, // background Darkvater@1747: { WWT_IMGBTN, RESIZE_NONE, 14, 2, 399, 1, 12, STR_NULL, STR_NULL}, // text box Darkvater@1747: { WWT_TEXTBTN, RESIZE_NONE, 14, 400, 519, 1, 12, STR_NETWORK_SEND, STR_NULL}, // send button Darkvater@1747: { WWT_TEXTBTN, RESIZE_NONE, 14, 520, 639, 1, 12, STR_012E_CANCEL, STR_NULL}, // cancel button dominik@649: { WIDGETS_END}, dominik@649: }; dominik@649: dominik@649: static const WindowDesc _chat_window_desc = { dominik@649: WDP_CENTER, -26, 640, 14, // x, y, width, height dominik@649: WC_SEND_NETWORK_MSG,0, Darkvater@1748: WDF_STD_TOOLTIPS | WDF_DEF_WIDGET, dominik@649: _chat_window_widgets, dominik@649: ChatWindowWndProc dominik@649: }; dominik@649: dominik@649: void ShowChatWindow(StringID str, StringID caption, int maxlen, int maxwidth, byte window_class, uint16 window_number) dominik@649: { dominik@649: Window *w; dominik@649: dominik@649: #define _orig_edit_str_buf (_edit_str_buf+MAX_QUERYSTR_LEN) dominik@649: dominik@649: DeleteWindowById(WC_SEND_NETWORK_MSG, 0); dominik@649: tron@1344: GetString(_orig_edit_str_buf, str); dominik@649: Darkvater@1390: _orig_edit_str_buf[maxlen] = '\0'; dominik@649: dominik@649: memcpy(_edit_str_buf, _orig_edit_str_buf, MAX_QUERYSTR_LEN); dominik@649: dominik@649: w = AllocateWindowDesc(&_chat_window_desc); dominik@649: dominik@649: w->click_state = 1 << 1; dominik@649: WP(w,querystr_d).caption = caption; dominik@649: WP(w,querystr_d).wnd_class = window_class; dominik@649: WP(w,querystr_d).wnd_num = window_number; Darkvater@1390: WP(w,querystr_d).text.caret = false; Darkvater@1390: WP(w,querystr_d).text.maxlength = maxlen - 1; Darkvater@1390: WP(w,querystr_d).text.maxwidth = maxwidth; Darkvater@1390: WP(w,querystr_d).text.buf = _edit_str_buf; Darkvater@1390: UpdateTextBufferSize(&WP(w, querystr_d).text); dominik@649: } dominik@649: darkvater@774: #else darkvater@774: void ShowJoinStatusWindowAfterJoin(void) {} truelight@543: #endif /* ENABLE_NETWORK */