diff --git a/data/language/english_uk.txt b/data/language/english_uk.txt
index 144bf8a857..361b08dea1 100644
--- a/data/language/english_uk.txt
+++ b/data/language/english_uk.txt
@@ -3836,3 +3836,9 @@ STR_5494 :Type the message you would like to send.
STR_5495 :Player List
STR_5496 :Player:
STR_5497 :Ping:
+STR_5498 :Server List
+STR_5499 :Player Name:
+STR_5500 :Add Server
+STR_5501 :Start Server
+STR_5502 :Multiplayer
+STR_5503 :Enter hostname or IP address:
diff --git a/projects/openrct2.vcxproj b/projects/openrct2.vcxproj
index 84ea401ec4..956fd8efe2 100644
--- a/projects/openrct2.vcxproj
+++ b/projects/openrct2.vcxproj
@@ -100,6 +100,7 @@
+
diff --git a/projects/openrct2.vcxproj.filters b/projects/openrct2.vcxproj.filters
index 49d011038e..8321dae4a5 100644
--- a/projects/openrct2.vcxproj.filters
+++ b/projects/openrct2.vcxproj.filters
@@ -519,6 +519,9 @@
Source\Interface
+
+ Source\Windows
+
diff --git a/src/interface/window.h b/src/interface/window.h
index 080c6c201a..a60cace19f 100644
--- a/src/interface/window.h
+++ b/src/interface/window.h
@@ -449,6 +449,7 @@ enum {
WC_CHAT_HOST = 124,
WC_PLAYER_LIST = 125,
WC_NETWORK_STATUS = 126,
+ WC_SERVER_LIST = 127,
// Only used for colour schemes
WC_STAFF = 220,
@@ -615,6 +616,7 @@ void window_sign_small_open(rct_windownumber number);
void window_cheats_open();
void window_player_list_open();
void window_network_status_open(const char* text);
+void window_server_list_open();
void window_research_open();
void window_research_development_page_paint(rct_window *w, rct_drawpixelinfo *dpi, int baseWidgetIndex);
diff --git a/src/localisation/string_ids.h b/src/localisation/string_ids.h
index 8fbbc0d93e..f9cc1abcfb 100644
--- a/src/localisation/string_ids.h
+++ b/src/localisation/string_ids.h
@@ -2035,6 +2035,12 @@ enum {
STR_PLAYER_LIST = 5495,
STR_PLAYER = 5496,
STR_PING = 5497,
+ STR_SERVER_LIST = 5498,
+ STR_PLAYER_NAME = 5499,
+ STR_ADD_SERVER = 5500,
+ STR_START_SERVER = 5501,
+ STR_MULTIPLAYER = 5502,
+ STR_ENTER_HOSTNAME_OR_IP_ADDRESS = 5503,
// Have to include resource strings (from scenarios and objects) for the time being now that language is partially working
STR_COUNT = 32768
diff --git a/src/windows/clear_scenery.c b/src/windows/clear_scenery.c
index 819340bc07..fcbb6a50c5 100644
--- a/src/windows/clear_scenery.c
+++ b/src/windows/clear_scenery.c
@@ -139,8 +139,6 @@ static void window_clear_scenery_close(rct_window *w)
*/
static void window_clear_scenery_mouseup(rct_window *w, int widgetIndex)
{
- int limit;
-
switch (widgetIndex) {
case WIDX_CLOSE:
window_close(w);
diff --git a/src/windows/editor_object_selection.c b/src/windows/editor_object_selection.c
index 26e05f526e..054b3918a2 100644
--- a/src/windows/editor_object_selection.c
+++ b/src/windows/editor_object_selection.c
@@ -1,3 +1,4 @@
+
/*****************************************************************************
* Copyright (c) 2014 Dániel Tar
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
diff --git a/src/windows/server_list.c b/src/windows/server_list.c
new file mode 100644
index 0000000000..c9716da512
--- /dev/null
+++ b/src/windows/server_list.c
@@ -0,0 +1,338 @@
+/*****************************************************************************
+* Copyright (c) 2014 Ted John
+* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
+*
+* This file is part of OpenRCT2.
+*
+* OpenRCT2 is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*****************************************************************************/
+
+#include "../interface/themes.h"
+#include "../interface/widget.h"
+#include "../interface/window.h"
+#include "../localisation/localisation.h"
+#include "../network/network.h"
+#include "../sprites.h"
+
+#define WWIDTH_MIN 500
+#define WHEIGHT_MIN 300
+#define WWIDTH_MAX 1200
+#define WHEIGHT_MAX 800
+#define ITEM_HEIGHT (3 + 11 + 1 + 11 + 3)
+
+typedef struct {
+ char *address;
+ utf8 *name;
+ utf8 *description;
+} saved_server;
+
+char _playerName[64] = "Player";
+saved_server *_savedServers = NULL;
+int _numSavedServers = 0;
+
+enum {
+ WIDX_BACKGROUND,
+ WIDX_TITLE,
+ WIDX_CLOSE,
+ WIDX_PLAYER_NAME_INPUT,
+ WIDX_LIST,
+ WIDX_ADD_SERVER,
+ WIDX_START_SERVER
+};
+
+enum {
+ WIDX_LIST_REMOVE,
+ WIDX_LIST_SPECTATE
+};
+
+static rct_widget window_server_list_widgets[] = {
+ { WWT_FRAME, 0, 0, 340, 0, 90, 0xFFFFFFFF, STR_NONE }, // panel / background
+ { WWT_CAPTION, 0, 1, 338, 1, 14, STR_SERVER_LIST, STR_WINDOW_TITLE_TIP }, // title bar
+ { WWT_CLOSEBOX, 0, 327, 337, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, // close x button
+ { WWT_TEXT_BOX, 1, 100, 344, 20, 31, (uint32)_playerName, STR_NONE }, // player name text box
+ { WWT_SCROLL, 1, 6, 337, 37, 50, STR_NONE, STR_NONE }, // server list
+ { WWT_DROPDOWN_BUTTON, 1, 6, 106, 53, 64, STR_ADD_SERVER, STR_NONE }, // add server button
+ { WWT_DROPDOWN_BUTTON, 1, 112, 212, 53, 64, STR_START_SERVER, STR_NONE }, // start server button
+ { WIDGETS_END },
+};
+
+static void window_server_list_mouseup(rct_window *w, int widgetIndex);
+static void window_server_list_resize(rct_window *w);
+static void window_server_list_update(rct_window *w);
+static void window_server_list_scroll_getsize(rct_window *w, int scrollIndex, int *width, int *height);
+static void window_server_list_scroll_mousedown(rct_window *w, int scrollIndex, int x, int y);
+static void window_server_list_scroll_mouseover(rct_window *w, int scrollIndex, int x, int y);
+static void window_server_list_textinput(rct_window *w, int widgetIndex, char *text);
+static void window_server_list_invalidate(rct_window *w);
+static void window_server_list_paint(rct_window *w, rct_drawpixelinfo *dpi);
+static void window_server_list_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int scrollIndex);
+
+static void server_list_get_item_button(int buttonIndex, int x, int y, int width, int *outX, int *outY);
+
+static rct_window_event_list window_server_list_events = {
+ NULL,
+ window_server_list_mouseup,
+ window_server_list_resize,
+ NULL,
+ NULL,
+ NULL,
+ window_server_list_update,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ window_server_list_scroll_getsize,
+ window_server_list_scroll_mousedown,
+ NULL,
+ window_server_list_scroll_mouseover,
+ window_server_list_textinput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ window_server_list_invalidate,
+ window_server_list_paint,
+ window_server_list_scrollpaint
+};
+
+static int _hoverButtonIndex = -1;
+
+void window_server_list_open()
+{
+ rct_window* window;
+
+ // Check if window is already open
+ window = window_bring_to_front_by_class(WC_SERVER_LIST);
+ if (window != NULL)
+ return;
+
+ window = window_create_centred(WWIDTH_MIN, WHEIGHT_MIN, &window_server_list_events, WC_SERVER_LIST, WF_10 | WF_RESIZABLE);
+
+ window->widgets = window_server_list_widgets;
+ window->enabled_widgets = (
+ (1 << WIDX_CLOSE) |
+ (1 << WIDX_PLAYER_NAME_INPUT) |
+ (1 << WIDX_ADD_SERVER) |
+ (1 << WIDX_START_SERVER)
+ );
+ window_init_scroll_widgets(window);
+ window->no_list_items = 0;
+ window->selected_list_item = -1;
+ window->frame_no = 0;
+ window->min_width = 320;
+ window->min_height = 90;
+ window->max_width = window->min_width;
+ window->max_height = window->min_height;
+
+ window->page = 0;
+ window->list_information_type = 0;
+ window->colours[0] = 1;
+ window->colours[1] = 26;
+ window->colours[2] = 26;
+
+ window_set_resize(window, WWIDTH_MIN, WHEIGHT_MIN, WWIDTH_MAX, WHEIGHT_MAX);
+
+ _numSavedServers = 2;
+ _savedServers = malloc(_numSavedServers * sizeof(saved_server));
+ _savedServers[0].address = "127.0.0.1";
+ _savedServers[0].name = "localhost";
+ _savedServers[0].description = "Home sweet home.";
+ _savedServers[1].address = "81.124.111.146:12428";
+ _savedServers[1].name = "Another server, #1";
+ _savedServers[1].description = "Another server...";
+ window->no_list_items = _numSavedServers;
+}
+
+static void window_server_list_mouseup(rct_window *w, int widgetIndex)
+{
+ switch (widgetIndex) {
+ case WIDX_CLOSE:
+ window_close(w);
+ break;
+ case WIDX_PLAYER_NAME_INPUT:
+ window_start_textbox(w, widgetIndex, 1170, (uint32)_playerName, 63);
+ break;
+ case WIDX_ADD_SERVER:
+ window_text_input_open(w, widgetIndex, STR_ADD_SERVER, STR_ENTER_HOSTNAME_OR_IP_ADDRESS, STR_NONE, 0, 128);
+ break;
+ }
+}
+
+static void window_server_list_resize(rct_window *w)
+{
+ window_set_resize(w, WWIDTH_MIN, WHEIGHT_MIN, WWIDTH_MAX, WHEIGHT_MAX);
+}
+
+static void window_server_list_update(rct_window *w)
+{
+ if (gCurrentTextBox.window.classification == w->classification && gCurrentTextBox.window.number == w->number) {
+ window_update_textbox_caret();
+ widget_invalidate(w, WIDX_PLAYER_NAME_INPUT);
+ }
+}
+
+static void window_server_list_scroll_getsize(rct_window *w, int scrollIndex, int *width, int *height)
+{
+ *width = 0;
+ *height = w->no_list_items * ITEM_HEIGHT;
+}
+
+static void window_server_list_scroll_mousedown(rct_window *w, int scrollIndex, int x, int y)
+{
+ if (w->selected_list_item == -1) return;
+
+ switch (_hoverButtonIndex) {
+ case WIDX_LIST_REMOVE:
+ break;
+ case WIDX_LIST_SPECTATE:
+ break;
+ default:
+ // Join
+ break;
+ }
+}
+
+static void window_server_list_scroll_mouseover(rct_window *w, int scrollIndex, int x, int y)
+{
+ // Item
+ int index = y / ITEM_HEIGHT;
+ if (index < 0 || index >= w->no_list_items) {
+ index = -1;
+ }
+
+ int hoverButtonIndex = -1;
+ if (index != -1) {
+ int width = w->widgets[WIDX_LIST].right - w->widgets[WIDX_LIST].left;
+ int sy = index * ITEM_HEIGHT;
+ for (int i = 0; i < 2; i++) {
+ int bx, by;
+
+ server_list_get_item_button(i, 0, sy, width, &bx, &by);
+ if (x >= bx && y >= by && x < bx + 24 && y < by + 24) {
+ hoverButtonIndex = i;
+ break;
+ }
+ }
+ }
+
+ if (w->selected_list_item != index || _hoverButtonIndex != hoverButtonIndex) {
+ w->selected_list_item = index;
+ _hoverButtonIndex = hoverButtonIndex;
+ window_invalidate(w);
+ }
+}
+
+static void window_server_list_textinput(rct_window *w, int widgetIndex, char *text)
+{
+ if (widgetIndex != WIDX_PLAYER_NAME_INPUT || text == NULL)
+ return;
+
+ if (strcmp(_playerName, text) == 0)
+ return;
+
+ if (strlen(text) == 0) {
+ memset(_playerName, 0, sizeof(_playerName));
+ } else {
+ memset(_playerName, 0, sizeof(_playerName));
+ strcpy(_playerName, text);
+ }
+
+ widget_invalidate(w, WIDX_PLAYER_NAME_INPUT);
+}
+
+static void window_server_list_invalidate(rct_window *w)
+{
+ window_server_list_widgets[WIDX_BACKGROUND].right = w->width - 1;
+ window_server_list_widgets[WIDX_BACKGROUND].bottom = w->height - 1;
+ window_server_list_widgets[WIDX_TITLE].right = w->width - 2;
+ window_server_list_widgets[WIDX_CLOSE].left = w->width - 2 - 11;
+ window_server_list_widgets[WIDX_CLOSE].right = w->width - 2 - 11 + 10;
+
+ window_server_list_widgets[WIDX_PLAYER_NAME_INPUT].right = w->width - 6;
+ window_server_list_widgets[WIDX_LIST].left = 6;
+ window_server_list_widgets[WIDX_LIST].right = w->width - 6;
+ window_server_list_widgets[WIDX_LIST].bottom = w->height - 6 - 11 - 6;
+ window_server_list_widgets[WIDX_ADD_SERVER].top = w->height - 6 - 11;
+ window_server_list_widgets[WIDX_ADD_SERVER].bottom = w->height - 6;
+ window_server_list_widgets[WIDX_START_SERVER].top = w->height - 6 - 11;
+ window_server_list_widgets[WIDX_START_SERVER].bottom = w->height - 6;
+}
+
+static void window_server_list_paint(rct_window *w, rct_drawpixelinfo *dpi)
+{
+ window_draw_widgets(w, dpi);
+
+ gfx_draw_string_left(dpi, STR_PLAYER_NAME, NULL, w->colours[1], w->x + 6, w->y + w->widgets[WIDX_PLAYER_NAME_INPUT].top);
+}
+
+static void window_server_list_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int scrollIndex)
+{
+ uint32 colour;
+ int bx, by;
+
+ colour = ((char*)0x0141FC48)[w->colours[1] * 8];
+ colour = (colour << 24) | (colour << 16) | (colour << 8) | colour;
+ gfx_clear(dpi, colour);
+
+ int width = w->widgets[WIDX_LIST].right - w->widgets[WIDX_LIST].left;
+
+ int y = 0;
+ for (int i = 0; i < w->no_list_items; i++) {
+ if (y >= dpi->y + dpi->height) continue;
+ // if (y + ITEM_HEIGHT < dpi->y) continue;
+
+ saved_server *serverDetails = &_savedServers[i];
+ bool highlighted = i == w->selected_list_item;
+
+ // Draw hover highlight
+ if (highlighted) {
+ gfx_fill_rect(dpi, 0, y, width, y + ITEM_HEIGHT, 0x02000031);
+ }
+
+ // Draw server information
+ if (highlighted) {
+ gfx_draw_string(dpi, serverDetails->address, w->colours[1], 3, y + 3);
+ } else {
+ gfx_draw_string(dpi, serverDetails->name, w->colours[1], 3, y + 3);
+ }
+ gfx_draw_string(dpi, serverDetails->description, w->colours[1], 3, y + 14);
+
+ // Draw delete server button
+ server_list_get_item_button(0, 0, y, width, &bx, &by);
+ if (highlighted && _hoverButtonIndex == WIDX_LIST_REMOVE) {
+ gfx_fill_rect_inset(dpi, bx, by, bx + 24, by + 24, w->colours[1], 0);
+ }
+ gfx_draw_sprite(dpi, SPR_DEMOLISH, bx, by, 0);
+
+ // Draw spectate server button
+ server_list_get_item_button(1, 0, y, width, &bx, &by);
+ if (highlighted && _hoverButtonIndex == WIDX_LIST_SPECTATE) {
+ gfx_fill_rect_inset(dpi, bx, by, bx + 24, by + 24, w->colours[1], 0);
+ }
+ gfx_draw_sprite(dpi, SPR_LOCATE, bx, by, 0);
+
+ y += ITEM_HEIGHT;
+ }
+}
+
+static void server_list_get_item_button(int buttonIndex, int x, int y, int width, int *outX, int *outY)
+{
+ *outX = width - 3 - 36 - (30 * buttonIndex);
+ *outY = y + 2;
+}
diff --git a/src/windows/title_menu.c b/src/windows/title_menu.c
index 9ef748705d..2bb2c004f1 100644
--- a/src/windows/title_menu.c
+++ b/src/windows/title_menu.c
@@ -35,6 +35,7 @@ enum {
WIDX_CONTINUE_SAVED_GAME,
WIDX_SHOW_TUTORIAL,
WIDX_GAME_TOOLS,
+ WIDX_MULTIPLAYER
};
static rct_widget window_title_menu_widgets[] = {
@@ -42,6 +43,7 @@ static rct_widget window_title_menu_widgets[] = {
{ WWT_IMGBTN, 2, 82, 163, 0, 81, SPR_MENU_LOAD_GAME, STR_CONTINUE_SAVED_GAME_TIP },
{ WWT_IMGBTN, 2, 164, 245, 0, 81, SPR_MENU_TUTORIAL, STR_SHOW_TUTORIAL_TIP },
{ WWT_IMGBTN, 2, 246, 327, 0, 81, SPR_MENU_TOOLBOX, STR_GAME_TOOLS },
+ { WWT_DROPDOWN_BUTTON, 2, 82, 245, 88, 99, STR_MULTIPLAYER, STR_NONE },
{ WIDGETS_END },
};
@@ -93,14 +95,23 @@ void window_title_menu_open()
window = window_create(
(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16) - 328) / 2, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16) - 142,
- 328, 82,
+ 328, 100,
&window_title_menu_events,
WC_TITLE_MENU,
- WF_STICK_TO_BACK | WF_TRANSPARENT
+ WF_STICK_TO_BACK | WF_TRANSPARENT | WF_5
);
window->widgets = window_title_menu_widgets;
- window->enabled_widgets |= (8 | 2 | 1);
- window->disabled_widgets |= (4); // Disable tutorial button
+ window->enabled_widgets = (
+ (1 << WIDX_START_NEW_GAME) |
+ (1 << WIDX_CONTINUE_SAVED_GAME) |
+ (1 << WIDX_SHOW_TUTORIAL) |
+ (1 << WIDX_GAME_TOOLS) |
+ (1 << WIDX_MULTIPLAYER)
+ );
+
+ // Disable tutorial button
+ window->disabled_widgets = (1 << WIDX_SHOW_TUTORIAL);
+
window_init_scroll_widgets(window);
}
@@ -113,6 +124,9 @@ static void window_title_menu_mouseup(rct_window *w, int widgetIndex)
case WIDX_CONTINUE_SAVED_GAME:
game_do_command(0, 1, 0, 0, GAME_COMMAND_LOAD_OR_QUIT, 0, 0);
break;
+ case WIDX_MULTIPLAYER:
+ window_server_list_open();
+ break;
}
}
@@ -175,6 +189,15 @@ static void window_title_menu_cursor(rct_window *w, int widgetIndex, int x, int
static void window_title_menu_paint(rct_window *w, rct_drawpixelinfo *dpi)
{
+ gfx_fill_rect(dpi, w->x, w->y, w->x + w->width - 1, w->y + 82 - 1, 0x2000000 | 51);
+ gfx_fill_rect(
+ dpi,
+ w->x + window_title_menu_widgets[WIDX_MULTIPLAYER].left,
+ w->y + window_title_menu_widgets[WIDX_MULTIPLAYER].top,
+ w->x + window_title_menu_widgets[WIDX_MULTIPLAYER].right,
+ w->y + window_title_menu_widgets[WIDX_MULTIPLAYER].bottom,
+ 0x2000000 | 51
+ );
window_draw_widgets(w, dpi);
}