diff --git a/data/language/english_uk.txt b/data/language/english_uk.txt
index d83f63383f..1111e14da7 100644
--- a/data/language/english_uk.txt
+++ b/data/language/english_uk.txt
@@ -3894,7 +3894,7 @@ STR_5552 :{POP16}{POP16}Year {COMMA16}, {PUSH16}{PUSH16}{PUSH16}{STRINGID} {M
STR_5553 :Pause game when Steam overlay is open
STR_5554 :{SMALLFONT}{BLACK}Enable mountain tool
STR_5555 :Show vehicles from other track types
-STR_5556 :Kick Player
+STR_5556 :{SMALLFONT}{BLACK}Kick Player
STR_5557 :Stay connected after desynchronisation (Multiplayer)
STR_5558 :A restart is required for this setting to take effect
STR_5559 :10 min. inspections
@@ -3973,6 +3973,14 @@ STR_5631 :Original DLC Parks
STR_5632 :Build your own...
STR_5633 :CMD +
STR_5634 :OPTION +
+STR_5635 :{WINDOW_COLOUR_2}Money spent: {BLACK}{CURRENCY2DP}
+STR_5636 :{WINDOW_COLOUR_2}Commands ran: {BLACK}{COMMA16}
+STR_5637 :Can't do this...
+STR_5638 :Permission denied
+STR_5639 :{SMALLFONT}{BLACK}Show list of players
+STR_5640 :{SMALLFONT}{BLACK}Manage groups
+STR_5641 :Default Group:
+STR_5642 :Group:
#############
# Scenarios #
diff --git a/openrct2.vcxproj b/openrct2.vcxproj
index 215b153a01..0e44e7076e 100644
--- a/openrct2.vcxproj
+++ b/openrct2.vcxproj
@@ -102,9 +102,10 @@
+
-
+
diff --git a/openrct2.vcxproj.filters b/openrct2.vcxproj.filters
index 2ad680ca98..5fc8dbe7c9 100644
--- a/openrct2.vcxproj.filters
+++ b/openrct2.vcxproj.filters
@@ -498,9 +498,6 @@
Source\Windows
-
- Source\Windows
-
Source\Interface
@@ -579,6 +576,12 @@
Source\Core
+
+ Source\Windows
+
+
+ Source\Windows
+
diff --git a/src/game.c b/src/game.c
index b9a750f995..2e60f07092 100644
--- a/src/game.c
+++ b/src/game.c
@@ -418,7 +418,7 @@ static int game_check_affordability(int cost)
return MONEY32_UNDEFINED;
}
-static GAME_COMMAND_POINTER* new_game_command_table[62];
+static GAME_COMMAND_POINTER* new_game_command_table[63];
/**
*
@@ -451,6 +451,10 @@ int game_do_command_p(int command, int *eax, int *ebx, int *ecx, int *edx, int *
original_edi = *edi;
original_ebp = *ebp;
+ if (command >= countof(new_game_command_table)) {
+ return MONEY32_UNDEFINED;
+ }
+
flags = *ebx;
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TEXT, uint16) = 0xFFFF;
@@ -533,6 +537,10 @@ int game_do_command_p(int command, int *eax, int *ebx, int *ecx, int *edx, int *
}
}
+ if (network_get_mode() == NETWORK_MODE_SERVER && !(flags & GAME_COMMAND_FLAG_NETWORKED)) {
+ network_add_player_money_spent(network_get_current_player_id(), cost);
+ }
+
return cost;
}
}
@@ -1169,7 +1177,7 @@ void game_load_or_quit_no_save_prompt()
}
}
-static GAME_COMMAND_POINTER* new_game_command_table[62] = {
+static GAME_COMMAND_POINTER* new_game_command_table[63] = {
game_command_set_ride_appearance,
game_command_set_land_height,
game_pause_toggle,
@@ -1231,5 +1239,6 @@ static GAME_COMMAND_POINTER* new_game_command_table[62] = {
game_command_set_banner_name,
game_command_set_sign_name,
game_command_set_banner_style,
- game_command_set_sign_style
+ game_command_set_sign_style,
+ game_command_set_player_group
};
diff --git a/src/game.h b/src/game.h
index 91e49141a5..fa646dafe5 100644
--- a/src/game.h
+++ b/src/game.h
@@ -87,7 +87,8 @@ enum GAME_COMMAND {
GAME_COMMAND_SET_BANNER_NAME,
GAME_COMMAND_SET_SIGN_NAME,
GAME_COMMAND_SET_BANNER_STYLE,
- GAME_COMMAND_SET_SIGN_STYLE
+ GAME_COMMAND_SET_SIGN_STYLE,
+ GAME_COMMAND_SET_PLAYER_GROUP
};
enum {
diff --git a/src/interface/window.h b/src/interface/window.h
index 15aaa1175a..1077364831 100644
--- a/src/interface/window.h
+++ b/src/interface/window.h
@@ -451,8 +451,8 @@ enum {
WC_CHANGELOG = 121,
WC_TITLE_EDITOR = 122,
WC_TITLE_COMMAND_EDITOR = 123,
- WC_CHAT_HOST = 124,
- WC_PLAYER_LIST = 125,
+ WC_MULTIPLAYER = 124,
+ WC_PLAYER = 125,
WC_NETWORK_STATUS = 126,
WC_SERVER_LIST = 127,
WC_SERVER_START = 128,
@@ -632,7 +632,8 @@ void window_sign_open(rct_windownumber number);
void window_sign_small_open(rct_windownumber number);
void window_news_options_open();
void window_cheats_open();
-void window_player_list_open();
+void window_multiplayer_open();
+void window_player_open(uint8 id);
void window_network_status_open(const char* text);
void window_network_status_close();
void window_network_status_open_password();
diff --git a/src/localisation/string_ids.h b/src/localisation/string_ids.h
index 3556161df2..24c9871d78 100644
--- a/src/localisation/string_ids.h
+++ b/src/localisation/string_ids.h
@@ -2176,7 +2176,7 @@ enum {
STR_CHEAT_SHOW_VEHICLES_FROM_OTHER_TRACK_TYPES = 5555,
- STR_KICK_PLAYER = 5556,
+ STR_KICK_PLAYER_TIP = 5556,
STR_STAY_CONNECTED_AFTER_DESYNC = 5557,
STR_AUTO_OPEN_SHOPS = 5586,
@@ -2260,6 +2260,15 @@ enum {
STR_CMD_PLUS = 5633,
STR_OPTION_PLUS = 5634,
+ STR_MONEY_SPENT = 5635,
+ STR_COMMANDS_RAN = 5636,
+ STR_CANT_DO_THIS = 5637,
+ STR_PERMISSION_DENIED = 5638,
+ STR_PLAYERS_TIP = 5639,
+ STR_GROUPS_TIP = 5640,
+ STR_DEFAULT_GROUP = 5641,
+ STR_GROUP = 5642,
+
// 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/network/network.cpp b/src/network/network.cpp
index 2bc9b132df..2308b3a94d 100644
--- a/src/network/network.cpp
+++ b/src/network/network.cpp
@@ -72,21 +72,11 @@ enum {
NETWORK_COMMAND_PINGLIST,
NETWORK_COMMAND_SETDISCONNECTMSG,
NETWORK_COMMAND_GAMEINFO,
+ NETWORK_COMMAND_SHOWERROR,
NETWORK_COMMAND_MAX,
NETWORK_COMMAND_INVALID = -1
};
-const char *NetworkCommandNames[] = {
- "NETWORK_COMMAND_AUTH",
- "NETWORK_COMMAND_MAP",
- "NETWORK_COMMAND_CHAT",
- "NETWORK_COMMAND_GAMECMD",
- "NETWORK_COMMAND_TICK",
- "NETWORK_COMMAND_PLAYERLIST",
- "NETWORK_COMMAND_PING",
- "NETWORK_COMMAND_PINGLIST",
-};
-
enum {
ADVERTISE_STATUS_DISABLED,
ADVERTISE_STATUS_UNREGISTERED,
@@ -165,7 +155,7 @@ const char* NetworkPacket::ReadString()
strend++;
}
if (*strend != 0) {
- return 0;
+ return nullptr;
}
read++;
return str;
@@ -196,6 +186,35 @@ NetworkPlayer::NetworkPlayer(const char* name)
NetworkPlayer::name[sizeof(NetworkPlayer::name) - 1] = 0;
ping = 0;
flags = 0;
+ money_spent = MONEY(0, 0);
+ commands_ran = 0;
+ group = 0;
+}
+
+void NetworkPlayer::AddMoneySpent(money32 cost)
+{
+ money_spent += cost;
+ commands_ran++;
+ window_invalidate_by_number(WC_PLAYER, id);
+}
+
+NetworkGroup::NetworkGroup()
+{
+ permissions = 0;
+ name_string_id = STR_NONE;
+}
+
+NetworkGroup::~NetworkGroup()
+{
+ user_string_free(name_string_id);
+}
+
+bool NetworkGroup::CanRun(int command)
+{
+ if (permissions) {
+ return true;
+ }
+ return false;
}
NetworkConnection::NetworkConnection()
@@ -414,6 +433,7 @@ Network::Network()
client_command_handlers[NETWORK_COMMAND_PING] = &Network::Client_Handle_PING;
client_command_handlers[NETWORK_COMMAND_PINGLIST] = &Network::Client_Handle_PINGLIST;
client_command_handlers[NETWORK_COMMAND_SETDISCONNECTMSG] = &Network::Client_Handle_SETDISCONNECTMSG;
+ client_command_handlers[NETWORK_COMMAND_SHOWERROR] = &Network::Client_Handle_SHOWERROR;
server_command_handlers.resize(NETWORK_COMMAND_MAX, 0);
server_command_handlers[NETWORK_COMMAND_AUTH] = &Network::Server_Handle_AUTH;
server_command_handlers[NETWORK_COMMAND_CHAT] = &Network::Server_Handle_CHAT;
@@ -440,6 +460,20 @@ bool Network::Init()
wsa_initialized = true;
}
#endif
+
+ // Hardcoded permission groups
+ std::unique_ptr spectator(new NetworkGroup()); // change to make_unique in c++14
+ spectator->name = "Spectator";
+ spectator->permissions = 0;
+ spectator->id = 0;
+ group_list.push_back(std::move(spectator));
+ std::unique_ptr fullaccess(new NetworkGroup()); // change to make_unique in c++14
+ fullaccess->name = "Full Access";
+ fullaccess->permissions = 1;
+ fullaccess->id = 1;
+ group_list.push_back(std::move(fullaccess));
+ SetDefaultGroup(0);
+
status = NETWORK_STATUS_READY;
return true;
}
@@ -467,6 +501,7 @@ void Network::Close()
client_connection_list.clear();
game_command_queue.clear();
player_list.clear();
+ group_list.clear();
#ifdef __WINDOWS__
if (wsa_initialized) {
@@ -540,6 +575,7 @@ bool Network::BeginServer(unsigned short port, const char* address)
NetworkPlayer* player = AddPlayer(gConfigNetwork.player_name);
player->flags |= NETWORK_PLAYER_FLAG_ISSERVER;
+ player->group = 1;
player_id = player->id;
printf("Ready for clients...\n");
@@ -760,12 +796,40 @@ void Network::UpdateClient()
}
}
-NetworkPlayer* Network::GetPlayerByID(int id) {
+std::vector>::iterator Network::GetPlayerIteratorByID(uint8 id)
+{
auto it = std::find_if(player_list.begin(), player_list.end(), [&id](std::unique_ptr const& player) { return player->id == id; });
if (it != player_list.end()) {
- return (*it).get();
+ return it;
}
- return 0;
+ return player_list.end();
+}
+
+NetworkPlayer* Network::GetPlayerByID(uint8 id)
+{
+ auto it = GetPlayerIteratorByID(id);
+ if (it != player_list.end()) {
+ return it->get();
+ }
+ return nullptr;
+}
+
+std::vector>::iterator Network::GetGroupIteratorByID(uint8 id)
+{
+ auto it = std::find_if(group_list.begin(), group_list.end(), [&id](std::unique_ptr const& group) { return group->id == id; });
+ if (it != group_list.end()) {
+ return it;
+ }
+ return group_list.end();
+}
+
+NetworkGroup* Network::GetGroupByID(uint8 id)
+{
+ auto it = GetGroupIteratorByID(id);
+ if (it != group_list.end()) {
+ return it->get();
+ }
+ return nullptr;
}
const char* Network::FormatChat(NetworkPlayer* fromplayer, const char* text)
@@ -958,6 +1022,18 @@ void Network::AdvertiseHeartbeat()
#endif
}
+uint8 Network::GetDefaultGroup()
+{
+ return default_group;
+}
+
+void Network::SetDefaultGroup(uint8 id)
+{
+ if (GetGroupByID(id)) {
+ default_group = id;
+ }
+}
+
void Network::Client_Send_AUTH(const char* name, const char* password)
{
std::unique_ptr packet = std::move(NetworkPacket::Allocate());
@@ -1063,7 +1139,7 @@ void Network::Server_Send_PLAYERLIST()
*packet << (uint32)NETWORK_COMMAND_PLAYERLIST << (uint32)player_list.size();
for (unsigned int i = 0; i < player_list.size(); i++) {
packet->WriteString((const char*)player_list[i]->name);
- *packet << player_list[i]->id << player_list[i]->flags;
+ *packet << player_list[i]->id << player_list[i]->flags << player_list[i]->group << player_list[i]->reserved;
}
SendPacketToClients(*packet);
}
@@ -1131,6 +1207,13 @@ void Network::Server_Send_GAMEINFO(NetworkConnection& connection)
connection.QueuePacket(std::move(packet));
}
+void Network::Server_Send_SHOWERROR(NetworkConnection& connection, rct_string_id title, rct_string_id message)
+{
+ std::unique_ptr packet = std::move(NetworkPacket::Allocate());
+ *packet << (uint32)NETWORK_COMMAND_SHOWERROR << title << message;
+ connection.QueuePacket(std::move(packet));
+}
+
bool Network::ProcessConnection(NetworkConnection& connection)
{
int packetStatus;
@@ -1171,7 +1254,6 @@ void Network::ProcessPacket(NetworkConnection& connection, NetworkPacket& packet
uint32 command;
packet >> command;
if (command < NETWORK_COMMAND_MAX) {
- // printf("RECV %s\n", NetworkCommandNames[command]);
switch (gNetwork.GetMode()) {
case NETWORK_MODE_SERVER:
if (server_command_handlers[command]) {
@@ -1198,7 +1280,13 @@ void Network::ProcessGameCommandQueue()
if (GetPlayerID() == gc.playerid) {
game_command_callback = game_command_callback_get_callback(gc.callback);
}
- game_do_command_p(gc.esi, (int*)&gc.eax, (int*)&gc.ebx, (int*)&gc.ecx, (int*)&gc.edx, (int*)&gc.esi, (int*)&gc.edi, (int*)&gc.ebp);
+ money32 cost = game_do_command_p(gc.esi, (int*)&gc.eax, (int*)&gc.ebx, (int*)&gc.ecx, (int*)&gc.edx, (int*)&gc.esi, (int*)&gc.edi, (int*)&gc.ebp);
+ if (cost != MONEY32_UNDEFINED) {
+ NetworkPlayer* player = GetPlayerByID(gc.playerid);
+ if (player) {
+ player->AddMoneySpent(cost);
+ }
+ }
game_command_queue.erase(game_command_queue.begin());
}
}
@@ -1235,7 +1323,7 @@ void Network::RemoveClient(std::unique_ptr& connection)
NetworkPlayer* Network::AddPlayer(const char* name)
{
- NetworkPlayer* addedplayer = 0;
+ NetworkPlayer* addedplayer = nullptr;
int newid = -1;
if (GetMode() == NETWORK_MODE_SERVER) {
// Find first unused player id
@@ -1251,6 +1339,7 @@ NetworkPlayer* Network::AddPlayer(const char* name)
if (newid != -1) {
std::unique_ptr player(new NetworkPlayer(name)); // change to make_unique in c++14
player->id = newid;
+ player->group = GetDefaultGroup();
addedplayer = player.get();
player_list.push_back(std::move(player));
if (GetMode() == NETWORK_MODE_SERVER) {
@@ -1439,24 +1528,40 @@ int Network::Server_Handle_GAMECMD(NetworkConnection& connection, NetworkPacket&
uint32 args[7];
uint8 playerid;
uint8 callback;
- if (connection.player) {
- playerid = connection.player->id;
+
+ if (!connection.player) {
+ return 0;
}
+
+ playerid = connection.player->id;
+
packet >> tick >> args[0] >> args[1] >> args[2] >> args[3] >> args[4] >> args[5] >> args[6] >> callback;
int commandCommand = args[4];
- // Don't let clients send pause or quit
- if (commandCommand != GAME_COMMAND_TOGGLE_PAUSE &&
- commandCommand != GAME_COMMAND_LOAD_OR_QUIT
- ) {
- // Run game command, and if it is successful send to clients
- money32 cost = game_do_command(args[0], args[1] | GAME_COMMAND_FLAG_NETWORKED, args[2], args[3], args[4], args[5], args[6]);
- if (cost != MONEY32_UNDEFINED) {
- Server_Send_GAMECMD(args[0], args[1], args[2], args[3], args[4], args[5], args[6], playerid, callback);
- }
+ // Check if player's group permission allows command to run
+ NetworkGroup* group = GetGroupByID(connection.player->group);
+ if (group && !group->CanRun(commandCommand)) {
+ Server_Send_SHOWERROR(connection, STR_CANT_DO_THIS, STR_PERMISSION_DENIED);
+ return 0;
}
+ // Don't let clients send pause or quit
+ if (commandCommand == GAME_COMMAND_TOGGLE_PAUSE ||
+ commandCommand == GAME_COMMAND_LOAD_OR_QUIT
+ ) {
+ return 0;
+ }
+
+
+ // Run game command, and if it is successful send to clients
+ money32 cost = game_do_command(args[0], args[1] | GAME_COMMAND_FLAG_NETWORKED, args[2], args[3], args[4], args[5], args[6]);
+ if (cost == MONEY32_UNDEFINED) {
+ return 0;
+ }
+ connection.player->AddMoneySpent(cost);
+ Server_Send_GAMECMD(args[0], args[1], args[2], args[3], args[4], args[5], args[6], playerid, callback);
+
return 1;
}
@@ -1473,19 +1578,39 @@ int Network::Client_Handle_TICK(NetworkConnection& connection, NetworkPacket& pa
int Network::Client_Handle_PLAYERLIST(NetworkConnection& connection, NetworkPacket& packet)
{
- player_list.clear();
uint32 size;
packet >> size;
+ std::vector ids;
for (unsigned int i = 0; i < size; i++) {
const char* name = packet.ReadString();
- NetworkPlayer* player = AddPlayer(name);
- if (player) {
- packet >> player->id >> player->flags;
- if (player->flags & NETWORK_PLAYER_FLAG_ISSERVER) {
- server_connection.player = player;
+ uint8 id;
+ uint8 flags;
+ uint8 group;
+ uint16 reserved;
+ packet >> id >> flags >> group >> reserved;
+ ids.push_back(id);
+ if (!GetPlayerByID(id)) {
+ NetworkPlayer* player = AddPlayer(name);
+ if (player) {
+ player->id = id;
+ player->flags = flags;
+ player->group = group;
+ player->reserved = reserved;
+ if (player->flags & NETWORK_PLAYER_FLAG_ISSERVER) {
+ server_connection.player = player;
+ }
}
}
}
+ // Remove any players that are not in newly received list
+ auto it = player_list.begin();
+ while (it != player_list.end()) {
+ if (std::find(ids.begin(), ids.end(), (*it)->id) == ids.end()) {
+ it = player_list.erase(it);
+ } else {
+ it++;
+ }
+ }
return 1;
}
@@ -1503,6 +1628,7 @@ int Network::Server_Handle_PING(NetworkConnection& connection, NetworkPacket& pa
}
if (connection.player) {
connection.player->ping = ping;
+ window_invalidate_by_number(WC_PLAYER, connection.player->id);
}
return 1;
}
@@ -1520,6 +1646,7 @@ int Network::Client_Handle_PINGLIST(NetworkConnection& connection, NetworkPacket
player->ping = ping;
}
}
+ window_invalidate_by_class(WC_PLAYER);
return 1;
}
@@ -1540,6 +1667,14 @@ int Network::Server_Handle_GAMEINFO(NetworkConnection& connection, NetworkPacket
return 1;
}
+int Network::Client_Handle_SHOWERROR(NetworkConnection& connection, NetworkPacket& packet)
+{
+ rct_string_id title, message;
+ packet >> title >> message;
+ window_error_open(title, message);
+ return 1;
+}
+
int network_init()
{
return gNetwork.Init();
@@ -1620,6 +1755,107 @@ int network_get_player_id(unsigned int index)
return gNetwork.player_list[index]->id;
}
+money32 network_get_player_money_spent(unsigned int index)
+{
+ return gNetwork.player_list[index]->money_spent;
+}
+
+void network_add_player_money_spent(unsigned int index, money32 cost)
+{
+ gNetwork.player_list[index]->AddMoneySpent(cost);
+}
+
+unsigned int network_get_player_commands_ran(unsigned int index)
+{
+ return gNetwork.player_list[index]->commands_ran;
+}
+
+int network_get_player_index(uint8 id)
+{
+ auto it = gNetwork.GetPlayerIteratorByID(id);
+ if(it == gNetwork.player_list.end()){
+ return -1;
+ }
+ return gNetwork.GetPlayerIteratorByID(id) - gNetwork.player_list.begin();
+}
+
+uint8 network_get_player_group(unsigned int index)
+{
+ return gNetwork.player_list[index]->group;
+}
+
+void network_set_player_group(unsigned int index, unsigned int groupindex)
+{
+ gNetwork.player_list[index]->group = gNetwork.group_list[groupindex]->id;
+}
+
+int network_get_group_index(uint8 id)
+{
+ auto it = gNetwork.GetGroupIteratorByID(id);
+ if(it == gNetwork.group_list.end()){
+ return -1;
+ }
+ return gNetwork.GetGroupIteratorByID(id) - gNetwork.group_list.begin();
+}
+
+uint8 network_get_group_id(unsigned int index)
+{
+ return gNetwork.group_list[index]->id;
+}
+
+int network_get_num_groups()
+{
+ return gNetwork.group_list.size();
+}
+
+const char* network_get_group_name(unsigned int index)
+{
+ return gNetwork.group_list[index]->name.c_str();
+}
+
+rct_string_id network_get_group_name_string_id(unsigned int index)
+{
+ if (gNetwork.group_list[index]->name_string_id == STR_NONE) {
+ gNetwork.group_list[index]->name_string_id = user_string_allocate(128, gNetwork.group_list[index]->name.c_str());
+ }
+ return gNetwork.group_list[index]->name_string_id;
+}
+
+void game_command_set_player_group(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp)
+{
+ uint8 playerid = (uint8)*ecx;
+ uint8 groupid = (uint8)*edx;
+ NetworkPlayer* player = gNetwork.GetPlayerByID(playerid);
+ if (!player) {
+ *ebx = MONEY32_UNDEFINED;
+ return;
+ }
+ if (!gNetwork.GetGroupByID(groupid)) {
+ *ebx = MONEY32_UNDEFINED;
+ return;
+ }
+ if (player->flags & NETWORK_PLAYER_FLAG_ISSERVER) {
+ RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_TITLE, uint16) = STR_CANT_DO_THIS;
+ *ebx = MONEY32_UNDEFINED;
+ return;
+ }
+ if (*ebx & GAME_COMMAND_FLAG_APPLY) {
+ player->group = groupid;
+ window_invalidate_by_number(WC_PLAYER, playerid);
+ }
+ *ebx = 0;
+}
+
+uint8 network_get_default_group()
+{
+ return gNetwork.GetDefaultGroup();
+}
+
+void network_set_default_group(uint8 id)
+{
+ gNetwork.SetDefaultGroup(id);
+}
+
void network_send_map()
{
gNetwork.Server_Send_MAP();
@@ -1680,6 +1916,20 @@ const char* network_get_player_name(unsigned int index) { return "local (OpenRCT
uint32 network_get_player_flags(unsigned int index) { return 0; }
int network_get_player_ping(unsigned int index) { return 0; }
int network_get_player_id(unsigned int index) { return 0; }
+money32 network_get_player_money_spent(unsigned int index) { return MONEY(0, 0); }
+void network_add_player_money_spent(unsigned int index, money32 cost) { }
+unsigned int network_get_player_commands_ran(unsigned int index) { return 0; }
+int network_get_player_index(uint8 id) { return -1; };
+uint8 network_get_player_group(unsigned int index) { return 0; }
+void network_set_player_group(unsigned int index, unsigned int groupindex) { }
+int network_get_group_index(uint8 id) { return -1; }
+uint8 network_get_group_id(unsigned int index) { return 0; }
+int network_get_num_groups() { return 0; }
+const char* network_get_group_name(unsigned int index) { return ""; };
+rct_string_id network_get_group_name_string_id(unsigned int index) { return -1; }
+void game_command_set_player_group(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) { }
+uint8 network_get_default_group() { return 0; }
+void network_set_default_group(uint8 id) { }
void network_send_chat(const char* text) {}
void network_send_password(const char* password) {}
void network_close() {}
diff --git a/src/network/network.h b/src/network/network.h
index 2c57c76c83..e5f9ba529e 100644
--- a/src/network/network.h
+++ b/src/network/network.h
@@ -145,10 +145,27 @@ class NetworkPlayer
{
public:
NetworkPlayer(const char* name);
+ void AddMoneySpent(money32 cost);
uint8 id;
uint8 name[32 + 1];
uint16 ping;
- uint32 flags;
+ uint8 flags;
+ uint8 group;
+ uint16 reserved;
+ money32 money_spent;
+ unsigned int commands_ran;
+};
+
+class NetworkGroup
+{
+public:
+ NetworkGroup();
+ ~NetworkGroup();
+ bool CanRun(int command);
+ std::string name;
+ uint32 permissions;
+ uint8 id;
+ rct_string_id name_string_id;
};
class NetworkConnection
@@ -220,7 +237,10 @@ public:
uint32 GetServerTick();
uint8 GetPlayerID();
void Update();
- NetworkPlayer* GetPlayerByID(int id);
+ std::vector>::iterator GetPlayerIteratorByID(uint8 id);
+ NetworkPlayer* GetPlayerByID(uint8 id);
+ std::vector>::iterator GetGroupIteratorByID(uint8 id);
+ NetworkGroup* GetGroupByID(uint8 id);
const char* FormatChat(NetworkPlayer* fromplayer, const char* text);
void SendPacketToClients(NetworkPacket& packet, bool front = false);
bool CheckSRAND(uint32 tick, uint32 srand0);
@@ -229,6 +249,8 @@ public:
void ShutdownClient();
void AdvertiseRegister();
void AdvertiseHeartbeat();
+ uint8 GetDefaultGroup();
+ void SetDefaultGroup(uint8 id);
void Client_Send_AUTH(const char* name, const char* password);
void Server_Send_AUTH(NetworkConnection& connection);
@@ -244,8 +266,10 @@ public:
void Server_Send_PINGLIST();
void Server_Send_SETDISCONNECTMSG(NetworkConnection& connection, const char* msg);
void Server_Send_GAMEINFO(NetworkConnection& connection);
+ void Server_Send_SHOWERROR(NetworkConnection& connection, rct_string_id title, rct_string_id message);
std::vector> player_list;
+ std::vector> group_list;
private:
bool ProcessConnection(NetworkConnection& connection);
@@ -255,7 +279,7 @@ private:
void RemoveClient(std::unique_ptr& connection);
NetworkPlayer* AddPlayer(const char* name);
void PrintError();
- const char *GetMasterServerUrl();
+ const char* GetMasterServerUrl();
std::string GenerateAdvertiseKey();
struct GameCommand
@@ -294,6 +318,7 @@ private:
std::string advertise_key;
int advertise_status;
uint32 last_heartbeat_time;
+ uint8 default_group;
void UpdateServer();
void UpdateClient();
@@ -315,6 +340,7 @@ private:
int Client_Handle_PINGLIST(NetworkConnection& connection, NetworkPacket& packet);
int Client_Handle_SETDISCONNECTMSG(NetworkConnection& connection, NetworkPacket& packet);
int Server_Handle_GAMEINFO(NetworkConnection& connection, NetworkPacket& packet);
+ int Client_Handle_SHOWERROR(NetworkConnection& connection, NetworkPacket& packet);
};
#endif // __cplusplus
@@ -340,6 +366,20 @@ const char* network_get_player_name(unsigned int index);
uint32 network_get_player_flags(unsigned int index);
int network_get_player_ping(unsigned int index);
int network_get_player_id(unsigned int index);
+money32 network_get_player_money_spent(unsigned int index);
+void network_add_player_money_spent(unsigned int index, money32 cost);
+unsigned int network_get_player_commands_ran(unsigned int index);
+int network_get_player_index(uint8 id);
+uint8 network_get_player_group(unsigned int index);
+void network_set_player_group(unsigned int index, unsigned int groupindex);
+int network_get_group_index(uint8 id);
+uint8 network_get_group_id(unsigned int index);
+int network_get_num_groups();
+const char* network_get_group_name(unsigned int index);
+rct_string_id network_get_group_name_string_id(unsigned int index);
+void game_command_set_player_group(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp);
+uint8 network_get_default_group();
+void network_set_default_group(uint8 id);
void network_send_map();
void network_send_chat(const char* text);
diff --git a/src/windows/multiplayer.c b/src/windows/multiplayer.c
new file mode 100644
index 0000000000..5c7b6c64f4
--- /dev/null
+++ b/src/windows/multiplayer.c
@@ -0,0 +1,536 @@
+/*****************************************************************************
+ * 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"
+#include "../util/util.h"
+#include "dropdown.h"
+
+enum {
+ WINDOW_MULTIPLAYER_PAGE_PLAYERS,
+ WINDOW_MULTIPLAYER_PAGE_GROUPS
+};
+
+enum WINDOW_MULTIPLAYER_WIDGET_IDX {
+ WIDX_BACKGROUND,
+ WIDX_TITLE,
+ WIDX_CLOSE,
+ WIDX_CONTENT_PANEL,
+ WIDX_TAB1,
+ WIDX_TAB2,
+
+ WIDX_LIST = 6,
+
+ WIDX_DEFAULT_GROUP = 6,
+ WIDX_DEFAULT_GROUP_DROPDOWN,
+};
+
+static rct_widget window_multiplayer_players_widgets[] = {
+ { WWT_FRAME, 0, 0, 339, 0, 239, 0x0FFFFFFFF, STR_NONE }, // panel / background
+ { WWT_CAPTION, 0, 1, 338, 1, 14, STR_MULTIPLAYER, STR_WINDOW_TITLE_TIP }, // title bar
+ { WWT_CLOSEBOX, 0, 327, 337, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, // close x button
+ { WWT_RESIZE, 1, 0, 339, 43, 239, 0x0FFFFFFFF, STR_NONE }, // content panel
+ { WWT_TAB, 1, 3, 33, 17, 43, 0x02000144E, STR_PLAYERS_TIP }, // tab
+ { WWT_TAB, 1, 3, 33, 17, 43, 0x02000144E, STR_GROUPS_TIP }, // tab
+ { WWT_SCROLL, 1, 3, 336, 60, 236, 2, STR_NONE }, // list
+ { WIDGETS_END }
+};
+
+static rct_widget window_multiplayer_groups_widgets[] = {
+ { WWT_FRAME, 0, 0, 339, 0, 239, 0x0FFFFFFFF, STR_NONE }, // panel / background
+ { WWT_CAPTION, 0, 1, 338, 1, 14, STR_MULTIPLAYER, STR_WINDOW_TITLE_TIP }, // title bar
+ { WWT_CLOSEBOX, 0, 327, 337, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP }, // close x button
+ { WWT_RESIZE, 1, 0, 339, 43, 239, 0x0FFFFFFFF, STR_NONE }, // content panel
+ { WWT_TAB, 1, 3, 33, 17, 43, 0x02000144E, STR_PLAYERS_TIP }, // tab
+ { WWT_TAB, 1, 3, 33, 17, 43, 0x02000144E, STR_GROUPS_TIP }, // tab
+ { WWT_DROPDOWN, 1, 141, 315, 46, 57, 0x0FFFFFFFF, STR_NONE }, // default group
+ { WWT_DROPDOWN_BUTTON, 1, 305, 315, 47, 56, 876, STR_NONE }, //
+ { WIDGETS_END }
+};
+
+static rct_widget *window_multiplayer_page_widgets[] = {
+ window_multiplayer_players_widgets,
+ window_multiplayer_groups_widgets
+};
+
+const uint64 window_multiplayer_page_enabled_widgets[] = {
+ (1 << WIDX_CLOSE) | (1 << WIDX_TAB1) | (1 << WIDX_TAB2),
+ (1 << WIDX_CLOSE) | (1 << WIDX_TAB1) | (1 << WIDX_TAB2) | (1 << WIDX_DEFAULT_GROUP) | (1 << WIDX_DEFAULT_GROUP_DROPDOWN)
+};
+
+static void window_multiplayer_players_mouseup(rct_window *w, int widgetIndex);
+static void window_multiplayer_players_resize(rct_window *w);
+static void window_multiplayer_players_mousedown(int widgetIndex, rct_window* w, rct_widget* widget);
+static void window_multiplayer_players_update(rct_window *w);
+static void window_multiplayer_players_scrollgetsize(rct_window *w, int scrollIndex, int *width, int *height);
+static void window_multiplayer_players_scrollmousedown(rct_window *w, int scrollIndex, int x, int y);
+static void window_multiplayer_players_scrollmouseover(rct_window *w, int scrollIndex, int x, int y);
+static void window_multiplayer_players_invalidate(rct_window *w);
+static void window_multiplayer_players_paint(rct_window *w, rct_drawpixelinfo *dpi);
+static void window_multiplayer_players_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int scrollIndex);
+
+static void window_multiplayer_groups_mouseup(rct_window *w, int widgetIndex);
+static void window_multiplayer_groups_resize(rct_window *w);
+static void window_multiplayer_groups_mousedown(int widgetIndex, rct_window* w, rct_widget* widget);
+static void window_multiplayer_groups_dropdown(rct_window *w, int widgetIndex, int dropdownIndex);
+static void window_multiplayer_groups_update(rct_window *w);
+static void window_multiplayer_groups_invalidate(rct_window *w);
+static void window_multiplayer_groups_paint(rct_window *w, rct_drawpixelinfo *dpi);
+
+static rct_window_event_list window_multiplayer_players_events = {
+ NULL,
+ window_multiplayer_players_mouseup,
+ window_multiplayer_players_resize,
+ window_multiplayer_players_mousedown,
+ NULL,
+ NULL,
+ window_multiplayer_players_update,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ window_multiplayer_players_scrollgetsize,
+ window_multiplayer_players_scrollmousedown,
+ NULL,
+ window_multiplayer_players_scrollmouseover,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ window_multiplayer_players_invalidate,
+ window_multiplayer_players_paint,
+ window_multiplayer_players_scrollpaint
+};
+
+static rct_window_event_list window_multiplayer_groups_events = {
+ NULL,
+ window_multiplayer_groups_mouseup,
+ window_multiplayer_groups_resize,
+ window_multiplayer_groups_mousedown,
+ window_multiplayer_groups_dropdown,
+ NULL,
+ window_multiplayer_groups_update,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ window_multiplayer_groups_invalidate,
+ window_multiplayer_groups_paint,
+ NULL,
+};
+
+static rct_window_event_list *window_multiplayer_page_events[] = {
+ &window_multiplayer_players_events,
+ &window_multiplayer_groups_events
+};
+
+const int window_multiplayer_animation_divisor[] = { 4, 4 };
+const int window_multiplayer_animation_frames[] = { 8, 16 };
+
+static void window_multiplayer_players_refresh_list(rct_window *w);
+static void window_multiplayer_draw_tab_images(rct_window *w, rct_drawpixelinfo *dpi);
+static void window_multiplayer_set_page(rct_window* w, int page);
+
+void window_multiplayer_open()
+{
+ rct_window* window;
+
+ // Check if window is already open
+ window = window_bring_to_front_by_class(WC_MULTIPLAYER);
+ if (window != NULL)
+ return;
+
+ window = window_create_auto_pos(320, 144, &window_multiplayer_players_events, WC_MULTIPLAYER, WF_10 | WF_RESIZABLE);
+
+ window_multiplayer_set_page(window, WINDOW_MULTIPLAYER_PAGE_PLAYERS);
+
+ window->page = WINDOW_MULTIPLAYER_PAGE_PLAYERS;
+ window->list_information_type = 0;
+ window->colours[0] = 7;
+ window->colours[1] = 7;
+ window->colours[2] = 7;
+}
+
+static void window_multiplayer_set_page(rct_window* w, int page){
+ w->page = page;
+ w->frame_no = 0;
+ w->no_list_items = 0;
+ w->selected_list_item = -1;
+
+ w->enabled_widgets = window_multiplayer_page_enabled_widgets[page];
+ w->hold_down_widgets = 0;
+ w->event_handlers = window_multiplayer_page_events[page];
+ w->pressed_widgets = 0;
+ w->widgets = window_multiplayer_page_widgets[page];
+ window_event_resize_call(w);
+ window_event_invalidate_call(w);
+ window_init_scroll_widgets(w);
+ window_invalidate(w);
+}
+
+static void window_multiplayer_anchor_border_widgets(rct_window *w)
+{
+ w->widgets[WIDX_BACKGROUND].right = w->width - 1;
+ w->widgets[WIDX_BACKGROUND].bottom = w->height - 1;
+ w->widgets[WIDX_TITLE].right = w->width - 2;
+ w->widgets[WIDX_CONTENT_PANEL].right = w->width - 1;
+ w->widgets[WIDX_CONTENT_PANEL].bottom = w->height - 1;
+ w->widgets[WIDX_CLOSE].left = w->width - 13;
+ w->widgets[WIDX_CLOSE].right = w->width - 3;
+}
+
+static void window_multiplayer_set_pressed_tab(rct_window *w)
+{
+ for (int i = 0; i < 2; i++) {
+ w->pressed_widgets &= ~(1 << (WIDX_TAB1 + i));
+ }
+ w->pressed_widgets |= 1LL << (WIDX_TAB1 + w->page);
+}
+
+static void window_multiplayer_groups_show_defaultgroup_dropdown(rct_window *w, rct_widget *widget)
+{
+ rct_widget *dropdownWidget;
+ int numItems, i;
+
+ dropdownWidget = widget - 1;
+
+ numItems = network_get_num_groups();
+
+ window_dropdown_show_text_custom_width(
+ w->x + dropdownWidget->left,
+ w->y + dropdownWidget->top,
+ dropdownWidget->bottom - dropdownWidget->top + 1,
+ w->colours[1],
+ 0,
+ numItems,
+ widget->right - dropdownWidget->left
+ );
+
+ for (i = 0; i < network_get_num_groups(); i++) {
+ gDropdownItemsFormat[i] = 1142;
+ gDropdownItemsArgs[i] = network_get_group_name_string_id(i);
+ }
+
+ dropdown_set_checked(network_get_group_index(network_get_default_group()), true);
+}
+
+static void window_multiplayer_players_mouseup(rct_window *w, int widgetIndex)
+{
+ switch (widgetIndex) {
+ case WIDX_CLOSE:
+ window_close(w);
+ break;
+ }
+}
+
+static void window_multiplayer_players_resize(rct_window *w)
+{
+ window_set_resize(w, 320, 124, 500, 450);
+
+ window_multiplayer_players_refresh_list(w);
+}
+
+static void window_multiplayer_players_mousedown(int widgetIndex, rct_window* w, rct_widget* widget)
+{
+ switch (widgetIndex) {
+ case WIDX_TAB1:
+ case WIDX_TAB2:
+ if (w->page != widgetIndex - WIDX_TAB1) {
+ window_multiplayer_set_page(w, widgetIndex - WIDX_TAB1);
+ }
+ break;
+ }
+}
+
+static void window_multiplayer_players_update(rct_window *w)
+{
+ w->frame_no++;
+ widget_invalidate(w, WIDX_TAB1 + w->page);
+}
+
+static void window_multiplayer_players_scrollgetsize(rct_window *w, int scrollIndex, int *width, int *height)
+{
+ int i;
+
+ if (w->selected_list_item != -1) {
+ w->selected_list_item = -1;
+ window_invalidate(w);
+ }
+
+ *height = network_get_num_players() * 10;
+ i = *height - window_multiplayer_players_widgets[WIDX_LIST].bottom + window_multiplayer_players_widgets[WIDX_LIST].top + 21;
+ if (i < 0)
+ i = 0;
+ if (i < w->scrolls[0].v_top) {
+ w->scrolls[0].v_top = i;
+ window_invalidate(w);
+ }
+}
+
+static void window_multiplayer_players_scrollmousedown(rct_window *w, int scrollIndex, int x, int y)
+{
+ int index;
+
+ index = y / 10;
+ if (index >= w->no_list_items)
+ return;
+
+ w->selected_list_item = index;
+ window_invalidate(w);
+
+ rct_widget *listWidget = &w->widgets[WIDX_LIST];
+ int ddx = w->x + listWidget->left + x - w->scrolls[0].h_left;
+ int ddy = w->y + listWidget->top + y - w->scrolls[0].v_top;
+
+ window_player_open(network_get_player_id(index));
+}
+
+static void window_multiplayer_players_scrollmouseover(rct_window *w, int scrollIndex, int x, int y)
+{
+ int index;
+
+ index = y / 10;
+ if (index >= w->no_list_items)
+ return;
+
+ w->selected_list_item = index;
+ window_invalidate(w);
+}
+
+static void window_multiplayer_players_invalidate(rct_window *w)
+{
+ window_multiplayer_set_pressed_tab(w);
+ window_multiplayer_anchor_border_widgets(w);
+ window_multiplayer_players_widgets[WIDX_LIST].right = w->width - 4;
+ window_multiplayer_players_widgets[WIDX_LIST].bottom = w->height - 0x0F;
+ window_align_tabs(w, WIDX_TAB1, WIDX_TAB2);
+}
+
+static void window_multiplayer_players_paint(rct_window *w, rct_drawpixelinfo *dpi)
+{
+ rct_string_id stringId;
+ int x, y;
+
+ window_draw_widgets(w, dpi);
+ window_multiplayer_draw_tab_images(w, dpi);
+
+ // Columns
+ gfx_draw_string_left(dpi, STR_PLAYER, NULL, w->colours[2], w->x + 6, 58 - 12 + w->y + 1);
+ gfx_draw_string_left(dpi, STR_GROUP, NULL, w->colours[2], w->x + 180, 58 - 12 + w->y + 1);
+ gfx_draw_string_left(dpi, STR_PING, NULL, w->colours[2], w->x + 263, 58 - 12 + w->y + 1);
+
+ // Number of players
+ stringId = w->no_list_items == 1 ? STR_X_PLAYER : STR_X_PLAYERS;
+ x = w->x + 4;
+ y = w->y + w->widgets[WIDX_LIST].bottom + 2;
+ gfx_draw_string_left(dpi, stringId, &w->no_list_items, w->colours[2], x, y);
+}
+
+static void window_multiplayer_players_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int scrollIndex)
+{
+ int y;
+
+ gfx_fill_rect(dpi, dpi->x, dpi->y, dpi->x + dpi->width - 1, dpi->y + dpi->height - 1, ColourMapA[w->colours[1]].mid_light);
+
+ y = 0;
+ for (int i = 0; i < network_get_num_players(); i++) {
+ if (y > dpi->y + dpi->height) {
+ break;
+ }
+
+ if (y + 11 >= dpi->y) {
+ char buffer[300];
+
+ // Draw player name
+ char* lineCh = buffer;
+ int colour = 0;
+ if (i == w->selected_list_item) {
+ gfx_fill_rect(dpi, 0, y, 800, y + 9, 0x02000031);
+ safe_strcpy(&buffer[0], network_get_player_name(i), sizeof(buffer));
+ colour = w->colours[2];
+ } else {
+ if (network_get_player_flags(i) & NETWORK_PLAYER_FLAG_ISSERVER) {
+ lineCh = utf8_write_codepoint(lineCh, FORMAT_BABYBLUE);
+ } else {
+ lineCh = utf8_write_codepoint(lineCh, FORMAT_BLACK);
+ }
+ safe_strcpy(lineCh, network_get_player_name(i), sizeof(buffer) - (lineCh - buffer));
+ }
+ gfx_clip_string(buffer, 230);
+ gfx_draw_string(dpi, buffer, colour, 0, y - 1);
+
+ // Draw group name
+ lineCh = buffer;
+ int group = network_get_group_index(network_get_player_group(i));
+ if (group != -1) {
+ lineCh = utf8_write_codepoint(lineCh, FORMAT_BLACK);
+ safe_strcpy(lineCh, network_get_group_name(group), sizeof(buffer) - (lineCh - buffer));
+ gfx_clip_string(buffer, 80);
+ gfx_draw_string(dpi, buffer, colour, 173, y - 1);
+ }
+
+ // Draw ping
+ lineCh = buffer;
+ int ping = network_get_player_ping(i);
+ if (ping <= 100) {
+ lineCh = utf8_write_codepoint(lineCh, FORMAT_GREEN);
+ } else
+ if (ping <= 250) {
+ lineCh = utf8_write_codepoint(lineCh, FORMAT_YELLOW);
+ } else {
+ lineCh = utf8_write_codepoint(lineCh, FORMAT_RED);
+ }
+ sprintf(lineCh, "%d ms", ping);
+ gfx_draw_string(dpi, buffer, colour, 256, y - 1);
+ }
+ y += 10;
+ }
+}
+
+static void window_multiplayer_groups_mouseup(rct_window *w, int widgetIndex)
+{
+ switch (widgetIndex) {
+ case WIDX_CLOSE:
+ window_close(w);
+ break;
+ }
+}
+
+static void window_multiplayer_groups_resize(rct_window *w)
+{
+ window_set_resize(w, 320, 61, 320, 61);
+}
+
+static void window_multiplayer_groups_mousedown(int widgetIndex, rct_window* w, rct_widget* widget)
+{
+ switch (widgetIndex) {
+ case WIDX_TAB1:
+ case WIDX_TAB2:
+ if (w->page != widgetIndex - WIDX_TAB1) {
+ window_multiplayer_set_page(w, widgetIndex - WIDX_TAB1);
+ }
+ break;
+ case WIDX_DEFAULT_GROUP_DROPDOWN:
+ window_multiplayer_groups_show_defaultgroup_dropdown(w, widget);
+ break;
+ }
+}
+
+static void window_multiplayer_groups_dropdown(rct_window *w, int widgetIndex, int dropdownIndex)
+{
+ if (dropdownIndex == -1) {
+ return;
+ }
+ if (network_get_mode() == NETWORK_MODE_SERVER) {
+ network_set_default_group(network_get_group_id(dropdownIndex));
+ }
+ window_invalidate(w);
+}
+
+static void window_multiplayer_groups_update(rct_window *w)
+{
+ w->frame_no++;
+ widget_invalidate(w, WIDX_TAB1 + w->page);
+}
+
+static void window_multiplayer_groups_invalidate(rct_window *w)
+{
+ window_multiplayer_set_pressed_tab(w);
+ window_multiplayer_anchor_border_widgets(w);
+ window_align_tabs(w, WIDX_TAB1, WIDX_TAB2);
+}
+
+static void window_multiplayer_groups_paint(rct_window *w, rct_drawpixelinfo *dpi)
+{
+ window_draw_widgets(w, dpi);
+ window_multiplayer_draw_tab_images(w, dpi);
+
+ rct_widget* widget = &window_multiplayer_groups_widgets[WIDX_DEFAULT_GROUP];
+ int group = network_get_group_index(network_get_default_group());
+ if (group != -1) {
+ RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint16) = network_get_group_name_string_id(group);
+ gfx_draw_string_centred(
+ dpi,
+ 1193,
+ w->x + (widget->left + widget->right - 11) / 2,
+ w->y + widget->top,
+ 0,
+ (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS
+ );
+ }
+
+ gfx_draw_string_left(dpi, STR_DEFAULT_GROUP, NULL, w->colours[2], w->x + 6, 58 - 12 + w->y + 1);
+}
+
+static void window_multiplayer_players_refresh_list(rct_window *w)
+{
+ w->no_list_items = network_get_num_players();
+ w->list_item_positions[0] = 0;
+
+ w->selected_list_item = -1;
+ window_invalidate(w);
+}
+
+static void window_multiplayer_draw_tab_image(rct_window *w, rct_drawpixelinfo *dpi, int page, int spriteIndex)
+{
+ int widgetIndex = WIDX_TAB1 + page;
+
+ if (!(w->disabled_widgets & (1LL << widgetIndex))) {
+ if (w->page == page) {
+ int numFrames = window_multiplayer_animation_frames[w->page];
+ if (numFrames > 1) {
+ int frame = w->frame_no / window_multiplayer_animation_divisor[w->page];
+ spriteIndex += (frame % numFrames);
+ }
+ }
+
+ gfx_draw_sprite(dpi, spriteIndex, w->x + w->widgets[widgetIndex].left, w->y + w->widgets[widgetIndex].top, 0);
+ }
+}
+
+static void window_multiplayer_draw_tab_images(rct_window *w, rct_drawpixelinfo *dpi)
+{
+ window_multiplayer_draw_tab_image(w, dpi, 0, SPR_TAB_GUESTS_0);
+ window_multiplayer_draw_tab_image(w, dpi, 1, SPR_TAB_OBJECTIVE_0);
+}
diff --git a/src/windows/player.c b/src/windows/player.c
new file mode 100644
index 0000000000..06ab25f34a
--- /dev/null
+++ b/src/windows/player.c
@@ -0,0 +1,377 @@
+/*****************************************************************************
+* Copyright (c) 2014 Ted John, Duncan Frost
+* 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 "../addresses.h"
+#include "../config.h"
+#include "../game.h"
+#include "../input.h"
+#include "../management/marketing.h"
+#include "../network/network.h"
+#include "../peep/peep.h"
+#include "../peep/staff.h"
+#include "../ride/ride.h"
+#include "../ride/ride_data.h"
+#include "../scenario.h"
+#include "../localisation/localisation.h"
+#include "../sprites.h"
+#include "../interface/themes.h"
+#include "../interface/viewport.h"
+#include "../interface/widget.h"
+#include "../interface/window.h"
+#include "../util/util.h"
+#include "../world/footpath.h"
+#include "../world/map.h"
+#include "../world/sprite.h"
+#include "dropdown.h"
+#include "error.h"
+
+enum WINDOW_PLAYER_PAGE {
+ WINDOW_PLAYER_OVERVIEW
+};
+
+enum WINDOW_PLAYER_WIDGET_IDX {
+ WIDX_BACKGROUND,
+ WIDX_TITLE,
+ WIDX_CLOSE,
+ WIDX_PAGE_BACKGROUND,
+ WIDX_TAB_1,
+ WIDX_GROUP,
+ WIDX_GROUP_DROPDOWN,
+ WIDX_KICK
+};
+
+rct_widget window_player_overview_widgets[] = {
+ { WWT_FRAME, 0, 0, 191, 0, 156, 0x0FFFFFFFF, STR_NONE }, // Panel / Background
+ { WWT_CAPTION, 0, 1, 190, 1, 14, 865, STR_WINDOW_TITLE_TIP }, // Title
+ { WWT_CLOSEBOX, 0, 179, 189, 2, 13, 824, STR_CLOSE_WINDOW_TIP }, // Close x button
+ { WWT_RESIZE, 1, 0, 191, 43, 156, 0x0FFFFFFFF, STR_NONE }, // Resize
+ { WWT_TAB, 1, 3, 33, 17, 43, 0x2000144E, STR_NONE }, // Tab 1
+ { WWT_DROPDOWN, 1, 3, 177, 46, 57, 0x0FFFFFFFF, STR_NONE }, // Permission group
+ { WWT_DROPDOWN_BUTTON, 1, 167, 177, 47, 56, 876, STR_NONE }, //
+ { WWT_FLATBTN, 1, 179, 190, 45, 68, SPR_DEMOLISH, STR_KICK_PLAYER_TIP }, // Kick button
+ { WIDGETS_END },
+};
+
+rct_widget *window_player_page_widgets[] = {
+ window_player_overview_widgets
+};
+
+void window_player_set_page(rct_window* w, int page);
+
+void window_player_overview_close(rct_window *w);
+void window_player_overview_mouse_up(rct_window *w, int widgetIndex);
+void window_player_overview_resize(rct_window *w);
+void window_player_overview_mouse_down(int widgetIndex, rct_window *w, rct_widget *widget);
+void window_player_overview_dropdown(rct_window *w, int widgetIndex, int dropdownIndex);
+void window_player_overview_update(rct_window* w);
+void window_player_overview_invalidate(rct_window *w);
+void window_player_overview_paint(rct_window *w, rct_drawpixelinfo *dpi);
+
+static rct_window_event_list window_player_overview_events = {
+ window_player_overview_close,
+ window_player_overview_mouse_up,
+ window_player_overview_resize,
+ window_player_overview_mouse_down,
+ window_player_overview_dropdown,
+ NULL,
+ window_player_overview_update,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ window_player_overview_invalidate,
+ window_player_overview_paint,
+ NULL
+};
+
+static rct_window_event_list *window_player_page_events[] = {
+ &window_player_overview_events
+};
+
+uint32 window_player_page_enabled_widgets[] = {
+ (1 << WIDX_CLOSE) |
+ (1 << WIDX_TAB_1) |
+ (1 << WIDX_GROUP) |
+ (1 << WIDX_GROUP_DROPDOWN) |
+ (1 << WIDX_KICK)
+};
+
+void window_player_open(uint8 id){
+ rct_window* window;
+
+ int player = network_get_player_index(id);
+ window = window_bring_to_front_by_number(WC_PLAYER, id);
+ if (window == NULL){
+ window = window_create_auto_pos(210, 134, &window_player_overview_events, WC_PLAYER, WF_RESIZABLE);
+ window->widgets = window_player_overview_widgets;
+ window->enabled_widgets = window_player_page_enabled_widgets[0];
+ window->number = id;
+ window->page = 0;
+ window->viewport_focus_coordinates.y = 0;
+ window->frame_no = 0;
+ window->list_information_type = 0;
+ window->picked_peep_frame = 0;
+ window->highlighted_item = 0;
+ window->min_width = 210;
+ window->min_height = 134;
+ window->max_width = 500;
+ window->max_height = 450;
+ window->no_list_items = 0;
+ window->selected_list_item = -1;
+
+ window->viewport_focus_coordinates.y = -1;
+ window->error.var_480 = user_string_allocate(128, network_get_player_name(player)); // repurposing var_480 to store this
+ }
+
+ window->page = 0;
+ window_invalidate(window);
+
+ window->widgets = window_player_page_widgets[WINDOW_PLAYER_OVERVIEW];
+ window->enabled_widgets = window_player_page_enabled_widgets[WINDOW_PLAYER_OVERVIEW];
+ window->hold_down_widgets = 0;
+ window->event_handlers = window_player_page_events[WINDOW_PLAYER_OVERVIEW];
+ window->pressed_widgets = 0;
+
+ window_init_scroll_widgets(window);
+}
+
+void window_player_overview_show_group_dropdown(rct_window *w, rct_widget *widget)
+{
+ rct_widget *dropdownWidget;
+ int numItems, i;
+ int player = network_get_player_index((uint8)w->number);
+ if (player == -1) {
+ return;
+ }
+
+ dropdownWidget = widget - 1;
+
+ numItems = network_get_num_groups();
+
+ window_dropdown_show_text_custom_width(
+ w->x + dropdownWidget->left,
+ w->y + dropdownWidget->top,
+ dropdownWidget->bottom - dropdownWidget->top + 1,
+ w->colours[1],
+ 0,
+ numItems,
+ widget->right - dropdownWidget->left
+ );
+
+ for (i = 0; i < network_get_num_groups(); i++) {
+ gDropdownItemsFormat[i] = 1142;
+ gDropdownItemsArgs[i] = network_get_group_name_string_id(i);
+ }
+
+ dropdown_set_checked(network_get_group_index(network_get_player_group(player)), true);
+}
+
+void window_player_overview_close(rct_window *w)
+{
+ if (w->error.var_480){
+ user_string_free(w->error.var_480);
+ w->error.var_480 = 0;
+ }
+}
+
+void window_player_overview_mouse_up(rct_window *w, int widgetIndex)
+{
+ switch(widgetIndex){
+ case WIDX_CLOSE:
+ window_close(w);
+ break;
+ case WIDX_TAB_1:
+ window_player_set_page(w, widgetIndex - WIDX_TAB_1);
+ break;
+ case WIDX_KICK:
+ network_kick_player(w->number);
+ break;
+ }
+}
+
+void window_player_overview_mouse_down(int widgetIndex, rct_window *w, rct_widget *widget)
+{
+ switch(widgetIndex){
+ case WIDX_GROUP_DROPDOWN:
+ window_player_overview_show_group_dropdown(w, widget);
+ break;
+ }
+}
+
+void window_player_overview_dropdown(rct_window *w, int widgetIndex, int dropdownIndex)
+{
+ int player = network_get_player_index((uint8)w->number);
+ if (player == -1) {
+ return;
+ }
+ if (dropdownIndex == -1) {
+ return;
+ }
+ int group = network_get_group_id(dropdownIndex);
+ game_do_command(0, GAME_COMMAND_FLAG_APPLY, w->number, group, GAME_COMMAND_SET_PLAYER_GROUP, 0, 0);
+ window_invalidate(w);
+}
+
+void window_player_overview_resize(rct_window *w){
+ window_set_resize(w, 210, 134, 210, 134);
+}
+
+void window_player_overview_update(rct_window* w){
+ int var_496 = w->highlighted_item >> 16;
+ var_496++;
+ var_496 %= 24;
+ w->highlighted_item &= 0x0000FFFF;
+ w->highlighted_item |= var_496 << 16;
+
+ widget_invalidate(w, WIDX_TAB_1);
+
+ w->list_information_type += 2;
+
+ if ((w->highlighted_item & 0xFFFF) == 0xFFFF)
+ w->highlighted_item &= 0xFFFF0000;
+ else
+ w->highlighted_item++;
+
+ if(network_get_player_index((uint8)w->number) == -1) {
+ window_close(w);
+ }
+}
+
+void window_player_set_page(rct_window* w, int page){
+ w->page = page;
+ w->frame_no = 0;
+ w->no_list_items = 0;
+ w->selected_list_item = -1;
+
+ w->enabled_widgets = window_player_page_enabled_widgets[page];
+ w->hold_down_widgets = 0;
+ w->event_handlers = window_player_page_events[page];
+ w->pressed_widgets = 0;
+ w->widgets = window_player_page_widgets[page];
+ window_invalidate(w);
+ window_event_resize_call(w);
+ window_event_invalidate_call(w);
+ window_init_scroll_widgets(w);
+ window_invalidate(w);
+}
+
+void window_player_overview_tab_paint(rct_window* w, rct_drawpixelinfo* dpi){
+ if (w->disabled_widgets & (1<widgets[WIDX_TAB_1];
+ int x = widget->left + w->x;
+ int y = widget->top + w->y;
+
+ int image_id = SPR_PEEP_LARGE_FACE_NORMAL;
+ gfx_draw_sprite(dpi, image_id, x, y, 0);
+}
+
+void window_player_overview_paint(rct_window *w, rct_drawpixelinfo *dpi)
+{
+ window_draw_widgets(w, dpi);
+ window_player_overview_tab_paint(w, dpi);
+
+ int player = network_get_player_index((uint8)w->number);
+ if (player == -1) {
+ return;
+ }
+
+ rct_widget* widget = &window_player_overview_widgets[WIDX_GROUP];
+ RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint16) = network_get_group_name_string_id(network_get_player_group(player));
+
+ gfx_draw_string_centred(
+ dpi,
+ 1193,
+ w->x + (widget->left + widget->right - 11) / 2,
+ w->y + widget->top,
+ 0,
+ (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS
+ );
+
+ int x = w->x + window_player_overview_widgets[WIDX_PAGE_BACKGROUND].left + 4;
+ int y = w->y + window_player_overview_widgets[WIDX_PAGE_BACKGROUND].top + 4 + 13;
+
+ RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint16) = STR_PING;
+ gfx_draw_string_left(dpi, STR_WINDOW_COLOUR_2_STRING, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS, 0, x, y);
+ char ping[64];
+ sprintf(ping, "%d ms", network_get_player_ping(player));
+ gfx_draw_string(dpi, ping, w->colours[2], x + 60, y);
+
+ y += 20;
+ gfx_fill_rect_inset(dpi, x, y - 6, x + 177, y - 5, w->colours[1], 32);
+
+ RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint32) = network_get_player_commands_ran(player);
+ gfx_draw_string_left(dpi, STR_COMMANDS_RAN, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS, 0,x, y);
+
+ y += 10;
+
+ RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint32) = network_get_player_money_spent(player);
+ gfx_draw_string_left(dpi, STR_MONEY_SPENT, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS, 0,x, y);
+}
+
+void window_player_overview_invalidate(rct_window *w)
+{
+ if (window_player_page_widgets[w->page] != w->widgets) {
+ w->widgets = window_player_page_widgets[w->page];
+ window_init_scroll_widgets(w);
+ }
+
+ w->colours[0] = 7;
+ w->colours[1] = 7;
+ w->colours[2] = 7;
+
+ w->pressed_widgets &= ~(WIDX_TAB_1);
+ w->pressed_widgets |= 1ULL << (w->page + WIDX_TAB_1);
+
+ RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint16) = w->error.var_480; // set title caption to player name
+
+ window_player_overview_widgets[WIDX_BACKGROUND].right = w->width - 1;
+ window_player_overview_widgets[WIDX_BACKGROUND].bottom = w->height - 1;
+
+ window_player_overview_widgets[WIDX_PAGE_BACKGROUND].right =w->width - 1;
+ window_player_overview_widgets[WIDX_PAGE_BACKGROUND].bottom = w->height - 1;
+
+ window_player_overview_widgets[WIDX_TITLE].right = w->width - 2;
+
+ window_player_overview_widgets[WIDX_CLOSE].left = w->width - 13;
+ window_player_overview_widgets[WIDX_CLOSE].right = w->width - 3;
+
+ window_player_overview_widgets[WIDX_KICK].right = w->width - 2;
+
+ window_player_overview_widgets[WIDX_KICK].left = w->width - 25;
+
+ window_align_tabs(w, WIDX_TAB_1, WIDX_TAB_1);
+}
+
diff --git a/src/windows/player_list.c b/src/windows/player_list.c
deleted file mode 100644
index 34279c8631..0000000000
--- a/src/windows/player_list.c
+++ /dev/null
@@ -1,359 +0,0 @@
-/*****************************************************************************
- * 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"
-#include "../windows/dropdown.h"
-#include "../util/util.h"
-
-enum WINDOW_PLAYER_LIST_WIDGET_IDX {
- WIDX_BACKGROUND,
- WIDX_TITLE,
- WIDX_CLOSE,
- WIDX_CONTENT_PANEL,
- WIDX_TAB1,
- WIDX_LIST,
-};
-
-static rct_widget window_player_list_widgets[] = {
- { WWT_FRAME, 0, 0, 339, 0, 239, 0x0FFFFFFFF, STR_NONE }, // panel / background
- { WWT_CAPTION, 0, 1, 338, 1, 14, STR_PLAYER_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_RESIZE, 1, 0, 339, 43, 239, 0x0FFFFFFFF, STR_NONE }, // content panel
- { WWT_TAB, 1, 3, 33, 17, 43, 0x02000144E, STR_NONE }, // tab
- { WWT_SCROLL, 1, 3, 336, 60, 236, 2, STR_NONE }, // list
- { WIDGETS_END },
-};
-
-static void window_player_list_mouseup(rct_window *w, int widgetIndex);
-static void window_player_list_resize(rct_window *w);
-static void window_player_list_mousedown(int widgetIndex, rct_window* w, rct_widget* widget);
-static void window_player_list_dropdown(rct_window *w, int widgetIndex, int dropdownIndex);
-static void window_player_list_update(rct_window *w);
-static void window_player_list_scrollgetsize(rct_window *w, int scrollIndex, int *width, int *height);
-static void window_player_list_scrollmousedown(rct_window *w, int scrollIndex, int x, int y);
-static void window_player_list_scrollmouseover(rct_window *w, int scrollIndex, int x, int y);
-static void window_player_list_invalidate(rct_window *w);
-static void window_player_list_paint(rct_window *w, rct_drawpixelinfo *dpi);
-static void window_player_list_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int scrollIndex);
-
-static rct_window_event_list window_player_list_events = {
- NULL,
- window_player_list_mouseup,
- window_player_list_resize,
- window_player_list_mousedown,
- window_player_list_dropdown,
- NULL,
- window_player_list_update,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- window_player_list_scrollgetsize,
- window_player_list_scrollmousedown,
- NULL,
- window_player_list_scrollmouseover,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- window_player_list_invalidate,
- window_player_list_paint,
- window_player_list_scrollpaint
-};
-
-enum {
- DDIDX_KICK
-};
-
-const int window_player_list_animation_divisor[] = { 4 };
-const int window_player_list_animation_frames[] = { 8 };
-
-static void window_player_list_refresh_list(rct_window *w);
-static void window_player_list_draw_tab_images(rct_window *w, rct_drawpixelinfo *dpi);
-
-static int _dropdownPlayerId;
-
-void window_player_list_open()
-{
- rct_window* window;
-
- // Check if window is already open
- window = window_bring_to_front_by_class(WC_PLAYER_LIST);
- if (window != NULL)
- return;
-
- window = window_create_auto_pos(320, 144, &window_player_list_events, WC_PLAYER_LIST, WF_10 | WF_RESIZABLE);
-
- window->widgets = window_player_list_widgets;
- window->enabled_widgets = 1 << WIDX_CLOSE;
- 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 = 124;
- window->max_width = 500;
- window->max_height = 450;
-
- window->page = 0;
- window->list_information_type = 0;
- window->colours[0] = 7;
- window->colours[1] = 7;
- window->colours[2] = 7;
-}
-
-static void window_player_list_mouseup(rct_window *w, int widgetIndex)
-{
- switch (widgetIndex) {
- case WIDX_CLOSE:
- window_close(w);
- break;
- }
-}
-
-static void window_player_list_resize(rct_window *w)
-{
- if (w->width < w->min_width) {
- window_invalidate(w);
- w->width = w->min_width;
- }
- if (w->height < w->min_height) {
- window_invalidate(w);
- w->height = w->min_height;
- }
-
- window_player_list_refresh_list(w);
-}
-
-static void window_player_list_mousedown(int widgetIndex, rct_window* w, rct_widget* widget)
-{
- switch (widgetIndex) {
- case WIDX_TAB1:
- if (w->page != widgetIndex - WIDX_TAB1) {
- w->page = widgetIndex - WIDX_TAB1;
- w->no_list_items = 0;
- w->frame_no = 0;
- w->selected_list_item = -1;
- window_invalidate(w);
- }
- break;
- }
-}
-
-static void window_player_list_dropdown(rct_window *w, int widgetIndex, int dropdownIndex)
-{
- switch (dropdownIndex) {
- case DDIDX_KICK:
- network_kick_player(_dropdownPlayerId);
- break;
- }
-}
-
-static void window_player_list_update(rct_window *w)
-{
- w->frame_no++;
- widget_invalidate(w, WIDX_TAB1 + w->page);
-}
-
-static void window_player_list_scrollgetsize(rct_window *w, int scrollIndex, int *width, int *height)
-{
- int i;
-
- if (w->selected_list_item != -1) {
- w->selected_list_item = -1;
- window_invalidate(w);
- }
-
- *height = network_get_num_players() * 10;
- i = *height - window_player_list_widgets[WIDX_LIST].bottom + window_player_list_widgets[WIDX_LIST].top + 21;
- if (i < 0)
- i = 0;
- if (i < w->scrolls[0].v_top) {
- w->scrolls[0].v_top = i;
- window_invalidate(w);
- }
-}
-
-static void window_player_list_scrollmousedown(rct_window *w, int scrollIndex, int x, int y)
-{
- if (network_get_mode() != NETWORK_MODE_SERVER) {
- return;
- }
-
- int index;
-
- index = y / 10;
- if (index >= w->no_list_items)
- return;
-
- w->selected_list_item = index;
- window_invalidate(w);
-
- rct_widget *listWidget = &w->widgets[WIDX_LIST];
- int ddx = w->x + listWidget->left + x - w->scrolls[0].h_left;
- int ddy = w->y + listWidget->top + y - w->scrolls[0].v_top;
-
- if (index == 0) {
- return;
- }
- _dropdownPlayerId = network_get_player_id(index);
- if (_dropdownPlayerId == network_get_current_player_id()) {
- return;
- }
-
- gDropdownItemsFormat[0] = STR_KICK_PLAYER;
- window_dropdown_show_text(ddx, ddy, 0, 7, 0, 1);
-}
-
-static void window_player_list_scrollmouseover(rct_window *w, int scrollIndex, int x, int y)
-{
- int index;
-
- index = y / 10;
- if (index >= w->no_list_items)
- return;
-
- w->selected_list_item = index;
- window_invalidate(w);
-}
-
-static void window_player_list_invalidate(rct_window *w)
-{
- for (int i = 0; i < 1; i++)
- w->pressed_widgets &= ~(1 << (WIDX_TAB1 + i));
- w->pressed_widgets |= 1LL << (WIDX_TAB1 + w->page);
- window_player_list_widgets[WIDX_BACKGROUND].right = w->width - 1;
- window_player_list_widgets[WIDX_BACKGROUND].bottom = w->height - 1;
- window_player_list_widgets[WIDX_CONTENT_PANEL].right = w->width - 1;
- window_player_list_widgets[WIDX_CONTENT_PANEL].bottom = w->height - 1;
- window_player_list_widgets[WIDX_TITLE].right = w->width - 2;
- window_player_list_widgets[WIDX_CLOSE].left = w->width - 2 - 0x0B;
- window_player_list_widgets[WIDX_CLOSE].right = w->width - 2 - 0x0B + 0x0A;
- window_player_list_widgets[WIDX_LIST].right = w->width - 4;
- window_player_list_widgets[WIDX_LIST].bottom = w->height - 0x0F;
-}
-
-static void window_player_list_paint(rct_window *w, rct_drawpixelinfo *dpi)
-{
- rct_string_id stringId;
- int x, y;
-
- window_draw_widgets(w, dpi);
- window_player_list_draw_tab_images(w, dpi);
-
- // Columns
- gfx_draw_string_left(dpi, STR_PLAYER, NULL, w->colours[2], w->x + 6, 58 - 12 + w->y + 1);
- gfx_draw_string_left(dpi, STR_PING, NULL, w->colours[2], w->x + 246, 58 - 12 + w->y + 1);
-
- // Number of players
- stringId = w->no_list_items == 1 ? STR_X_PLAYER : STR_X_PLAYERS;
- x = w->x + 4;
- y = w->y + w->widgets[WIDX_LIST].bottom + 2;
- gfx_draw_string_left(dpi, stringId, &w->no_list_items, w->colours[2], x, y);
-}
-
-static void window_player_list_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int scrollIndex)
-{
- int y;
-
- gfx_fill_rect(dpi, dpi->x, dpi->y, dpi->x + dpi->width - 1, dpi->y + dpi->height - 1, ColourMapA[w->colours[1]].mid_light);
-
- y = 0;
- for (int i = 0; i < network_get_num_players(); i++) {
- if (y > dpi->y + dpi->height) {
- break;
- }
-
- if (y + 11 >= dpi->y) {
- char buffer[300];
- char* lineCh = buffer;
- int colour = 0;
- if (i == w->selected_list_item) {
- gfx_fill_rect(dpi, 0, y, 800, y + 9, 0x02000031);
- safe_strcpy(&buffer[0], network_get_player_name(i), sizeof(buffer));
- colour = w->colours[2];
- } else {
- if (network_get_player_flags(i) & NETWORK_PLAYER_FLAG_ISSERVER) {
- lineCh = utf8_write_codepoint(lineCh, FORMAT_BABYBLUE);
- } else {
- lineCh = utf8_write_codepoint(lineCh, FORMAT_BLACK);
- }
- safe_strcpy(lineCh, network_get_player_name(i), sizeof(buffer) - (lineCh - buffer));
- }
- gfx_clip_string(buffer, 230);
- gfx_draw_string(dpi, buffer, colour, 0, y - 1);
- lineCh = buffer;
- int ping = network_get_player_ping(i);
- if (ping <= 100) {
- lineCh = utf8_write_codepoint(lineCh, FORMAT_GREEN);
- } else
- if (ping <= 250) {
- lineCh = utf8_write_codepoint(lineCh, FORMAT_YELLOW);
- } else {
- lineCh = utf8_write_codepoint(lineCh, FORMAT_RED);
- }
- sprintf(lineCh, "%d ms", ping);
- gfx_draw_string(dpi, buffer, colour, 240, y - 1);
- }
- y += 10;
- }
-}
-
-static void window_player_list_refresh_list(rct_window *w)
-{
- w->no_list_items = network_get_num_players();
- w->list_item_positions[0] = 0;
-
- w->selected_list_item = -1;
- window_invalidate(w);
-}
-
-static void window_player_list_draw_tab_image(rct_window *w, rct_drawpixelinfo *dpi, int page, int spriteIndex)
-{
- int widgetIndex = WIDX_TAB1 + page;
-
- if (!(w->disabled_widgets & (1LL << widgetIndex))) {
- if (w->page == page) {
- int numFrames = window_player_list_animation_frames[w->page];
- if (numFrames > 1) {
- int frame = w->frame_no / window_player_list_animation_divisor[w->page];
- spriteIndex += (frame % numFrames);
- }
- }
-
- gfx_draw_sprite(dpi, spriteIndex, w->x + w->widgets[widgetIndex].left, w->y + w->widgets[widgetIndex].top, 0);
- }
-}
-
-static void window_player_list_draw_tab_images(rct_window *w, rct_drawpixelinfo *dpi)
-{
- window_player_list_draw_tab_image(w, dpi, 0, SPR_TAB_GUESTS_0);
-}
diff --git a/src/windows/top_toolbar.c b/src/windows/top_toolbar.c
index aaa579afc5..c3ea83ec7d 100644
--- a/src/windows/top_toolbar.c
+++ b/src/windows/top_toolbar.c
@@ -110,7 +110,7 @@ typedef enum {
} TOP_TOOLBAR_DEBUG_DDIDX;
typedef enum {
- DDIDX_PLAYER_LIST = 0
+ DDIDX_MULTIPLAYER = 0
} TOP_TOOLBAR_NETWORK_DDIDX;
enum {
@@ -2941,7 +2941,7 @@ void top_toolbar_init_debug_menu(rct_window* w, rct_widget* widget)
void top_toolbar_init_network_menu(rct_window* w, rct_widget* widget)
{
- gDropdownItemsFormat[0] = STR_PLAYER_LIST;
+ gDropdownItemsFormat[0] = STR_MULTIPLAYER;
window_dropdown_show_text(
w->x + widget->left,
@@ -2952,7 +2952,7 @@ void top_toolbar_init_network_menu(rct_window* w, rct_widget* widget)
1
);
- gDropdownDefaultIndex = DDIDX_PLAYER_LIST;
+ gDropdownDefaultIndex = DDIDX_MULTIPLAYER;
}
void top_toolbar_debug_menu_dropdown(short dropdownIndex)
@@ -2985,8 +2985,8 @@ void top_toolbar_network_menu_dropdown(short dropdownIndex)
rct_window* w = window_get_main();
if (w) {
switch (dropdownIndex) {
- case DDIDX_PLAYER_LIST:
- window_player_list_open();
+ case DDIDX_MULTIPLAYER:
+ window_multiplayer_open();
break;
}
}