mirror of
https://github.com/JGRennison/OpenTTD-patches.git
synced 2024-11-17 21:25:40 +00:00
ddd4958164
- CodeChange: remove cmd-misuses CmdStartScenario() and CmdDestroyCompanyHQ() - Fix (invisible): when parameter checking CmdRestoreOrderIndex() the vehicle did not have its orders yet, so it would fail. So move doing this until AFTER the orders have been added back in RestoreVehicleOrders()
1483 lines
47 KiB
C
1483 lines
47 KiB
C
#include "stdafx.h"
|
|
#include "ttd.h"
|
|
#include "string.h"
|
|
#include "strings.h"
|
|
#include "table/sprites.h"
|
|
#include "network.h"
|
|
#include "saveload.h"
|
|
|
|
#include "hal.h" // for file list
|
|
|
|
#ifdef ENABLE_NETWORK
|
|
|
|
#include "table/strings.h"
|
|
#include "network_data.h"
|
|
#include "network_gamelist.h"
|
|
#include "window.h"
|
|
#include "gui.h"
|
|
#include "gfx.h"
|
|
#include "command.h"
|
|
#include "functions.h"
|
|
#include "variables.h"
|
|
#include "network_server.h"
|
|
#include "network_udp.h"
|
|
|
|
#define BGC 5
|
|
#define BTC 15
|
|
#define MAX_QUERYSTR_LEN 64
|
|
static char _edit_str_buf[MAX_QUERYSTR_LEN*2];
|
|
static void ShowNetworkStartServerWindow(void);
|
|
static void ShowNetworkLobbyWindow(void);
|
|
|
|
static byte _selected_field;
|
|
static bool _first_time_show_network_game_window = true;
|
|
|
|
static const StringID _connection_types_dropdown[] = {
|
|
STR_NETWORK_LAN_INTERNET,
|
|
STR_NETWORK_INTERNET_ADVERTISE,
|
|
INVALID_STRING_ID
|
|
};
|
|
|
|
static const StringID _lan_internet_types_dropdown[] = {
|
|
STR_NETWORK_LAN,
|
|
STR_NETWORK_INTERNET,
|
|
INVALID_STRING_ID
|
|
};
|
|
|
|
static StringID _str_map_name, _str_game_name, _str_server_version, _str_server_address;
|
|
|
|
enum {
|
|
NET_PRC__OFFSET_TOP_WIDGET = 74,
|
|
NET_PRC__OFFSET_TOP_WIDGET_COMPANY = 42,
|
|
NET_PRC__SIZE_OF_ROW = 14,
|
|
NET_PRC__SIZE_OF_ROW_COMPANY = 12,
|
|
};
|
|
|
|
static NetworkGameList *_selected_item = NULL;
|
|
static int8 _selected_company_item = -1;
|
|
|
|
// Truncates a string to max_width (via GetStringWidth) and adds 3 dots
|
|
// at the end of the name.
|
|
static void NetworkTruncateString(char *name, const int max_width)
|
|
{
|
|
char temp[NETWORK_NAME_LENGTH];
|
|
char internal_name[NETWORK_NAME_LENGTH];
|
|
|
|
ttd_strlcpy(internal_name, name, sizeof(internal_name));
|
|
|
|
if (GetStringWidth(internal_name) > max_width) {
|
|
// Servername is too long, trunc it!
|
|
snprintf(temp, sizeof(temp), "%s...", internal_name);
|
|
// Continue to delete 1 char of the string till it is in range
|
|
while (GetStringWidth(temp) > max_width) {
|
|
internal_name[strlen(internal_name) - 1] = '\0';
|
|
snprintf(temp, sizeof(temp), "%s...", internal_name);
|
|
}
|
|
ttd_strlcpy(name, temp, sizeof(temp));
|
|
}
|
|
}
|
|
|
|
extern const char _openttd_revision[];
|
|
|
|
static FiosItem *selected_map = NULL; // to highlight slected map
|
|
|
|
// called when a new server is found on the network
|
|
void UpdateNetworkGameWindow(bool unselect)
|
|
{
|
|
Window *w;
|
|
w = FindWindowById(WC_NETWORK_WINDOW, 0);
|
|
if (w != NULL) {
|
|
if (unselect)
|
|
_selected_item = NULL;
|
|
w->vscroll.count = _network_game_count;
|
|
SetWindowDirty(w);
|
|
}
|
|
}
|
|
|
|
static void NetworkGameWindowWndProc(Window *w, WindowEvent *e)
|
|
{
|
|
switch (e->event) {
|
|
case WE_CREATE: /* focus input box */
|
|
_selected_field = 3;
|
|
_selected_item = NULL;
|
|
break;
|
|
case WE_PAINT: {
|
|
w->disabled_state = 0;
|
|
|
|
if (_selected_item == NULL) {
|
|
SETBIT(w->disabled_state, 17); SETBIT(w->disabled_state, 18);
|
|
} else if (!_selected_item->online) {
|
|
SETBIT(w->disabled_state, 17); // Server offline, join button disabled
|
|
} else if (_selected_item->info.clients_on == _selected_item->info.clients_max) {
|
|
SETBIT(w->disabled_state, 17); // Server full, join button disabled
|
|
|
|
// revisions don't match, check if server has no revision; then allow connection
|
|
} else if (strncmp(_selected_item->info.server_revision, _openttd_revision, NETWORK_REVISION_LENGTH - 1) != 0) {
|
|
if (strncmp(_selected_item->info.server_revision, NOREV_STRING, sizeof(_selected_item->info.server_revision)) != 0)
|
|
SETBIT(w->disabled_state, 17); // Revision mismatch, join button disabled
|
|
}
|
|
|
|
SetDParam(0, 0x00);
|
|
SetDParam(7, _lan_internet_types_dropdown[_network_lan_internet]);
|
|
DrawWindowWidgets(w);
|
|
|
|
DrawEditBox(w, 3);
|
|
|
|
DrawString(9, 23, STR_NETWORK_PLAYER_NAME, 2);
|
|
DrawString(9, 43, STR_NETWORK_CONNECTION, 2);
|
|
|
|
DrawString(15, 63, STR_NETWORK_GAME_NAME, 2);
|
|
DrawString(135, 63, STR_NETWORK_CLIENTS_CAPTION, 2);
|
|
|
|
{ // draw list of games
|
|
uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3;
|
|
int32 n = 0;
|
|
int32 pos = w->vscroll.pos;
|
|
char servername[NETWORK_NAME_LENGTH];
|
|
const NetworkGameList *cur_item = _network_game_list;
|
|
|
|
while (pos > 0 && cur_item != NULL) {
|
|
pos--;
|
|
cur_item = cur_item->next;
|
|
}
|
|
|
|
while (cur_item != NULL) {
|
|
bool compatible = (strncmp(cur_item->info.server_revision, _openttd_revision, NETWORK_REVISION_LENGTH - 1) == 0);
|
|
if (strncmp(cur_item->info.server_revision, NOREV_STRING, sizeof(cur_item->info.server_revision)) == 0)
|
|
compatible = true;
|
|
|
|
if (cur_item == _selected_item)
|
|
GfxFillRect(11, y - 2, 218, y + 9, 10); // show highlighted item with a different colour
|
|
|
|
snprintf(servername, sizeof(servername), "%s", cur_item->info.server_name);
|
|
NetworkTruncateString(servername, 110);
|
|
DoDrawString(servername, 15, y, 16); // server name
|
|
|
|
SetDParam(0, cur_item->info.clients_on);
|
|
SetDParam(1, cur_item->info.clients_max);
|
|
DrawString(135, y, STR_NETWORK_CLIENTS_ONLINE, 2);
|
|
|
|
// only draw icons if the server is online
|
|
if (cur_item->online) {
|
|
|
|
// draw a lock if the server is password protected.
|
|
if(cur_item->info.use_password)
|
|
DrawSprite(SPR_LOCK, 186, y-1);
|
|
|
|
// draw red or green icon, depending on compatibility with server.
|
|
DrawSprite(SPR_BLOT | (compatible?0x30d8000:0x30b8000), 195, y);
|
|
|
|
// draw flag according to server language
|
|
DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, 206, y);
|
|
}
|
|
|
|
cur_item = cur_item->next;
|
|
y += NET_PRC__SIZE_OF_ROW;
|
|
if (++n == w->vscroll.cap) { break;} // max number of games in the window
|
|
}
|
|
}
|
|
|
|
// right menu
|
|
GfxFillRect(252, 23, 478, 65, 157);
|
|
if (_selected_item == NULL) {
|
|
DrawStringMultiCenter(365, 40, STR_NETWORK_GAME_INFO, 0);
|
|
} else if (!_selected_item->online) {
|
|
SetDParam(0, _str_game_name);
|
|
DrawStringMultiCenter(365, 42, STR_ORANGE, 2); // game name
|
|
|
|
DrawStringMultiCenter(365, 110, STR_NETWORK_SERVER_OFFLINE, 2); // server offline
|
|
} else { // show game info
|
|
uint16 y = 70;
|
|
|
|
DrawStringMultiCenter(365, 30, STR_NETWORK_GAME_INFO, 0);
|
|
|
|
SetDParam(0, _str_game_name);
|
|
DrawStringMultiCenter(365, 42, STR_ORANGE, 2); // game name
|
|
|
|
SetDParam(0, _str_map_name);
|
|
DrawStringMultiCenter(365, 54, STR_02BD, 2); // map name
|
|
|
|
SetDParam(0, _selected_item->info.clients_on);
|
|
SetDParam(1, _selected_item->info.clients_max);
|
|
DrawString(260, y, STR_NETWORK_CLIENTS, 2); // clients on the server / maximum slots
|
|
y+=10;
|
|
|
|
SetDParam(0, STR_NETWORK_LANG_ANY+_selected_item->info.server_lang);
|
|
DrawString(260, y, STR_NETWORK_LANGUAGE, 2); // server language
|
|
y+=10;
|
|
|
|
SetDParam(0, STR_TEMPERATE_LANDSCAPE+_selected_item->info.map_set);
|
|
DrawString(260, y, STR_NETWORK_TILESET, 2); // tileset
|
|
y+=10;
|
|
|
|
SetDParam(0, _selected_item->info.map_width);
|
|
SetDParam(1, _selected_item->info.map_height);
|
|
DrawString(260, y, STR_NETWORK_MAP_SIZE, 2); // map size
|
|
y+=10;
|
|
|
|
SetDParam(0, _str_server_version);
|
|
DrawString(260, y, STR_NETWORK_SERVER_VERSION, 2); // server version
|
|
y+=10;
|
|
|
|
SetDParam(0, _str_server_address);
|
|
SetDParam(1, _selected_item->port);
|
|
DrawString(260, y, STR_NETWORK_SERVER_ADDRESS, 2); // server address
|
|
y+=10;
|
|
|
|
SetDParam(0, _selected_item->info.start_date);
|
|
DrawString(260, y, STR_NETWORK_START_DATE, 2); // start date
|
|
y+=10;
|
|
|
|
SetDParam(0, _selected_item->info.game_date);
|
|
DrawString(260, y, STR_NETWORK_CURRENT_DATE, 2); // current date
|
|
y+=10;
|
|
|
|
y+=2;
|
|
|
|
if (strncmp(_selected_item->info.server_revision, _openttd_revision, NETWORK_REVISION_LENGTH - 1) != 0) {
|
|
if (strncmp(_selected_item->info.server_revision, NOREV_STRING, sizeof(_selected_item->info.server_revision)) != 0)
|
|
DrawStringMultiCenter(365, y, STR_NETWORK_VERSION_MISMATCH, 2); // server mismatch
|
|
} else if (_selected_item->info.clients_on == _selected_item->info.clients_max) {
|
|
// Show: server full, when clients_on == clients_max
|
|
DrawStringMultiCenter(365, y, STR_NETWORK_SERVER_FULL, 2); // server full
|
|
} else if (_selected_item->info.use_password)
|
|
DrawStringMultiCenter(365, y, STR_NETWORK_PASSWORD, 2); // password warning
|
|
|
|
y+=10;
|
|
}
|
|
} break;
|
|
|
|
case WE_CLICK:
|
|
_selected_field = e->click.widget;
|
|
switch(e->click.widget) {
|
|
case 0: case 14: /* Close 'X' | Cancel button */
|
|
DeleteWindowById(WC_NETWORK_WINDOW, 0);
|
|
break;
|
|
case 4: case 5:
|
|
ShowDropDownMenu(w, _lan_internet_types_dropdown, _network_lan_internet, 5, 0, 0); // do it for widget 5
|
|
break;
|
|
case 9: { /* Matrix to show networkgames */
|
|
uint32 id_v = (e->click.pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW;
|
|
|
|
if (id_v >= w->vscroll.cap) { return;} // click out of bounds
|
|
id_v += w->vscroll.pos;
|
|
|
|
{
|
|
NetworkGameList *cur_item = _network_game_list;
|
|
for (; id_v > 0 && cur_item != NULL; id_v--)
|
|
cur_item = cur_item->next;
|
|
|
|
if (cur_item == NULL) {
|
|
// click out of vehicle bounds
|
|
_selected_item = NULL;
|
|
SetWindowDirty(w);
|
|
return;
|
|
}
|
|
_selected_item = cur_item;
|
|
|
|
DeleteName(_str_game_name);
|
|
DeleteName(_str_map_name);
|
|
DeleteName(_str_server_version);
|
|
DeleteName(_str_server_address);
|
|
if (_selected_item->info.server_name[0] != '\0')
|
|
_str_game_name = AllocateName(_selected_item->info.server_name, 0);
|
|
else
|
|
_str_game_name = STR_EMPTY;
|
|
|
|
if (_selected_item->info.map_name[0] != '\0')
|
|
_str_map_name = AllocateName(_selected_item->info.map_name, 0);
|
|
else
|
|
_str_map_name = STR_EMPTY;
|
|
|
|
if (_selected_item->info.server_revision[0] != '\0')
|
|
_str_server_version = AllocateName(_selected_item->info.server_revision, 0);
|
|
else
|
|
_str_server_version = STR_EMPTY;
|
|
|
|
if (_selected_item->info.hostname[0] != '\0')
|
|
_str_server_address = AllocateName(_selected_item->info.hostname, 0);
|
|
else
|
|
_str_server_address = STR_EMPTY;
|
|
}
|
|
SetWindowDirty(w);
|
|
} break;
|
|
case 11: /* Find server automatically */
|
|
switch (_network_lan_internet) {
|
|
case 0: NetworkUDPSearchGame(); break;
|
|
case 1: NetworkUDPQueryMasterServer(); break;
|
|
}
|
|
break;
|
|
case 12: { // Add a server
|
|
StringID str = AllocateName(_network_default_ip, 0);
|
|
|
|
ShowQueryString(
|
|
str,
|
|
STR_NETWORK_ENTER_IP,
|
|
31 | 0x1000, // maximum number of characters OR
|
|
250, // characters up to this width pixels, whichever is satisfied first
|
|
w->window_class,
|
|
w->window_number);
|
|
DeleteName(str);
|
|
} break;
|
|
case 13: /* Start server */
|
|
ShowNetworkStartServerWindow();
|
|
break;
|
|
case 17: /* Join Game */
|
|
if (_selected_item != NULL) {
|
|
memcpy(&_network_game_info, &_selected_item->info, sizeof(NetworkGameInfo));
|
|
snprintf(_network_last_host, sizeof(_network_last_host), "%s", inet_ntoa(*(struct in_addr *)&_selected_item->ip));
|
|
_network_last_port = _selected_item->port;
|
|
ShowNetworkLobbyWindow();
|
|
}
|
|
break;
|
|
case 18: // Refresh
|
|
if (_selected_item != NULL) {
|
|
NetworkQueryServer(_selected_item->info.hostname, _selected_item->port, true);
|
|
}
|
|
break;
|
|
|
|
} break;
|
|
|
|
case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
|
|
switch(e->dropdown.button) {
|
|
case 5:
|
|
_network_lan_internet = e->dropdown.index;
|
|
break;
|
|
}
|
|
|
|
SetWindowDirty(w);
|
|
break;
|
|
|
|
case WE_MOUSELOOP:
|
|
if (_selected_field == 3)
|
|
HandleEditBox(w, 3);
|
|
|
|
break;
|
|
|
|
case WE_KEYPRESS:
|
|
if (_selected_field != 3) {
|
|
if ( e->keypress.keycode == WKC_DELETE ) { // press 'delete' to remove servers
|
|
if (_selected_item != NULL) {
|
|
NetworkGameListRemoveItem(_selected_item);
|
|
NetworkRebuildHostList();
|
|
SetWindowDirty(w);
|
|
_network_game_count--;
|
|
// reposition scrollbar
|
|
if (_network_game_count >= w->vscroll.cap && w->vscroll.pos > _network_game_count-w->vscroll.cap) w->vscroll.pos--;
|
|
UpdateNetworkGameWindow(false);
|
|
_selected_item = NULL;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (HandleEditBoxKey(w, 3, e) == 1) break; // enter pressed
|
|
|
|
// The name is only allowed when it starts with a letter!
|
|
if (_edit_str_buf[0] != '\0' && _edit_str_buf[0] != ' ')
|
|
ttd_strlcpy(_network_player_name, _edit_str_buf, lengthof(_network_player_name));
|
|
else
|
|
ttd_strlcpy(_network_player_name, "Player", lengthof(_network_player_name));
|
|
|
|
break;
|
|
|
|
case WE_ON_EDIT_TEXT: {
|
|
NetworkAddServer(e->edittext.str);
|
|
NetworkRebuildHostList();
|
|
} break;
|
|
}
|
|
}
|
|
|
|
static const Widget _network_game_window_widgets[] = {
|
|
{ WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
|
{ WWT_CAPTION, RESIZE_NONE, BGC, 11, 489, 0, 13, STR_NETWORK_MULTIPLAYER, STR_NULL},
|
|
{ WWT_IMGBTN, RESIZE_NONE, BGC, 0, 489, 14, 214, 0x0, STR_NULL},
|
|
|
|
/* LEFT SIDE */
|
|
{ WWT_IMGBTN, RESIZE_NONE, BGC, 90, 231, 22, 33, 0x0, STR_NETWORK_ENTER_NAME_TIP},
|
|
|
|
{ WWT_6, RESIZE_NONE, BGC, 90, 231, 42, 53, STR_NETWORK_COMBO1, STR_NETWORK_CONNECTION_TIP},
|
|
{ WWT_CLOSEBOX, RESIZE_NONE, BGC, 220, 230, 43, 52, STR_0225, STR_NETWORK_CONNECTION_TIP},
|
|
|
|
{ WWT_IMGBTN, RESIZE_NONE, BTC, 10, 130, 62, 73, 0x0, STR_NETWORK_GAME_NAME_TIP },
|
|
{ WWT_IMGBTN, RESIZE_NONE, BTC, 131, 180, 62, 73, 0x0, STR_NETWORK_CLIENTS_CAPTION_TIP },
|
|
{ WWT_IMGBTN, RESIZE_NONE, BTC, 181, 219, 62, 73, 0x0, STR_NETWORK_INFO_ICONS_TIP },
|
|
|
|
{ WWT_MATRIX, RESIZE_NONE, BGC, 10, 219, 74, 185, 0x801, STR_NETWORK_CLICK_GAME_TO_SELECT},
|
|
{ WWT_SCROLLBAR, RESIZE_NONE, BGC, 220, 231, 62, 185, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
|
|
|
|
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 10, 115, 195, 206, STR_NETWORK_FIND_SERVER, STR_NETWORK_FIND_SERVER_TIP},
|
|
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 125, 231, 195, 206, STR_NETWORK_ADD_SERVER, STR_NETWORK_ADD_SERVER_TIP},
|
|
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 250, 360, 195, 206, STR_NETWORK_START_SERVER, STR_NETWORK_START_SERVER_TIP},
|
|
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 370, 480, 195, 206, STR_012E_CANCEL, STR_NULL},
|
|
|
|
/* RIGHT SIDE */
|
|
{ WWT_IMGBTN, RESIZE_NONE, BGC, 250, 480, 22, 185, 0x0, STR_NULL},
|
|
{ WWT_6, RESIZE_NONE, BGC, 251, 479, 23, 184, 0x0, STR_NULL},
|
|
|
|
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 257, 360, 164, 175, STR_NETWORK_JOIN_GAME, STR_NULL},
|
|
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 370, 473, 164, 175, STR_NETWORK_REFRESH, STR_NETWORK_REFRESH_TIP},
|
|
|
|
{ WIDGETS_END},
|
|
};
|
|
|
|
static const WindowDesc _network_game_window_desc = {
|
|
WDP_CENTER, WDP_CENTER, 490, 215,
|
|
WC_NETWORK_WINDOW,0,
|
|
WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM,
|
|
_network_game_window_widgets,
|
|
NetworkGameWindowWndProc,
|
|
};
|
|
|
|
void ShowNetworkGameWindow(void)
|
|
{
|
|
uint i;
|
|
Window *w;
|
|
DeleteWindowById(WC_NETWORK_WINDOW, 0);
|
|
|
|
/* Only show once */
|
|
if (_first_time_show_network_game_window) {
|
|
_first_time_show_network_game_window = false;
|
|
// add all servers from the config file to our list
|
|
for (i=0; i != lengthof(_network_host_list); i++) {
|
|
if (_network_host_list[i] == NULL) break;
|
|
NetworkAddServer(_network_host_list[i]);
|
|
}
|
|
}
|
|
|
|
w = AllocateWindowDesc(&_network_game_window_desc);
|
|
ttd_strlcpy(_edit_str_buf, _network_player_name, MAX_QUERYSTR_LEN);
|
|
w->vscroll.cap = 8;
|
|
|
|
WP(w, querystr_d).text.caret = true;
|
|
WP(w, querystr_d).text.maxlength = MAX_QUERYSTR_LEN - 1;
|
|
WP(w, querystr_d).text.maxwidth = 120;
|
|
WP(w, querystr_d).text.buf = _edit_str_buf;
|
|
UpdateTextBufferSize(&WP(w, querystr_d).text);
|
|
|
|
UpdateNetworkGameWindow(true);
|
|
}
|
|
|
|
static const StringID _players_dropdown[] = {
|
|
STR_NETWORK_2_CLIENTS,
|
|
STR_NETWORK_3_CLIENTS,
|
|
STR_NETWORK_4_CLIENTS,
|
|
STR_NETWORK_5_CLIENTS,
|
|
STR_NETWORK_6_CLIENTS,
|
|
STR_NETWORK_7_CLIENTS,
|
|
STR_NETWORK_8_CLIENTS,
|
|
STR_NETWORK_9_CLIENTS,
|
|
STR_NETWORK_10_CLIENTS,
|
|
INVALID_STRING_ID
|
|
};
|
|
|
|
static const StringID _language_dropdown[] = {
|
|
STR_NETWORK_LANG_ANY,
|
|
STR_NETWORK_LANG_ENGLISH,
|
|
STR_NETWORK_LANG_GERMAN,
|
|
STR_NETWORK_LANG_FRENCH,
|
|
INVALID_STRING_ID
|
|
};
|
|
|
|
enum {
|
|
NSSWND_START = 64,
|
|
NSSWND_ROWSIZE = 12
|
|
};
|
|
|
|
static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e)
|
|
{
|
|
switch (e->event) {
|
|
case WE_CREATE: /* focus input box */
|
|
_selected_field = 3;
|
|
_network_game_info.use_password = (_network_server_password[0] == '\0') ? 0 : 1;
|
|
break;
|
|
|
|
case WE_PAINT: {
|
|
int y = NSSWND_START, pos;
|
|
const FiosItem *item;
|
|
|
|
SetDParam(7, STR_NETWORK_LAN_INTERNET + _network_advertise);
|
|
SetDParam(9, STR_NETWORK_2_CLIENTS + _network_game_info.clients_max - 2);
|
|
SetDParam(11, STR_NETWORK_LANG_ANY + _network_game_info.server_lang);
|
|
DrawWindowWidgets(w);
|
|
|
|
GfxFillRect(11, 63, 259, 171, 0xD7);
|
|
|
|
DrawEditBox(w, 3);
|
|
|
|
DrawString(10, 22, STR_NETWORK_NEW_GAME_NAME, 2);
|
|
|
|
DrawString(10, 43, STR_NETWORK_SELECT_MAP, 2);
|
|
DrawString(280, 63, STR_NETWORK_CONNECTION, 2);
|
|
DrawString(280, 95, STR_NETWORK_NUMBER_OF_CLIENTS, 2);
|
|
DrawString(280, 127, STR_NETWORK_LANGUAGE_SPOKEN, 2);
|
|
|
|
if (_network_game_info.use_password) DoDrawString("*", 408, 23, 3);
|
|
|
|
// draw list of maps
|
|
pos = w->vscroll.pos;
|
|
while (pos < _fios_num + 1) {
|
|
item = _fios_list + pos - 1;
|
|
if (item == selected_map || (pos == 0 && selected_map == NULL))
|
|
GfxFillRect(11, y - 1, 259, y + 10, 155); // show highlighted item with a different colour
|
|
|
|
if (pos == 0) DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, 9);
|
|
else DoDrawString(item->title[0] ? item->title : item->name, 14, y, _fios_colors[item->type] );
|
|
pos++;
|
|
y += NSSWND_ROWSIZE;
|
|
|
|
if (y >= w->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break;
|
|
}
|
|
} break;
|
|
|
|
case WE_CLICK:
|
|
_selected_field = e->click.widget;
|
|
switch(e->click.widget) {
|
|
case 0: case 15: /* Close 'X' | Cancel button */
|
|
ShowNetworkGameWindow();
|
|
break;
|
|
case 4: { /* Set password button */
|
|
StringID str;
|
|
str = AllocateName(_network_server_password, 0);
|
|
ShowQueryString(str, STR_NETWORK_SET_PASSWORD, 20, 250, w->window_class, w->window_number);
|
|
DeleteName(str);
|
|
} break;
|
|
case 5: { /* Select map */
|
|
int y = (e->click.pt.y - NSSWND_START) / NSSWND_ROWSIZE;
|
|
if ((y += w->vscroll.pos) >= w->vscroll.count)
|
|
return;
|
|
if (y == 0) selected_map = NULL;
|
|
else selected_map = _fios_list + y-1;
|
|
SetWindowDirty(w);
|
|
} break;
|
|
case 7: case 8: /* Connection type */
|
|
ShowDropDownMenu(w, _connection_types_dropdown, _network_advertise, 8, 0, 0); // do it for widget 8
|
|
break;
|
|
case 9: case 10: /* Number of Players */
|
|
ShowDropDownMenu(w, _players_dropdown, _network_game_info.clients_max - 2, 10, 0, 0); // do it for widget 10
|
|
return;
|
|
case 11: case 12: /* Language */
|
|
ShowDropDownMenu(w, _language_dropdown, _network_game_info.server_lang, 12, 0, 0); // do it for widget 12
|
|
return;
|
|
case 13: /* Start game */
|
|
_is_network_server = true;
|
|
ttd_strlcpy(_network_server_name, WP(w, querystr_d).text.buf, sizeof(_network_server_name));
|
|
UpdateTextBufferSize(&WP(w, querystr_d).text);
|
|
if (selected_map == NULL) { // start random new game
|
|
GenRandomNewGame(Random(), InteractiveRandom());
|
|
} else { // load a scenario
|
|
char *name;
|
|
if ((name = FiosBrowseTo(selected_map)) != NULL) {
|
|
SetFiosType(selected_map->type);
|
|
strcpy(_file_to_saveload.name, name);
|
|
snprintf(_network_game_info.map_name, sizeof(_network_game_info.map_name), "Loaded scenario");
|
|
DeleteWindow(w);
|
|
StartScenarioEditor(Random(), InteractiveRandom());
|
|
}
|
|
}
|
|
break;
|
|
case 14: /* Load game */
|
|
_is_network_server = true;
|
|
ttd_strlcpy(_network_server_name, WP(w, querystr_d).text.buf, sizeof(_network_server_name));
|
|
UpdateTextBufferSize(&WP(w, querystr_d).text);
|
|
snprintf(_network_game_info.map_name, sizeof(_network_game_info.map_name), "Loaded game");
|
|
/* XXX - WC_NETWORK_WINDOW should stay, but if it stays, it gets
|
|
* copied all the elements of 'load game' and upon closing that, it segfaults */
|
|
DeleteWindowById(WC_NETWORK_WINDOW, 0);
|
|
ShowSaveLoadDialog(SLD_LOAD_GAME);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
|
|
switch(e->dropdown.button) {
|
|
case 8:
|
|
_network_advertise = (e->dropdown.index == 0) ? false : true;
|
|
break;
|
|
case 10:
|
|
_network_game_info.clients_max = e->dropdown.index + 2;
|
|
break;
|
|
case 12:
|
|
_network_game_info.server_lang = e->dropdown.index;
|
|
break;
|
|
}
|
|
|
|
SetWindowDirty(w);
|
|
break;
|
|
|
|
case WE_MOUSELOOP:
|
|
if (_selected_field == 3)
|
|
HandleEditBox(w, 3);
|
|
break;
|
|
|
|
case WE_KEYPRESS:
|
|
if (_selected_field == 3)
|
|
HandleEditBoxKey(w, 3, e);
|
|
break;
|
|
|
|
case WE_ON_EDIT_TEXT: {
|
|
const char *b = e->edittext.str;
|
|
ttd_strlcpy(_network_server_password, b, sizeof(_network_server_password));
|
|
_network_game_info.use_password = (_network_server_password[0] == '\0') ? 0 : 1;
|
|
SetWindowDirty(w);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
static const Widget _network_start_server_window_widgets[] = {
|
|
{ WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW },
|
|
{ WWT_CAPTION, RESIZE_NONE, BGC, 11, 419, 0, 13, STR_NETWORK_START_GAME_WINDOW, STR_NULL},
|
|
{ WWT_IMGBTN, RESIZE_NONE, BGC, 0, 419, 14, 199, 0x0, STR_NULL},
|
|
|
|
{ WWT_IMGBTN, RESIZE_NONE, BGC, 100, 272, 22, 33, 0x0, STR_NETWORK_NEW_GAME_NAME_TIP},
|
|
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 285, 405, 22, 33, STR_NETWORK_SET_PASSWORD, STR_NETWORK_PASSWORD_TIP},
|
|
|
|
{ WWT_6, RESIZE_NONE, BGC, 10, 271, 62, 172, 0x0, STR_NETWORK_SELECT_MAP_TIP},
|
|
{ WWT_SCROLLBAR, RESIZE_NONE, BGC, 260, 271, 63, 171, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
|
|
|
|
{ WWT_6, RESIZE_NONE, BGC, 280, 410, 77, 88, STR_NETWORK_COMBO1, STR_NETWORK_CONNECTION_TIP},
|
|
{ WWT_CLOSEBOX, RESIZE_NONE, BGC, 399, 409, 78, 87, STR_0225, STR_NETWORK_CONNECTION_TIP},
|
|
|
|
{ WWT_6, RESIZE_NONE, BGC, 280, 410, 109, 120, STR_NETWORK_COMBO2, STR_NETWORK_NUMBER_OF_CLIENTS_TIP},
|
|
{ WWT_CLOSEBOX, RESIZE_NONE, BGC, 399, 409, 110, 119, STR_0225, STR_NETWORK_NUMBER_OF_CLIENTS_TIP},
|
|
|
|
{ WWT_6, RESIZE_NONE, BGC, 280, 410, 141, 152, STR_NETWORK_COMBO3, STR_NETWORK_LANGUAGE_TIP},
|
|
{ WWT_CLOSEBOX, RESIZE_NONE, BGC, 399, 409, 142, 151, STR_0225, STR_NETWORK_LANGUAGE_TIP},
|
|
|
|
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 40, 140, 180, 191, STR_NETWORK_START_GAME, STR_NETWORK_START_GAME_TIP},
|
|
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 150, 250, 180, 191, STR_NETWORK_LOAD_GAME, STR_NETWORK_LOAD_GAME_TIP},
|
|
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 260, 360, 180, 191, STR_012E_CANCEL, STR_NULL},
|
|
{ WIDGETS_END},
|
|
};
|
|
|
|
static const WindowDesc _network_start_server_window_desc = {
|
|
WDP_CENTER, WDP_CENTER, 420, 200,
|
|
WC_NETWORK_WINDOW,0,
|
|
WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM,
|
|
_network_start_server_window_widgets,
|
|
NetworkStartServerWindowWndProc,
|
|
};
|
|
|
|
static void ShowNetworkStartServerWindow(void)
|
|
{
|
|
Window *w;
|
|
DeleteWindowById(WC_NETWORK_WINDOW, 0);
|
|
|
|
w = AllocateWindowDesc(&_network_start_server_window_desc);
|
|
ttd_strlcpy(_edit_str_buf, _network_server_name, MAX_QUERYSTR_LEN);
|
|
|
|
_saveload_mode = SLD_NEW_GAME;
|
|
BuildFileList();
|
|
w->vscroll.cap = 9;
|
|
w->vscroll.count = _fios_num+1;
|
|
|
|
WP(w, querystr_d).text.caret = true;
|
|
WP(w, querystr_d).text.maxlength = MAX_QUERYSTR_LEN - 1;
|
|
WP(w, querystr_d).text.maxwidth = 160;
|
|
WP(w, querystr_d).text.buf = _edit_str_buf;
|
|
UpdateTextBufferSize(&WP(w, querystr_d).text);
|
|
}
|
|
|
|
static byte NetworkLobbyFindCompanyIndex(byte pos)
|
|
{
|
|
byte i;
|
|
/* Scroll through all _network_player_info and get the 'pos' item
|
|
that is not empty */
|
|
for (i = 0; i < MAX_PLAYERS; i++) {
|
|
if (_network_player_info[i].company_name[0] != '\0') {
|
|
if (pos-- == 0)
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void NetworkLobbyWindowWndProc(Window *w, WindowEvent *e)
|
|
{
|
|
switch(e->event) {
|
|
case WE_PAINT: {
|
|
int y = NET_PRC__OFFSET_TOP_WIDGET_COMPANY, pos;
|
|
StringID str;
|
|
|
|
w->disabled_state = (_selected_company_item == -1) ? 1 << 7 : 0;
|
|
|
|
if (_network_lobby_company_count == MAX_PLAYERS)
|
|
SETBIT(w->disabled_state, 8);
|
|
/* You can not join a server as spectator when it has no companies active..
|
|
it causes some nasty crashes */
|
|
if (_network_lobby_company_count == 0)
|
|
SETBIT(w->disabled_state, 9);
|
|
|
|
DrawWindowWidgets(w);
|
|
|
|
SetDParam(0, _str_game_name);
|
|
DrawString(10, 22, STR_NETWORK_PREPARE_TO_JOIN, 2);
|
|
|
|
// draw company list
|
|
GfxFillRect(11, 41, 154, 165, 0xD7);
|
|
pos = w->vscroll.pos;
|
|
while (pos < _network_lobby_company_count) {
|
|
byte index = NetworkLobbyFindCompanyIndex(pos);
|
|
bool income = false;
|
|
if (_selected_company_item == index)
|
|
GfxFillRect(11, y - 1, 154, y + 10, 155); // show highlighted item with a different colour
|
|
|
|
DoDrawString(_network_player_info[index].company_name, 13, y, 2);
|
|
if(_network_player_info[index].use_password != 0)
|
|
DrawSprite(SPR_LOCK, 135, y);
|
|
|
|
/* If the company's income was positive puts a green dot else a red dot */
|
|
if ((_network_player_info[index].income) >= 0)
|
|
income = true;
|
|
DrawSprite(SPR_BLOT | (income ? PALETTE_TO_GREEN : PALETTE_TO_RED), 145, y);
|
|
|
|
pos++;
|
|
y += NET_PRC__SIZE_OF_ROW_COMPANY;
|
|
if (pos >= w->vscroll.cap)
|
|
break;
|
|
}
|
|
|
|
// draw info about selected company
|
|
DrawStringMultiCenter(290, 48, STR_NETWORK_COMPANY_INFO, 0);
|
|
if (_selected_company_item != -1) { // if a company is selected...
|
|
// show company info
|
|
const uint x = 183;
|
|
uint xm;
|
|
y = 65;
|
|
|
|
str = AllocateName(_network_player_info[_selected_company_item].company_name, 0);
|
|
SetDParam(0, str);
|
|
DrawString(x, y, STR_NETWORK_COMPANY_NAME, 2);
|
|
DeleteName(str);
|
|
y += 10;
|
|
|
|
SetDParam(0, _network_player_info[_selected_company_item].inaugurated_year + MAX_YEAR_BEGIN_REAL);
|
|
DrawString(x, y, STR_NETWORK_INAUGURATION_YEAR, 2); // inauguration year
|
|
y += 10;
|
|
|
|
SetDParam64(0, _network_player_info[_selected_company_item].company_value);
|
|
DrawString(x, y, STR_NETWORK_VALUE, 2); // company value
|
|
y += 10;
|
|
|
|
SetDParam64(0, _network_player_info[_selected_company_item].money);
|
|
DrawString(x, y, STR_NETWORK_CURRENT_BALANCE, 2); // current balance
|
|
y += 10;
|
|
|
|
SetDParam64(0, _network_player_info[_selected_company_item].income);
|
|
DrawString(x, y, STR_NETWORK_LAST_YEARS_INCOME, 2); // last year's income
|
|
y += 10;
|
|
|
|
SetDParam(0, _network_player_info[_selected_company_item].performance);
|
|
DrawString(x, y, STR_NETWORK_PERFORMANCE, 2); // performance
|
|
y += 10;
|
|
|
|
SetDParam(0, _network_player_info[_selected_company_item].num_vehicle[0]);
|
|
SetDParam(1, _network_player_info[_selected_company_item].num_vehicle[1]);
|
|
SetDParam(2, _network_player_info[_selected_company_item].num_vehicle[2]);
|
|
SetDParam(3, _network_player_info[_selected_company_item].num_vehicle[3]);
|
|
SetDParam(4, _network_player_info[_selected_company_item].num_vehicle[4]);
|
|
DrawString(x, y, STR_NETWORK_VEHICLES, 2); // vehicles
|
|
y += 10;
|
|
|
|
SetDParam(0, _network_player_info[_selected_company_item].num_station[0]);
|
|
SetDParam(1, _network_player_info[_selected_company_item].num_station[1]);
|
|
SetDParam(2, _network_player_info[_selected_company_item].num_station[2]);
|
|
SetDParam(3, _network_player_info[_selected_company_item].num_station[3]);
|
|
SetDParam(4, _network_player_info[_selected_company_item].num_station[4]);
|
|
DrawString(x, y, STR_NETWORK_STATIONS, 2); // stations
|
|
y += 10;
|
|
|
|
str = AllocateName(_network_player_info[_selected_company_item].players, 0);
|
|
SetDParam(0, str);
|
|
xm = DrawString(x, y, STR_NETWORK_PLAYERS, 2); // players
|
|
DeleteName(str);
|
|
y += 10;
|
|
}
|
|
} break;
|
|
|
|
case WE_CLICK:
|
|
switch(e->click.widget) {
|
|
case 0: case 11: /* Close 'X' | Cancel button */
|
|
ShowNetworkGameWindow();
|
|
break;
|
|
case 3: /* Company list */
|
|
_selected_company_item = (e->click.pt.y - NET_PRC__OFFSET_TOP_WIDGET_COMPANY) / NET_PRC__SIZE_OF_ROW_COMPANY;
|
|
|
|
if (_selected_company_item >= w->vscroll.cap) {
|
|
// click out of bounds
|
|
_selected_company_item = -1;
|
|
SetWindowDirty(w);
|
|
return;
|
|
}
|
|
_selected_company_item += w->vscroll.pos;
|
|
if (_selected_company_item >= _network_lobby_company_count) {
|
|
_selected_company_item = -1;
|
|
SetWindowDirty(w);
|
|
return;
|
|
}
|
|
|
|
_selected_company_item = NetworkLobbyFindCompanyIndex(_selected_company_item);
|
|
|
|
SetWindowDirty(w);
|
|
break;
|
|
case 7: /* Join company */
|
|
if (_selected_company_item != -1) {
|
|
_network_playas = _selected_company_item + 1;
|
|
NetworkClientConnectGame(_network_last_host, _network_last_port);
|
|
}
|
|
break;
|
|
case 8: /* New company */
|
|
_network_playas = 0;
|
|
NetworkClientConnectGame(_network_last_host, _network_last_port);
|
|
break;
|
|
case 9: /* Spectate game */
|
|
_network_playas = OWNER_SPECTATOR;
|
|
NetworkClientConnectGame(_network_last_host, _network_last_port);
|
|
break;
|
|
case 10: /* Refresh */
|
|
NetworkQueryServer(_network_last_host, _network_last_port, false);
|
|
break;
|
|
} break;
|
|
|
|
case WE_CREATE:
|
|
_selected_company_item = -1;
|
|
}
|
|
}
|
|
|
|
static const Widget _network_lobby_window_widgets[] = {
|
|
{ WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW },
|
|
{ WWT_CAPTION, RESIZE_NONE, BGC, 11, 419, 0, 13, STR_NETWORK_GAME_LOBBY, STR_NULL},
|
|
{ WWT_IMGBTN, RESIZE_NONE, BGC, 0, 419, 14, 209, 0x0, STR_NULL},
|
|
|
|
// company list
|
|
{ WWT_6, RESIZE_NONE, BGC, 10, 167, 40, 166, 0x0, STR_NETWORK_COMPANY_LIST_TIP},
|
|
{ WWT_SCROLLBAR, RESIZE_NONE, BGC, 155, 166, 41, 165, 0x1, STR_0190_SCROLL_BAR_SCROLLS_LIST},
|
|
|
|
// company/player info
|
|
{ WWT_IMGBTN, RESIZE_NONE, BGC, 173, 404, 38, 166, 0x0, STR_NULL},
|
|
{ WWT_6, RESIZE_NONE, BGC, 174, 403, 39, 165, 0x0, STR_NULL},
|
|
|
|
// buttons
|
|
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 10, 151, 175, 186, STR_NETWORK_JOIN_COMPANY, STR_NETWORK_JOIN_COMPANY_TIP},
|
|
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 10, 151, 190, 201, STR_NETWORK_NEW_COMPANY, STR_NETWORK_NEW_COMPANY_TIP},
|
|
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 158, 268, 175, 186, STR_NETWORK_SPECTATE_GAME, STR_NETWORK_SPECTATE_GAME_TIP},
|
|
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 158, 268, 190, 201, STR_NETWORK_REFRESH, STR_NETWORK_REFRESH_TIP},
|
|
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 278, 388, 175, 186, STR_012E_CANCEL, STR_NULL},
|
|
|
|
{ WIDGETS_END},
|
|
};
|
|
|
|
static const WindowDesc _network_lobby_window_desc = {
|
|
WDP_CENTER, WDP_CENTER, 420, 210,
|
|
WC_NETWORK_WINDOW,0,
|
|
WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
|
|
_network_lobby_window_widgets,
|
|
NetworkLobbyWindowWndProc,
|
|
};
|
|
|
|
|
|
static void ShowNetworkLobbyWindow(void)
|
|
{
|
|
Window *w;
|
|
DeleteWindowById(WC_NETWORK_WINDOW, 0);
|
|
|
|
_network_lobby_company_count = 0;
|
|
|
|
NetworkQueryServer(_network_last_host, _network_last_port, false);
|
|
|
|
w = AllocateWindowDesc(&_network_lobby_window_desc);
|
|
strcpy(_edit_str_buf, "");
|
|
w->vscroll.pos = 0;
|
|
w->vscroll.cap = 8;
|
|
}
|
|
|
|
|
|
|
|
|
|
// The window below gives information about the connected clients
|
|
// and also makes able to give money to them, kick them (if server)
|
|
// and stuff like that.
|
|
|
|
extern void DrawPlayerIcon(int p, int x, int y);
|
|
|
|
// Every action must be of this form
|
|
typedef void ClientList_Action_Proc(byte client_no);
|
|
|
|
// Max 10 actions per client
|
|
#define MAX_CLIENTLIST_ACTION 10
|
|
|
|
// Some standard bullshit.. defines variables ;)
|
|
static void ClientListWndProc(Window *w, WindowEvent *e);
|
|
static void ClientListPopupWndProc(Window *w, WindowEvent *e);
|
|
static byte _selected_clientlist_item = 255;
|
|
static byte _selected_clientlist_y = 0;
|
|
static char _clientlist_action[MAX_CLIENTLIST_ACTION][50];
|
|
static ClientList_Action_Proc *_clientlist_proc[MAX_CLIENTLIST_ACTION];
|
|
|
|
enum {
|
|
CLNWND_OFFSET = 16,
|
|
CLNWND_ROWSIZE = 10
|
|
};
|
|
|
|
static const Widget _client_list_widgets[] = {
|
|
{ WWT_TEXTBTN, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
|
{ WWT_CAPTION, RESIZE_NONE, 14, 11, 249, 0, 13, STR_NETWORK_CLIENT_LIST, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
|
|
|
{ WWT_IMGBTN, RESIZE_NONE, 14, 0, 249, 14, 14 + CLNWND_ROWSIZE + 1, 0x0, STR_NULL},
|
|
{ WIDGETS_END},
|
|
};
|
|
|
|
static const Widget _client_list_popup_widgets[] = {
|
|
{ WWT_PANEL, RESIZE_NONE, 14, 0, 99, 0, 0, 0, STR_NULL},
|
|
{ WIDGETS_END},
|
|
};
|
|
|
|
static WindowDesc _client_list_desc = {
|
|
-1, -1, 250, 1,
|
|
WC_CLIENT_LIST,0,
|
|
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
|
|
_client_list_widgets,
|
|
ClientListWndProc
|
|
};
|
|
|
|
// Finds the Xth client-info that is active
|
|
static NetworkClientInfo *NetworkFindClientInfo(byte client_no)
|
|
{
|
|
NetworkClientInfo *ci;
|
|
for (ci = _network_client_info; ci != &_network_client_info[MAX_CLIENT_INFO]; ci++) {
|
|
// Skip non-active items
|
|
if (ci->client_index == NETWORK_EMPTY_INDEX) continue;
|
|
if (client_no == 0) return ci;
|
|
client_no--;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Here we start to define the options out of the menu
|
|
static void ClientList_Kick(byte client_no)
|
|
{
|
|
if (client_no < MAX_PLAYERS)
|
|
SEND_COMMAND(PACKET_SERVER_ERROR)(&_clients[client_no], NETWORK_ERROR_KICKED);
|
|
}
|
|
|
|
static void ClientList_Ban(byte client_no)
|
|
{
|
|
uint i;
|
|
uint32 ip = NetworkFindClientInfo(client_no)->client_ip;
|
|
|
|
for (i = 0; i < lengthof(_network_ban_list); i++) {
|
|
if (_network_ban_list[i] == NULL || _network_ban_list[i][0] == '\0') {
|
|
_network_ban_list[i] = strdup(inet_ntoa(*(struct in_addr *)&ip));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (client_no < MAX_PLAYERS)
|
|
SEND_COMMAND(PACKET_SERVER_ERROR)(&_clients[client_no], NETWORK_ERROR_KICKED);
|
|
}
|
|
|
|
static void ClientList_GiveMoney(byte client_no)
|
|
{
|
|
if (NetworkFindClientInfo(client_no) != NULL)
|
|
ShowNetworkGiveMoneyWindow(NetworkFindClientInfo(client_no)->client_playas - 1);
|
|
}
|
|
|
|
static void ClientList_SpeakToClient(byte client_no)
|
|
{
|
|
if (NetworkFindClientInfo(client_no) != NULL)
|
|
ShowNetworkChatQueryWindow(DESTTYPE_CLIENT, NetworkFindClientInfo(client_no)->client_index);
|
|
}
|
|
|
|
static void ClientList_SpeakToPlayer(byte client_no)
|
|
{
|
|
if (NetworkFindClientInfo(client_no) != NULL)
|
|
ShowNetworkChatQueryWindow(DESTTYPE_PLAYER, NetworkFindClientInfo(client_no)->client_playas);
|
|
}
|
|
|
|
static void ClientList_SpeakToAll(byte client_no)
|
|
{
|
|
ShowNetworkChatQueryWindow(DESTTYPE_BROADCAST, 0);
|
|
}
|
|
|
|
static void ClientList_None(byte client_no)
|
|
{
|
|
// No action ;)
|
|
}
|
|
|
|
|
|
|
|
// Help, a action is clicked! What do we do?
|
|
static void HandleClientListPopupClick(byte index, byte clientno) {
|
|
// A click on the Popup of the ClientList.. handle the command
|
|
if (index < MAX_CLIENTLIST_ACTION && _clientlist_proc[index] != NULL) {
|
|
_clientlist_proc[index](clientno);
|
|
}
|
|
}
|
|
|
|
// Finds the amount of clients and set the height correct
|
|
static bool CheckClientListHeight(Window *w)
|
|
{
|
|
int num = 0;
|
|
NetworkClientInfo *ci;
|
|
|
|
// Should be replaced with a loop through all clients
|
|
for (ci = _network_client_info; ci != &_network_client_info[MAX_CLIENT_INFO]; ci++) {
|
|
// Skip non-active items
|
|
if (ci->client_index == NETWORK_EMPTY_INDEX) continue;
|
|
num++;
|
|
}
|
|
|
|
num *= CLNWND_ROWSIZE;
|
|
|
|
// If height is changed
|
|
if (w->height != CLNWND_OFFSET + num + 1) {
|
|
// XXX - magic unfortunately; (num + 2) has to be one bigger than heigh (num + 1)
|
|
SetWindowDirty(w);
|
|
w->widget[2].bottom = w->widget[2].top + num + 2;
|
|
w->height = CLNWND_OFFSET + num + 1;
|
|
SetWindowDirty(w);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Finds the amount of actions in the popup and set the height correct
|
|
static uint ClientListPopupHeigth(void) {
|
|
int i, num = 0;
|
|
|
|
// Find the amount of actions
|
|
for (i = 0; i < MAX_CLIENTLIST_ACTION; i++) {
|
|
if (_clientlist_action[i][0] == '\0') continue;
|
|
if (_clientlist_proc[i] == NULL) continue;
|
|
num++;
|
|
}
|
|
|
|
num *= CLNWND_ROWSIZE;
|
|
|
|
return num + 1;
|
|
}
|
|
|
|
// Show the popup (action list)
|
|
static Window *PopupClientList(Window *w, int client_no, int x, int y)
|
|
{
|
|
int i, h;
|
|
NetworkClientInfo *ci;
|
|
DeleteWindowById(WC_TOOLBAR_MENU, 0);
|
|
|
|
// Clean the current actions
|
|
for (i = 0; i < MAX_CLIENTLIST_ACTION; i++) {
|
|
_clientlist_action[i][0] = '\0';
|
|
_clientlist_proc[i] = NULL;
|
|
}
|
|
|
|
// Fill the actions this client has
|
|
// Watch is, max 50 chars long!
|
|
|
|
ci = NetworkFindClientInfo(client_no);
|
|
if (ci == NULL) return NULL;
|
|
|
|
i = 0;
|
|
if (_network_own_client_index != ci->client_index) {
|
|
GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT);
|
|
_clientlist_proc[i++] = &ClientList_SpeakToClient;
|
|
}
|
|
|
|
if (ci->client_playas >= 1 && ci->client_playas <= MAX_PLAYERS) {
|
|
GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY);
|
|
_clientlist_proc[i++] = &ClientList_SpeakToPlayer;
|
|
}
|
|
GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL);
|
|
_clientlist_proc[i++] = &ClientList_SpeakToAll;
|
|
|
|
if (_network_own_client_index != ci->client_index) {
|
|
if (_network_playas >= 1 && _network_playas <= MAX_PLAYERS) {
|
|
// We are no spectator
|
|
if (ci->client_playas >= 1 && ci->client_playas <= MAX_PLAYERS) {
|
|
GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_GIVE_MONEY);
|
|
_clientlist_proc[i++] = &ClientList_GiveMoney;
|
|
}
|
|
}
|
|
}
|
|
|
|
// A server can kick clients (but not hisself)
|
|
if (_network_server && _network_own_client_index != ci->client_index) {
|
|
GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_KICK);
|
|
_clientlist_proc[i++] = &ClientList_Kick;
|
|
|
|
sprintf(_clientlist_action[i],"Ban");
|
|
_clientlist_proc[i++] = &ClientList_Ban;
|
|
}
|
|
|
|
if (i == 0) {
|
|
GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_NONE);
|
|
_clientlist_proc[i++] = &ClientList_None;
|
|
}
|
|
|
|
/* Calculate the height */
|
|
h = ClientListPopupHeigth();
|
|
|
|
// Allocate the popup
|
|
w = AllocateWindow(x, y, 100, h + 1, ClientListPopupWndProc, WC_TOOLBAR_MENU, _client_list_popup_widgets);
|
|
w->widget[0].bottom = w->widget[0].top + h;
|
|
|
|
w->flags4 &= ~WF_WHITE_BORDER_MASK;
|
|
WP(w,menu_d).item_count = 0;
|
|
// Save our client
|
|
WP(w,menu_d).main_button = client_no;
|
|
WP(w,menu_d).sel_index = 0;
|
|
// We are a popup
|
|
_popup_menu_active = true;
|
|
|
|
return w;
|
|
}
|
|
|
|
// Main handle for the popup
|
|
static void ClientListPopupWndProc(Window *w, WindowEvent *e)
|
|
{
|
|
switch(e->event) {
|
|
case WE_PAINT: {
|
|
int i, y, sel;
|
|
byte colour;
|
|
DrawWindowWidgets(w);
|
|
|
|
// Draw the actions
|
|
sel = WP(w,menu_d).sel_index;
|
|
y = 1;
|
|
for (i = 0; i < MAX_CLIENTLIST_ACTION; i++, y += CLNWND_ROWSIZE) {
|
|
if (_clientlist_action[i][0] == '\0') continue;
|
|
if (_clientlist_proc[i] == NULL) continue;
|
|
|
|
if (sel-- == 0) { // Selected item, highlight it
|
|
GfxFillRect(1, y, 98, y + CLNWND_ROWSIZE - 1, 0);
|
|
colour = 0xC;
|
|
} else colour = 0x10;
|
|
|
|
DoDrawString(_clientlist_action[i], 4, y, colour);
|
|
}
|
|
} break;
|
|
|
|
case WE_POPUPMENU_SELECT: {
|
|
// We selected an action
|
|
int index = (e->popupmenu.pt.y - w->top) / CLNWND_ROWSIZE;
|
|
|
|
if (index >= 0 && e->popupmenu.pt.y >= w->top)
|
|
HandleClientListPopupClick(index, WP(w,menu_d).main_button);
|
|
|
|
// Sometimes, because of the bad DeleteWindow-proc, the 'w' pointer is
|
|
// invalid after the last functions (mostly because it kills a window
|
|
// that is in front of 'w', and because of a silly memmove, the address
|
|
// 'w' was pointing to becomes invalid), so we need to refetch
|
|
// the right address...
|
|
DeleteWindowById(WC_TOOLBAR_MENU, 0);
|
|
} break;
|
|
|
|
case WE_POPUPMENU_OVER: {
|
|
// Our mouse hoovers over an action? Select it!
|
|
int index = (e->popupmenu.pt.y - w->top) / CLNWND_ROWSIZE;
|
|
|
|
if (index == -1 || index == WP(w,menu_d).sel_index)
|
|
return;
|
|
|
|
WP(w,menu_d).sel_index = index;
|
|
SetWindowDirty(w);
|
|
} break;
|
|
|
|
}
|
|
}
|
|
|
|
// Main handle for clientlist
|
|
static void ClientListWndProc(Window *w, WindowEvent *e)
|
|
{
|
|
switch(e->event) {
|
|
case WE_PAINT: {
|
|
NetworkClientInfo *ci;
|
|
int y, i = 0;
|
|
byte colour;
|
|
|
|
// Check if we need to reset the height
|
|
if (!CheckClientListHeight(w)) break;
|
|
|
|
DrawWindowWidgets(w);
|
|
|
|
y = CLNWND_OFFSET;
|
|
|
|
for (ci = _network_client_info; ci != &_network_client_info[MAX_CLIENT_INFO]; ci++) {
|
|
// Skip non-active items
|
|
if (ci->client_index == NETWORK_EMPTY_INDEX) continue;
|
|
|
|
if (_selected_clientlist_item == i++) { // Selected item, highlight it
|
|
GfxFillRect(1, y, 248, y + CLNWND_ROWSIZE - 1, 0);
|
|
colour = 0xC;
|
|
} else
|
|
colour = 0x10;
|
|
|
|
if (ci->client_index == NETWORK_SERVER_INDEX) {
|
|
DrawString(4, y, STR_NETWORK_SERVER, colour);
|
|
} else
|
|
DrawString(4, y, STR_NETWORK_CLIENT, colour);
|
|
|
|
// Filter out spectators
|
|
if (ci->client_playas > 0 && ci->client_playas <= MAX_PLAYERS)
|
|
DrawPlayerIcon(ci->client_playas - 1, 44, y + 1);
|
|
|
|
DoDrawString(ci->client_name, 61, y, colour);
|
|
|
|
y += CLNWND_ROWSIZE;
|
|
}
|
|
} break;
|
|
|
|
case WE_CLICK:
|
|
// Show the popup with option
|
|
if (_selected_clientlist_item != 255) {
|
|
PopupClientList(w, _selected_clientlist_item, e->click.pt.x + w->left, e->click.pt.y + w->top);
|
|
}
|
|
|
|
break;
|
|
|
|
case WE_MOUSEOVER:
|
|
// -1 means we left the current window
|
|
if (e->mouseover.pt.y == -1) {
|
|
_selected_clientlist_y = 0;
|
|
_selected_clientlist_item = 255;
|
|
SetWindowDirty(w);
|
|
break;
|
|
}
|
|
// It did not change.. no update!
|
|
if (e->mouseover.pt.y == _selected_clientlist_y) break;
|
|
|
|
// Find the new selected item (if any)
|
|
_selected_clientlist_y = e->mouseover.pt.y;
|
|
if (e->mouseover.pt.y > CLNWND_OFFSET) {
|
|
_selected_clientlist_item = (e->mouseover.pt.y - CLNWND_OFFSET) / CLNWND_ROWSIZE;
|
|
} else
|
|
_selected_clientlist_item = 255;
|
|
|
|
// Repaint
|
|
SetWindowDirty(w);
|
|
break;
|
|
|
|
case WE_DESTROY: case WE_CREATE:
|
|
// When created or destroyed, data is reset
|
|
_selected_clientlist_item = 255;
|
|
_selected_clientlist_y = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ShowClientList(void)
|
|
{
|
|
Window *w = AllocateWindowDescFront(&_client_list_desc, 0);
|
|
if (w)
|
|
w->window_number = 0;
|
|
}
|
|
|
|
extern void SwitchMode(int new_mode);
|
|
|
|
static void NetworkJoinStatusWindowWndProc(Window *w, WindowEvent *e)
|
|
{
|
|
switch(e->event) {
|
|
case WE_PAINT: {
|
|
uint8 progress; // used for progress bar
|
|
DrawWindowWidgets(w);
|
|
|
|
DrawStringCentered(125, 35, STR_NETWORK_CONNECTING_1 + _network_join_status, 14);
|
|
switch (_network_join_status) {
|
|
case NETWORK_JOIN_STATUS_CONNECTING: case NETWORK_JOIN_STATUS_AUTHORIZING:
|
|
case NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO:
|
|
progress = 10; // first two stages 10%
|
|
break;
|
|
case NETWORK_JOIN_STATUS_WAITING:
|
|
SetDParam(0, _network_join_waiting);
|
|
DrawStringCentered(125, 46, STR_NETWORK_CONNECTING_WAITING, 14);
|
|
progress = 15; // third stage is 15%
|
|
break;
|
|
case NETWORK_JOIN_STATUS_DOWNLOADING:
|
|
SetDParam(0, _network_join_kbytes);
|
|
SetDParam(1, _network_join_kbytes_total);
|
|
DrawStringCentered(125, 46, STR_NETWORK_CONNECTING_DOWNLOADING, 14);
|
|
/* Fallthrough */
|
|
default: /* Waiting is 15%, so the resting receivement of map is maximum 70% */
|
|
progress = 15 + _network_join_kbytes * (100 - 15) / _network_join_kbytes_total;
|
|
}
|
|
|
|
/* Draw nice progress bar :) */
|
|
DrawFrameRect(20, 18, (int)((w->width - 20) * progress / 100), 28, 10, 0);
|
|
} break;
|
|
|
|
case WE_CLICK:
|
|
switch(e->click.widget) {
|
|
case 0: case 3: /* Close 'X' | Disconnect button */
|
|
NetworkDisconnect();
|
|
DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
|
|
SwitchMode(SM_MENU);
|
|
ShowNetworkGameWindow();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
static const Widget _network_join_status_window_widget[] = {
|
|
{ WWT_TEXTBTN, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
|
|
{ WWT_CAPTION, RESIZE_NONE, 14, 11, 249, 0, 13, STR_NETWORK_CONNECTING, STR_018C_WINDOW_TITLE_DRAG_THIS},
|
|
{ WWT_IMGBTN, RESIZE_NONE, 14, 0, 249, 14, 84, 0x0,STR_NULL},
|
|
{ WWT_PUSHTXTBTN, RESIZE_NONE, BTC, 75, 175, 69, 80, STR_NETWORK_DISCONNECT, STR_NULL},
|
|
{ WIDGETS_END},
|
|
};
|
|
|
|
static const WindowDesc _network_join_status_window_desc = {
|
|
WDP_CENTER, WDP_CENTER, 250, 85,
|
|
WC_NETWORK_STATUS_WINDOW, 0,
|
|
WDF_STD_TOOLTIPS | WDF_DEF_WIDGET,
|
|
_network_join_status_window_widget,
|
|
NetworkJoinStatusWindowWndProc,
|
|
};
|
|
|
|
void ShowJoinStatusWindow(void)
|
|
{
|
|
DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
|
|
_network_join_status = NETWORK_JOIN_STATUS_CONNECTING;
|
|
AllocateWindowDesc(&_network_join_status_window_desc);
|
|
}
|
|
|
|
void ShowJoinStatusWindowAfterJoin(void)
|
|
{
|
|
/* This is a special instant of ShowJoinStatusWindow, because
|
|
it is opened after the map is loaded, but the client maybe is not
|
|
done registering itself to the server */
|
|
DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
|
|
_network_join_status = NETWORK_JOIN_STATUS_REGISTERING;
|
|
AllocateWindowDesc(&_network_join_status_window_desc);
|
|
}
|
|
|
|
|
|
|
|
#define MAX_QUERYSTR_LEN 64
|
|
|
|
static void ChatWindowWndProc(Window *w, WindowEvent *e)
|
|
{
|
|
static bool closed = false;
|
|
switch (e->event) {
|
|
case WE_CREATE:
|
|
SendWindowMessage(WC_NEWS_WINDOW, 0, WE_CREATE, w->height, 0);
|
|
closed = false;
|
|
break;
|
|
|
|
case WE_PAINT:
|
|
DrawWindowWidgets(w);
|
|
DrawEditBox(w, 1);
|
|
break;
|
|
|
|
case WE_CLICK:
|
|
switch(e->click.widget) {
|
|
case 3: DeleteWindow(w); break; // Cancel
|
|
case 2: // Send
|
|
press_ok:;
|
|
if (strcmp(WP(w, querystr_d).text.buf, WP(w, querystr_d).text.buf + MAX_QUERYSTR_LEN) == 0) {
|
|
DeleteWindow(w);
|
|
} else {
|
|
char *buf = WP(w, querystr_d).text.buf;
|
|
WindowClass wnd_class = WP(w, querystr_d).wnd_class;
|
|
WindowNumber wnd_num = WP(w, querystr_d).wnd_num;
|
|
Window *parent;
|
|
|
|
// Mask the edit-box as closed, so we don't send out a CANCEL
|
|
closed = true;
|
|
|
|
DeleteWindow(w);
|
|
|
|
parent = FindWindowById(wnd_class, wnd_num);
|
|
if (parent != NULL) {
|
|
WindowEvent e;
|
|
e.event = WE_ON_EDIT_TEXT;
|
|
e.edittext.str = buf;
|
|
parent->wndproc(parent, &e);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WE_MOUSELOOP: {
|
|
if (!FindWindowById(WP(w,querystr_d).wnd_class, WP(w,querystr_d).wnd_num)) {
|
|
DeleteWindow(w);
|
|
return;
|
|
}
|
|
HandleEditBox(w, 1);
|
|
} break;
|
|
|
|
case WE_KEYPRESS: {
|
|
switch(HandleEditBoxKey(w, 1, e)) {
|
|
case 1: // Return
|
|
goto press_ok;
|
|
case 2: // Escape
|
|
DeleteWindow(w);
|
|
break;
|
|
}
|
|
} break;
|
|
|
|
case WE_DESTROY:
|
|
SendWindowMessage(WC_NEWS_WINDOW, 0, WE_DESTROY, 0, 0);
|
|
// If the window is not closed yet, it means it still needs to send a CANCEL
|
|
if (!closed) {
|
|
Window *parent = FindWindowById(WP(w,querystr_d).wnd_class, WP(w,querystr_d).wnd_num);
|
|
if (parent != NULL) {
|
|
WindowEvent e;
|
|
e.event = WE_ON_EDIT_TEXT_CANCEL;
|
|
parent->wndproc(parent, &e);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static const Widget _chat_window_widgets[] = {
|
|
{ WWT_IMGBTN, RESIZE_NONE, 14, 0, 639, 0, 13, STR_NULL, STR_NULL}, // background
|
|
{ WWT_IMGBTN, RESIZE_NONE, 14, 2, 399, 1, 12, STR_NULL, STR_NULL}, // text box
|
|
{ WWT_TEXTBTN, RESIZE_NONE, 14, 400, 519, 1, 12, STR_NETWORK_SEND, STR_NULL}, // send button
|
|
{ WWT_TEXTBTN, RESIZE_NONE, 14, 520, 639, 1, 12, STR_012E_CANCEL, STR_NULL}, // cancel button
|
|
{ WIDGETS_END},
|
|
};
|
|
|
|
static const WindowDesc _chat_window_desc = {
|
|
WDP_CENTER, -26, 640, 14, // x, y, width, height
|
|
WC_SEND_NETWORK_MSG,0,
|
|
WDF_STD_TOOLTIPS | WDF_DEF_WIDGET,
|
|
_chat_window_widgets,
|
|
ChatWindowWndProc
|
|
};
|
|
|
|
void ShowChatWindow(StringID str, StringID caption, int maxlen, int maxwidth, byte window_class, uint16 window_number)
|
|
{
|
|
Window *w;
|
|
|
|
#define _orig_edit_str_buf (_edit_str_buf+MAX_QUERYSTR_LEN)
|
|
|
|
DeleteWindowById(WC_SEND_NETWORK_MSG, 0);
|
|
|
|
GetString(_orig_edit_str_buf, str);
|
|
|
|
_orig_edit_str_buf[maxlen] = '\0';
|
|
|
|
memcpy(_edit_str_buf, _orig_edit_str_buf, MAX_QUERYSTR_LEN);
|
|
|
|
w = AllocateWindowDesc(&_chat_window_desc);
|
|
|
|
w->click_state = 1 << 1;
|
|
WP(w,querystr_d).caption = caption;
|
|
WP(w,querystr_d).wnd_class = window_class;
|
|
WP(w,querystr_d).wnd_num = window_number;
|
|
WP(w,querystr_d).text.caret = false;
|
|
WP(w,querystr_d).text.maxlength = maxlen - 1;
|
|
WP(w,querystr_d).text.maxwidth = maxwidth;
|
|
WP(w,querystr_d).text.buf = _edit_str_buf;
|
|
UpdateTextBufferSize(&WP(w, querystr_d).text);
|
|
}
|
|
|
|
#else
|
|
void ShowJoinStatusWindowAfterJoin(void) {}
|
|
#endif /* ENABLE_NETWORK */
|