mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 04:49:39 -10:00
time_t is 64bit on 64bit systems; casting a pointer to a larger type is dangerous, but assigning to a larger type should be fine
5402 lines
137 KiB
C++
5402 lines
137 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 2005. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
#include <winsock.h>
|
|
#endif
|
|
|
|
#include "network/multi_pxo.h"
|
|
#include "anim/animplay.h"
|
|
#include "ui/ui.h"
|
|
#include "io/key.h"
|
|
#include "bmpman/bmpman.h"
|
|
#include "palman/palman.h"
|
|
#include "gamesnd/gamesnd.h"
|
|
#include "gamesequence/gamesequence.h"
|
|
#include "cfile/cfile.h"
|
|
#include "network/chat_api.h"
|
|
#include "popup/popup.h"
|
|
#include "freespace2/freespace.h"
|
|
#include "graphics/font.h"
|
|
#include "network/multi.h"
|
|
#include "network/multiui.h"
|
|
#include "network/multi_log.h"
|
|
#include "stats/medals.h"
|
|
#include "globalincs/alphacolors.h"
|
|
#include "graphics/2d.h"
|
|
#include "graphics/generic.h"
|
|
#include "io/timer.h"
|
|
#include "inetfile/inetgetfile.h"
|
|
#include "cfile/cfilesystem.h"
|
|
#include "osapi/osregistry.h"
|
|
#include "parse/parselo.h"
|
|
#include "stats/scoring.h"
|
|
#include "playerman/player.h"
|
|
#include "fs2netd/fs2netd_client.h"
|
|
#include "menuui/mainhallmenu.h"
|
|
#include "debugconsole/console.h"
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------------------
|
|
// PXO DEFINES/VARS
|
|
//
|
|
|
|
#define MAX_PXO_TEXT_LEN 255
|
|
|
|
// button definitions
|
|
#define MULTI_PXO_NUM_BUTTONS 15
|
|
#define MULTI_PXO_PLIST_UP 0
|
|
#define MULTI_PXO_PLIST_DOWN 1
|
|
#define MULTI_PXO_RANKINGS 2
|
|
#define MULTI_PXO_PINFO 3
|
|
#define MULTI_PXO_FIND 4
|
|
#define MULTI_PXO_MOTD 5
|
|
#define MULTI_PXO_JOIN 6
|
|
#define MULTI_PXO_JOIN_PRIV 7
|
|
#define MULTI_PXO_CHAN_UP 8
|
|
#define MULTI_PXO_CHAN_DOWN 9
|
|
#define MULTI_PXO_TEXT_UP 10
|
|
#define MULTI_PXO_TEXT_DOWN 11
|
|
#define MULTI_PXO_EXIT 12
|
|
#define MULTI_PXO_HELP 13
|
|
#define MULTI_PXO_GAMES 14
|
|
|
|
|
|
ui_button_info Multi_pxo_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_NUM_BUTTONS] = {
|
|
{ // GR_640
|
|
ui_button_info( "PXB_00", 1, 104, -1, -1, 0 ), // scroll player list up
|
|
ui_button_info( "PXB_01", 1, 334, -1, -1, 1 ), // scroll player list down
|
|
ui_button_info( "PXB_02", 18, 385, -1, -1, 2 ), // rankings webpage
|
|
ui_button_info( "PXB_03", 71, 385, -1, -1, 3 ), // pilot info
|
|
ui_button_info( "PXB_04", 115, 385, -1, -1, 4 ), // find player
|
|
ui_button_info( "PXB_05", 1, 443, -1, -1, 5 ), // motd
|
|
ui_button_info( "PXB_06", 330, 96, -1, -1, 6 ), // join channel
|
|
ui_button_info( "PXB_07", 330, 131, -1, -1, 7 ), // join private channel
|
|
ui_button_info( "PXB_08", 618, 92, -1, -1, 8 ), // scroll channels up
|
|
ui_button_info( "PXB_09", 618, 128, -1, -1, 9 ), // scroll channels down
|
|
ui_button_info( "PXB_10", 615, 171, -1, -1, 10 ), // scroll text up
|
|
ui_button_info( "PXB_11", 615, 355, -1, -1, 11 ), // scroll text down
|
|
ui_button_info( "PXB_12", 482, 435, -1, -1, 12 ), // exit
|
|
ui_button_info( "PXB_13", 533, 432, -1, -1, 13 ), // help
|
|
ui_button_info( "PXB_14", 573, 432, -1, -1, 14 ), // games list
|
|
},
|
|
{ // GR_1024
|
|
ui_button_info( "2_PXB_00", 2, 166, -1, -1, 0 ), // scroll player list up
|
|
ui_button_info( "2_PXB_01", 2, 534, -1, -1, 1 ), // scroll player list down
|
|
ui_button_info( "2_PXB_02", 29, 616, -1, -1, 2 ), // rankings webpage
|
|
ui_button_info( "2_PXB_03", 114, 616, -1, -1, 3 ), // pilot info
|
|
ui_button_info( "2_PXB_04", 184, 616, -1, -1, 4 ), // find player
|
|
ui_button_info( "2_PXB_05", 2, 709, -1, -1, 5 ), // motd
|
|
ui_button_info( "2_PXB_06", 528, 119, -1, -1, 6 ), // join channel
|
|
ui_button_info( "2_PXB_07", 528, 175, -1, -1, 7 ), // join private channel
|
|
ui_button_info( "2_PXB_08", 989, 112, -1, -1, 8 ), // scroll channels up
|
|
ui_button_info( "2_PXB_09", 989, 170, -1, -1, 9 ), // scroll channels down
|
|
ui_button_info( "2_PXB_10", 984, 240, -1, -1, 10 ), // scroll text up
|
|
ui_button_info( "2_PXB_11", 984, 568, -1, -1, 11 ), // scroll text down
|
|
ui_button_info( "2_PXB_12", 771, 696, -1, -1, 12 ), // exit
|
|
ui_button_info( "2_PXB_13", 853, 691, -1, -1, 13 ), // help
|
|
ui_button_info( "2_PXB_14", 917, 691, -1, -1, 14 ), // games list
|
|
},
|
|
};
|
|
|
|
#define MULTI_PXO_NUM_TEXT 16
|
|
UI_XSTR Multi_pxo_text[GR_NUM_RESOLUTIONS][MULTI_PXO_NUM_TEXT] = {
|
|
{ // GR_640
|
|
{"Web", 1313, 20, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_RANKINGS].button},
|
|
{"Ranking", 1314, 6, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_RANKINGS].button},
|
|
{"Pilot", 1310, 68, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_PINFO].button},
|
|
{"Info", 1311, 72, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_PINFO].button},
|
|
{"Find", 1315, 119, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_FIND].button},
|
|
{"Motd", 1316, 36, 456, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_MOTD].button},
|
|
{"Join", 1505, 291, 100, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN].button},
|
|
{"Channel", 1317, 266, 112, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN].button},
|
|
{"Join", 1506, 291, 134, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN_PRIV].button},
|
|
{"Private", 1318, 273, 146, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN_PRIV].button},
|
|
{"Exit", 1416, 493, 424, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_EXIT].button},
|
|
{"Help", 928, 535, 416, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_HELP].button},
|
|
{"Games", 1319, 579, 416, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_GAMES].button},
|
|
{"Players", 1269, 29, 102, UI_XSTR_COLOR_GREEN, -1, NULL},
|
|
{"Players", 1269, 507, 90, UI_XSTR_COLOR_GREEN, -1, NULL},
|
|
{"Games", 1319, 568, 90, UI_XSTR_COLOR_GREEN, -1, NULL}
|
|
},
|
|
{ // GR_1024
|
|
{"Web", 1313, 32, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_RANKINGS].button},
|
|
{"Ranking", 1314, 9, 674, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_RANKINGS].button},
|
|
{"Pilot", 1310, 109, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_PINFO].button},
|
|
{"Info", 1311, 115, 674, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_PINFO].button},
|
|
{"Find", 1315, 190, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_FIND].button},
|
|
{"Motd", 1316, 58, 729, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_MOTD].button},
|
|
{"Join", 1505, 488, 129, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN].button},
|
|
{"Channel", 1317, 461, 139, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN].button},
|
|
{"Join", 1506, 487, 184, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN_PRIV].button},
|
|
{"Private", 1318, 467, 194, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN_PRIV].button},
|
|
{"Exit", 1416, 789, 678, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_EXIT].button},
|
|
{"Help", 928, 857, 667, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_HELP].button},
|
|
{"Games", 1319, 917, 667, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_GAMES].button},
|
|
{"Players", 1269, 47, 163, UI_XSTR_COLOR_GREEN, -1, NULL},
|
|
{"Players", 1269, 852, 109, UI_XSTR_COLOR_GREEN, -1, NULL},
|
|
{"Games", 1319, 926, 109, UI_XSTR_COLOR_GREEN, -1, NULL}
|
|
}
|
|
};
|
|
|
|
char Multi_pxo_bitmap_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
|
|
"PXOChat",
|
|
"2_PXOChat"
|
|
};
|
|
char Multi_pxo_mask_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
|
|
"PXOChat-M",
|
|
"2_PXOChat-M"
|
|
};
|
|
|
|
UI_WINDOW Multi_pxo_window;
|
|
int Multi_pxo_bitmap = -1;
|
|
int Multi_pxo_palette = -1;
|
|
|
|
|
|
// pxo animation
|
|
#define MULTI_PXO_ANIM_FNAME "pxologo"
|
|
#define MULTI_PXO_ANIM_X 0
|
|
#define MULTI_PXO_ANIM_Y 4
|
|
generic_anim Multi_pxo_anim;
|
|
|
|
// rankings last clicked time
|
|
#define MULTI_PXO_RANK_TIME (5.0f)
|
|
float Multi_pxo_ranking_last = -1.0f;
|
|
|
|
// chat api vars
|
|
int Multi_pxo_must_connect = 0; // if we still need to connect
|
|
int Multi_pxo_connected = 0; // if we are connected
|
|
int Multi_pxo_must_validate = 0; // if we need to validate on the tracker
|
|
int Multi_pxo_must_autojoin = 1; // still need to autojoin a channel
|
|
|
|
// mode
|
|
#define MULTI_PXO_MODE_NORMAL 0 // normal mode
|
|
#define MULTI_PXO_MODE_PRIVATE 1 // private channel popup
|
|
#define MULTI_PXO_MODE_FIND 2 // find player popup
|
|
int Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
|
|
|
|
// our nick for this session
|
|
char Multi_pxo_nick[NAME_LENGTH+1];
|
|
|
|
// check for button presses
|
|
void multi_pxo_check_buttons();
|
|
|
|
// handle a button press
|
|
void multi_pxo_button_pressed(int n);
|
|
|
|
// condition function for popup_do_with_condition for connected to Parallax Online
|
|
// return 10 : on successful connect
|
|
int multi_pxo_connect_do();
|
|
|
|
// attempt to connect to Parallax Online, return success or fail
|
|
int multi_pxo_connect();
|
|
|
|
// run the networking functions for the PXO API
|
|
void multi_pxo_api_process();
|
|
|
|
// process a "nick" change event
|
|
void multi_pxo_process_nick_change(char *data);
|
|
|
|
// run normally (no popups)
|
|
void multi_pxo_do_normal();
|
|
|
|
// blit everything on the "normal" screen
|
|
void multi_pxo_blit_all();
|
|
|
|
// process common stuff
|
|
void multi_pxo_process_common();
|
|
|
|
// get selected player information
|
|
void multi_pxo_get_data(char *name);
|
|
|
|
// handle being kicked
|
|
void multi_pxo_handle_kick();
|
|
|
|
// handle being disconnected
|
|
void multi_pxo_handle_disconnect();
|
|
|
|
// return string2, which is the first substring of string 1 without a space
|
|
// it is safe to pass the same pointer for both parameters
|
|
void multi_pxo_strip_space(char *string1,char *string2);
|
|
|
|
// fire up the given URL
|
|
void multi_pxo_url(char *url);
|
|
|
|
// load/set the palette
|
|
void multi_pxo_load_palette();
|
|
|
|
// unload the palette
|
|
void multi_pxo_unload_palette();
|
|
|
|
// if we're currently on a private channel
|
|
int multi_pxo_on_private_channel();
|
|
|
|
// convert string 1 into string 2, substituting underscores for spaces
|
|
void multi_pxo_underscore_nick(char *string1,char *string2);
|
|
|
|
// if the command is a potential "nick" command
|
|
int multi_pxo_is_nick_command(char *msg);
|
|
|
|
|
|
// status bar stuff -----------------------------------------------
|
|
int Multi_pxo_status_coords[GR_NUM_RESOLUTIONS][4] = {
|
|
{ // GR_640
|
|
95, 467, 354, 12
|
|
},
|
|
{ // GR_1024
|
|
152, 750, 570, 12
|
|
},
|
|
};
|
|
|
|
// the status text itself
|
|
char Multi_pxo_status_text[MAX_PXO_TEXT_LEN];
|
|
|
|
// set the status text
|
|
void multi_pxo_set_status_text(const char *txt);
|
|
|
|
// blit the status text
|
|
void multi_pxo_blit_status_text();
|
|
|
|
|
|
// channel related stuff -------------------------------------------
|
|
#define MAX_CHANNEL_NAME_LEN 32
|
|
#define MAX_CHANNEL_DESCRIPT_LEN 120
|
|
|
|
// some convenient macros
|
|
#define SWITCHING_CHANNELS() (Multi_pxo_channel_switch.num_users != -1)
|
|
#define ON_CHANNEL() (Multi_pxo_channel_current.num_users != -1)
|
|
|
|
typedef struct pxo_channel {
|
|
pxo_channel *next,*prev; // next and previous items in the list
|
|
char name[MAX_CHANNEL_NAME_LEN+1]; // name
|
|
char desc[MAX_CHANNEL_DESCRIPT_LEN+1]; // description
|
|
short num_users; // # users, or -1 if not in use
|
|
short num_servers; // the # of servers registered on this channel
|
|
} pxo_channel;
|
|
|
|
// last channel we were on before going to the game list screen
|
|
char Multi_pxo_channel_last[MAX_CHANNEL_NAME_LEN+1] = "";
|
|
int Multi_pxo_use_last_channel = 0;
|
|
|
|
// all channels which are prefixed with this are "lobby" channels
|
|
#define MULTI_PXO_AUTOJOIN_PREFIX "#lobby"
|
|
|
|
// join this channel to get put in an appropriate lobby channel
|
|
#define MULTI_PXO_AUTOJOIN_CHANNEL "#autoselect"
|
|
|
|
int Multi_pxo_chan_coords[GR_NUM_RESOLUTIONS][4] = {
|
|
{ // GR_640
|
|
369, 101, 241, 60
|
|
},
|
|
{ // GR_1024
|
|
593, 124, 386, 100
|
|
},
|
|
};
|
|
|
|
// this is the offset from the RIGHT side of the channel box
|
|
#define CHAN_PLAYERS_COLUMN 0
|
|
#define CHAN_GAMES_COLUMN 1
|
|
static int Multi_pxo_chan_column_offsets[GR_NUM_RESOLUTIONS][2] = {
|
|
{ 81, 26 },
|
|
{ 103, 35 }
|
|
};
|
|
|
|
#define CHANNEL_REFRESH_TIME (75.0f)
|
|
float Multi_pxo_channel_last_refresh = -1.0f;
|
|
|
|
#define CHANNEL_SERVER_REFRESH_TIME (35.0f)
|
|
float Multi_pxo_channel_server_refresh = -1.0f;
|
|
|
|
int Multi_pxo_max_chan_display[GR_NUM_RESOLUTIONS] = {
|
|
6, // GR_640
|
|
10 // GR_1024
|
|
};
|
|
|
|
UI_BUTTON Multi_pxo_channel_button;
|
|
|
|
// head of the list of available (displayed) channels
|
|
pxo_channel *Multi_pxo_channels = NULL;
|
|
int Multi_pxo_channel_count = 0;
|
|
|
|
// item we're going to start displaying at
|
|
pxo_channel *Multi_pxo_channel_start = NULL;
|
|
int Multi_pxo_channel_start_index = -1;
|
|
|
|
// items we've currently got selected
|
|
pxo_channel *Multi_pxo_channel_select = NULL;
|
|
|
|
// channel we're currently connected to, num_users == -1, if we're not connected
|
|
pxo_channel Multi_pxo_channel_current;
|
|
|
|
// channel we're currently trying to change to, num_users == -1, if we're not trying to change channels
|
|
pxo_channel Multi_pxo_channel_switch;
|
|
|
|
// get a list of channels on the server (clear any old list as well)
|
|
void multi_pxo_get_channels();
|
|
|
|
// clear the old channel list
|
|
void multi_pxo_clear_channels();
|
|
|
|
// parse the input string and make a list of new channels
|
|
void multi_pxo_make_channels(char *chan_str);
|
|
|
|
// create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
|
|
pxo_channel *multi_pxo_add_channel(char *name, pxo_channel **list);
|
|
|
|
// lookup a channel with the specified name
|
|
pxo_channel *multi_pxo_find_channel(char *name, pxo_channel *list);
|
|
|
|
// process the channel list (select, etc)
|
|
void multi_pxo_process_channels();
|
|
|
|
// display the channel list
|
|
void multi_pxo_blit_channels();
|
|
|
|
// scroll channel list up
|
|
void multi_pxo_scroll_channels_up();
|
|
|
|
// scroll channel list down
|
|
void multi_pxo_scroll_channels_down();
|
|
|
|
// attempt to join a channel
|
|
void multi_pxo_join_channel(pxo_channel *chan);
|
|
|
|
// handle any processing details if we're currently trying to join a channel
|
|
void multi_pxo_handle_channel_change();
|
|
|
|
// autojoin an appropriate channel
|
|
void multi_pxo_autojoin();
|
|
|
|
// does the string match the "autojoin" prefic
|
|
int multi_pxo_is_autojoin(char *name);
|
|
|
|
// send a request to refresh our channel server counts
|
|
void multi_pxo_channel_refresh_servers();
|
|
|
|
// refresh current channel server count
|
|
void multi_pxo_channel_refresh_current();
|
|
|
|
|
|
// player related stuff -------------------------------------------
|
|
#define MAX_PLAYER_NAME_LEN 32
|
|
|
|
typedef struct player_list {
|
|
player_list *next,*prev;
|
|
char name[MAX_PLAYER_NAME_LEN+1];
|
|
} player_list;
|
|
|
|
// channel list region
|
|
int Multi_pxo_player_coords[GR_NUM_RESOLUTIONS][4] = {
|
|
{ // GR_640
|
|
27, 121, 141, 261
|
|
},
|
|
{ // GR_1024
|
|
43, 194, 154, 417
|
|
},
|
|
};
|
|
|
|
int Multi_pxo_max_player_display[GR_NUM_RESOLUTIONS] = {
|
|
25, // GR_640
|
|
41 // GR_1024
|
|
};
|
|
UI_BUTTON Multi_pxo_player_button;
|
|
|
|
// slider coords
|
|
int Multi_pxo_player_slider_coords[GR_NUM_RESOLUTIONS][4] = {
|
|
{ // GR_640
|
|
1, 139, 21, 192
|
|
},
|
|
{ // GR_1024
|
|
2, 219, 33, 314
|
|
}
|
|
};
|
|
char *Multi_pxo_player_slider_name[GR_NUM_RESOLUTIONS] = {
|
|
"slider", // GR_640
|
|
"2_slider" // GR_1024
|
|
};
|
|
|
|
// head of the list of players in this channel
|
|
player_list *Multi_pxo_players = NULL;
|
|
int Multi_pxo_player_count = 0;
|
|
|
|
// item we're going to start displaying at
|
|
player_list *Multi_pxo_player_start = NULL;
|
|
// int Multi_pxo_player_start_index = -1;
|
|
|
|
// items we've currently got selected
|
|
player_list *Multi_pxo_player_select = NULL;
|
|
|
|
// clear the old player list
|
|
void multi_pxo_clear_players();
|
|
|
|
// create a new player with the given name and place it on the player list, return a pointer or NULL on fail
|
|
player_list *multi_pxo_add_player(char *name);
|
|
|
|
// remove a player with the given name
|
|
void multi_pxo_del_player(char *name);
|
|
|
|
// try and find a player with the given name, return a pointer to his entry (or NULL)
|
|
player_list *multi_pxo_find_player(char *name);
|
|
|
|
// process the player list (select, etc)
|
|
void multi_pxo_process_players();
|
|
|
|
// display the player list
|
|
void multi_pxo_blit_players();
|
|
|
|
// scroll player list up
|
|
void multi_pxo_scroll_players_up();
|
|
|
|
// scroll player list down
|
|
void multi_pxo_scroll_players_down();
|
|
|
|
// get the absolute index of the displayed items which our currently selected one is
|
|
int multi_pxo_get_select_index();
|
|
|
|
DCF(players, "Adds the specified number of bogus players to the PXO listing (Multiplayer)")
|
|
{
|
|
char name[512] = "";
|
|
int i;
|
|
// add a bunch of bogus players
|
|
dc_stuff_int(&i);
|
|
|
|
for(int idx = 0; idx < i; idx++){
|
|
sprintf(name, "bogus player %d", idx);
|
|
multi_pxo_add_player(name);
|
|
}
|
|
}
|
|
|
|
// chat text stuff -----------------------------------------
|
|
#define MAX_CHAT_LINES 60
|
|
#define MAX_CHAT_LINE_LEN 256
|
|
|
|
int Multi_pxo_chat_title_y[GR_NUM_RESOLUTIONS] = {
|
|
181, // GR_640
|
|
253 // GR_1024
|
|
};
|
|
|
|
int Multi_pxo_chat_coords[GR_NUM_RESOLUTIONS][4] = {
|
|
{ // GR_640
|
|
196, 197, 412, 185
|
|
},
|
|
{ // GR_1024
|
|
314, 271, 665, 330
|
|
}
|
|
};
|
|
|
|
int Multi_pxo_input_coords[GR_NUM_RESOLUTIONS][4] = {
|
|
{ // GR_640
|
|
196, 386, 407, 24
|
|
},
|
|
{ // GR_1024
|
|
314, 617, 660, 38
|
|
}
|
|
};
|
|
|
|
int Multi_pxo_max_chat_display[GR_NUM_RESOLUTIONS] = {
|
|
17, // GR_640
|
|
32 // GR_1024
|
|
};
|
|
|
|
// all messages from the server are prefixed with this
|
|
#define MULTI_PXO_SERVER_PREFIX "*** "
|
|
|
|
// the "has left" message from the server
|
|
#define MULTI_PXO_HAS_LEFT "has left"
|
|
|
|
// chat flags
|
|
#define CHAT_MODE_NORMAL 0 // normal chat from someone
|
|
#define CHAT_MODE_SERVER 1 // is from the server, display appropriately
|
|
#define CHAT_MODE_CARRY 2 // is a carryover from a previous line
|
|
#define CHAT_MODE_PRIVATE 3 // is a private message
|
|
#define CHAT_MODE_CHANNEL_SWITCH 4 // "switching channels" message - draw in red
|
|
#define CHAT_MODE_MOTD 5 // message of the day from the chat server
|
|
|
|
typedef struct chat_line {
|
|
chat_line *next,*prev;
|
|
char text[MAX_CHAT_LINE_LEN+1];
|
|
int mode;
|
|
} chat_line;
|
|
|
|
// the chat linked list itself
|
|
chat_line *Multi_pxo_chat = NULL;
|
|
|
|
// the current add line
|
|
chat_line *Multi_pxo_chat_add = NULL;
|
|
|
|
// the current line to start displaying from
|
|
chat_line *Multi_pxo_chat_start = NULL;
|
|
int Multi_pxo_chat_start_index = -1;
|
|
|
|
// input box for text
|
|
UI_INPUTBOX Multi_pxo_chat_input;
|
|
|
|
// slider for chat
|
|
UI_SLIDER2 Multi_pxo_chat_slider;
|
|
|
|
int Multi_pxo_chat_slider_coords[GR_NUM_RESOLUTIONS][4] = {
|
|
{ // GR_640
|
|
620, 206, 21, 147
|
|
},
|
|
{ // GR_1024
|
|
990, 295, 34, 269
|
|
}
|
|
};
|
|
|
|
char *Multi_pxo_chat_slider_name[GR_NUM_RESOLUTIONS] = {
|
|
"slider",
|
|
"2_slider"
|
|
};
|
|
|
|
// how many chat lines we have
|
|
int Multi_pxo_chat_count = 0;
|
|
|
|
// extra delay time when switching channels
|
|
#define MULTI_PXO_SWITCH_DELAY_TIME 2000
|
|
int Multi_pxo_switch_delay = -1;
|
|
|
|
// initialize and create the chat text linked list
|
|
void multi_pxo_chat_init();
|
|
|
|
// free up all chat list stuff
|
|
void multi_pxo_chat_free();
|
|
|
|
// clear all lines of chat text in the chat area
|
|
void multi_pxo_chat_clear();
|
|
|
|
// blit the chat text
|
|
void multi_pxo_chat_blit();
|
|
|
|
// add a line of text
|
|
void multi_pxo_chat_add_line(const char *txt,int mode);
|
|
|
|
// process an incoming line of text
|
|
void multi_pxo_chat_process_incoming(const char *txt, int mode = CHAT_MODE_NORMAL);
|
|
|
|
// scroll to the very bottom of the chat area
|
|
void multi_pxo_goto_bottom();
|
|
|
|
// check whether we can scroll down or not
|
|
int multi_pxo_can_scroll_down();
|
|
|
|
static int Can_scroll_down = 0;
|
|
|
|
// scroll the text up
|
|
void multi_pxo_scroll_chat_up();
|
|
|
|
// scroll the text down
|
|
void multi_pxo_scroll_chat_down();
|
|
|
|
// process chat controls
|
|
void multi_pxo_chat_process();
|
|
|
|
// if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
|
|
const char *multi_pxo_chat_is_private(const char *txt);
|
|
|
|
// if the text came from the server
|
|
int multi_pxo_is_server_text(const char *txt);
|
|
|
|
// if the text is message of the day text
|
|
int multi_pxo_is_motd_text(const char *txt);
|
|
|
|
// if the text is the end of motd text
|
|
int multi_pxo_is_end_of_motd_text(const char *txt);
|
|
|
|
// if the text is a "has left message" from the server
|
|
int multi_pxo_chat_is_left_message(const char *txt);
|
|
|
|
// recalculate the chat start index, and adjust the slider properly
|
|
void multi_pxo_chat_adjust_start();
|
|
|
|
|
|
// motd stuff ---------------------------------------------------------
|
|
#define MAX_PXO_MOTD_LEN 1024
|
|
#define PXO_MOTD_BLINK_TIME 500
|
|
char Pxo_motd[1024] = "";
|
|
int Pxo_motd_end = 0;
|
|
int Pxo_motd_read = 0;
|
|
int Pxo_motd_blink_stamp = -1;
|
|
int Pxo_motd_blink_on = 0;
|
|
int Pxo_motd_blinked_already = 0;
|
|
|
|
// initialize motd when going into this screen
|
|
void multi_pxo_motd_init();
|
|
|
|
// set the motd text
|
|
void multi_pxo_motd_add_text(const char *text);
|
|
|
|
// set end of motd
|
|
void multi_pxo_set_end_of_motd();
|
|
|
|
// display the motd dialog
|
|
void multi_pxo_motd_dialog();
|
|
|
|
// call to maybe blink the motd button
|
|
void multi_pxo_motd_maybe_blit();
|
|
|
|
|
|
// common dialog stuff ------------------------------------------------
|
|
char *Multi_pxo_com_fname[GR_NUM_RESOLUTIONS] = {
|
|
"PXOPop",
|
|
"2_PXOPop"
|
|
};
|
|
char *Multi_pxo_com_mask_fname[GR_NUM_RESOLUTIONS] = {
|
|
"PXOPop-m",
|
|
"2_PXOPop-m"
|
|
};
|
|
|
|
// popup coords
|
|
int Multi_pxo_com_coords[GR_NUM_RESOLUTIONS][2] = {
|
|
{ // GR_640
|
|
38, 129
|
|
},
|
|
{ // GR_1024
|
|
61, 207
|
|
}
|
|
};
|
|
|
|
// input box coords
|
|
int Multi_pxo_com_input_coords[GR_NUM_RESOLUTIONS][4] = {
|
|
{ // GR_640
|
|
53, 233, 448, 25
|
|
},
|
|
{ // GR_1024
|
|
85, 372, 716, 40
|
|
}
|
|
};
|
|
|
|
#define MULTI_PXO_COM_NUM_BUTTONS 2
|
|
#define MULTI_PXO_COM_CANCEL 0
|
|
#define MULTI_PXO_COM_OK 1
|
|
|
|
ui_button_info Multi_pxo_com_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_BUTTONS] = {
|
|
{ // GR_640
|
|
ui_button_info("PXP_00", 573, 192, -1, -1, 0),
|
|
ui_button_info("PXP_01", 573, 226, -1, -1, 1)
|
|
},
|
|
{ // GR_1024
|
|
ui_button_info("2_PXP_00", 917, 308, -1, -1, 0),
|
|
ui_button_info("2_PXP_01", 917, 361, -1, -1, 1)
|
|
}
|
|
};
|
|
|
|
#define MULTI_PXO_COM_NUM_TEXT 2
|
|
UI_XSTR Multi_pxo_com_text[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_TEXT] = {
|
|
{ // GR_640
|
|
{ "&Cancel", 645, 510, 204, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_CANCEL].button },
|
|
{ "&Ok", 669, 548, 233, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_OK].button }
|
|
},
|
|
{ // GR_1024
|
|
{ "&Cancel", 645, 847, 327, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_CANCEL].button },
|
|
{ "&Ok", 669, 877, 372, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_OK].button }
|
|
}
|
|
};
|
|
|
|
int Multi_pxo_com_bitmap = -1;
|
|
UI_WINDOW Multi_pxo_com_window;
|
|
UI_INPUTBOX Multi_pxo_com_input;
|
|
|
|
// text on the "top" half of the dialog display area
|
|
char Multi_pxo_com_top_text[MAX_PXO_TEXT_LEN];
|
|
|
|
// text on the "middle" portion of the dialog display area
|
|
char Multi_pxo_com_middle_text[MAX_PXO_TEXT_LEN];
|
|
|
|
// text on the "bottom" half of the dialog display area
|
|
char Multi_pxo_com_bottom_text[MAX_PXO_TEXT_LEN];
|
|
|
|
int Multi_pxo_com_top_text_coords[GR_NUM_RESOLUTIONS][2] = {
|
|
{ // GR_640
|
|
58, 152
|
|
},
|
|
{ // GR_1024
|
|
91, 227
|
|
}
|
|
};
|
|
int Multi_pxo_com_middle_text_y[GR_NUM_RESOLUTIONS] = {
|
|
172, // GR_640
|
|
280 // GR_1024
|
|
};
|
|
int Multi_pxo_com_bottom_text_y[GR_NUM_RESOLUTIONS] = {
|
|
192, // GR_640
|
|
326 // GR_1024
|
|
};
|
|
|
|
// initialize the common dialog with the passed max input length
|
|
void multi_pxo_com_init();
|
|
|
|
// close down the common dialog
|
|
void multi_pxo_com_close();
|
|
|
|
// blit all text lines, top, middle, bottoms
|
|
void multi_pxo_com_blit_text();
|
|
|
|
// set the top text, shortening as necessary
|
|
void multi_pxo_com_set_top_text(const char *txt);
|
|
|
|
// set the middle text, shortening as necessary
|
|
void multi_pxo_com_set_middle_text(const char *txt);
|
|
|
|
// set the bottom text, shortening as necessary
|
|
void multi_pxo_com_set_bottom_text(const char *txt);
|
|
|
|
|
|
// private channel join stuff -----------------------------------------
|
|
#define MULTI_PXO_PRIV_MAX_TEXT_LEN 30
|
|
|
|
// max private channel name length
|
|
char Multi_pxo_priv_chan[MULTI_PXO_PRIV_MAX_TEXT_LEN+100];
|
|
|
|
// return code, set to something other than -1 if we're supposed to return
|
|
int Multi_pxo_priv_return_code = -1;
|
|
|
|
// initialize the popup
|
|
void multi_pxo_priv_init();
|
|
|
|
// close down the popup
|
|
void multi_pxo_priv_close();
|
|
|
|
// run the popup, 0 if still running, -1 if cancel, 1 if ok
|
|
int multi_pxo_priv_popup();
|
|
|
|
// process button presses
|
|
void multi_pxo_priv_process_buttons();
|
|
|
|
// handle a button press
|
|
void multi_pxo_priv_button_pressed(int n);
|
|
|
|
// process the inputbox
|
|
void multi_pxo_priv_process_input();
|
|
|
|
|
|
// find player stuff -----------------------------------------
|
|
|
|
char Multi_pxo_find_channel[MAX_CHANNEL_NAME_LEN+1];
|
|
|
|
// return code, set to something other than -1 if we're supposed to return
|
|
int Multi_pxo_find_return_code = -1;
|
|
|
|
// initialize the popup
|
|
void multi_pxo_find_init();
|
|
|
|
// close down the popup
|
|
void multi_pxo_find_close();
|
|
|
|
// run the popup, 0 if still running, -1 if cancel, 1 if ok
|
|
int multi_pxo_find_popup();
|
|
|
|
// process button presses
|
|
void multi_pxo_find_process_buttons();
|
|
|
|
// handle a button press
|
|
void multi_pxo_find_button_pressed(int n);
|
|
|
|
// process the inputbox
|
|
void multi_pxo_find_process_input();
|
|
|
|
// process search mode if applicable
|
|
void multi_pxo_find_search_process();
|
|
|
|
|
|
// player info stuff -----------------------------------------
|
|
char *Multi_pxo_pinfo_fname[GR_NUM_RESOLUTIONS] = {
|
|
"PilotInfo2",
|
|
"2_PilotInfo2"
|
|
};
|
|
char *Multi_pxo_pinfo_mask_fname[GR_NUM_RESOLUTIONS] = {
|
|
"PilotInfo2-M",
|
|
"2_PilotInfo2-M"
|
|
};
|
|
|
|
// medals
|
|
#define MULTI_PXO_PINFO_NUM_BUTTONS 2
|
|
#define MULTI_PXO_PINFO_MEDALS 0
|
|
#define MULTI_PXO_PINFO_OK 1
|
|
|
|
ui_button_info Multi_pxo_pinfo_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_BUTTONS] = {
|
|
{ // GR_640
|
|
ui_button_info("PI2_00", 328, 446, 319, 433, 0),
|
|
ui_button_info("PI2_01", 376, 446, 382, 433, 1),
|
|
},
|
|
{ // GR_1024
|
|
ui_button_info("2_PI2_00", 525, 714, 510, 695, 0),
|
|
ui_button_info("2_PI2_01", 601, 714, 611, 695, 1),
|
|
}
|
|
};
|
|
|
|
// text
|
|
#define MULTI_PXO_PINFO_NUM_TEXT 2
|
|
UI_XSTR Multi_pxo_pinfo_text[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_TEXT] = {
|
|
{ // GR_640
|
|
{ "Medals", 1037, 319, 433, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_MEDALS].button },
|
|
{ "Ok", 345, 382, 433, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_OK].button },
|
|
},
|
|
{ // GR_1024
|
|
{ "Medals", 1037, 510, 695, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_MEDALS].button },
|
|
{ "Ok", 345, 611, 695, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_OK].button },
|
|
}
|
|
};
|
|
|
|
int Multi_pxo_pinfo_bitmap = -1;
|
|
UI_WINDOW Multi_pxo_pinfo_window;
|
|
|
|
|
|
player Multi_pxo_pinfo_player;
|
|
|
|
int Multi_pxo_retrieve_mode = -1;
|
|
|
|
char Multi_pxo_retrieve_name[MAX_PLAYER_NAME_LEN+1];
|
|
char Multi_pxo_retrieve_id[128];
|
|
|
|
// stats label stuff
|
|
#define MULTI_PXO_PINFO_NUM_LABELS 18
|
|
|
|
int Multi_pxo_pinfo_coords[GR_NUM_RESOLUTIONS][4] = {
|
|
{ // GR_640
|
|
37, 142, 377, 289
|
|
},
|
|
{ // GR_640
|
|
54, 227, 602, 462
|
|
},
|
|
};
|
|
int Multi_pxo_pinfo_val_x[GR_NUM_RESOLUTIONS] = {
|
|
230, // GR_640
|
|
310 // GR_1024
|
|
};
|
|
|
|
char *Multi_pxo_pinfo_stats_labels[MULTI_PXO_PINFO_NUM_LABELS];
|
|
|
|
char Multi_pxo_pinfo_vals[MULTI_PXO_PINFO_NUM_LABELS][50];
|
|
|
|
int Multi_pxo_pinfo_stats_spacing[MULTI_PXO_PINFO_NUM_LABELS] = {
|
|
10,20,10,10,20,10,10,20,10,10,20,10,10,20,10,20,10,0
|
|
};
|
|
|
|
// popup conditional functions, returns 10 on successful get of stats
|
|
int multi_pxo_pinfo_cond();
|
|
|
|
// return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
|
|
int multi_pxo_pinfo_get(char *name);
|
|
|
|
// fire up the stats view popup
|
|
void multi_pxo_pinfo_show();
|
|
|
|
// build the stats labels values
|
|
void multi_pxo_pinfo_build_vals();
|
|
|
|
// initialize the popup
|
|
void multi_pxo_pinfo_init();
|
|
|
|
// do frame
|
|
int multi_pxo_pinfo_do();
|
|
|
|
// close
|
|
void multi_pxo_pinfo_close();
|
|
|
|
// blit all the stats on this screen
|
|
void multi_pxo_pinfo_blit();
|
|
|
|
// run the medals screen
|
|
void multi_pxo_run_medals();
|
|
|
|
// notify stuff stuff -----------------------------------------
|
|
#define MULTI_PXO_NOTIFY_TIME 4000
|
|
#define MULTI_PXO_NOTIFY_Y 435
|
|
|
|
char Multi_pxo_notify_text[MAX_PXO_TEXT_LEN];
|
|
int Multi_pxo_notify_stamp = -1;
|
|
|
|
// add a notification string
|
|
void multi_pxo_notify_add(const char *txt);
|
|
|
|
// blit and process the notification string
|
|
void multi_pxo_notify_blit();
|
|
|
|
|
|
// help screen stuff -----------------------------------------
|
|
//XSTR:OFF
|
|
char *Multi_pxo_help_fname[GR_NUM_RESOLUTIONS] = {
|
|
"PXHelp",
|
|
"2_PXHelp"
|
|
};
|
|
char *Multi_pxo_help_mask_fname[GR_NUM_RESOLUTIONS] = {
|
|
"PXOHelp-M",
|
|
"2_PXOHelp-M"
|
|
};
|
|
|
|
#define MULTI_PXO_HELP_NUM_BUTTONS 3
|
|
#define MULTI_PXO_HELP_PREV 0
|
|
#define MULTI_PXO_HELP_NEXT 1
|
|
#define MULTI_PXO_HELP_CONTINUE 2
|
|
|
|
ui_button_info Multi_pxo_help_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_BUTTONS] = {
|
|
{ // GR_640
|
|
ui_button_info("PXH_00", 15, 389, -1, -1, 0),
|
|
ui_button_info("PXH_01", 60, 389, -1, -1, 1),
|
|
ui_button_info("PXH_02", 574, 431, 571, 413, 2),
|
|
},
|
|
{ // GR_1024
|
|
ui_button_info("2_PXH_00", 24, 622, -1, -1, 0),
|
|
ui_button_info("2_PXH_01", 96, 622, -1, -1, 1),
|
|
ui_button_info("2_PXH_02", 919, 689, 928, 663, 2),
|
|
}
|
|
};
|
|
|
|
#define MULTI_PXO_HELP_NUM_TEXT 1
|
|
UI_XSTR Multi_pxo_help_text[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_TEXT] = {
|
|
{ // GR_640
|
|
{"Continue", 1069, 571, 413, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[0][MULTI_PXO_HELP_CONTINUE].button },
|
|
},
|
|
{ // GR_1024
|
|
{"Continue", 1069, 928, 663, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[1][MULTI_PXO_HELP_CONTINUE].button },
|
|
},
|
|
};
|
|
|
|
// help text
|
|
#define MULTI_PXO_HELP_FILE "pxohelp.txt"
|
|
#define MULTI_PXO_MAX_LINES_PP 57
|
|
#define MULTI_PXO_MAX_PAGES 5
|
|
|
|
int Multi_pxo_help_coords[GR_NUM_RESOLUTIONS][2] = {
|
|
{ // GR_640
|
|
40, 40
|
|
},
|
|
{ // GR_1024
|
|
60, 40
|
|
}
|
|
};
|
|
|
|
int Multi_pxo_chars_per_line[GR_NUM_RESOLUTIONS] = {
|
|
130, // GR_640
|
|
130 // GR_1024
|
|
};
|
|
|
|
int Multi_pxo_lines_pp[GR_NUM_RESOLUTIONS] = {
|
|
32, // GR_640
|
|
57 // GR_1024
|
|
};
|
|
|
|
// help text pages
|
|
typedef struct help_page {
|
|
char *text[MULTI_PXO_MAX_LINES_PP];
|
|
int num_lines;
|
|
} help_page;
|
|
|
|
help_page Multi_pxo_help_pages[MULTI_PXO_MAX_PAGES];
|
|
|
|
int Multi_pxo_help_num_pages = 0;
|
|
|
|
int Multi_pxo_help_bitmap = -1;
|
|
UI_WINDOW Multi_pxo_help_window;
|
|
|
|
// current page we're on
|
|
int Multi_pxo_help_cur = 0;
|
|
|
|
// load the help file up
|
|
void multi_pxo_help_load();
|
|
|
|
// blit the current page
|
|
void multi_pxo_help_blit_page();
|
|
|
|
// process button presses
|
|
void multi_pxo_help_process_buttons();
|
|
|
|
// button pressed
|
|
void multi_pxo_help_button_pressed(int n);
|
|
|
|
|
|
// http banner stuff ---------------------------------------------
|
|
InetGetFile *Multi_pxo_ban_get = NULL;
|
|
|
|
// banners file
|
|
#define PXO_BANNERS_CONFIG_FILE "pxobanners.cfg"
|
|
|
|
// coords to display banners at
|
|
int Pxo_ban_coords[GR_NUM_RESOLUTIONS][4] = {
|
|
{ // GR_640
|
|
149, 3, 475, 75
|
|
},
|
|
{ // GR_1024
|
|
524, 3, 475, 75
|
|
}
|
|
};
|
|
|
|
// http modes
|
|
#define PXO_BAN_MODE_LIST_STARTUP 0 // start downloading list
|
|
#define PXO_BAN_MODE_LIST 1 // downloading list
|
|
#define PXO_BAN_MODE_IMAGES_STARTUP 2 // start downloading images
|
|
#define PXO_BAN_MODE_IMAGES 3 // downloading images
|
|
#define PXO_BAN_MODE_IMAGES_DONE 4 // done downloading everything - now maybe load an image
|
|
#define PXO_BAN_MODE_IDLE 5 // done with everything - doing nothing
|
|
#define PXO_BAN_MODE_CHOOSE_RANDOM 6 // choose a bitmap we've already downloaded at random
|
|
|
|
// interface button for detecting clicks
|
|
UI_BUTTON Multi_pxo_ban_button;
|
|
|
|
// banners
|
|
typedef struct pxo_banner {
|
|
char ban_file[MAX_FILENAME_LEN+1]; // base filename of the banner
|
|
char ban_file_url[MULTI_OPTIONS_STRING_LEN+1]; // full url of the file to get (convenient)
|
|
char ban_url[MULTI_OPTIONS_STRING_LEN+1]; // url to go to when clicked
|
|
int ban_bitmap; // banner bitmap
|
|
} pxo_banner;
|
|
|
|
// active pxo banner
|
|
pxo_banner Multi_pxo_banner;
|
|
|
|
// mode
|
|
int Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
|
|
|
|
// init
|
|
void multi_pxo_ban_init();
|
|
|
|
// process http download details
|
|
void multi_pxo_ban_process();
|
|
|
|
// close
|
|
void multi_pxo_ban_close();
|
|
|
|
// parse the banners file and maybe fill in Multi_pxo_dl_file[]
|
|
void multi_pxo_ban_parse_banner_file(int choose_existing);
|
|
|
|
// any bitmap or info or whatever
|
|
void multi_pxo_ban_draw();
|
|
|
|
// called when the URL button is clicked
|
|
void multi_pxo_ban_clicked();
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------------------
|
|
// PXO FUNCTIONS
|
|
//
|
|
|
|
// initialize the PXO screen
|
|
void multi_pxo_init(int use_last_channel)
|
|
{
|
|
int idx;
|
|
|
|
// load the background bitmap
|
|
Multi_pxo_bitmap = bm_load(Multi_pxo_bitmap_fname[gr_screen.res]);
|
|
if(Multi_pxo_bitmap < 0){
|
|
// we failed to load the bitmap - this is very bad
|
|
Int3();
|
|
}
|
|
|
|
// load up the private channel bitmap
|
|
Multi_pxo_com_bitmap = bm_load(Multi_pxo_com_fname[gr_screen.res]);
|
|
Assert(Multi_pxo_com_bitmap != -1);
|
|
|
|
// create the interface window
|
|
Multi_pxo_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
|
|
Multi_pxo_window.set_mask_bmap(Multi_pxo_mask_fname[gr_screen.res]);
|
|
|
|
// multiplayer screen common palettes
|
|
multi_pxo_load_palette();
|
|
|
|
// create the interface buttons
|
|
for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
|
|
// create the object
|
|
Multi_pxo_buttons[gr_screen.res][idx].button.create(&Multi_pxo_window, "", Multi_pxo_buttons[gr_screen.res][idx].x, Multi_pxo_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
|
|
|
|
// set the sound to play when highlighted
|
|
Multi_pxo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
|
|
|
|
// set the ani for the button
|
|
Multi_pxo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_buttons[gr_screen.res][idx].filename);
|
|
|
|
// set the hotspot
|
|
Multi_pxo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_buttons[gr_screen.res][idx].hotspot);
|
|
}
|
|
|
|
// add all xstrs
|
|
for(idx=0; idx<MULTI_PXO_NUM_TEXT; idx++){
|
|
Multi_pxo_window.add_XSTR(&Multi_pxo_text[gr_screen.res][idx]);
|
|
}
|
|
|
|
if(use_last_channel && strlen(Multi_pxo_channel_last)){
|
|
Multi_pxo_use_last_channel = 1;
|
|
} else {
|
|
memset(Multi_pxo_channel_last, 0, MAX_CHANNEL_NAME_LEN + 1);
|
|
Multi_pxo_use_last_channel = 0;
|
|
}
|
|
|
|
// make all scrolling buttons repeatable
|
|
Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_UP].button.repeatable(1);
|
|
Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_DOWN].button.repeatable(1);
|
|
Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_UP].button.repeatable(1);
|
|
Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_DOWN].button.repeatable(1);
|
|
Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_UP].button.repeatable(1);
|
|
Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_DOWN].button.repeatable(1);
|
|
|
|
// set the mouseover cursor if it loaded ok
|
|
if (Web_cursor_bitmap > 0) {
|
|
Multi_pxo_buttons[gr_screen.res][MULTI_PXO_RANKINGS].button.set_custom_cursor_bmap(Web_cursor_bitmap);
|
|
}
|
|
|
|
// create the channel list select button and hide it
|
|
Multi_pxo_channel_button.create(&Multi_pxo_window, "", Multi_pxo_chan_coords[gr_screen.res][0], Multi_pxo_chan_coords[gr_screen.res][1], Multi_pxo_chan_coords[gr_screen.res][2], Multi_pxo_chan_coords[gr_screen.res][3], 0, 1);
|
|
Multi_pxo_channel_button.hide();
|
|
|
|
// create the player list select button and hide it
|
|
Multi_pxo_player_button.create(&Multi_pxo_window, "", Multi_pxo_player_coords[gr_screen.res][0], Multi_pxo_player_coords[gr_screen.res][1], Multi_pxo_player_coords[gr_screen.res][2], Multi_pxo_player_coords[gr_screen.res][3], 0, 1);
|
|
Multi_pxo_player_button.hide();
|
|
|
|
// create the chat input box
|
|
Multi_pxo_chat_input.create(&Multi_pxo_window, Multi_pxo_input_coords[gr_screen.res][0], Multi_pxo_input_coords[gr_screen.res][1], Multi_pxo_input_coords[gr_screen.res][2], MAX_CHAT_LINE_LEN + 1, "", UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_EAT_USED);
|
|
Multi_pxo_chat_input.set_focus();
|
|
|
|
// create the banner button and hide it
|
|
Multi_pxo_ban_button.create(&Multi_pxo_window, "", Pxo_ban_coords[gr_screen.res][0], Pxo_ban_coords[gr_screen.res][1], Pxo_ban_coords[gr_screen.res][2], Pxo_ban_coords[gr_screen.res][3], 0, 1);
|
|
Multi_pxo_ban_button.hide();
|
|
|
|
// create the chat slider
|
|
Multi_pxo_chat_slider.create(&Multi_pxo_window, Multi_pxo_chat_slider_coords[gr_screen.res][0], Multi_pxo_chat_slider_coords[gr_screen.res][1], Multi_pxo_chat_slider_coords[gr_screen.res][2], Multi_pxo_chat_slider_coords[gr_screen.res][3], 0, Multi_pxo_chat_slider_name[gr_screen.res], multi_pxo_scroll_chat_up, multi_pxo_scroll_chat_down, NULL);
|
|
|
|
// set our connection status so that we do the right stuff next frame
|
|
Multi_pxo_must_validate = 1;
|
|
Multi_pxo_must_connect = 0;
|
|
Multi_pxo_connected = 0;
|
|
|
|
// channel we're currently connected to
|
|
memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
|
|
Multi_pxo_channel_current.num_users = -1;
|
|
|
|
// channel we're currently trying to change to, or NULL if nont
|
|
memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
|
|
Multi_pxo_channel_switch.num_users = -1;
|
|
|
|
// last time clicked the url button (so we don't have repeats)
|
|
Multi_pxo_ranking_last = -1.0f;
|
|
|
|
// channel switching extra time delay stamp
|
|
Multi_pxo_switch_delay = -1;
|
|
|
|
// our nick for this session
|
|
multi_pxo_underscore_nick(Player->callsign,Multi_pxo_nick);
|
|
|
|
// clear the channel list
|
|
multi_pxo_clear_channels();
|
|
|
|
// clear the player list
|
|
multi_pxo_clear_players();
|
|
|
|
// initialize the chat system
|
|
multi_pxo_chat_init();
|
|
|
|
// initialize http
|
|
multi_pxo_ban_init();
|
|
|
|
// load the animation up
|
|
if (gr_screen.res == GR_1024) {
|
|
char anim_filename[32] = "2_";
|
|
strcat_s(anim_filename, MULTI_PXO_ANIM_FNAME);
|
|
generic_anim_init(&Multi_pxo_anim, anim_filename);
|
|
Multi_pxo_anim.ani.bg_type = bm_get_type(Multi_pxo_bitmap);
|
|
|
|
// if hi-res is not there, fallback to low
|
|
if (generic_anim_stream(&Multi_pxo_anim) == -1) {
|
|
generic_anim_init(&Multi_pxo_anim, MULTI_PXO_ANIM_FNAME);
|
|
generic_anim_stream(&Multi_pxo_anim);
|
|
}
|
|
} else {
|
|
generic_anim_init(&Multi_pxo_anim, MULTI_PXO_ANIM_FNAME);
|
|
Multi_pxo_anim.ani.bg_type = bm_get_type(Multi_pxo_bitmap);
|
|
generic_anim_stream(&Multi_pxo_anim);
|
|
}
|
|
|
|
// clear the status text
|
|
multi_pxo_set_status_text("");
|
|
|
|
// last refresh time
|
|
Multi_pxo_channel_last_refresh = -1.0f;
|
|
|
|
// server count last refresh time
|
|
Multi_pxo_channel_server_refresh = -1.0f;
|
|
|
|
// set our mode
|
|
Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
|
|
|
|
// init motd
|
|
multi_pxo_motd_init();
|
|
|
|
// make sure we autojoin
|
|
Multi_pxo_must_autojoin = 1;
|
|
|
|
// clear all tracker channel related strings
|
|
memset(Multi_fs_tracker_channel, 0, MAX_PATH);
|
|
memset(Multi_fs_tracker_filter, 0, MAX_PATH);
|
|
|
|
main_hall_start_music();
|
|
}
|
|
|
|
// do frame for the PXO screen
|
|
void multi_pxo_do()
|
|
{
|
|
pxo_channel priv_chan;
|
|
|
|
// run api stuff
|
|
if(Multi_pxo_connected) {
|
|
multi_pxo_api_process();
|
|
}
|
|
|
|
// process common stuff
|
|
multi_pxo_process_common();
|
|
|
|
switch(Multi_pxo_mode){
|
|
// private channel join mode
|
|
case MULTI_PXO_MODE_PRIVATE:
|
|
switch(multi_pxo_priv_popup()){
|
|
// still running
|
|
case 0:
|
|
break;
|
|
|
|
// user hit "cancel"
|
|
case -1:
|
|
// return to normal mode
|
|
Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
|
|
break;
|
|
|
|
// user hit "ok"
|
|
case 1 :
|
|
// setup some information
|
|
memset(&priv_chan, 0, sizeof(pxo_channel));
|
|
priv_chan.num_users = 0;
|
|
strcpy_s(priv_chan.name, Multi_pxo_priv_chan);
|
|
|
|
// see if we know about this channel already
|
|
multi_pxo_join_channel(&priv_chan);
|
|
|
|
// return to normal mode
|
|
Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
// find player mode
|
|
case MULTI_PXO_MODE_FIND:
|
|
switch(multi_pxo_find_popup()){
|
|
// still running
|
|
case 0:
|
|
break;
|
|
|
|
// user hit "cancel"
|
|
case -1:
|
|
// return to normal mode
|
|
Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
|
|
break;
|
|
|
|
// user hit "ok"
|
|
case 1 :
|
|
// return to normal mode
|
|
Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
|
|
|
|
// if there is a valid channel name try and join it
|
|
if(strlen(Multi_pxo_find_channel) && !SWITCHING_CHANNELS()){
|
|
pxo_channel join;
|
|
|
|
// setup the info
|
|
memset(&join,0,sizeof(pxo_channel));
|
|
join.num_users = 0;
|
|
strcpy_s(join.name,Multi_pxo_find_channel);
|
|
|
|
// try and join
|
|
multi_pxo_join_channel(&join);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
// normal mode
|
|
case MULTI_PXO_MODE_NORMAL:
|
|
multi_pxo_do_normal();
|
|
break;
|
|
}
|
|
}
|
|
//XSTR:ON
|
|
// close the PXO screen
|
|
void multi_pxo_close()
|
|
{
|
|
// unload any bitmaps
|
|
bm_release(Multi_pxo_bitmap);
|
|
bm_release(Multi_pxo_com_bitmap);
|
|
|
|
// record the last channel we were on, if any
|
|
memset(Multi_fs_tracker_channel, 0, MAX_PATH);
|
|
memset(Multi_fs_tracker_filter, 0, MAX_PATH);
|
|
|
|
if ( ON_CHANNEL() && strlen(Multi_pxo_channel_current.name) ) {
|
|
// channel name
|
|
strcpy(Multi_fs_tracker_channel, Multi_pxo_channel_current.name);
|
|
|
|
// filter name
|
|
strcpy(Multi_fs_tracker_filter, Multi_pxo_channel_current.name);
|
|
}
|
|
|
|
// disconnect from the server
|
|
DisconnectFromChatServer();
|
|
Multi_pxo_connected = 0;
|
|
|
|
// unload the animation
|
|
if(Multi_pxo_anim.num_frames > 0){
|
|
generic_anim_unload(&Multi_pxo_anim);
|
|
}
|
|
|
|
// unload the palette for this screen
|
|
multi_pxo_unload_palette();
|
|
|
|
// destroy the UI_WINDOW
|
|
Multi_pxo_window.destroy();
|
|
|
|
// clear the channel list
|
|
multi_pxo_clear_channels();
|
|
|
|
// close the chat system
|
|
multi_pxo_chat_free();
|
|
|
|
// close http stuff
|
|
multi_pxo_ban_close();
|
|
}
|
|
|
|
// run normally (no popups)
|
|
void multi_pxo_do_normal()
|
|
{
|
|
int k = Multi_pxo_window.process();
|
|
|
|
// process any keypresses
|
|
switch (k)
|
|
{
|
|
case KEY_ESC:
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
gameseq_post_event(GS_EVENT_MAIN_MENU);
|
|
break;
|
|
}
|
|
|
|
// check for button presses
|
|
multi_pxo_check_buttons();
|
|
|
|
// if we're not in a chatroom, disable and hide the chat input box
|
|
if ( !ON_CHANNEL() ) {
|
|
Multi_pxo_chat_input.hide();
|
|
Multi_pxo_chat_input.disable();
|
|
} else {
|
|
Multi_pxo_chat_input.enable();
|
|
Multi_pxo_chat_input.unhide();
|
|
}
|
|
|
|
// blit everything
|
|
multi_pxo_blit_all();
|
|
|
|
// flip the page
|
|
gr_flip();
|
|
|
|
// if we need to get tracker info for ourselves, do so
|
|
if (Multi_pxo_must_validate) {
|
|
// validate the current player with the master tracker (will create the pilot on the MT if necessary)
|
|
bool validate_code = fs2netd_login();
|
|
|
|
if ( !validate_code ) {
|
|
// go back to the main hall
|
|
gameseq_post_event(GS_EVENT_MAIN_MENU);
|
|
|
|
Multi_pxo_must_connect = 0;
|
|
Multi_pxo_must_validate = 0;
|
|
}
|
|
// now we have to conenct to PXO
|
|
else {
|
|
Multi_pxo_must_connect = 1;
|
|
Multi_pxo_must_validate = 0;
|
|
}
|
|
}
|
|
|
|
// if we need to connect, do so now
|
|
if (Multi_pxo_must_connect) {
|
|
// for now, just try once
|
|
Multi_pxo_connected = multi_pxo_connect();
|
|
|
|
// if we successfully connected, send a request for a list of channels on the server
|
|
if(Multi_pxo_connected){
|
|
multi_pxo_get_channels();
|
|
|
|
// set our status
|
|
multi_pxo_set_status_text(XSTR("Retrieving Public Channels",939));
|
|
} else {
|
|
// set our status
|
|
multi_pxo_set_status_text(XSTR("Failed to connect to Parallax Online",940));
|
|
}
|
|
|
|
// no longer need to connect
|
|
Multi_pxo_must_connect = 0;
|
|
}
|
|
}
|
|
|
|
// blit everything on the "normal" screen
|
|
void multi_pxo_blit_all()
|
|
{
|
|
// draw the background, etc
|
|
gr_reset_clip();
|
|
// GR_MAYBE_CLEAR_RES(Multi_pxo_bitmap);
|
|
int bmap = Multi_pxo_bitmap;
|
|
do {
|
|
int bmw = -1;
|
|
int bmh = -1;
|
|
if(bmap != -1){
|
|
bm_get_info( bmap, &bmw, &bmh);
|
|
if((bmw != gr_screen.max_w) || (bmh != gr_screen.max_h)){
|
|
gr_clear();
|
|
}
|
|
} else {
|
|
gr_clear();
|
|
}
|
|
} while(0);
|
|
if(Multi_pxo_bitmap != -1){
|
|
gr_set_bitmap(Multi_pxo_bitmap);
|
|
gr_bitmap(0,0,GR_RESIZE_MENU);
|
|
}
|
|
Multi_pxo_window.draw();
|
|
|
|
// display the channel list
|
|
multi_pxo_blit_channels();
|
|
|
|
// display the player list
|
|
multi_pxo_blit_players();
|
|
|
|
// blit the chat text
|
|
multi_pxo_chat_blit();
|
|
|
|
// blit the status text
|
|
multi_pxo_blit_status_text();
|
|
|
|
// blit and process the notification string
|
|
multi_pxo_notify_blit();
|
|
|
|
// any bitmap or info or whatever
|
|
multi_pxo_ban_draw();
|
|
|
|
// draw any motd stuff
|
|
multi_pxo_motd_maybe_blit();
|
|
|
|
// if we have a valid animation handle, play it
|
|
// display the mission start countdown timer (if any)
|
|
//anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
|
|
if(gameseq_get_state() == GS_STATE_PXO && Multi_pxo_anim.num_frames > 0)
|
|
generic_anim_render(&Multi_pxo_anim, flFrametime, MULTI_PXO_ANIM_X, MULTI_PXO_ANIM_Y, true);
|
|
}
|
|
|
|
// process common stuff
|
|
void multi_pxo_process_common()
|
|
{
|
|
// process the channel list (select, etc)
|
|
multi_pxo_process_channels();
|
|
|
|
// process the player list (select, etc)
|
|
multi_pxo_process_players();
|
|
|
|
// process chat controls
|
|
multi_pxo_chat_process();
|
|
|
|
// process http download details
|
|
multi_pxo_ban_process();
|
|
}
|
|
|
|
// get selected player information
|
|
void multi_pxo_get_data(char *name)
|
|
{
|
|
}
|
|
|
|
// handle being kicked
|
|
void multi_pxo_handle_kick()
|
|
{
|
|
// remove ourselves from the room
|
|
memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
|
|
Multi_pxo_channel_current.num_users = -1;
|
|
|
|
// clear text
|
|
multi_pxo_chat_clear();
|
|
|
|
// clear the old player list
|
|
multi_pxo_clear_players();
|
|
|
|
// add a notification string
|
|
multi_pxo_notify_add(XSTR("You have been kicked",941));
|
|
}
|
|
|
|
// handle being disconnected
|
|
void multi_pxo_handle_disconnect()
|
|
{
|
|
ml_printf("PXO: Got DISCONNECT from server!");
|
|
|
|
if ( popup_active() ) {
|
|
popup_change_text( XSTR("You have been disconnected from the server", 942) );
|
|
} else {
|
|
popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You have been disconnected from the server", 942));
|
|
gameseq_post_event(GS_EVENT_MAIN_MENU);
|
|
}
|
|
}
|
|
|
|
// return string2, which is the first substring of string 1 without a space
|
|
// it is safe to pass the same pointer for both parameters
|
|
void multi_pxo_strip_space(char *string1,char *string2)
|
|
{
|
|
char midway[MAX_PXO_TEXT_LEN];
|
|
char *tok;
|
|
|
|
// copy the original
|
|
strcpy_s(midway,string1);
|
|
tok = strtok(midway," ");
|
|
if(tok != NULL){
|
|
strcpy(string2,tok);
|
|
} else {
|
|
strcpy(string2,"");
|
|
}
|
|
}
|
|
|
|
// fire up the given URL
|
|
void multi_pxo_url(char *url)
|
|
{
|
|
#if 0
|
|
// execute the shell command
|
|
int r = (int) ShellExecute(NULL, NOX("open"), url, NULL, NULL, SW_SHOW);
|
|
if (r < 32) {
|
|
switch (r) {
|
|
case 0:
|
|
case ERROR_BAD_FORMAT:
|
|
case SE_ERR_ACCESSDENIED:
|
|
case SE_ERR_ASSOCINCOMPLETE:
|
|
case SE_ERR_DDEBUSY:
|
|
case SE_ERR_DDEFAIL:
|
|
case SE_ERR_DDETIMEOUT:
|
|
case SE_ERR_DLLNOTFOUND:
|
|
case SE_ERR_OOM:
|
|
case SE_ERR_SHARE:
|
|
case SE_ERR_NOASSOC:
|
|
case ERROR_FILE_NOT_FOUND:
|
|
case ERROR_PATH_NOT_FOUND:
|
|
popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,XSTR("Warning\nCould not locate/launch default Internet Browser",943));
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// load/set the palette
|
|
void multi_pxo_load_palette()
|
|
{
|
|
// use the palette
|
|
#ifndef HARDWARE_ONLY
|
|
palette_use_bm_palette(Multi_pxo_palette);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Unload the palette
|
|
*/
|
|
void multi_pxo_unload_palette()
|
|
{
|
|
// unload the palette if it exists
|
|
if(Multi_pxo_palette != -1){
|
|
bm_release(Multi_pxo_palette);
|
|
Multi_pxo_palette = -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If we're currently on a private channel
|
|
*/
|
|
int multi_pxo_on_private_channel()
|
|
{
|
|
// if we're connected to a channel with the "+" symbol on front
|
|
if(ON_CHANNEL() && (Multi_pxo_channel_current.name[0] == '+')){
|
|
return 1;
|
|
}
|
|
|
|
// otherwise return falos
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Convert string 1 into string 2, substituting underscores for spaces
|
|
*/
|
|
void multi_pxo_underscore_nick(char *string1,char *string2)
|
|
{
|
|
char nick_temp[512];
|
|
char *tok;
|
|
|
|
// don't do anything if we have bogus string
|
|
if((string1 == NULL) || (string2 == NULL)){
|
|
return;
|
|
}
|
|
|
|
// copy the nickname
|
|
memset(nick_temp,0,512);
|
|
strcpy_s(nick_temp,string1);
|
|
|
|
// get the first token
|
|
tok = strtok(nick_temp," ");
|
|
if(tok != NULL){
|
|
strcpy(string2,tok);
|
|
|
|
// get the next token
|
|
tok = strtok(NULL," ");
|
|
while(tok != NULL){
|
|
if(tok != NULL){
|
|
strcat(string2,"_");
|
|
strcat(string2,tok);
|
|
}
|
|
|
|
tok = strtok(NULL," ");
|
|
}
|
|
} else {
|
|
strcpy(string2,string1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If the command is a potential "nick" command
|
|
*/
|
|
int multi_pxo_is_nick_command(char *msg)
|
|
{
|
|
char *tok;
|
|
char tmp[512];
|
|
|
|
// get the first token in the message
|
|
memset(tmp,0,512);
|
|
strcpy_s(tmp,msg);
|
|
tok = strtok(tmp," ");
|
|
if(tok == NULL){
|
|
// can't be a nick message
|
|
return 0;
|
|
}
|
|
|
|
return !stricmp(tok,NOX("/nick"));
|
|
}
|
|
|
|
/**
|
|
* Check for button presses
|
|
*/
|
|
void multi_pxo_check_buttons()
|
|
{
|
|
int idx;
|
|
|
|
// go through all buttons
|
|
for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
|
|
if(Multi_pxo_buttons[gr_screen.res][idx].button.pressed()){
|
|
multi_pxo_button_pressed(idx);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle a button press
|
|
*/
|
|
void multi_pxo_button_pressed(int n)
|
|
{
|
|
switch(n){
|
|
case MULTI_PXO_EXIT:
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
gameseq_post_event(GS_EVENT_MAIN_MENU);
|
|
break;
|
|
|
|
case MULTI_PXO_CHAN_UP:
|
|
multi_pxo_scroll_channels_up();
|
|
break;
|
|
|
|
case MULTI_PXO_CHAN_DOWN:
|
|
multi_pxo_scroll_channels_down();
|
|
break;
|
|
|
|
case MULTI_PXO_TEXT_UP:
|
|
multi_pxo_scroll_chat_up();
|
|
break;
|
|
|
|
case MULTI_PXO_TEXT_DOWN:
|
|
multi_pxo_scroll_chat_down();
|
|
break;
|
|
|
|
case MULTI_PXO_PLIST_UP:
|
|
multi_pxo_scroll_players_up();
|
|
multi_pxo_chat_adjust_start();
|
|
break;
|
|
|
|
case MULTI_PXO_PLIST_DOWN:
|
|
multi_pxo_scroll_players_down();
|
|
multi_pxo_chat_adjust_start();
|
|
break;
|
|
|
|
case MULTI_PXO_JOIN:
|
|
// if there are no channels to join, let the user know
|
|
if((Multi_pxo_channel_count == 0) || (Multi_pxo_channels == NULL)){
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
multi_pxo_notify_add(XSTR("No channels!",944));
|
|
break;
|
|
}
|
|
|
|
// if we're not already trying to join, allow this
|
|
if(!SWITCHING_CHANNELS() && (Multi_pxo_channel_select != NULL)){
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
multi_pxo_join_channel(Multi_pxo_channel_select);
|
|
} else {
|
|
multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
}
|
|
break;
|
|
|
|
case MULTI_PXO_GAMES:
|
|
// move to the join game screen as normally (temporary!)
|
|
gameseq_post_event( GS_EVENT_MULTI_JOIN_GAME );
|
|
break;
|
|
|
|
case MULTI_PXO_JOIN_PRIV:
|
|
// if we're not already trying to join, allow this
|
|
if(!SWITCHING_CHANNELS()){
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
|
|
// fire up the private join popup
|
|
multi_pxo_priv_popup();
|
|
} else {
|
|
multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
}
|
|
break;
|
|
|
|
case MULTI_PXO_FIND:
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
|
|
// fire up the find join popup
|
|
multi_pxo_find_popup();
|
|
break;
|
|
|
|
case MULTI_PXO_HELP:
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
gameseq_post_event(GS_EVENT_PXO_HELP);
|
|
break;
|
|
|
|
case MULTI_PXO_PINFO:
|
|
char stats[MAX_PXO_TEXT_LEN];
|
|
|
|
// if we have a guy selected, try and get his info
|
|
if(Multi_pxo_player_select != NULL){
|
|
// if we successfully got info for this guy
|
|
if(multi_pxo_pinfo_get(Multi_pxo_player_select->name)){
|
|
// show the stats
|
|
multi_pxo_pinfo_show();
|
|
}
|
|
// if we didn't get stats for this guy.
|
|
else {
|
|
memset(stats,0,MAX_PXO_TEXT_LEN);
|
|
sprintf(stats,XSTR("Could not get stats for %s\n(May not be a registered pilot)",946),Multi_pxo_player_select->name);
|
|
popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,stats);
|
|
}
|
|
} else {
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
}
|
|
break;
|
|
|
|
case MULTI_PXO_RANKINGS:
|
|
// make sure he doesn't click it too many times
|
|
if((Multi_pxo_ranking_last < 0.0f) || ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_ranking_last) > MULTI_PXO_RANK_TIME) ){
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
|
|
// fire up the url
|
|
multi_pxo_url(Multi_options_g.pxo_rank_url);
|
|
|
|
// mark the time down
|
|
Multi_pxo_ranking_last = f2fl(timer_get_fixed_seconds());
|
|
} else {
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
}
|
|
break;
|
|
|
|
case MULTI_PXO_MOTD:
|
|
// maybe fire up the pxo motd dialog
|
|
multi_pxo_motd_dialog();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Condition function for popup_do_with_condition for connected to Parallax Online
|
|
*/
|
|
int mpxo_failed = 0;
|
|
int multi_pxo_connect_do()
|
|
{
|
|
int ret_code;
|
|
char id_string[MAX_PXO_TEXT_LEN] = "";
|
|
char ip_string[MAX_PXO_TEXT_LEN] = "";
|
|
|
|
// if we already tried and failed, sit around until the user presses cancel
|
|
if(!mpxo_failed){
|
|
// try and connect to the server
|
|
Assert(Player);
|
|
|
|
// build the tracker id string
|
|
memset(id_string, 0, MAX_PXO_TEXT_LEN);
|
|
sprintf(id_string, "%s %s", Multi_tracker_id_string, Player->callsign);
|
|
|
|
// build the ip string
|
|
memset(ip_string, 0, MAX_PXO_TEXT_LEN);
|
|
sprintf(ip_string, "%s:%d", Multi_options_g.pxo_ip, PXO_CHAT_PORT);
|
|
|
|
// connect to the server
|
|
ret_code = ConnectToChatServer(ip_string, Multi_pxo_nick, id_string);
|
|
|
|
// give some time to the pxo api.
|
|
multi_pxo_api_process();
|
|
|
|
switch(ret_code){
|
|
// already connected, return success
|
|
case -2:
|
|
return 10;
|
|
|
|
// failed to connect, return fail
|
|
case -1 :
|
|
mpxo_failed = 1;
|
|
return 1;
|
|
|
|
// connected, return success
|
|
case 1 :
|
|
return 10;
|
|
|
|
// still connecting
|
|
case 0 :
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Popup loop which does an autojoin of a public channel.
|
|
*
|
|
* Returns when the autojoin process is complete
|
|
*/
|
|
int multi_pxo_autojoin_do()
|
|
{
|
|
pxo_channel last_channel;
|
|
|
|
// if we need to autojoin, do so now
|
|
if (Multi_pxo_must_autojoin) {
|
|
Multi_pxo_must_autojoin = 0;
|
|
|
|
// if we're supposed to be using a (valid) "last" channel, do so
|
|
if ( Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last) ) {
|
|
// setup the data
|
|
memset(&last_channel, 0, sizeof(pxo_channel));
|
|
last_channel.num_users = 0;
|
|
strcpy_s(last_channel.name, Multi_pxo_channel_last);
|
|
|
|
// join the channel
|
|
multi_pxo_join_channel(&last_channel);
|
|
|
|
nprintf(("Network","PXO : using last channel\n"));
|
|
} else {
|
|
multi_pxo_autojoin();
|
|
|
|
nprintf(("Network","PXO : using autojoin channel\n"));
|
|
}
|
|
|
|
multi_pxo_get_channels();
|
|
}
|
|
|
|
// give some time to the pxo api.
|
|
multi_pxo_api_process();
|
|
multi_pxo_process_common();
|
|
|
|
// next value is not -1 when actually switching channels, so keep processing by returning 0.
|
|
if ( SWITCHING_CHANNELS() )
|
|
return 0;
|
|
|
|
// couldn't switch channel for some reason. bail out with -1
|
|
if ( !ON_CHANNEL() )
|
|
return -1;
|
|
|
|
// return success
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Attempt to connect to Parallax Online, return success or fail
|
|
*/
|
|
int multi_pxo_connect()
|
|
{
|
|
char join_str[256];
|
|
char join_fail_str[256];
|
|
|
|
// intiialize chat api
|
|
ChatInit();
|
|
|
|
// set us to "must autojoin"
|
|
Multi_pxo_must_autojoin = 1;
|
|
|
|
// run the connect dialog/popup
|
|
mpxo_failed = 0;
|
|
|
|
if ( popup_till_condition(multi_pxo_connect_do, XSTR("&Cancel", 779), XSTR("Logging into Parallax Online", 949)) == 10 ) {
|
|
int rval;
|
|
|
|
memset(join_str, 0, 256);
|
|
memset(join_fail_str, 0, 256);
|
|
|
|
// if we're going to use the "last" channel
|
|
if ( Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last) ) {
|
|
strcpy_s(join_str, XSTR("Joining last channel (", 982));
|
|
strcat_s(join_str, Multi_pxo_channel_last + 1);
|
|
strcat_s(join_str, ")");
|
|
|
|
strcpy_s(join_fail_str, XSTR("Unable to join last channel", 983));
|
|
} else {
|
|
strcpy_s(join_str, XSTR("Autojoining public channel", 984));
|
|
strcpy_s(join_fail_str, XSTR("Unable to autojoin public channel", 985));
|
|
}
|
|
|
|
// once connected, we should do an autojoin before allowing the guy to continue.
|
|
rval = popup_till_condition( multi_pxo_autojoin_do, XSTR("&Cancel", 779), join_str );
|
|
|
|
if ( rval == 1 )
|
|
return 1;
|
|
|
|
popup(PF_USE_AFFIRMATIVE_ICON, 1, XSTR("OK", 1492), join_fail_str);
|
|
}
|
|
|
|
// otherwise disconnect just to be safe
|
|
DisconnectFromChatServer();
|
|
|
|
// we failed to connect, so give a nice popup about that
|
|
if (mpxo_failed) {
|
|
popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Failed to connect to Parallax Online!", 947));
|
|
}
|
|
|
|
// if we are coming from the mainhall then fail to the join game screen rather
|
|
// than keeping the user constantly at the mainhall
|
|
if (gameseq_get_previous_state() == GS_STATE_MAIN_MENU) {
|
|
gameseq_post_event(GS_EVENT_MULTI_JOIN_GAME);
|
|
} else {
|
|
gameseq_post_event(GS_EVENT_MAIN_MENU);
|
|
}
|
|
|
|
// did not successfully connect
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Run the networking functions for the PXO API
|
|
*/
|
|
void multi_pxo_api_process()
|
|
{
|
|
char *p;
|
|
char msg_str[512];
|
|
Chat_command *cmd;
|
|
pxo_channel *lookup;
|
|
|
|
// give some time to psnet
|
|
PSNET_TOP_LAYER_PROCESS();
|
|
|
|
// give some time to FS2NetD
|
|
fs2netd_do_frame();
|
|
|
|
// get any incoming text
|
|
do
|
|
{
|
|
p = GetChatText();
|
|
|
|
if (p) {
|
|
// process the chat line
|
|
multi_pxo_chat_process_incoming(p);
|
|
}
|
|
} while(p);
|
|
|
|
// get any incoming channel list stuff
|
|
p = GetChannelList();
|
|
|
|
if (p) {
|
|
multi_pxo_make_channels(p);
|
|
}
|
|
|
|
// process any chat commands
|
|
cmd = GetChatCommand();
|
|
|
|
while (cmd) {
|
|
switch (cmd->command)
|
|
{
|
|
case CC_USER_JOINING:
|
|
// add a user, if he doesn't already exist
|
|
if (multi_pxo_find_player(cmd->data) == NULL)
|
|
multi_pxo_add_player(cmd->data);
|
|
|
|
// increase the player count
|
|
if (ON_CHANNEL() ) {
|
|
lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name, Multi_pxo_channels);
|
|
|
|
if (lookup != NULL)
|
|
lookup->num_users++;
|
|
}
|
|
break;
|
|
|
|
case CC_USER_LEAVING:
|
|
// delete a user
|
|
multi_pxo_del_player(cmd->data);
|
|
|
|
// add a text message
|
|
memset(msg_str, 0, 512);
|
|
sprintf(msg_str, XSTR("*** %s has left", 950), cmd->data);
|
|
multi_pxo_chat_process_incoming(msg_str);
|
|
|
|
// decrease the player count
|
|
if ( ON_CHANNEL() ) {
|
|
lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
|
|
|
|
if (lookup != NULL)
|
|
lookup->num_users--;
|
|
}
|
|
break;
|
|
|
|
case CC_DISCONNECTED:
|
|
multi_pxo_handle_disconnect();
|
|
break;
|
|
|
|
case CC_KICKED:
|
|
multi_pxo_handle_kick();
|
|
break;
|
|
|
|
case CC_NICKCHANGED:
|
|
// process a nick change
|
|
multi_pxo_process_nick_change(cmd->data);
|
|
break;
|
|
|
|
case CC_YOURCHANNEL:
|
|
// copy the current channel info, and unset the switching status
|
|
memset( &Multi_pxo_channel_current, 0, sizeof(pxo_channel) );
|
|
Multi_pxo_channel_switch.num_users = -1;
|
|
|
|
SetNewChatChannel(NULL);
|
|
|
|
strcpy_s(Multi_pxo_channel_current.name, cmd->data);
|
|
|
|
// if we don't already have this guy on the list, add him
|
|
lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name, Multi_pxo_channels);
|
|
|
|
if (lookup == NULL) {
|
|
// create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
|
|
lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name, &Multi_pxo_channels);
|
|
}
|
|
|
|
// set the user count to be 0
|
|
if (lookup != NULL)
|
|
lookup->num_users = 0;
|
|
|
|
// set our "last" channel to be this one
|
|
strcpy_s(Multi_pxo_channel_last, Multi_pxo_channel_current.name);
|
|
|
|
// refresh current channel server count
|
|
multi_pxo_channel_refresh_current();
|
|
|
|
break;
|
|
|
|
default:
|
|
Int3();
|
|
}
|
|
|
|
cmd = GetChatCommand();
|
|
}
|
|
|
|
// handle any processing details if we're currently trying to join a channel
|
|
multi_pxo_handle_channel_change();
|
|
}
|
|
|
|
/**
|
|
* Process a "nick" change event
|
|
*/
|
|
void multi_pxo_process_nick_change(char *data)
|
|
{
|
|
char *from, *to;
|
|
player_list *lookup;
|
|
|
|
// get the new string
|
|
from = strtok(data," ");
|
|
to = strtok(NULL,"");
|
|
if((from != NULL) && (to != NULL)){
|
|
lookup = multi_pxo_find_player(from);
|
|
if(lookup != NULL){
|
|
strcpy_s(lookup->name,to);
|
|
|
|
// if this is also my nick, change it
|
|
if(!stricmp(Multi_pxo_nick,from)){
|
|
strcpy_s(Multi_pxo_nick,to);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Autojoin an appropriate channel
|
|
*/
|
|
void multi_pxo_autojoin()
|
|
{
|
|
pxo_channel sw;
|
|
|
|
memset( &sw, 0, sizeof(pxo_channel) );
|
|
sw.num_users = 0;
|
|
strcpy_s(sw.name,MULTI_PXO_AUTOJOIN_CHANNEL);
|
|
|
|
// if we found a valid room, attempt to join it
|
|
multi_pxo_join_channel(&sw);
|
|
}
|
|
|
|
/**
|
|
* Does the string match the "autojoin" prefix
|
|
*/
|
|
int multi_pxo_is_autojoin(char *name)
|
|
{
|
|
// check to see if the name is long enough
|
|
if ( strlen(name) < strlen(MULTI_PXO_AUTOJOIN_PREFIX) )
|
|
return 0;
|
|
|
|
// check to see if the first n chars match
|
|
return !strnicmp(name, MULTI_PXO_AUTOJOIN_PREFIX, strlen(MULTI_PXO_AUTOJOIN_PREFIX));
|
|
}
|
|
|
|
/**
|
|
* Called from the game tracker API - server count update for a channel
|
|
*/
|
|
void multi_pxo_channel_count_update(char *name, int count)
|
|
{
|
|
pxo_channel *lookup;
|
|
|
|
// lookup the channel name on the normal list
|
|
lookup = multi_pxo_find_channel(name,Multi_pxo_channels);
|
|
|
|
if (lookup != NULL) {
|
|
lookup->num_servers = (ushort)count;
|
|
|
|
nprintf(("Network","PXO : updated channel %s server count to %d\n",name,count));
|
|
ml_printf("PXO : updated channel %s server count to %d", name, count);
|
|
} else {
|
|
ml_printf("PXO : unable to locate channel when trying to update count for %s", name);
|
|
}
|
|
}
|
|
|
|
// status bar stuff -----------------------------------------------
|
|
|
|
/**
|
|
* Set the status text
|
|
*/
|
|
void multi_pxo_set_status_text(const char *txt)
|
|
{
|
|
// copy in the text
|
|
memset(Multi_pxo_status_text, 0, MAX_PXO_TEXT_LEN);
|
|
strncpy(Multi_pxo_status_text, txt, MAX_PXO_TEXT_LEN-1);
|
|
|
|
// make sure it fits properly
|
|
gr_force_fit_string(Multi_pxo_status_text, MAX_PXO_TEXT_LEN-1, Multi_pxo_status_coords[gr_screen.res][2]);
|
|
}
|
|
|
|
/**
|
|
* Blit the status text
|
|
*/
|
|
void multi_pxo_blit_status_text()
|
|
{
|
|
int w;
|
|
|
|
// center and draw the text
|
|
if(strlen(Multi_pxo_status_text)) {
|
|
gr_set_color_fast(&Color_bright);
|
|
gr_get_string_size(&w, NULL, Multi_pxo_status_text);
|
|
gr_string(Multi_pxo_status_coords[gr_screen.res][0] + ((Multi_pxo_status_coords[gr_screen.res][2] - w)/2), Multi_pxo_status_coords[gr_screen.res][1], Multi_pxo_status_text, GR_RESIZE_MENU);
|
|
}
|
|
}
|
|
|
|
|
|
// channel related stuff -------------------------------------------
|
|
|
|
/**
|
|
* Get a list of channels on the server
|
|
*/
|
|
void multi_pxo_get_channels()
|
|
{
|
|
SendChatString(NOX("/list"));
|
|
}
|
|
|
|
/**
|
|
* Clear the old channel list
|
|
*/
|
|
void multi_pxo_clear_channels()
|
|
{
|
|
pxo_channel *moveup,*backup;
|
|
|
|
// only clear a non-null list
|
|
if(Multi_pxo_channels != NULL){
|
|
// otherwise
|
|
moveup = Multi_pxo_channels;
|
|
backup = NULL;
|
|
if(moveup != NULL){
|
|
do {
|
|
backup = moveup;
|
|
moveup = moveup->next;
|
|
|
|
// free the struct itself
|
|
vm_free(backup);
|
|
backup = NULL;
|
|
} while(moveup != Multi_pxo_channels);
|
|
Multi_pxo_channels = NULL;
|
|
}
|
|
|
|
// head of the list of available channels
|
|
Multi_pxo_channels = NULL;
|
|
Multi_pxo_channel_count = 0;
|
|
|
|
// item we're going to start displaying at
|
|
Multi_pxo_channel_start = NULL;
|
|
Multi_pxo_channel_start_index = -1;
|
|
|
|
// items we've currently got selected
|
|
Multi_pxo_channel_select = NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse the input string and make a list of new channels
|
|
*/
|
|
void multi_pxo_make_channels(char *chan_str)
|
|
{
|
|
char *name_tok,*user_tok,*desc_tok;
|
|
pxo_channel *res;
|
|
pxo_channel *lookup;
|
|
int num_users;
|
|
|
|
nprintf(("Network","Making some channels!\n"));
|
|
|
|
// set the last get time
|
|
Multi_pxo_channel_last_refresh = f2fl(timer_get_fixed_seconds());
|
|
|
|
name_tok = strtok(chan_str," ");
|
|
if(name_tok == NULL){
|
|
return;
|
|
}
|
|
name_tok += 1;
|
|
do {
|
|
// parse the user count token
|
|
user_tok = strtok(NULL," ");
|
|
|
|
// parse the channel description token
|
|
desc_tok = strtok(NULL,"$");
|
|
|
|
// something invalid in the data, return here.....
|
|
if((name_tok == NULL) || (user_tok == NULL) || (desc_tok == NULL)){
|
|
return;
|
|
}
|
|
|
|
// get the # of users
|
|
num_users = (ubyte)atoi(user_tok);
|
|
|
|
// if the # of users is > 0, or its not an autojoin, place it on the display list
|
|
if((num_users > 0) || !multi_pxo_is_autojoin(name_tok)){
|
|
// see if it exists already, and if so, just update the user count
|
|
lookup = multi_pxo_find_channel(name_tok,Multi_pxo_channels);
|
|
|
|
if(lookup != NULL){
|
|
lookup->num_users = (short)num_users;
|
|
}
|
|
// add the channel
|
|
else {
|
|
res = multi_pxo_add_channel(name_tok,&Multi_pxo_channels);
|
|
if(res != NULL){
|
|
res->num_users = (short)num_users;
|
|
strcpy_s(res->desc,desc_tok);
|
|
}
|
|
}
|
|
}
|
|
|
|
// get the next name token
|
|
name_tok = strtok(NULL," ");
|
|
} while(name_tok != NULL);
|
|
|
|
// refresh channels
|
|
multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
|
|
|
|
// if we haven't refreshed server counts yet, do it now
|
|
if(Multi_pxo_channel_server_refresh < 0.0f){
|
|
multi_pxo_channel_refresh_servers();
|
|
}
|
|
|
|
// if we don't already have this guy on the list, add him
|
|
if(ON_CHANNEL()){
|
|
lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
|
|
if(lookup == NULL){
|
|
// create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
|
|
multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
|
|
*/
|
|
pxo_channel *multi_pxo_add_channel(char *name,pxo_channel **list)
|
|
{
|
|
pxo_channel *new_channel;
|
|
|
|
// try and allocate a new pxo_channel struct
|
|
new_channel = (pxo_channel *)vm_malloc(sizeof(pxo_channel));
|
|
if ( new_channel == NULL ) {
|
|
nprintf(("Network", "Cannot allocate space for new pxo_channel structure\n"));
|
|
return NULL;
|
|
}
|
|
memset(new_channel,0,sizeof(pxo_channel));
|
|
// try and allocate a string for the channel name
|
|
strncpy(new_channel->name,name,MAX_CHANNEL_NAME_LEN);
|
|
|
|
// insert it on the list
|
|
if ( *list != NULL ) {
|
|
new_channel->next = (*list)->next;
|
|
new_channel->next->prev = new_channel;
|
|
(*list)->next = new_channel;
|
|
new_channel->prev = *list;
|
|
} else {
|
|
*list = new_channel;
|
|
(*list)->next = (*list)->prev = *list;
|
|
}
|
|
|
|
Multi_pxo_channel_count++;
|
|
return new_channel;
|
|
}
|
|
|
|
/**
|
|
* Lookup a channel with the specified name
|
|
*/
|
|
pxo_channel *multi_pxo_find_channel(char *name,pxo_channel *list)
|
|
{
|
|
pxo_channel *moveup;
|
|
|
|
// look the sucker up
|
|
moveup = list;
|
|
if(moveup == NULL){
|
|
return NULL;
|
|
}
|
|
do {
|
|
if(!stricmp(name,moveup->name)){
|
|
return moveup;
|
|
}
|
|
|
|
moveup = moveup->next;
|
|
} while((moveup != list) && (moveup != NULL));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Process the channel list (select, etc)
|
|
*/
|
|
void multi_pxo_process_channels()
|
|
{
|
|
int item_index,my;
|
|
int idx;
|
|
|
|
// if we don't have a start item, but the list is non-null
|
|
if((Multi_pxo_channel_start == NULL) && (Multi_pxo_channels != NULL)){
|
|
Multi_pxo_channel_start = Multi_pxo_channels;
|
|
Multi_pxo_channel_start_index = 0;
|
|
}
|
|
|
|
// if we don't have a selected item, but the list is non-null
|
|
if((Multi_pxo_channel_select == NULL) && (Multi_pxo_channels != NULL)){
|
|
Multi_pxo_channel_select = Multi_pxo_channels;
|
|
|
|
// set the text
|
|
multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
|
|
}
|
|
|
|
// if the "switch" delay timestamp is set, see if it has expired
|
|
if((Multi_pxo_switch_delay != -1) && timestamp_elapsed(Multi_pxo_switch_delay)){
|
|
Multi_pxo_switch_delay = -1;
|
|
}
|
|
|
|
// see if we have a mouse click on the channel region
|
|
if(Multi_pxo_channel_button.pressed()){
|
|
Multi_pxo_channel_button.get_mouse_pos(NULL,&my);
|
|
|
|
// index from the top
|
|
item_index = my / (gr_get_font_height() + 1);
|
|
|
|
// select the item if possible
|
|
if((item_index + Multi_pxo_channel_start_index) < Multi_pxo_channel_count){
|
|
Multi_pxo_channel_select = Multi_pxo_channel_start;
|
|
for(idx=0;idx<item_index;idx++){
|
|
Multi_pxo_channel_select = Multi_pxo_channel_select->next;
|
|
}
|
|
|
|
// set the text
|
|
multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
|
|
}
|
|
}
|
|
|
|
// last refresh time
|
|
if((Multi_pxo_channel_last_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_last_refresh) > CHANNEL_REFRESH_TIME) ){
|
|
// refresh channels
|
|
multi_pxo_set_status_text(XSTR("Refreshing Public Channel List",952));
|
|
|
|
// get a list of channels on the server (clear any old list as well)
|
|
multi_pxo_get_channels();
|
|
|
|
// refresh
|
|
Multi_pxo_channel_last_refresh = -1.0f;
|
|
|
|
nprintf(("Network","Refreshing channels\n"));
|
|
}
|
|
|
|
// if we haven't updated our server channel counts in a while, do so again
|
|
// last refresh time
|
|
if((Multi_pxo_channel_server_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_server_refresh) > CHANNEL_SERVER_REFRESH_TIME) ){
|
|
|
|
// do it _NOW_ I"M RIGHT HERE KILL ME WHAT ARE YOU WAITING FOR DO IT KILL ME DO IT NOW!
|
|
multi_pxo_channel_refresh_servers();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send a request to refresh our channel server counts
|
|
*/
|
|
void multi_pxo_channel_refresh_servers()
|
|
{
|
|
pxo_channel *lookup;
|
|
|
|
// traverse the list of existing channels we know about and query the game tracker about them
|
|
lookup = Multi_pxo_channels;
|
|
|
|
if (lookup == NULL) {
|
|
return;
|
|
}
|
|
|
|
do {
|
|
if ( strlen(lookup->name) ) {
|
|
// send the request
|
|
fs2netd_update_game_count(lookup->name);
|
|
}
|
|
|
|
// next item
|
|
lookup = lookup->next;
|
|
} while ( (lookup != NULL) && (lookup != Multi_pxo_channels) );
|
|
|
|
// record the time
|
|
Multi_pxo_channel_server_refresh = f2fl(timer_get_fixed_seconds());
|
|
}
|
|
|
|
/**
|
|
* Refresh current channel server count
|
|
*/
|
|
void multi_pxo_channel_refresh_current()
|
|
{
|
|
// send a request for a server count on this channel
|
|
if ( strlen(Multi_pxo_channel_current.name) ) {
|
|
// send the request
|
|
fs2netd_update_game_count(Multi_pxo_channel_current.name);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Display the channel list
|
|
*/
|
|
void multi_pxo_blit_channels()
|
|
{
|
|
pxo_channel *moveup;
|
|
char chan_name[MAX_PXO_TEXT_LEN];
|
|
char chan_users[15];
|
|
char chan_servers[15];
|
|
int user_w,server_w;
|
|
int disp_count,y_start;
|
|
int line_height = gr_get_font_height() + 1;
|
|
|
|
// blit as many channels as we can
|
|
disp_count = 0;
|
|
y_start = Multi_pxo_chan_coords[gr_screen.res][1];
|
|
moveup = Multi_pxo_channel_start;
|
|
if(moveup == NULL){
|
|
return;
|
|
}
|
|
do {
|
|
// if this is the currently selected item, highlight it
|
|
if(moveup == Multi_pxo_channel_select){
|
|
gr_set_color_fast(&Color_bright);
|
|
}
|
|
// otherwise draw it normally
|
|
else {
|
|
gr_set_color_fast(&Color_normal);
|
|
}
|
|
|
|
// get the # of users on the channel
|
|
memset(chan_users, 0, 15);
|
|
sprintf(chan_users, "%d", moveup->num_users);
|
|
|
|
// get the width of the user count string
|
|
gr_get_string_size(&user_w, NULL, chan_users);
|
|
|
|
// get the # of servers on the channel
|
|
memset(chan_servers,0,15);
|
|
sprintf(chan_servers, "%d", moveup->num_servers);
|
|
|
|
// get the width of the user count string
|
|
gr_get_string_size(&server_w, NULL, chan_servers);
|
|
|
|
// make sure the name fits
|
|
memset(chan_name, 0, MAX_PXO_TEXT_LEN);
|
|
Assert(moveup->name);
|
|
strcpy_s(chan_name,moveup->name);
|
|
gr_force_fit_string(chan_name, MAX_PXO_TEXT_LEN-1, Multi_pxo_chan_coords[gr_screen.res][2] - Multi_pxo_chan_column_offsets[gr_screen.res][CHAN_PLAYERS_COLUMN]);
|
|
|
|
// blit the strings
|
|
gr_string(Multi_pxo_chan_coords[gr_screen.res][0], y_start, chan_name + 1, GR_RESIZE_MENU);
|
|
gr_string(Multi_pxo_chan_coords[gr_screen.res][0] + Multi_pxo_chan_coords[gr_screen.res][2] - Multi_pxo_chan_column_offsets[gr_screen.res][CHAN_PLAYERS_COLUMN], y_start, chan_users, GR_RESIZE_MENU);
|
|
gr_set_color_fast(&Color_bright);
|
|
gr_string(Multi_pxo_chan_coords[gr_screen.res][0] + Multi_pxo_chan_coords[gr_screen.res][2] - Multi_pxo_chan_column_offsets[gr_screen.res][CHAN_GAMES_COLUMN], y_start, chan_servers, GR_RESIZE_MENU);
|
|
|
|
// increment the displayed count
|
|
disp_count++;
|
|
y_start += line_height;
|
|
|
|
// next item
|
|
moveup = moveup->next;
|
|
} while((moveup != Multi_pxo_channels) && (disp_count < Multi_pxo_max_chan_display[gr_screen.res]));
|
|
}
|
|
|
|
/**
|
|
* Scroll channel list up
|
|
*/
|
|
void multi_pxo_scroll_channels_up()
|
|
{
|
|
// if we're already at the head of the list, do nothing
|
|
if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start == Multi_pxo_channels)){
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
return;
|
|
}
|
|
|
|
// otherwise move up one
|
|
Multi_pxo_channel_start = Multi_pxo_channel_start->prev;
|
|
Multi_pxo_channel_start_index--;
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
}
|
|
|
|
/**
|
|
* Scroll channel list down
|
|
*/
|
|
void multi_pxo_scroll_channels_down()
|
|
{
|
|
// if we're already at the tail of the list, do nothing
|
|
if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start->next == Multi_pxo_channels)){
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
return;
|
|
}
|
|
|
|
// if we can't scroll further without going past the end of the viewable list, don't
|
|
if((Multi_pxo_channel_start_index + Multi_pxo_max_chan_display[gr_screen.res]) >= Multi_pxo_channel_count){
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
return;
|
|
}
|
|
|
|
// otherwise move down one
|
|
Multi_pxo_channel_start = Multi_pxo_channel_start->next;
|
|
Multi_pxo_channel_start_index++;
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
}
|
|
|
|
/**
|
|
* Attempt to join a channel
|
|
*/
|
|
void multi_pxo_join_channel(pxo_channel *chan)
|
|
{
|
|
char switch_msg[256];
|
|
|
|
// if we're already on this channel, do nothing
|
|
if(ON_CHANNEL() && !stricmp(chan->name, Multi_pxo_channel_current.name)){
|
|
return;
|
|
}
|
|
|
|
// if we're already trying to join a channel, do nothing
|
|
if(SWITCHING_CHANNELS()){
|
|
return;
|
|
}
|
|
|
|
// try and join the channel
|
|
switch(SetNewChatChannel(chan->name)){
|
|
case -1 :
|
|
Int3();
|
|
break;
|
|
|
|
case 0 :
|
|
// decrement the count of our current channel
|
|
pxo_channel *lookup;
|
|
lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
|
|
if(lookup != NULL){
|
|
lookup->num_users--;
|
|
}
|
|
|
|
// set our current channel as none
|
|
memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
|
|
Multi_pxo_channel_current.num_users = -1;
|
|
|
|
multi_pxo_set_status_text(XSTR("Switching channels",953));
|
|
|
|
// copy the channel
|
|
memcpy(&Multi_pxo_channel_switch,chan,sizeof(pxo_channel));
|
|
|
|
// clear the player list
|
|
multi_pxo_clear_players();
|
|
|
|
// display a line of text indicating that we're switching channels
|
|
memset(switch_msg,0,256);
|
|
|
|
if(strlen(Multi_pxo_channel_switch.name) > 1){
|
|
sprintf(switch_msg, "[Switching to channel %s]", Multi_pxo_channel_switch.name + 1);
|
|
} else {
|
|
sprintf(switch_msg, "[Switching to channel %s]", Multi_pxo_channel_switch.name);
|
|
}
|
|
|
|
multi_pxo_chat_process_incoming(switch_msg, CHAT_MODE_CHANNEL_SWITCH);
|
|
break;
|
|
|
|
case 1 :
|
|
Int3();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle any processing details if we're currently trying to join a channel
|
|
*/
|
|
void multi_pxo_handle_channel_change()
|
|
{
|
|
// if we're not switching channels, do nothing
|
|
if(!SWITCHING_CHANNELS()){
|
|
return;
|
|
}
|
|
|
|
// if we are, check the status
|
|
switch(SetNewChatChannel(NULL)){
|
|
// failed to switch
|
|
case -1 :
|
|
// unset our switching struct
|
|
memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
|
|
Multi_pxo_channel_switch.num_users = -1;
|
|
|
|
// notify of error
|
|
multi_pxo_set_status_text(XSTR("No channel (error while switching)",954));
|
|
break;
|
|
|
|
// still switching
|
|
case 0:
|
|
break;
|
|
|
|
// successfully changed
|
|
case 1:
|
|
// copy the current channel info, and unset the switching status
|
|
memcpy(&Multi_pxo_channel_current,&Multi_pxo_channel_switch,sizeof(pxo_channel));
|
|
Multi_pxo_channel_switch.num_users = -1;
|
|
|
|
// set our "last" channel
|
|
strcpy_s(Multi_pxo_channel_last, Multi_pxo_channel_current.name);
|
|
|
|
// notify the user
|
|
multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
|
|
|
|
// if we don't already have this guy on the list, add him
|
|
pxo_channel *lookup;
|
|
lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
|
|
if(lookup == NULL){
|
|
// create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
|
|
lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
|
|
}
|
|
|
|
// set the user count to be 1 (just me)
|
|
if(lookup != NULL){
|
|
lookup->num_users = 1;
|
|
}
|
|
|
|
// set the "switch" delay timestamp
|
|
Multi_pxo_switch_delay = timestamp(MULTI_PXO_SWITCH_DELAY_TIME);
|
|
|
|
// refresh current channel server count
|
|
multi_pxo_channel_refresh_current();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// player related stuff -------------------------------------------
|
|
|
|
/**
|
|
* Clear the old player list
|
|
*/
|
|
void multi_pxo_clear_players()
|
|
{
|
|
player_list *moveup,*backup;
|
|
|
|
// if the list is null, don't free it up
|
|
if(Multi_pxo_players != NULL){
|
|
// otherwise
|
|
moveup = Multi_pxo_players;
|
|
backup = NULL;
|
|
if(moveup != NULL){
|
|
do {
|
|
backup = moveup;
|
|
moveup = moveup->next;
|
|
|
|
// free the struct itself
|
|
vm_free(backup);
|
|
backup = NULL;
|
|
} while(moveup != Multi_pxo_players);
|
|
Multi_pxo_players = NULL;
|
|
}
|
|
}
|
|
|
|
Multi_pxo_player_start = NULL;
|
|
Multi_pxo_player_select = NULL;
|
|
}
|
|
|
|
/**
|
|
* Create a new player with the given name and place it on the player list, return a pointer or NULL on fail
|
|
*/
|
|
player_list *multi_pxo_add_player(char *name)
|
|
{
|
|
player_list *new_player;
|
|
|
|
// try and allocate a new player_list struct
|
|
new_player = (player_list *)vm_malloc(sizeof(player_list));
|
|
if ( new_player == NULL ) {
|
|
nprintf(("Network", "Cannot allocate space for new player_list structure\n"));
|
|
return NULL;
|
|
}
|
|
// try and allocate a string for the channel name
|
|
strncpy(new_player->name, name, MAX_PLAYER_NAME_LEN);
|
|
|
|
// insert it on the list
|
|
if ( Multi_pxo_players != NULL ) {
|
|
new_player->next = Multi_pxo_players->next;
|
|
new_player->next->prev = new_player;
|
|
Multi_pxo_players->next = new_player;
|
|
new_player->prev = Multi_pxo_players;
|
|
} else {
|
|
Multi_pxo_players = new_player;
|
|
Multi_pxo_players->next = Multi_pxo_players->prev = Multi_pxo_players;
|
|
}
|
|
|
|
// new player
|
|
Multi_pxo_player_count++;
|
|
|
|
return new_player;
|
|
}
|
|
|
|
/**
|
|
* Remove a player with the given name
|
|
*/
|
|
void multi_pxo_del_player(char *name)
|
|
{
|
|
player_list *lookup;
|
|
|
|
// try and find this guy
|
|
lookup = Multi_pxo_players;
|
|
if(lookup == NULL){
|
|
return;
|
|
}
|
|
do {
|
|
// if we found a match, delete it
|
|
if(!stricmp(name,lookup->name)){
|
|
// if this is the only item on the list, free stuff up
|
|
if(lookup->next == lookup){
|
|
Assert(lookup == Multi_pxo_players);
|
|
vm_free(lookup);
|
|
Multi_pxo_players = NULL;
|
|
multi_pxo_clear_players();
|
|
}
|
|
// otherwise, just delete it
|
|
else {
|
|
lookup->next->prev = lookup->prev;
|
|
lookup->prev->next = lookup->next;
|
|
|
|
// if this was our selected item, unselect it
|
|
if((lookup == Multi_pxo_player_select) && (Multi_pxo_player_select != NULL)){
|
|
Multi_pxo_player_select = Multi_pxo_player_select->next;
|
|
}
|
|
|
|
// if this was our point to start viewing from, select another
|
|
if(lookup == Multi_pxo_player_start){
|
|
// if this is the head of the list, move up one
|
|
if(Multi_pxo_players == lookup){
|
|
Multi_pxo_player_start = Multi_pxo_player_start->next;
|
|
// Multi_pxo_player_start_index = 0;
|
|
}
|
|
// otherwise move back
|
|
else {
|
|
Multi_pxo_player_start = Multi_pxo_player_start->prev;
|
|
}
|
|
}
|
|
|
|
// if this is the head of the list, move it up
|
|
if(lookup == Multi_pxo_players){
|
|
Multi_pxo_players = Multi_pxo_players->next;
|
|
}
|
|
|
|
// free the item up
|
|
lookup->next = NULL;
|
|
lookup->prev = NULL;
|
|
vm_free(lookup);
|
|
}
|
|
|
|
// new player
|
|
Multi_pxo_player_count--;
|
|
Assert(Multi_pxo_player_count >= 0);
|
|
|
|
// we're done now
|
|
return;
|
|
}
|
|
|
|
// next item
|
|
lookup = lookup->next;
|
|
} while((lookup != NULL) && (lookup != Multi_pxo_players));
|
|
}
|
|
|
|
/**
|
|
* Try and find a player with the given name, return a pointer to his entry (or NULL)
|
|
*/
|
|
player_list *multi_pxo_find_player(char *name)
|
|
{
|
|
player_list *lookup;
|
|
|
|
// look through all players
|
|
lookup = Multi_pxo_players;
|
|
if(lookup == NULL){
|
|
return NULL;
|
|
}
|
|
do {
|
|
if(!stricmp(name,lookup->name)){
|
|
return lookup;
|
|
}
|
|
|
|
lookup = lookup->next;
|
|
} while((lookup != NULL) && (lookup != Multi_pxo_players));
|
|
|
|
// return NULL
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Process the player list (select, etc)
|
|
*/
|
|
void multi_pxo_process_players()
|
|
{
|
|
int item_index,my;
|
|
player_list *lookup;
|
|
|
|
// if we don't have a start item, but the list is non-null
|
|
if((Multi_pxo_player_start == NULL) && (Multi_pxo_players != NULL)){
|
|
Multi_pxo_player_start = Multi_pxo_players;
|
|
}
|
|
|
|
// if we don't have a selected item, but the list is non-null
|
|
if((Multi_pxo_player_select == NULL) && (Multi_pxo_players != NULL)){
|
|
Multi_pxo_player_select = Multi_pxo_players;
|
|
}
|
|
|
|
// see if we have a mouse click on the channel region
|
|
if(Multi_pxo_player_button.pressed()){
|
|
Multi_pxo_player_button.get_mouse_pos(NULL,&my);
|
|
|
|
// index from the top
|
|
item_index = my / (gr_get_font_height() + 1);
|
|
|
|
// select the item if possible
|
|
lookup = Multi_pxo_player_start;
|
|
if(lookup == NULL){
|
|
return;
|
|
}
|
|
// select item 0
|
|
if(item_index == 0){
|
|
Multi_pxo_player_select = Multi_pxo_player_start;
|
|
return;
|
|
}
|
|
do {
|
|
// move to the next item
|
|
lookup = lookup->next;
|
|
item_index--;
|
|
|
|
// if this item is our guy
|
|
if((item_index == 0) && (lookup != Multi_pxo_players)){
|
|
Multi_pxo_player_select = lookup;
|
|
return;
|
|
}
|
|
} while((lookup != Multi_pxo_players) && (item_index > 0));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Display the player list
|
|
*/
|
|
void multi_pxo_blit_players()
|
|
{
|
|
player_list *moveup;
|
|
char player_name[MAX_PXO_TEXT_LEN];
|
|
int disp_count,y_start;
|
|
int line_height = gr_get_font_height() + 1;
|
|
|
|
// blit as many channels as we can
|
|
disp_count = 0;
|
|
y_start = Multi_pxo_player_coords[gr_screen.res][1];
|
|
moveup = Multi_pxo_player_start;
|
|
if(moveup == NULL){
|
|
return;
|
|
}
|
|
do {
|
|
// if this is the currently selected item, highlight it
|
|
if(moveup == Multi_pxo_player_select){
|
|
gr_set_color_fast(&Color_bright);
|
|
}
|
|
// otherwise draw it normally
|
|
else {
|
|
gr_set_color_fast(&Color_normal);
|
|
}
|
|
|
|
// make sure the string fits
|
|
strcpy_s(player_name,moveup->name);
|
|
gr_force_fit_string(player_name, MAX_PXO_TEXT_LEN-1, Multi_pxo_player_coords[gr_screen.res][2]);
|
|
|
|
// blit the string
|
|
gr_string(Multi_pxo_player_coords[gr_screen.res][0], y_start, player_name, GR_RESIZE_MENU);
|
|
|
|
// increment the displayed count
|
|
disp_count++;
|
|
y_start += line_height;
|
|
|
|
// next item
|
|
moveup = moveup->next;
|
|
} while((moveup != Multi_pxo_players) && (disp_count < Multi_pxo_max_player_display[gr_screen.res]));
|
|
}
|
|
|
|
/**
|
|
* Scroll player list up
|
|
*/
|
|
void multi_pxo_scroll_players_up()
|
|
{
|
|
// if we're already at the head of the list, do nothing
|
|
if((Multi_pxo_player_start == NULL) || (Multi_pxo_player_start == Multi_pxo_players)){
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
return;
|
|
}
|
|
|
|
// otherwise move up one
|
|
Multi_pxo_player_start = Multi_pxo_player_start->prev;
|
|
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
}
|
|
|
|
/**
|
|
* Scroll player list down
|
|
*/
|
|
void multi_pxo_scroll_players_down()
|
|
{
|
|
player_list *lookup;
|
|
int count = 0;
|
|
|
|
// see if its okay to scroll down
|
|
lookup = Multi_pxo_player_start;
|
|
if(lookup == NULL ){
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
return;
|
|
}
|
|
count = 0;
|
|
while(lookup->next != Multi_pxo_players){
|
|
lookup = lookup->next;
|
|
count++;
|
|
}
|
|
|
|
// if we can move down
|
|
if(count >= Multi_pxo_max_player_display[gr_screen.res]){
|
|
Multi_pxo_player_start = Multi_pxo_player_start->next;
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
} else {
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
}
|
|
}
|
|
|
|
|
|
// chat text stuff -----------------------------------------
|
|
|
|
/**
|
|
* Initialize and create the chat text linked list
|
|
*/
|
|
void multi_pxo_chat_init()
|
|
{
|
|
int idx;
|
|
chat_line *new_line;
|
|
|
|
// no chat lines
|
|
Multi_pxo_chat = NULL;
|
|
Multi_pxo_chat_add = NULL;
|
|
Multi_pxo_chat_start = NULL;
|
|
Multi_pxo_chat_start_index = -1;
|
|
|
|
// create the lines in a non-circular doubly linked list
|
|
for(idx=0;idx<MAX_CHAT_LINES;idx++){
|
|
new_line = (chat_line*)vm_malloc(sizeof(chat_line));
|
|
|
|
// clear the line out
|
|
Assert(new_line != NULL);
|
|
if(new_line == NULL){
|
|
return;
|
|
}
|
|
memset(new_line,0,sizeof(chat_line));
|
|
new_line->prev = NULL;
|
|
new_line->next = NULL;
|
|
|
|
// insert it into the (empty) list
|
|
if(Multi_pxo_chat == NULL){
|
|
Multi_pxo_chat = new_line;
|
|
}
|
|
// insert it onto the (non-empty) list
|
|
else {
|
|
Multi_pxo_chat->prev = new_line;
|
|
new_line->next = Multi_pxo_chat;
|
|
Multi_pxo_chat = new_line;
|
|
}
|
|
}
|
|
|
|
// start adding chat lines at the beginning of the list
|
|
Multi_pxo_chat_add = Multi_pxo_chat;
|
|
}
|
|
|
|
/**
|
|
* Free up all chat list stuff
|
|
*/
|
|
void multi_pxo_chat_free()
|
|
{
|
|
chat_line *moveup, *backup;
|
|
|
|
// free all items up
|
|
moveup = Multi_pxo_chat;
|
|
while(moveup != NULL){
|
|
backup = moveup;
|
|
moveup = moveup->next;
|
|
|
|
vm_free(backup);
|
|
}
|
|
|
|
// no chat lines
|
|
Multi_pxo_chat = NULL;
|
|
Multi_pxo_chat_add = NULL;
|
|
Multi_pxo_chat_start = NULL;
|
|
Multi_pxo_chat_start_index = -1;
|
|
Multi_pxo_chat_count = 0;
|
|
Multi_pxo_chat_slider.set_numberItems(0);
|
|
}
|
|
|
|
/**
|
|
* Clear all lines of chat text in the chat area
|
|
*/
|
|
void multi_pxo_chat_clear()
|
|
{
|
|
chat_line *moveup;
|
|
|
|
// clear the text in all the lines
|
|
moveup = Multi_pxo_chat;
|
|
while(moveup != NULL){
|
|
memset(moveup->text,0,MAX_CHAT_LINE_LEN+1);
|
|
moveup = moveup->next;
|
|
}
|
|
|
|
// how many chat lines we have
|
|
Multi_pxo_chat_count = 0;
|
|
|
|
// start adding chat lines at the beginning of the list
|
|
Multi_pxo_chat_add = Multi_pxo_chat;
|
|
}
|
|
|
|
/**
|
|
* Add a line of text
|
|
*/
|
|
void multi_pxo_chat_add_line(const char *txt, int mode)
|
|
{
|
|
chat_line *temp;
|
|
|
|
// copy in the text
|
|
Assert(Multi_pxo_chat_add != NULL);
|
|
strncpy(Multi_pxo_chat_add->text, txt, MAX_CHAT_LINE_LEN);
|
|
Multi_pxo_chat_add->mode = mode;
|
|
|
|
// if we're at the end of the list, move the front item down
|
|
if(Multi_pxo_chat_add->next == NULL) {
|
|
// store the new "head" of the list
|
|
temp = Multi_pxo_chat->next;
|
|
|
|
// move the current head to the end of the list
|
|
Multi_pxo_chat_add->next = Multi_pxo_chat;
|
|
temp->prev = NULL;
|
|
Multi_pxo_chat->prev = Multi_pxo_chat_add;
|
|
Multi_pxo_chat->next = NULL;
|
|
|
|
// reset the head of the list
|
|
Multi_pxo_chat = temp;
|
|
|
|
// set the new add line
|
|
Multi_pxo_chat_add = Multi_pxo_chat_add->next;
|
|
memset(Multi_pxo_chat_add->text, 0, MAX_CHAT_LINE_LEN+1);
|
|
Multi_pxo_chat_add->mode = CHAT_MODE_NORMAL;
|
|
}
|
|
// if we're not at the end of the list, just move up by one
|
|
else {
|
|
// set the new add line
|
|
Multi_pxo_chat_add = Multi_pxo_chat_add->next;
|
|
}
|
|
|
|
// if we've reached max chat lines, don't increment
|
|
if(Multi_pxo_chat_count < MAX_CHAT_LINES) {
|
|
Multi_pxo_chat_count++;
|
|
}
|
|
|
|
// set the count
|
|
Multi_pxo_chat_slider.set_numberItems(Multi_pxo_chat_count > Multi_pxo_max_chat_display[gr_screen.res] ? Multi_pxo_chat_count - Multi_pxo_max_chat_display[gr_screen.res] : 0, 0); // the 0 means don't reset
|
|
|
|
multi_pxo_goto_bottom();
|
|
}
|
|
|
|
/**
|
|
* Process an incoming line of text
|
|
*/
|
|
void multi_pxo_chat_process_incoming(const char *txt,int mode)
|
|
{
|
|
char msg_total[512],line[512];
|
|
int n_lines,idx;
|
|
int n_chars[20];
|
|
const char *p_str[20]; // the initial line (unindented)
|
|
const char *priv_ptr;
|
|
|
|
// filter out "has left" channel messages, when switching channels
|
|
if((SWITCHING_CHANNELS() || ((Multi_pxo_switch_delay != -1) && !timestamp_elapsed(Multi_pxo_switch_delay))) &&
|
|
multi_pxo_chat_is_left_message(txt)){
|
|
return;
|
|
}
|
|
|
|
// if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
|
|
priv_ptr = multi_pxo_chat_is_private(txt);
|
|
if(priv_ptr != NULL){
|
|
strcpy_s(msg_total, priv_ptr);
|
|
} else {
|
|
strcpy_s(msg_total, txt);
|
|
}
|
|
|
|
// determine what mode to display this text in
|
|
|
|
// if this is private chat
|
|
if(priv_ptr != NULL){
|
|
mode = CHAT_MODE_PRIVATE;
|
|
}
|
|
// all other chat
|
|
else {
|
|
// if this is a server message
|
|
if(multi_pxo_is_server_text(txt)){
|
|
mode = CHAT_MODE_SERVER;
|
|
}
|
|
// if this is a MOTD
|
|
else if(multi_pxo_is_motd_text(txt)){
|
|
multi_pxo_motd_add_text(txt);
|
|
return;
|
|
}
|
|
// if this is the end of motd text
|
|
else if(multi_pxo_is_end_of_motd_text(txt)){
|
|
multi_pxo_set_end_of_motd();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// split the text up into as many lines as necessary
|
|
n_lines = split_str(msg_total, Multi_pxo_chat_coords[gr_screen.res][2] - 5, n_chars, p_str, 3);
|
|
Assert((n_lines != -1) && (n_lines <= 20));
|
|
if((n_lines < 0) || (n_lines > 20)) {
|
|
return;
|
|
}
|
|
|
|
// if the string fits on one line
|
|
if(n_lines == 1) {
|
|
multi_pxo_chat_add_line(msg_total,mode);
|
|
}
|
|
// if the string was split into multiple lines
|
|
else {
|
|
// add the first line
|
|
memcpy(line,p_str[0],n_chars[0]);
|
|
line[n_chars[0]] = '\0';
|
|
multi_pxo_chat_add_line(line,mode);
|
|
|
|
// copy the rest of the lines
|
|
for(idx=1; idx<n_lines; idx++){
|
|
memcpy(line,p_str[idx],n_chars[idx]);
|
|
line[n_chars[idx]] = '\0';
|
|
|
|
// unless the current mode is server or "switching channels", make all these CHAT_MODE_CARRY
|
|
if((mode != CHAT_MODE_SERVER) && (mode != CHAT_MODE_CHANNEL_SWITCH)){
|
|
mode = CHAT_MODE_CARRY;
|
|
}
|
|
multi_pxo_chat_add_line(line, mode);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Blit the chat text
|
|
*/
|
|
void multi_pxo_chat_blit()
|
|
{
|
|
int y_start, line_height;
|
|
int disp_count,token_width;
|
|
char piece[100];
|
|
char title[MAX_PXO_TEXT_LEN];
|
|
char *tok;
|
|
chat_line *moveup;
|
|
|
|
// blit the title line
|
|
memset(title,0,MAX_PXO_TEXT_LEN);
|
|
if(ON_CHANNEL()){
|
|
if(strlen(Multi_pxo_channel_current.name) > 1){
|
|
sprintf(title, XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name+1); // [[ <who> on <channel> ]]
|
|
} else {
|
|
sprintf(title, XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name); // [[ <who> on <channel> ]]
|
|
}
|
|
} else {
|
|
strcpy_s(title,XSTR("Parallax Online - No Channel", 956));
|
|
}
|
|
gr_force_fit_string(title, MAX_PXO_TEXT_LEN-1, Multi_pxo_chat_coords[gr_screen.res][2] - 10);
|
|
gr_get_string_size(&token_width,NULL,title);
|
|
gr_set_color_fast(&Color_normal);
|
|
gr_string(Multi_pxo_chat_coords[gr_screen.res][0] + ((Multi_pxo_chat_coords[gr_screen.res][2] - token_width)/2), Multi_pxo_chat_title_y[gr_screen.res], title, GR_RESIZE_MENU);
|
|
|
|
// blit all active lines of text
|
|
moveup = Multi_pxo_chat_start;
|
|
disp_count = 0;
|
|
y_start = Multi_pxo_chat_coords[gr_screen.res][1];
|
|
line_height = gr_get_font_height() + 1;
|
|
while((moveup != NULL) && (moveup != Multi_pxo_chat_add) && (disp_count < (Multi_pxo_max_chat_display[gr_screen.res]))){
|
|
switch(moveup->mode){
|
|
// if this is text from the server, display it all "bright"
|
|
case CHAT_MODE_SERVER:
|
|
gr_set_color_fast(&Color_bright);
|
|
gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text, GR_RESIZE_MENU);
|
|
break;
|
|
|
|
// if this is motd, display it all "bright"
|
|
case CHAT_MODE_MOTD:
|
|
gr_set_color_fast(&Color_bright_white);
|
|
gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text, GR_RESIZE_MENU);
|
|
break;
|
|
|
|
// normal mode, just highlight the server
|
|
case CHAT_MODE_PRIVATE:
|
|
case CHAT_MODE_NORMAL:
|
|
strcpy_s(piece,moveup->text);
|
|
tok = strtok(piece," ");
|
|
if(tok != NULL){
|
|
// get the width of just the first "piece"
|
|
gr_get_string_size(&token_width, NULL, tok);
|
|
|
|
// draw it brightly
|
|
gr_set_color_fast(&Color_bright);
|
|
gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, tok, GR_RESIZE_MENU);
|
|
|
|
// draw the rest of the string normally
|
|
tok = strtok(NULL,"");
|
|
if(tok != NULL){
|
|
gr_set_color_fast(&Color_normal);
|
|
gr_string(Multi_pxo_chat_coords[gr_screen.res][0] + token_width + 6, y_start, tok, GR_RESIZE_MENU);
|
|
}
|
|
}
|
|
break;
|
|
|
|
// carry mode, display with no highlight
|
|
case CHAT_MODE_CARRY:
|
|
gr_set_color_fast(&Color_normal);
|
|
gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text, GR_RESIZE_MENU);
|
|
break;
|
|
|
|
// "switching channels mode", display it bright
|
|
case CHAT_MODE_CHANNEL_SWITCH:
|
|
gr_set_color_fast(&Color_bright);
|
|
gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text, GR_RESIZE_MENU);
|
|
break;
|
|
}
|
|
|
|
// next chat line
|
|
moveup = moveup->next;
|
|
disp_count++;
|
|
y_start += line_height;
|
|
}
|
|
|
|
if ((moveup != Multi_pxo_chat_add) && (moveup != NULL)) {
|
|
Can_scroll_down = 1;
|
|
} else {
|
|
Can_scroll_down = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Scroll to the very bottom of the chat area
|
|
*/
|
|
void multi_pxo_goto_bottom()
|
|
{
|
|
chat_line *backup;
|
|
int idx;
|
|
|
|
if (Multi_pxo_chat == NULL) {
|
|
return;
|
|
}
|
|
|
|
// if we have less than the displayable amount of lines, do nothing
|
|
if(Multi_pxo_chat_count <= Multi_pxo_max_chat_display[gr_screen.res]){
|
|
Multi_pxo_chat_start = Multi_pxo_chat;
|
|
|
|
// nothing to do for the slider
|
|
Multi_pxo_chat_slider.set_numberItems(0);
|
|
return;
|
|
}
|
|
|
|
if (!Can_scroll_down)
|
|
{
|
|
// otherwise move back the right # of items
|
|
backup = Multi_pxo_chat_add;
|
|
for(idx=0; idx<Multi_pxo_max_chat_display[gr_screen.res]; idx++){
|
|
Assert(backup->prev != NULL);
|
|
backup = backup->prev;
|
|
}
|
|
|
|
Multi_pxo_chat_start = backup;
|
|
|
|
// fixup the start index
|
|
multi_pxo_chat_adjust_start();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Scroll the text up
|
|
*/
|
|
void multi_pxo_scroll_chat_up()
|
|
{
|
|
// if we're already at the top of the list, don't do anything
|
|
if ((Multi_pxo_chat_start == NULL) || (Multi_pxo_chat_start == Multi_pxo_chat)) {
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
return;
|
|
}
|
|
|
|
// otherwise move up one
|
|
Multi_pxo_chat_start = Multi_pxo_chat_start->prev;
|
|
|
|
multi_pxo_chat_adjust_start();
|
|
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
}
|
|
|
|
/**
|
|
* Returns 1 if we can scroll down, 0 otherwise
|
|
*/
|
|
int multi_pxo_can_scroll_down()
|
|
{
|
|
chat_line *lookup;
|
|
int count = 0;
|
|
|
|
// see if its okay to scroll down
|
|
lookup = Multi_pxo_chat_start;
|
|
if (lookup == NULL) {
|
|
return 0;
|
|
}
|
|
count = 0;
|
|
while (lookup != Multi_pxo_chat_add) {
|
|
lookup = lookup->next;
|
|
count++;
|
|
}
|
|
|
|
// check if we can move down, return accordingly
|
|
if (count > Multi_pxo_max_chat_display[gr_screen.res]) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Scroll the text down
|
|
*/
|
|
void multi_pxo_scroll_chat_down()
|
|
{
|
|
// if we can move down
|
|
if (multi_pxo_can_scroll_down()) {
|
|
Multi_pxo_chat_start = Multi_pxo_chat_start->next;
|
|
multi_pxo_chat_adjust_start();
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
} else {
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process chat controls
|
|
*/
|
|
void multi_pxo_chat_process()
|
|
{
|
|
char *remainder;
|
|
const char *result;
|
|
char msg[512];
|
|
int msg_pixel_width;
|
|
|
|
// if the chat line is getting too long, fire off the message, putting the last
|
|
// word on the next input line.
|
|
memset(msg, 0, 512);
|
|
Multi_pxo_chat_input.get_text(msg);
|
|
remainder = "";
|
|
|
|
// determine if the width of the string in pixels is > than the inputbox width -- if so,
|
|
// then send the message
|
|
gr_get_string_size(&msg_pixel_width, NULL, msg);
|
|
if ( msg_pixel_width >= (Multi_pxo_input_coords[gr_screen.res][2])) {
|
|
remainder = strrchr(msg, ' ');
|
|
if ( remainder ) {
|
|
*remainder = '\0';
|
|
remainder++;
|
|
} else {
|
|
remainder = "";
|
|
}
|
|
|
|
// if we're connected to a channel, send the chat to the server
|
|
if(ON_CHANNEL()){
|
|
result = SendChatString(msg,1);
|
|
if(result != NULL){
|
|
multi_pxo_chat_process_incoming(result);
|
|
}
|
|
|
|
// display any remainder of text on the next line
|
|
Multi_pxo_chat_input.set_text(remainder);
|
|
} else {
|
|
Multi_pxo_chat_input.set_text("");
|
|
}
|
|
} else if((Multi_pxo_chat_input.pressed() && (msg[0] != '\0')) || (strlen(msg) >= MAX_CHAT_LINE_LEN)) {
|
|
// tack on the null terminator in the boundary case
|
|
int x = strlen(msg);
|
|
if(x >= MAX_CHAT_LINE_LEN){
|
|
msg[MAX_CHAT_LINE_LEN-1] = '\0';
|
|
}
|
|
|
|
// ignore "/nick" commands
|
|
if(multi_pxo_is_nick_command(msg)){
|
|
Multi_pxo_chat_input.set_text("");
|
|
return;
|
|
}
|
|
|
|
// send the chat to the server
|
|
// if we're connected to a channel, send the chat to the server
|
|
if(ON_CHANNEL()){
|
|
result = SendChatString(msg,1);
|
|
if(result != NULL){
|
|
multi_pxo_chat_process_incoming(result);
|
|
}
|
|
|
|
// display any remainder of text on the next line
|
|
Multi_pxo_chat_input.set_text(remainder);
|
|
} else {
|
|
Multi_pxo_chat_input.set_text("");
|
|
}
|
|
}
|
|
}
|
|
|
|
// if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
|
|
//XSTR:OFF
|
|
|
|
// NOTE : DO NOT LOCALIZE THESE STRINGS!!!! THEY ARE CONSTANTS WHICH ARE CHECKED AGAINST
|
|
// PXO CHAT SERVER DATA. THEY CANNOT CHANGE!!!
|
|
#define PMSG_FROM "private message from "
|
|
#define PMSG_TO "private message to "
|
|
const char *multi_pxo_chat_is_private(const char *txt)
|
|
{
|
|
// quick check
|
|
size_t from_len = strlen( PMSG_FROM );
|
|
if( strlen(txt) > from_len ){
|
|
// otherwise do a comparison
|
|
if( !strnicmp(txt, PMSG_FROM, from_len) ){
|
|
return &txt[from_len];
|
|
}
|
|
}
|
|
|
|
// quick check
|
|
size_t to_len = strlen( PMSG_TO );
|
|
if( strlen(txt) > to_len ){
|
|
// otherwise do a comparison
|
|
if( !strnicmp(txt, PMSG_TO, to_len) ){
|
|
return &txt[to_len];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
//XSTR:ON
|
|
|
|
static const size_t pxo_prefix_len = strlen(MULTI_PXO_SERVER_PREFIX);
|
|
|
|
/**
|
|
* If the text came from the server
|
|
*/
|
|
int multi_pxo_is_server_text(const char *txt)
|
|
{
|
|
// if the message is prefaced by a ***
|
|
if((strlen(txt) >= pxo_prefix_len) && !strncmp(txt, MULTI_PXO_SERVER_PREFIX, pxo_prefix_len)){
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const size_t motd_prefix_len = strlen(PXO_CHAT_MOTD_PREFIX);
|
|
|
|
/**
|
|
* If the text is message of the day text
|
|
*/
|
|
int multi_pxo_is_motd_text(const char *txt)
|
|
{
|
|
// if we're not on a channel, and this is not a channel switching message assume its coming from a server
|
|
if((strlen(txt) >= motd_prefix_len) && !strncmp(txt, PXO_CHAT_MOTD_PREFIX, motd_prefix_len)){
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const size_t end_motd_prefix_len = strlen(PXO_CHAT_END_OF_MOTD_PREFIX);
|
|
|
|
/**
|
|
* If the text is the end of motd text
|
|
*/
|
|
int multi_pxo_is_end_of_motd_text(const char *txt)
|
|
{
|
|
// if we're not on a channel, and this is not a channel switching message assume its coming from a server
|
|
if((strlen(txt) >= end_motd_prefix_len) && !strncmp(txt, PXO_CHAT_END_OF_MOTD_PREFIX, end_motd_prefix_len)){
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* If the text is a "has left message" from the server
|
|
*/
|
|
int multi_pxo_chat_is_left_message(const char *txt)
|
|
{
|
|
// if the text is not server text
|
|
if(!multi_pxo_is_server_text(txt)){
|
|
return 0;
|
|
}
|
|
|
|
// check to see if the last portion is the correct wording
|
|
if((strlen(txt) > strlen(MULTI_PXO_HAS_LEFT)) && !strcmp(&txt[strlen(txt) - strlen(MULTI_PXO_HAS_LEFT)], MULTI_PXO_HAS_LEFT)){
|
|
return 1;
|
|
}
|
|
|
|
// check the end of the line
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Recalculate the chat start index, and adjust the slider properly
|
|
*/
|
|
void multi_pxo_chat_adjust_start()
|
|
{
|
|
chat_line *moveup;
|
|
|
|
// if we have no chat
|
|
if (Multi_pxo_chat == NULL) {
|
|
Multi_pxo_chat_start_index = -1;
|
|
return;
|
|
}
|
|
|
|
// traverse
|
|
Multi_pxo_chat_start_index = 0;
|
|
moveup = Multi_pxo_chat;
|
|
while((moveup != Multi_pxo_chat_start) && (moveup != NULL)){
|
|
Multi_pxo_chat_start_index++;
|
|
moveup = moveup->next;
|
|
}
|
|
|
|
// set the slider index
|
|
Multi_pxo_chat_slider.force_currentItem(Multi_pxo_chat_start_index);
|
|
}
|
|
|
|
// motd stuff ---------------------------------------------------------
|
|
|
|
/**
|
|
* Initialize motd when going into this screen
|
|
*/
|
|
void multi_pxo_motd_init()
|
|
{
|
|
// zero the motd string
|
|
strcpy_s(Pxo_motd, "");
|
|
|
|
// haven't gotten it yet
|
|
Pxo_motd_end = 0;
|
|
|
|
// haven't read it yet either
|
|
Pxo_motd_read = 0;
|
|
}
|
|
|
|
/**
|
|
* Set the motd text
|
|
*/
|
|
void multi_pxo_motd_add_text(const char *text)
|
|
{
|
|
int cur_len = strlen(Pxo_motd);
|
|
int new_len;
|
|
|
|
// sanity
|
|
if(text == NULL){
|
|
return;
|
|
}
|
|
|
|
// make sure its motd text
|
|
Assert(multi_pxo_is_motd_text(text));
|
|
if(!multi_pxo_is_motd_text(text)){
|
|
return;
|
|
}
|
|
|
|
// if its a 0 line motd
|
|
if(strlen(text) <= motd_prefix_len){
|
|
return;
|
|
}
|
|
|
|
// add text to the motd
|
|
new_len = strlen(text + motd_prefix_len) - 1;
|
|
if((cur_len + new_len + 1) < MAX_PXO_MOTD_LEN){
|
|
strcat_s(Pxo_motd, text + motd_prefix_len + 1);
|
|
strcat_s(Pxo_motd, "\n");
|
|
mprintf(("MOTD ADD : %s\n", Pxo_motd));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set end of motd
|
|
*/
|
|
void multi_pxo_set_end_of_motd()
|
|
{
|
|
int blink = 1;
|
|
|
|
Pxo_motd_end = 1;
|
|
mprintf(("MOTD ALL : %s\n", Pxo_motd));
|
|
|
|
Pxo_motd_read = 0;
|
|
|
|
// do we have an old MOTD file laying around? If so, read it in and see if its the same
|
|
uint old_chksum;
|
|
uint new_chksum;
|
|
|
|
// checksum the current motd
|
|
new_chksum = cf_add_chksum_long(0, (ubyte*)Pxo_motd, strlen(Pxo_motd));
|
|
|
|
// checksum the old motd if its lying around
|
|
CFILE *in = cfopen("oldmotd.txt", "rb");
|
|
if(in != NULL){
|
|
// read the old checksum
|
|
cfread(&old_chksum, sizeof(old_chksum), 1, in);
|
|
cfclose(in);
|
|
|
|
// same checksum? no blink
|
|
if(new_chksum == old_chksum){
|
|
blink = 0;
|
|
}
|
|
}
|
|
|
|
// write out the motd for next time
|
|
if(strlen(Pxo_motd)){
|
|
CFILE *out = cfopen("oldmotd.txt", "wb", CFILE_NORMAL, CF_TYPE_DATA);
|
|
if(out != NULL){
|
|
// write all the text
|
|
cfwrite(&new_chksum, sizeof(new_chksum), 1, out);
|
|
|
|
// close the outfile
|
|
cfclose(out);
|
|
}
|
|
}
|
|
|
|
// set the blink stamp
|
|
Pxo_motd_blink_stamp = -1;
|
|
if(blink){
|
|
Pxo_motd_blink_on = 0;
|
|
if(!Pxo_motd_blinked_already){
|
|
Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
|
|
Pxo_motd_blink_on = 1;
|
|
}
|
|
}
|
|
|
|
Pxo_motd_blinked_already = 1;
|
|
}
|
|
|
|
/**
|
|
* Display the motd dialog
|
|
*/
|
|
void multi_pxo_motd_dialog()
|
|
{
|
|
// mark the motd as read
|
|
Pxo_motd_read = 1;
|
|
|
|
// simple popup, with a slider
|
|
popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, Pxo_motd);
|
|
}
|
|
|
|
/**
|
|
* Call to maybe blink the motd button
|
|
*/
|
|
void multi_pxo_motd_maybe_blit()
|
|
{
|
|
// if we got the end of the motd, and he hasn't read it yet
|
|
if(Pxo_motd_end && !Pxo_motd_read && (Pxo_motd_blink_stamp != -1)){
|
|
// if the timestamp elapsed, flip the blink flag
|
|
if(timestamp_elapsed(Pxo_motd_blink_stamp)){
|
|
Pxo_motd_blink_on = !Pxo_motd_blink_on;
|
|
Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
|
|
}
|
|
|
|
// draw properly
|
|
if(Pxo_motd_blink_on){
|
|
Multi_pxo_buttons[gr_screen.res][MULTI_PXO_MOTD].button.draw_forced(2);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// common dialog stuff ------------------------------------------------
|
|
|
|
int Multi_pxo_searching = 0;
|
|
|
|
/**
|
|
* Initialize the common dialog with the passed max input length
|
|
*/
|
|
void multi_pxo_com_init(int input_len)
|
|
{
|
|
int idx;
|
|
|
|
// create the interface window
|
|
Multi_pxo_com_window.create(0, 0, gr_screen.max_w,gr_screen.max_h, 0);
|
|
Multi_pxo_com_window.set_mask_bmap(Multi_pxo_com_mask_fname[gr_screen.res]);
|
|
|
|
// create the interface buttons
|
|
for(idx=0; idx<MULTI_PXO_COM_NUM_BUTTONS; idx++){
|
|
// create the object
|
|
Multi_pxo_com_buttons[gr_screen.res][idx].button.create(&Multi_pxo_com_window, "", Multi_pxo_com_buttons[gr_screen.res][idx].x, Multi_pxo_com_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
|
|
|
|
// set the sound to play when highlighted
|
|
Multi_pxo_com_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
|
|
|
|
// set the ani for the button
|
|
Multi_pxo_com_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_com_buttons[gr_screen.res][idx].filename);
|
|
|
|
// set the hotspot
|
|
Multi_pxo_com_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_com_buttons[gr_screen.res][idx].hotspot);
|
|
}
|
|
|
|
// add xstrs
|
|
for(idx=0; idx<MULTI_PXO_COM_NUM_TEXT; idx++){
|
|
Multi_pxo_com_window.add_XSTR(&Multi_pxo_com_text[gr_screen.res][idx]);
|
|
}
|
|
|
|
// create the input box
|
|
Multi_pxo_com_input.create(&Multi_pxo_com_window, Multi_pxo_com_input_coords[gr_screen.res][0], Multi_pxo_com_input_coords[gr_screen.res][1], Multi_pxo_com_input_coords[gr_screen.res][2], input_len, "", UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_EAT_USED);
|
|
Multi_pxo_com_input.set_focus();
|
|
|
|
// clear all text lines
|
|
memset(Multi_pxo_com_bottom_text, 0, MAX_PXO_TEXT_LEN);
|
|
memset(Multi_pxo_com_middle_text, 0, MAX_PXO_TEXT_LEN);
|
|
memset(Multi_pxo_com_top_text, 0, MAX_PXO_TEXT_LEN);
|
|
}
|
|
|
|
/**
|
|
* Close down the common dialog
|
|
*/
|
|
void multi_pxo_com_close()
|
|
{
|
|
// destroy the UI_WINDOW
|
|
Multi_pxo_com_window.destroy();
|
|
}
|
|
|
|
/**
|
|
* Blit all text lines, top, middle, bottoms
|
|
*/
|
|
void multi_pxo_com_blit_text()
|
|
{
|
|
// blit top, middle and bottom text if possible
|
|
if(Multi_pxo_com_top_text[0] != '\0'){
|
|
gr_set_color_fast(&Color_bright);
|
|
gr_string(Multi_pxo_com_top_text_coords[gr_screen.res][0], Multi_pxo_com_top_text_coords[gr_screen.res][1], Multi_pxo_com_top_text, GR_RESIZE_MENU);
|
|
}
|
|
if(Multi_pxo_com_middle_text[0] != '\0'){
|
|
gr_set_color_fast(&Color_bright);
|
|
gr_string(Multi_pxo_com_top_text_coords[gr_screen.res][0], Multi_pxo_com_middle_text_y[gr_screen.res], Multi_pxo_com_middle_text, GR_RESIZE_MENU);
|
|
}
|
|
if(Multi_pxo_com_bottom_text[0] != '\0'){
|
|
gr_set_color_fast(&Color_bright);
|
|
gr_string(Multi_pxo_com_top_text_coords[gr_screen.res][0], Multi_pxo_com_bottom_text_y[gr_screen.res], Multi_pxo_com_bottom_text, GR_RESIZE_MENU);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the top text, shortening as necessary
|
|
*/
|
|
void multi_pxo_com_set_top_text(const char *txt)
|
|
{
|
|
if((txt != NULL) && strlen(txt)){
|
|
strcpy_s(Multi_pxo_com_top_text,txt);
|
|
gr_force_fit_string(Multi_pxo_com_top_text, MAX_PXO_TEXT_LEN-1, Multi_pxo_com_input_coords[gr_screen.res][2]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the middle text, shortening as necessary
|
|
*/
|
|
void multi_pxo_com_set_middle_text(const char *txt)
|
|
{
|
|
if((txt != NULL) && strlen(txt)){
|
|
strcpy_s(Multi_pxo_com_middle_text,txt);
|
|
gr_force_fit_string(Multi_pxo_com_middle_text, MAX_PXO_TEXT_LEN-1, Multi_pxo_com_input_coords[gr_screen.res][2]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the bottom text, shortening as necessary
|
|
*/
|
|
void multi_pxo_com_set_bottom_text(const char *txt)
|
|
{
|
|
if((txt != NULL) && strlen(txt)){
|
|
strcpy_s(Multi_pxo_com_bottom_text,txt);
|
|
gr_force_fit_string(Multi_pxo_com_bottom_text, MAX_PXO_TEXT_LEN-1, Multi_pxo_com_input_coords[gr_screen.res][2]);
|
|
}
|
|
}
|
|
|
|
|
|
// private channel join stuff -----------------------------------------
|
|
|
|
/**
|
|
* Initialize the popup
|
|
*/
|
|
void multi_pxo_priv_init()
|
|
{
|
|
Assert(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE);
|
|
|
|
// initialize the common dialog with the passed max input length
|
|
multi_pxo_com_init(MULTI_PXO_PRIV_MAX_TEXT_LEN);
|
|
|
|
// initialize the return code
|
|
Multi_pxo_priv_return_code = -1;
|
|
|
|
// mark us as running
|
|
Multi_pxo_mode = MULTI_PXO_MODE_PRIVATE;
|
|
|
|
// set some text
|
|
multi_pxo_com_set_middle_text(XSTR("Type the name of the channel to join/create",961));
|
|
}
|
|
|
|
/**
|
|
* Close down the popup
|
|
*/
|
|
void multi_pxo_priv_close()
|
|
{
|
|
// close down the common dialog
|
|
multi_pxo_com_close();
|
|
|
|
// mark us as not running any more
|
|
Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
|
|
}
|
|
|
|
/**
|
|
* Run the popup, 0 if still running, -1 if cancel, 1 if ok
|
|
*/
|
|
int multi_pxo_priv_popup()
|
|
{
|
|
int k;
|
|
|
|
// if we're not already running, initialize stuff
|
|
if(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE){
|
|
// intialize
|
|
multi_pxo_priv_init();
|
|
|
|
// return "still running"
|
|
return 0;
|
|
}
|
|
|
|
k = Multi_pxo_com_window.process();
|
|
|
|
// process keypresses
|
|
switch(k){
|
|
// like hitting the cancel button
|
|
case KEY_ESC:
|
|
Multi_pxo_priv_return_code = 0;
|
|
break;
|
|
}
|
|
|
|
// process button presses
|
|
multi_pxo_priv_process_buttons();
|
|
|
|
// process the inputbox
|
|
multi_pxo_priv_process_input();
|
|
|
|
// blit the background
|
|
multi_pxo_blit_all();
|
|
|
|
// blit my stuff
|
|
gr_reset_clip();
|
|
gr_set_bitmap(Multi_pxo_com_bitmap);
|
|
gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1], GR_RESIZE_MENU);
|
|
Multi_pxo_com_window.draw();
|
|
|
|
// blit all text lines, top, middle, bottoms
|
|
multi_pxo_com_blit_text();
|
|
|
|
gr_flip();
|
|
|
|
// check the return code
|
|
switch(Multi_pxo_priv_return_code){
|
|
// still in progress
|
|
case -1 :
|
|
return 0;
|
|
|
|
// user hit cancel
|
|
case 0 :
|
|
multi_pxo_priv_close();
|
|
return -1;
|
|
|
|
// user hit ok
|
|
case 1 :
|
|
multi_pxo_priv_close();
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Process button presses
|
|
*/
|
|
void multi_pxo_priv_process_buttons()
|
|
{
|
|
int idx;
|
|
|
|
// check all buttons
|
|
for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
|
|
if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
|
|
multi_pxo_priv_button_pressed(idx);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle a button press
|
|
*/
|
|
void multi_pxo_priv_button_pressed(int n)
|
|
{
|
|
char priv_chan_name[128];
|
|
|
|
switch(n){
|
|
case MULTI_PXO_COM_CANCEL:
|
|
Multi_pxo_priv_return_code = 0;
|
|
break;
|
|
|
|
case MULTI_PXO_COM_OK:
|
|
Multi_pxo_com_input.get_text(priv_chan_name);
|
|
multi_pxo_strip_space(priv_chan_name,priv_chan_name);
|
|
|
|
// if its a 0 length string, interpret as a cancel
|
|
if(strlen(priv_chan_name) <= 0){
|
|
Multi_pxo_priv_return_code = 0;
|
|
return;
|
|
}
|
|
|
|
Multi_pxo_priv_return_code = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process the inputbox
|
|
*/
|
|
void multi_pxo_priv_process_input()
|
|
{
|
|
char priv_chan_name[128];
|
|
|
|
// see if the user has pressed enter
|
|
if(Multi_pxo_com_input.pressed()){
|
|
Multi_pxo_com_input.get_text(priv_chan_name);
|
|
multi_pxo_strip_space(priv_chan_name,priv_chan_name);
|
|
|
|
// if its a 0 length string, interpret as a cancel
|
|
if(strlen(priv_chan_name) <= 0){
|
|
Multi_pxo_priv_return_code = 0;
|
|
return;
|
|
}
|
|
|
|
// otherwise interpret as "accept"
|
|
Multi_pxo_priv_return_code = 1;
|
|
|
|
// add in the "+" which indicates a private room
|
|
strcpy_s(Multi_pxo_priv_chan,"+");
|
|
strcat_s(Multi_pxo_priv_chan, priv_chan_name);
|
|
}
|
|
}
|
|
|
|
// find player stuff -----------------------------------------
|
|
|
|
char name_lookup[MAX_PXO_TEXT_LEN];
|
|
|
|
/**
|
|
* Initialize the popup
|
|
*/
|
|
void multi_pxo_find_init()
|
|
{
|
|
Assert(Multi_pxo_mode != MULTI_PXO_MODE_FIND);
|
|
|
|
// initialize the common dialog with the passed max input length
|
|
multi_pxo_com_init(MAX_PLAYER_NAME_LEN);
|
|
|
|
// return code, set to something other than -1 if we're supposed to return
|
|
Multi_pxo_find_return_code = -1;
|
|
|
|
// mark us as running
|
|
Multi_pxo_mode = MULTI_PXO_MODE_FIND;
|
|
|
|
// not searching yet
|
|
Multi_pxo_searching = 0;
|
|
|
|
// set the top text
|
|
multi_pxo_com_set_top_text(XSTR("Enter user to be found",962));
|
|
|
|
// 0 length
|
|
strcpy_s(Multi_pxo_find_channel,"");
|
|
|
|
// 0 length
|
|
strcpy_s(name_lookup,"");
|
|
}
|
|
|
|
/**
|
|
* Close down the popup
|
|
*/
|
|
void multi_pxo_find_close()
|
|
{
|
|
// close down the common dialog
|
|
multi_pxo_com_close();
|
|
|
|
// mark us as not running any more
|
|
Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
|
|
}
|
|
|
|
/**
|
|
* Run the popup, 0 if still running, -1 if cancel, 1 if ok
|
|
*/
|
|
int multi_pxo_find_popup()
|
|
{
|
|
int k;
|
|
|
|
// if we're not already running, initialize stuff
|
|
if(Multi_pxo_mode != MULTI_PXO_MODE_FIND){
|
|
// intialize
|
|
multi_pxo_find_init();
|
|
|
|
// return "still running"
|
|
return 0;
|
|
}
|
|
|
|
k = Multi_pxo_com_window.process();
|
|
|
|
// process keypresses
|
|
switch(k){
|
|
// like hitting the cancel button
|
|
case KEY_ESC:
|
|
Multi_pxo_find_return_code = 0;
|
|
break;
|
|
}
|
|
|
|
// process button presses
|
|
multi_pxo_find_process_buttons();
|
|
|
|
// process the inputbox
|
|
multi_pxo_find_process_input();
|
|
|
|
// process search mode if applicable
|
|
multi_pxo_find_search_process();
|
|
|
|
// blit the background
|
|
multi_pxo_blit_all();
|
|
|
|
// blit my stuff
|
|
gr_reset_clip();
|
|
gr_set_bitmap(Multi_pxo_com_bitmap);
|
|
gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1], GR_RESIZE_MENU);
|
|
Multi_pxo_com_window.draw();
|
|
|
|
// blit any text lines
|
|
multi_pxo_com_blit_text();
|
|
|
|
gr_flip();
|
|
|
|
// check the return code
|
|
switch(Multi_pxo_find_return_code){
|
|
// still in progress
|
|
case -1 :
|
|
return 0;
|
|
|
|
// user hit cancel
|
|
case 0 :
|
|
// close the popup down
|
|
multi_pxo_find_close();
|
|
return -1;
|
|
|
|
// user hit ok
|
|
case 1 :
|
|
// close the popup down
|
|
multi_pxo_find_close();
|
|
|
|
// if we have a channel, join it now if possible
|
|
if(Multi_pxo_find_channel[0] != '\0'){
|
|
pxo_channel *lookup;
|
|
lookup = multi_pxo_find_channel(Multi_pxo_find_channel,Multi_pxo_channels);
|
|
|
|
// if we couldn't find it, don't join
|
|
if(lookup != NULL){
|
|
multi_pxo_join_channel(lookup);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Process button presses
|
|
*/
|
|
void multi_pxo_find_process_buttons()
|
|
{
|
|
int idx;
|
|
|
|
// check all buttons
|
|
for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
|
|
if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
|
|
multi_pxo_find_button_pressed(idx);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle a button press
|
|
*/
|
|
void multi_pxo_find_button_pressed(int n)
|
|
{
|
|
switch(n){
|
|
case MULTI_PXO_COM_CANCEL:
|
|
Multi_pxo_find_return_code = 0;
|
|
break;
|
|
|
|
case MULTI_PXO_COM_OK:
|
|
Multi_pxo_find_return_code = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process the inputbox
|
|
*/
|
|
void multi_pxo_find_process_input()
|
|
{
|
|
// see if the user has pressed enter
|
|
if(Multi_pxo_com_input.pressed()){
|
|
// if we're not already in search mode
|
|
if(!Multi_pxo_searching){
|
|
// clear all text
|
|
memset(Multi_pxo_com_middle_text,0,MAX_PXO_TEXT_LEN);
|
|
memset(Multi_pxo_com_bottom_text,0,MAX_PXO_TEXT_LEN);
|
|
|
|
Multi_pxo_com_input.get_text(name_lookup);
|
|
multi_pxo_strip_space(name_lookup,name_lookup);
|
|
|
|
// never search with a zero length string
|
|
if(name_lookup[0] != '\0'){
|
|
char search_text[512];
|
|
|
|
// put us in search mode
|
|
Multi_pxo_searching = 1;
|
|
|
|
// look for the guy
|
|
GetChannelByUser(name_lookup);
|
|
|
|
// set the top text
|
|
memset(search_text,0,512);
|
|
sprintf(search_text,XSTR("Searching for %s",963),name_lookup);
|
|
multi_pxo_com_set_top_text(search_text);
|
|
}
|
|
// clear everything
|
|
else {
|
|
memset(Multi_pxo_com_top_text,0,MAX_PXO_TEXT_LEN);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process search mode if applicable
|
|
*/
|
|
void multi_pxo_find_search_process()
|
|
{
|
|
char *channel;
|
|
|
|
// if we're not searching for anything, return
|
|
if(!Multi_pxo_searching){
|
|
return;
|
|
}
|
|
|
|
// otherwise check to see if we've found him
|
|
channel = GetChannelByUser(NULL);
|
|
|
|
// if we've got a result, let the user know
|
|
if(channel){
|
|
// if he couldn't be found
|
|
if((ptr_s)channel == -1){
|
|
multi_pxo_com_set_middle_text(XSTR("User not found",964));
|
|
strcpy_s(Multi_pxo_find_channel,"");
|
|
} else {
|
|
if(channel[0] == '*'){
|
|
multi_pxo_com_set_middle_text(XSTR("Player is logged in but is not on a channel",965));
|
|
strcpy_s(Multi_pxo_find_channel,"");
|
|
} else {
|
|
char p_text[512];
|
|
memset(p_text,0,512);
|
|
|
|
// if this guy is on a public channel, display which one
|
|
if(channel[0] == '#'){
|
|
sprintf(p_text,XSTR("Found %s on :",966),name_lookup);
|
|
|
|
// display the results
|
|
multi_pxo_com_set_middle_text(p_text);
|
|
multi_pxo_com_set_bottom_text(channel+1);
|
|
|
|
// mark down the channel name so we know where to find him
|
|
strcpy_s(Multi_pxo_find_channel,channel);
|
|
// strip out trailing whitespace
|
|
if(Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] == ' '){
|
|
Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] = '\0';
|
|
}
|
|
}
|
|
// if this is a private channel
|
|
else if(channel[0] == '+'){
|
|
sprintf(p_text,XSTR("Found %s on a private channel",967),name_lookup);
|
|
multi_pxo_com_set_middle_text(p_text);
|
|
|
|
strcpy_s(Multi_pxo_find_channel,"");
|
|
}
|
|
}
|
|
}
|
|
|
|
// unset search mode
|
|
Multi_pxo_searching = 0;
|
|
|
|
// clear the inputbox
|
|
Multi_pxo_com_input.set_text("");
|
|
}
|
|
}
|
|
|
|
|
|
// player info stuff -----------------------------------------
|
|
|
|
/**
|
|
* Popup conditional functions, returns 10 on successful get of stats
|
|
*/
|
|
int multi_pxo_pinfo_cond()
|
|
{
|
|
// process common stuff
|
|
multi_pxo_process_common();
|
|
|
|
// run the networking functions for the PXO API
|
|
multi_pxo_api_process();
|
|
|
|
// process depending on what mode we're in
|
|
switch (Multi_pxo_retrieve_mode)
|
|
{
|
|
// we don't need to do anything extra here, just move on to mode 1
|
|
case 0:
|
|
{
|
|
char *ret_string;
|
|
char temp_string[MAX_PXO_TEXT_LEN];
|
|
char *tok;
|
|
|
|
// if the thing is non-null, do something
|
|
ret_string = GetTrackerIdByUser(Multi_pxo_retrieve_name);
|
|
|
|
if (ret_string != NULL) {
|
|
// user not-online/not found
|
|
if ( (int)ret_string[0] == -1) {
|
|
return 1;
|
|
}
|
|
|
|
// user not a tracker pilot
|
|
if ( !stricmp(ret_string,"-1") ) {
|
|
return 1;
|
|
}
|
|
|
|
// otherwise parse into his id and callsign
|
|
strcpy_s(temp_string, ret_string);
|
|
tok = strtok(temp_string, " ");
|
|
|
|
// get tracker id
|
|
if (tok != NULL) {
|
|
strcpy_s(Multi_pxo_retrieve_id, tok);
|
|
|
|
// get the callsign
|
|
tok = strtok(NULL, "");
|
|
|
|
if (tok != NULL) {
|
|
strcpy_s(Multi_pxo_retrieve_name, tok);
|
|
}
|
|
// failure
|
|
else {
|
|
return 1;
|
|
}
|
|
}
|
|
// failure of some kind or another
|
|
else {
|
|
return 1;
|
|
}
|
|
|
|
Multi_pxo_retrieve_mode = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// initial call to get his stats
|
|
case 1:
|
|
{
|
|
// change the popup text
|
|
popup_change_text(XSTR("Getting player stats",968));
|
|
|
|
switch ( fs2netd_get_pilot_info(Multi_pxo_retrieve_name, &Multi_pxo_pinfo_player, true) )
|
|
{
|
|
// there was some failure
|
|
case -2:
|
|
return 2;
|
|
|
|
// still processing
|
|
case -1:
|
|
Multi_pxo_retrieve_mode = 2;
|
|
break;
|
|
|
|
// we got the data
|
|
case 0:
|
|
return 10;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// busy retrieving his stats
|
|
case 2:
|
|
{
|
|
switch ( fs2netd_get_pilot_info(Multi_pxo_retrieve_name, &Multi_pxo_pinfo_player, false) )
|
|
{
|
|
// there was some failure
|
|
case -2:
|
|
return 2;
|
|
|
|
// still processing
|
|
case -1:
|
|
break;
|
|
|
|
// we got the data
|
|
case 0:
|
|
return 10;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// return not done yet
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
|
|
*/
|
|
int multi_pxo_pinfo_get(char *name)
|
|
{
|
|
// run the popup
|
|
Multi_pxo_retrieve_mode = 0;
|
|
strcpy_s(Multi_pxo_retrieve_name, name);
|
|
|
|
switch ( popup_till_condition(multi_pxo_pinfo_cond, XSTR("&Cancel", 779), XSTR("Retrieving player tracker id", 969)) )
|
|
{
|
|
// success
|
|
case 10 :
|
|
return 1;
|
|
|
|
// failed to get his tracker id
|
|
case 1 :
|
|
return 0;
|
|
|
|
// failed to get his stats
|
|
case 2 :
|
|
return 0;
|
|
}
|
|
|
|
// we didn't get the stats
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Fire up the stats view popup
|
|
*/
|
|
void multi_pxo_pinfo_show()
|
|
{
|
|
// initialize the popup
|
|
multi_pxo_pinfo_init();
|
|
|
|
// run the popup
|
|
do {
|
|
game_set_frametime(GS_STATE_PXO);
|
|
} while ( !multi_pxo_pinfo_do() );
|
|
|
|
// close down the popup
|
|
multi_pxo_pinfo_close();
|
|
}
|
|
|
|
/**
|
|
* Build the stats labels values
|
|
*/
|
|
void multi_pxo_pinfo_build_vals()
|
|
{
|
|
player *fs = &Multi_pxo_pinfo_player;
|
|
|
|
// pilot name
|
|
memset(Multi_pxo_pinfo_vals[0], 0, 50);
|
|
strcpy_s(Multi_pxo_pinfo_vals[0], fs->callsign);
|
|
gr_force_fit_string(Multi_pxo_pinfo_vals[0], 49, Multi_pxo_pinfo_coords[gr_screen.res][2] - (Multi_pxo_pinfo_val_x[gr_screen.res] - Multi_pxo_pinfo_coords[gr_screen.res][0]));
|
|
|
|
// rank
|
|
memset(Multi_pxo_pinfo_vals[1], 0, 50);
|
|
multi_sg_rank_build_name(Ranks[fs->stats.rank].name, Multi_pxo_pinfo_vals[1]);
|
|
gr_force_fit_string(Multi_pxo_pinfo_vals[1], 49, Multi_pxo_pinfo_coords[gr_screen.res][2] - (Multi_pxo_pinfo_val_x[gr_screen.res] - Multi_pxo_pinfo_coords[gr_screen.res][0]));
|
|
|
|
// kills
|
|
memset(Multi_pxo_pinfo_vals[2], 0, 50);
|
|
sprintf(Multi_pxo_pinfo_vals[2], "%d", fs->stats.kill_count);
|
|
|
|
// assists
|
|
memset(Multi_pxo_pinfo_vals[3], 0, 50);
|
|
sprintf(Multi_pxo_pinfo_vals[3], "%d", fs->stats.assists);
|
|
|
|
// friendly kills
|
|
memset(Multi_pxo_pinfo_vals[4], 0, 50);
|
|
sprintf(Multi_pxo_pinfo_vals[4], "%d", fs->stats.kill_count - fs->stats.kill_count_ok);
|
|
|
|
// missions flown
|
|
memset(Multi_pxo_pinfo_vals[5], 0, 50);
|
|
sprintf(Multi_pxo_pinfo_vals[5], "%d", (int)fs->stats.missions_flown);
|
|
|
|
// flight time
|
|
memset(Multi_pxo_pinfo_vals[6], 0, 50);
|
|
game_format_time( fl2f((float)fs->stats.flight_time), Multi_pxo_pinfo_vals[6] );
|
|
|
|
// last flown
|
|
memset(Multi_pxo_pinfo_vals[7], 0, 50);
|
|
if (fs->stats.last_flown == 0) {
|
|
strcpy_s(Multi_pxo_pinfo_vals[7], XSTR("No missions flown", 970) );
|
|
} else {
|
|
time_t tmp_lf = fs->stats.last_flown; // don't cast a pointer to a type that can be either 32 or 64bit
|
|
tm *tmr = gmtime( &tmp_lf );
|
|
|
|
if (tmr != NULL)
|
|
strftime(Multi_pxo_pinfo_vals[7], 30, "%m/%d/%y %H:%M", tmr);
|
|
else
|
|
strcpy_s(Multi_pxo_pinfo_vals[7], "");
|
|
}
|
|
|
|
// primary shots fired
|
|
memset(Multi_pxo_pinfo_vals[8], 0, 50);
|
|
sprintf(Multi_pxo_pinfo_vals[8], "%d", (int)fs->stats.p_shots_fired);
|
|
|
|
// primary shots hit
|
|
memset(Multi_pxo_pinfo_vals[9],0,50);
|
|
sprintf(Multi_pxo_pinfo_vals[9], "%d", (int)fs->stats.p_shots_hit);
|
|
|
|
// primary hit pct
|
|
memset(Multi_pxo_pinfo_vals[10], 0, 50);
|
|
if (fs->stats.p_shots_fired > 0) {
|
|
sprintf(Multi_pxo_pinfo_vals[10], "%d%%", (int)((float)fs->stats.p_shots_hit / (float)fs->stats.p_shots_fired * 100.0f));
|
|
} else {
|
|
strcpy_s(Multi_pxo_pinfo_vals[10], "0%");
|
|
}
|
|
|
|
// secondary shots fired
|
|
memset(Multi_pxo_pinfo_vals[11], 0, 50);
|
|
sprintf(Multi_pxo_pinfo_vals[11], "%d", (int)fs->stats.s_shots_fired);
|
|
|
|
// secondary shots hit
|
|
memset(Multi_pxo_pinfo_vals[12], 0, 50);
|
|
sprintf(Multi_pxo_pinfo_vals[12], "%d", (int)fs->stats.s_shots_hit);
|
|
|
|
// secondary hit pct
|
|
memset(Multi_pxo_pinfo_vals[13], 0, 50);
|
|
if (fs->stats.s_shots_fired > 0) {
|
|
sprintf(Multi_pxo_pinfo_vals[13], "%d%%", (int)((float)fs->stats.s_shots_hit / (float)fs->stats.s_shots_fired * 100.0f));
|
|
} else {
|
|
strcpy_s(Multi_pxo_pinfo_vals[13], "0%");
|
|
}
|
|
|
|
// primary friendly hits
|
|
memset(Multi_pxo_pinfo_vals[14], 0, 50);
|
|
sprintf(Multi_pxo_pinfo_vals[14], "%u", fs->stats.p_bonehead_hits);
|
|
|
|
// primary friendly hit %
|
|
memset(Multi_pxo_pinfo_vals[15], 0, 50);
|
|
if (fs->stats.p_shots_hit > 0) {
|
|
sprintf(Multi_pxo_pinfo_vals[15], "%d%%", (int)((float)100.0f*((float)fs->stats.p_bonehead_hits/(float)fs->stats.p_shots_fired)));
|
|
} else {
|
|
strcpy_s(Multi_pxo_pinfo_vals[15], "0%");
|
|
}
|
|
|
|
// secondary friendly hits
|
|
memset(Multi_pxo_pinfo_vals[16], 0, 50);
|
|
sprintf(Multi_pxo_pinfo_vals[16], "%u", fs->stats.s_bonehead_hits);
|
|
|
|
// secondary friendly hit %
|
|
memset(Multi_pxo_pinfo_vals[17], 0, 50);
|
|
if (fs->stats.s_shots_hit > 0) {
|
|
sprintf(Multi_pxo_pinfo_vals[17], "%d%%", (int)((float)100.0f*((float)fs->stats.s_bonehead_hits/(float)fs->stats.s_shots_fired)));
|
|
} else {
|
|
strcpy_s(Multi_pxo_pinfo_vals[17], "0%");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize the popup
|
|
*/
|
|
void multi_pxo_pinfo_init()
|
|
{
|
|
int idx;
|
|
|
|
// create the interface window
|
|
Multi_pxo_pinfo_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
|
|
Multi_pxo_pinfo_window.set_mask_bmap(Multi_pxo_pinfo_mask_fname[gr_screen.res]);
|
|
|
|
Multi_pxo_pinfo_bitmap = bm_load(Multi_pxo_pinfo_fname[gr_screen.res]);
|
|
Assert(Multi_pxo_pinfo_bitmap != -1);
|
|
|
|
// create the interface buttons
|
|
for(idx=0; idx<MULTI_PXO_PINFO_NUM_BUTTONS; idx++){
|
|
// create the object
|
|
Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.create(&Multi_pxo_pinfo_window, "", Multi_pxo_pinfo_buttons[gr_screen.res][idx].x, Multi_pxo_pinfo_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
|
|
|
|
// set the sound to play when highlighted
|
|
Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
|
|
|
|
// set the ani for the button
|
|
Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_pinfo_buttons[gr_screen.res][idx].filename);
|
|
|
|
// set the hotspot
|
|
Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_pinfo_buttons[gr_screen.res][idx].hotspot);
|
|
}
|
|
|
|
// add xstrs
|
|
for(idx=0; idx<MULTI_PXO_PINFO_NUM_TEXT; idx++){
|
|
Multi_pxo_pinfo_window.add_XSTR(&Multi_pxo_pinfo_text[gr_screen.res][idx]);
|
|
}
|
|
|
|
// set up the stats labels
|
|
Multi_pxo_pinfo_stats_labels[0] = vm_strdup(XSTR("Name", 1532));
|
|
Multi_pxo_pinfo_stats_labels[1] = vm_strdup(XSTR("Rank", 1533));
|
|
Multi_pxo_pinfo_stats_labels[2] = vm_strdup(XSTR("Kills", 1534));
|
|
Multi_pxo_pinfo_stats_labels[3] = vm_strdup(XSTR("Assists", 1535));
|
|
Multi_pxo_pinfo_stats_labels[4] = vm_strdup(XSTR("Friendly kills", 1536));
|
|
Multi_pxo_pinfo_stats_labels[5] = vm_strdup(XSTR("Missions flown", 1537));
|
|
Multi_pxo_pinfo_stats_labels[6] = vm_strdup(XSTR("Flight time", 1538));
|
|
Multi_pxo_pinfo_stats_labels[7] = vm_strdup(XSTR("Last flown", 1539));
|
|
Multi_pxo_pinfo_stats_labels[8] = vm_strdup(XSTR("Primary shots fired", 1540));
|
|
Multi_pxo_pinfo_stats_labels[9] = vm_strdup(XSTR("Primary shots hit", 1541));
|
|
Multi_pxo_pinfo_stats_labels[10] = vm_strdup(XSTR("Primary hit %", 1542));
|
|
Multi_pxo_pinfo_stats_labels[11] = vm_strdup(XSTR("Secondary shots fired", 1543));
|
|
Multi_pxo_pinfo_stats_labels[12] = vm_strdup(XSTR("Secondary shots hit", 1544));
|
|
Multi_pxo_pinfo_stats_labels[13] = vm_strdup(XSTR("Secondary hit %", 1545));
|
|
Multi_pxo_pinfo_stats_labels[14] = vm_strdup(XSTR("Primary friendly hits", 1546));
|
|
Multi_pxo_pinfo_stats_labels[15] = vm_strdup(XSTR("Primary friendly hit %", 1547));
|
|
Multi_pxo_pinfo_stats_labels[16] = vm_strdup(XSTR("Secondary friendly hits", 1548));
|
|
Multi_pxo_pinfo_stats_labels[17] = vm_strdup(XSTR("Secondary friendly hit %", 1549));
|
|
|
|
// build the stats labels values
|
|
multi_pxo_pinfo_build_vals();
|
|
}
|
|
|
|
/**
|
|
* Do frame
|
|
*/
|
|
int multi_pxo_pinfo_do()
|
|
{
|
|
int k = Multi_pxo_pinfo_window.process();
|
|
|
|
// process common stuff
|
|
multi_pxo_process_common();
|
|
|
|
// run the networking functions for the PXO API
|
|
multi_pxo_api_process();
|
|
|
|
// check to see if he pressed escp
|
|
if(k == KEY_ESC){
|
|
return 1;
|
|
}
|
|
|
|
// if he pressed the ok button
|
|
if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_OK].button.pressed()){
|
|
return 1;
|
|
}
|
|
|
|
// if he pressed the medals buttons, run the medals screen
|
|
if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_MEDALS].button.pressed()){
|
|
multi_pxo_run_medals();
|
|
}
|
|
|
|
// draw stuff
|
|
|
|
// blit everything on the "normal" screen
|
|
multi_pxo_blit_all();
|
|
|
|
// blit our own stuff
|
|
gr_reset_clip();
|
|
gr_set_bitmap(Multi_pxo_pinfo_bitmap);
|
|
gr_bitmap(0, 0, GR_RESIZE_MENU);
|
|
Multi_pxo_pinfo_window.draw();
|
|
|
|
// blit the stats themselves
|
|
multi_pxo_pinfo_blit();
|
|
|
|
// flip the page
|
|
gr_flip();
|
|
|
|
// not done yet
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Close
|
|
*/
|
|
void multi_pxo_pinfo_close()
|
|
{
|
|
int i;
|
|
|
|
// destroy the UI_WINDOW
|
|
Multi_pxo_pinfo_window.destroy();
|
|
|
|
// unload the bitmap
|
|
if(Multi_pxo_pinfo_bitmap != -1){
|
|
bm_unload(Multi_pxo_pinfo_bitmap);
|
|
}
|
|
|
|
// free the stats labels strings
|
|
for (i=0; i<MULTI_PXO_PINFO_NUM_LABELS; i++) {
|
|
vm_free(Multi_pxo_pinfo_stats_labels[i]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Blit all the stats on this screen
|
|
*/
|
|
void multi_pxo_pinfo_blit()
|
|
{
|
|
int idx;
|
|
int y_start;
|
|
|
|
// blit all the labels
|
|
y_start = Multi_pxo_pinfo_coords[gr_screen.res][1];
|
|
for(idx=0; idx<MULTI_PXO_PINFO_NUM_LABELS; idx++){
|
|
// blit the label
|
|
gr_set_color_fast(&Color_bright);
|
|
gr_string(Multi_pxo_pinfo_coords[gr_screen.res][0], y_start, Multi_pxo_pinfo_stats_labels[idx], GR_RESIZE_MENU);
|
|
|
|
// blit the label's value
|
|
gr_set_color_fast(&Color_normal);
|
|
gr_string(Multi_pxo_pinfo_val_x[gr_screen.res], y_start, Multi_pxo_pinfo_vals[idx], GR_RESIZE_MENU);
|
|
|
|
// spacing
|
|
y_start += Multi_pxo_pinfo_stats_spacing[idx];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Run the medals screen
|
|
*/
|
|
void multi_pxo_run_medals()
|
|
{
|
|
int ret_code;
|
|
|
|
// process common stuff
|
|
multi_pxo_process_common();
|
|
|
|
// run the networking functions for the PXO API
|
|
multi_pxo_api_process();
|
|
|
|
// initialize the medals screen
|
|
medal_main_init(&Multi_pxo_pinfo_player, MM_POPUP);
|
|
|
|
// run the medals screen until it says that it should be closed
|
|
do {
|
|
// set frametime and run common functions
|
|
game_set_frametime(-1);
|
|
game_do_state_common( gameseq_get_state() );
|
|
|
|
// run the medals screen
|
|
ret_code = medal_main_do();
|
|
} while(ret_code);
|
|
|
|
// close the medals screen down
|
|
medal_main_close();
|
|
|
|
// reset the palette
|
|
multi_pxo_load_palette();
|
|
}
|
|
|
|
|
|
// notify stuff stuff -----------------------------------------
|
|
|
|
/**
|
|
* Add a notification string
|
|
*/
|
|
void multi_pxo_notify_add(const char *txt)
|
|
{
|
|
// copy the text
|
|
strcpy_s(Multi_pxo_notify_text, txt);
|
|
|
|
// set the timestamp
|
|
Multi_pxo_notify_stamp = timestamp(MULTI_PXO_NOTIFY_TIME);
|
|
}
|
|
|
|
/**
|
|
* Blit and process the notification string
|
|
*/
|
|
void multi_pxo_notify_blit()
|
|
{
|
|
int w;
|
|
|
|
// if the timestamp is -1, do nothing
|
|
if(Multi_pxo_notify_stamp == -1){
|
|
return;
|
|
}
|
|
|
|
// if it has expired, do nothing
|
|
if(timestamp_elapsed(Multi_pxo_notify_stamp)){
|
|
Multi_pxo_notify_stamp = -1;
|
|
}
|
|
|
|
// otherwise blit the text
|
|
gr_set_color_fast(&Color_bright);
|
|
gr_get_string_size(&w,NULL,Multi_pxo_notify_text);
|
|
gr_string((gr_screen.max_w - w)/2,MULTI_PXO_NOTIFY_Y,Multi_pxo_notify_text,GR_RESIZE_MENU);
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialize the PXO help screen
|
|
*/
|
|
void multi_pxo_help_init()
|
|
{
|
|
int idx;
|
|
|
|
// load the background bitmap
|
|
Multi_pxo_help_bitmap = bm_load(Multi_pxo_help_fname[gr_screen.res]);
|
|
if(Multi_pxo_help_bitmap < 0){
|
|
// we failed to load the bitmap - this is very bad
|
|
Int3();
|
|
}
|
|
|
|
// create the interface window
|
|
Multi_pxo_help_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
|
|
Multi_pxo_help_window.set_mask_bmap(Multi_pxo_help_mask_fname[gr_screen.res]);
|
|
|
|
// create the interface buttons
|
|
for(idx=0; idx<MULTI_PXO_HELP_NUM_BUTTONS; idx++){
|
|
// create the object
|
|
Multi_pxo_help_buttons[gr_screen.res][idx].button.create(&Multi_pxo_help_window, "", Multi_pxo_help_buttons[gr_screen.res][idx].x, Multi_pxo_help_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
|
|
|
|
// set the sound to play when highlighted
|
|
Multi_pxo_help_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
|
|
|
|
// set the ani for the button
|
|
Multi_pxo_help_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_help_buttons[gr_screen.res][idx].filename);
|
|
|
|
// set the hotspot
|
|
Multi_pxo_help_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_help_buttons[gr_screen.res][idx].hotspot);
|
|
}
|
|
|
|
// add xstrs
|
|
for(idx=0; idx<MULTI_PXO_HELP_NUM_TEXT; idx++){
|
|
Multi_pxo_help_window.add_XSTR(&Multi_pxo_help_text[gr_screen.res][idx]);
|
|
}
|
|
|
|
multi_pxo_help_load();
|
|
|
|
// set the current page to 0
|
|
Multi_pxo_help_cur = 0;
|
|
}
|
|
|
|
/**
|
|
* Do frame for PXO help
|
|
*/
|
|
void multi_pxo_help_do()
|
|
{
|
|
// run api stuff
|
|
if(Multi_pxo_connected){
|
|
multi_pxo_api_process();
|
|
}
|
|
|
|
// process common stuff
|
|
multi_pxo_process_common();
|
|
|
|
int k = Multi_pxo_help_window.process();
|
|
|
|
// process any keypresses
|
|
switch(k){
|
|
case KEY_ESC:
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
gameseq_post_event(GS_EVENT_PXO);
|
|
break;
|
|
}
|
|
|
|
// process button presses
|
|
multi_pxo_help_process_buttons();
|
|
|
|
// draw the background, etc
|
|
gr_reset_clip();
|
|
GR_MAYBE_CLEAR_RES(Multi_pxo_help_bitmap);
|
|
if(Multi_pxo_help_bitmap != -1){
|
|
gr_set_bitmap(Multi_pxo_help_bitmap);
|
|
gr_bitmap(0,0,GR_RESIZE_MENU);
|
|
}
|
|
Multi_pxo_help_window.draw();
|
|
|
|
// blit the current page
|
|
multi_pxo_help_blit_page();
|
|
|
|
// page flip
|
|
gr_flip();
|
|
}
|
|
|
|
/**
|
|
* Close the pxo screen
|
|
*/
|
|
void multi_pxo_help_close()
|
|
{
|
|
int idx, idx2;
|
|
|
|
// unload any bitmaps
|
|
bm_unload(Multi_pxo_help_bitmap);
|
|
|
|
// destroy the UI_WINDOW
|
|
Multi_pxo_help_window.destroy();
|
|
|
|
// free all pages
|
|
for(idx=0; idx<Multi_pxo_help_num_pages; idx++){
|
|
for(idx2=0; idx2<Multi_pxo_help_pages[idx].num_lines; idx2++){
|
|
// maybe free
|
|
if(Multi_pxo_help_pages[idx].text[idx2] != NULL){
|
|
vm_free(Multi_pxo_help_pages[idx].text[idx2]);
|
|
Multi_pxo_help_pages[idx].text[idx2] = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load the help file up
|
|
*/
|
|
void multi_pxo_help_load()
|
|
{
|
|
CFILE *in;
|
|
help_page *cp;
|
|
|
|
// read in the text file
|
|
in = NULL;
|
|
in = cfopen(MULTI_PXO_HELP_FILE,"rt",CFILE_NORMAL,CF_TYPE_DATA);
|
|
Assert(in != NULL);
|
|
if(in == NULL){
|
|
return;
|
|
}
|
|
|
|
Multi_pxo_help_num_pages = 0;
|
|
|
|
// blast all the help pages clear
|
|
memset(Multi_pxo_help_pages, 0, sizeof(help_page) * MULTI_PXO_MAX_PAGES);
|
|
Multi_pxo_help_num_pages = 0;
|
|
cp = &Multi_pxo_help_pages[0];
|
|
|
|
while(!cfeof(in)){
|
|
// malloc the line
|
|
cp->text[cp->num_lines] = (char*)vm_malloc(Multi_pxo_chars_per_line[gr_screen.res]);
|
|
if(cp->text[cp->num_lines] == NULL){
|
|
break;
|
|
}
|
|
|
|
// read in the next line
|
|
cfgets(cp->text[cp->num_lines++], Multi_pxo_chars_per_line[gr_screen.res], in);
|
|
|
|
// skip to the next page if necessary
|
|
if(cp->num_lines == Multi_pxo_lines_pp[gr_screen.res]){
|
|
Multi_pxo_help_num_pages++;
|
|
Assert(Multi_pxo_help_num_pages < MULTI_PXO_MAX_PAGES);
|
|
if(Multi_pxo_help_num_pages >= MULTI_PXO_MAX_PAGES){
|
|
Multi_pxo_help_num_pages--;
|
|
break;
|
|
}
|
|
cp = &Multi_pxo_help_pages[Multi_pxo_help_num_pages];
|
|
}
|
|
}
|
|
|
|
// close the file
|
|
cfclose(in);
|
|
}
|
|
|
|
/**
|
|
* Blit the current page
|
|
*/
|
|
void multi_pxo_help_blit_page()
|
|
{
|
|
int idx;
|
|
int start_pos;
|
|
int y_start, line_height;
|
|
help_page *cp = &Multi_pxo_help_pages[Multi_pxo_help_cur];
|
|
|
|
// blit each line
|
|
y_start = Multi_pxo_help_coords[gr_screen.res][1];
|
|
line_height = gr_get_font_height() + 1;
|
|
for(idx=0;idx<cp->num_lines;idx++){
|
|
// if the first symbol is "@", highlight the line
|
|
if(cp->text[idx][0] == '@'){
|
|
gr_set_color_fast(&Color_bright);
|
|
start_pos = 1;
|
|
} else {
|
|
gr_set_color_fast(&Color_normal);
|
|
start_pos = 0;
|
|
}
|
|
|
|
// blit the line
|
|
gr_string(Multi_pxo_help_coords[gr_screen.res][0], y_start, cp->text[idx] + start_pos, GR_RESIZE_MENU);
|
|
|
|
// increment the y location
|
|
y_start += line_height;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process button presses
|
|
*/
|
|
void multi_pxo_help_process_buttons()
|
|
{
|
|
int idx;
|
|
|
|
// process all buttons
|
|
for(idx=0;idx<MULTI_PXO_HELP_NUM_BUTTONS;idx++){
|
|
if(Multi_pxo_help_buttons[gr_screen.res][idx].button.pressed()){
|
|
multi_pxo_help_button_pressed(idx);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Button pressed
|
|
*/
|
|
void multi_pxo_help_button_pressed(int n)
|
|
{
|
|
switch(n){
|
|
case MULTI_PXO_HELP_PREV:
|
|
// if we're already at page 0, do nothing
|
|
if(Multi_pxo_help_cur == 0){
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
} else {
|
|
Multi_pxo_help_cur--;
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
}
|
|
break;
|
|
|
|
case MULTI_PXO_HELP_NEXT:
|
|
// if we're already at max pages, do nothing
|
|
if(Multi_pxo_help_cur == Multi_pxo_help_num_pages){
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
} else {
|
|
Multi_pxo_help_cur++;
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
}
|
|
break;
|
|
|
|
case MULTI_PXO_HELP_CONTINUE:
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
gameseq_post_event(GS_EVENT_PXO);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// http banner stuff ---------------------------------------------
|
|
|
|
/**
|
|
* Initialisation
|
|
*/
|
|
void multi_pxo_ban_init()
|
|
{
|
|
// zero the active banner bitmap
|
|
Multi_pxo_banner.ban_bitmap = -1;
|
|
|
|
// are we doing banners at all?
|
|
if ( os_config_read_uint(NULL, "PXOBanners", 1) ) {
|
|
// if we're already in idle mode, we're done downloading for this instance of freespace. pick a random image we already have
|
|
if(Multi_pxo_ban_mode == PXO_BAN_MODE_IDLE){
|
|
Multi_pxo_ban_mode = PXO_BAN_MODE_CHOOSE_RANDOM;
|
|
return;
|
|
}
|
|
|
|
// set ourselves to startup mode
|
|
Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
|
|
Multi_pxo_ban_get = NULL;
|
|
} else {
|
|
// set ourselves to idle mode
|
|
Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
|
|
Multi_pxo_ban_get = NULL;
|
|
}
|
|
|
|
// zero the active banner bitmap
|
|
Multi_pxo_banner.ban_bitmap = -1;
|
|
strcpy_s(Multi_pxo_banner.ban_file, "");
|
|
strcpy_s(Multi_pxo_banner.ban_file_url, "");
|
|
strcpy_s(Multi_pxo_banner.ban_url, "");
|
|
}
|
|
|
|
/**
|
|
* Process http download details
|
|
*/
|
|
void multi_pxo_ban_process()
|
|
{
|
|
char url_string[512] = "";
|
|
char local_file[512] = "";
|
|
|
|
// process stuff
|
|
switch(Multi_pxo_ban_mode){
|
|
// start downloading list
|
|
case PXO_BAN_MODE_LIST_STARTUP:
|
|
// remote file
|
|
sprintf(url_string, "http://www.pxo.net/files/%s", PXO_BANNERS_CONFIG_FILE);
|
|
|
|
// local file
|
|
cf_create_default_path_string(local_file, sizeof(local_file) - 1, CF_TYPE_MULTI_CACHE, PXO_BANNERS_CONFIG_FILE);
|
|
|
|
// try creating the file get object
|
|
Multi_pxo_ban_get = NULL;
|
|
|
|
// bad
|
|
if (Multi_pxo_ban_get == NULL) {
|
|
Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
|
|
break;
|
|
}
|
|
|
|
// go to the downloading list mode
|
|
Multi_pxo_ban_mode = PXO_BAN_MODE_LIST;
|
|
break;
|
|
|
|
// downloading list
|
|
case PXO_BAN_MODE_LIST:
|
|
// error
|
|
if ( Multi_pxo_ban_get->IsFileError() ) {
|
|
delete Multi_pxo_ban_get;
|
|
Multi_pxo_ban_get = NULL;
|
|
Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
|
|
break;
|
|
}
|
|
|
|
// connecting, receiving
|
|
if ( Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving() )
|
|
break;
|
|
|
|
// done!
|
|
if ( Multi_pxo_ban_get->IsFileReceived() ) {
|
|
delete Multi_pxo_ban_get;
|
|
Multi_pxo_ban_get = NULL;
|
|
Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_STARTUP;
|
|
}
|
|
break;
|
|
|
|
// start downloading files
|
|
case PXO_BAN_MODE_IMAGES_STARTUP:
|
|
// first thing - parse the banners file and pick a file
|
|
multi_pxo_ban_parse_banner_file(0);
|
|
|
|
// if we have no active file, we're done
|
|
if ( (strlen(Multi_pxo_banner.ban_file) <= 0) || (strlen(Multi_pxo_banner.ban_file_url) <= 0) ) {
|
|
Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
|
|
break;
|
|
}
|
|
|
|
// if the file already exists, we're done
|
|
if ( cf_exists(Multi_pxo_banner.ban_file, CF_TYPE_MULTI_CACHE) ) {
|
|
Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
|
|
break;
|
|
}
|
|
|
|
// otherwise try and download it
|
|
cf_create_default_path_string(local_file, sizeof(local_file) - 1, CF_TYPE_MULTI_CACHE, Multi_pxo_banner.ban_file);
|
|
|
|
// try creating the file get object
|
|
Multi_pxo_ban_get = NULL;
|
|
|
|
// bad
|
|
if (Multi_pxo_ban_get == NULL) {
|
|
Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
|
|
break;
|
|
}
|
|
|
|
// go to the downloading images mode
|
|
Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES;
|
|
break;
|
|
|
|
// downloading files
|
|
case PXO_BAN_MODE_IMAGES:
|
|
// error
|
|
if ( Multi_pxo_ban_get->IsFileError() ) {
|
|
delete Multi_pxo_ban_get;
|
|
Multi_pxo_ban_get = NULL;
|
|
Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
|
|
break;
|
|
}
|
|
|
|
// connecting, receiving
|
|
if ( Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving() )
|
|
break;
|
|
|
|
// done!
|
|
if ( Multi_pxo_ban_get->IsFileReceived() ) {
|
|
delete Multi_pxo_ban_get;
|
|
Multi_pxo_ban_get = NULL;
|
|
Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
|
|
}
|
|
break;
|
|
|
|
// done downloading - maybe load an image
|
|
case PXO_BAN_MODE_IMAGES_DONE:
|
|
// make sure we have a valid filename
|
|
if (Multi_pxo_banner.ban_file[0] != '\0')
|
|
Multi_pxo_banner.ban_bitmap = bm_load(Multi_pxo_banner.ban_file);
|
|
|
|
// now we're idle
|
|
Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
|
|
break;
|
|
|
|
// idle (done with EVERYTHING)
|
|
case PXO_BAN_MODE_IDLE:
|
|
// if the banner button was clicked
|
|
if ( Multi_pxo_ban_button.pressed() ) {
|
|
multi_pxo_ban_clicked();
|
|
}
|
|
break;
|
|
|
|
case PXO_BAN_MODE_CHOOSE_RANDOM:
|
|
// first thing - parse the banners file and pick a file
|
|
multi_pxo_ban_parse_banner_file(1);
|
|
Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Close
|
|
*/
|
|
void multi_pxo_ban_close()
|
|
{
|
|
// if we have a currently active transfer
|
|
if(Multi_pxo_ban_get != NULL){
|
|
Multi_pxo_ban_get->AbortGet();
|
|
delete Multi_pxo_ban_get;
|
|
Multi_pxo_ban_get = NULL;
|
|
}
|
|
|
|
// if we have a loaded bitmap, unload it
|
|
if(Multi_pxo_banner.ban_bitmap != -1){
|
|
bm_unload(Multi_pxo_banner.ban_bitmap);
|
|
Multi_pxo_banner.ban_bitmap = -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse the banners file and maybe fill in Multi_pxo_dl_file
|
|
*/
|
|
void multi_pxo_ban_parse_banner_file(int choose_existing)
|
|
{
|
|
char file_url[512] = "";
|
|
char banners[10][512];
|
|
char urls[10][512];
|
|
int exists[10];
|
|
int exist_count;
|
|
int num_banners, idx;
|
|
CFILE *in = cfopen(PXO_BANNERS_CONFIG_FILE, "rt", CFILE_NORMAL, CF_TYPE_MULTI_CACHE);
|
|
|
|
Multi_pxo_banner.ban_bitmap = -1;
|
|
strcpy_s(Multi_pxo_banner.ban_file, "");
|
|
strcpy_s(Multi_pxo_banner.ban_file_url, "");
|
|
strcpy_s(Multi_pxo_banner.ban_url, "");
|
|
|
|
// bad
|
|
if(in == NULL){
|
|
return;
|
|
}
|
|
|
|
// clear all strings
|
|
for(idx=0; idx<10; idx++){
|
|
strcpy_s(banners[idx], "");
|
|
strcpy_s(urls[idx], "");
|
|
}
|
|
|
|
// get the global banner url
|
|
if(cfgets(file_url, 254, in) == NULL){
|
|
cfclose(in);
|
|
cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
|
|
return;
|
|
}
|
|
drop_leading_white_space(file_url);
|
|
drop_trailing_white_space(file_url);
|
|
|
|
// otherwise read in
|
|
num_banners = 0;
|
|
while(num_banners < 10){
|
|
// try and get the pcx
|
|
if(cfgets(banners[num_banners], 254, in) == NULL){
|
|
break;
|
|
}
|
|
// try and get the url
|
|
if(cfgets(urls[num_banners], 254, in) == NULL){
|
|
break;
|
|
}
|
|
|
|
// strip off trailing and leading whitespace
|
|
drop_leading_white_space(banners[num_banners]);
|
|
drop_trailing_white_space(banners[num_banners]);
|
|
drop_leading_white_space(urls[num_banners]);
|
|
drop_trailing_white_space(urls[num_banners]);
|
|
|
|
// got one
|
|
num_banners++;
|
|
}
|
|
|
|
// close the file
|
|
cfclose(in);
|
|
|
|
// no banners
|
|
if(num_banners <= 0){
|
|
return;
|
|
}
|
|
|
|
// if we're only selecting files which already exist (previously downloaded)
|
|
if(choose_existing){
|
|
// non exist
|
|
for(idx=0; idx<10; idx++){
|
|
exists[idx] = 0;
|
|
}
|
|
|
|
// build a list of existing files
|
|
exist_count = 0;
|
|
for (idx = 0; idx < num_banners; idx++) {
|
|
if ( cf_exists(banners[idx], CF_TYPE_MULTI_CACHE) ) {
|
|
exists[idx] = 1;
|
|
exist_count++;
|
|
}
|
|
}
|
|
|
|
// bogus
|
|
if(exist_count <= 0){
|
|
return;
|
|
}
|
|
|
|
// select one
|
|
int select = (int)frand_range(0.0f, (float)exist_count);
|
|
if(select >= exist_count){
|
|
select = exist_count - 1;
|
|
}
|
|
if(select < 0){
|
|
select = 0;
|
|
}
|
|
for(idx=0; idx<exist_count; idx++){
|
|
if(select == 0){
|
|
break;
|
|
}
|
|
if(exists[idx]){
|
|
select--;
|
|
}
|
|
}
|
|
|
|
// valid?
|
|
if(idx < exist_count){
|
|
// base filename
|
|
strncpy(Multi_pxo_banner.ban_file, banners[idx], MAX_FILENAME_LEN);
|
|
|
|
// get the full file url
|
|
strncpy(Multi_pxo_banner.ban_file_url, file_url, MULTI_OPTIONS_STRING_LEN);
|
|
strncat(Multi_pxo_banner.ban_file_url, banners[idx], MULTI_OPTIONS_STRING_LEN);
|
|
|
|
// url of where to go to when clicked
|
|
strncpy(Multi_pxo_banner.ban_url, urls[idx], MULTI_OPTIONS_STRING_LEN);
|
|
}
|
|
}
|
|
// randomly pick a file for download
|
|
else {
|
|
idx = (int)frand_range(0.0f, (float)num_banners);
|
|
|
|
if(idx >= num_banners){
|
|
idx = num_banners - 1;
|
|
}
|
|
if(idx < 0){
|
|
idx = 0;
|
|
}
|
|
|
|
// base filename
|
|
strncpy(Multi_pxo_banner.ban_file, banners[idx], MAX_FILENAME_LEN);
|
|
|
|
// get the full file url
|
|
strncpy(Multi_pxo_banner.ban_file_url, file_url, MULTI_OPTIONS_STRING_LEN);
|
|
strncat(Multi_pxo_banner.ban_file_url, banners[idx], MULTI_OPTIONS_STRING_LEN);
|
|
|
|
// url of where to go to when clicked
|
|
strncpy(Multi_pxo_banner.ban_url, urls[idx], MULTI_OPTIONS_STRING_LEN);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Any bitmap or info or whatever
|
|
*/
|
|
void multi_pxo_ban_draw()
|
|
{
|
|
// if we have a valid bitmap
|
|
if(Multi_pxo_banner.ban_bitmap >= 0){
|
|
// if the mouse is over the banner button, highlight with a rectangle
|
|
if(Multi_pxo_ban_button.is_mouse_on()){
|
|
gr_set_color_fast(&Color_bright_blue);
|
|
gr_rect(Pxo_ban_coords[gr_screen.res][0] - 1, Pxo_ban_coords[gr_screen.res][1] - 1, Pxo_ban_coords[gr_screen.res][2] + 2, Pxo_ban_coords[gr_screen.res][3] + 2, GR_RESIZE_MENU);
|
|
}
|
|
|
|
// draw the bitmap itself
|
|
gr_set_bitmap(Multi_pxo_banner.ban_bitmap);
|
|
gr_bitmap(Pxo_ban_coords[gr_screen.res][0], Pxo_ban_coords[gr_screen.res][1], GR_RESIZE_MENU);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when the URL button is clicked
|
|
*/
|
|
void multi_pxo_ban_clicked()
|
|
{
|
|
// if we have a valid bitmap and URL, launch the URL
|
|
if((Multi_pxo_banner.ban_bitmap >= 0) && (Multi_pxo_banner.ban_url[0] != '\0')){
|
|
multi_pxo_url(Multi_pxo_banner.ban_url);
|
|
}
|
|
}
|