FS2_Open
Open source remastering of the Freespace 2 engine
multi_pxo.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) Volition, Inc. 2005. All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 #ifdef _WIN32
13 #include <winsock.h>
14 #endif
15 
16 #include "network/multi_pxo.h"
17 #include "anim/animplay.h"
18 #include "ui/ui.h"
19 #include "io/key.h"
20 #include "bmpman/bmpman.h"
21 #include "palman/palman.h"
22 #include "gamesnd/gamesnd.h"
24 #include "cfile/cfile.h"
25 #include "network/chat_api.h"
26 #include "popup/popup.h"
27 #include "freespace2/freespace.h"
28 #include "graphics/font.h"
29 #include "network/multi.h"
30 #include "network/multiui.h"
31 #include "network/multi_log.h"
32 #include "stats/medals.h"
33 #include "globalincs/alphacolors.h"
34 #include "graphics/2d.h"
35 #include "graphics/generic.h"
36 #include "io/timer.h"
37 #include "inetfile/inetgetfile.h"
38 #include "cfile/cfilesystem.h"
39 #include "osapi/osregistry.h"
40 #include "parse/parselo.h"
41 #include "stats/scoring.h"
42 #include "playerman/player.h"
43 #include "fs2netd/fs2netd_client.h"
44 #include "menuui/mainhallmenu.h"
45 #include "debugconsole/console.h"
46 
47 
48 
49 // ----------------------------------------------------------------------------------------------------
50 // PXO DEFINES/VARS
51 //
52 
53 #define MAX_PXO_TEXT_LEN 255
54 
55 // button definitions
56 #define MULTI_PXO_NUM_BUTTONS 15
57 #define MULTI_PXO_PLIST_UP 0
58 #define MULTI_PXO_PLIST_DOWN 1
59 #define MULTI_PXO_RANKINGS 2
60 #define MULTI_PXO_PINFO 3
61 #define MULTI_PXO_FIND 4
62 #define MULTI_PXO_MOTD 5
63 #define MULTI_PXO_JOIN 6
64 #define MULTI_PXO_JOIN_PRIV 7
65 #define MULTI_PXO_CHAN_UP 8
66 #define MULTI_PXO_CHAN_DOWN 9
67 #define MULTI_PXO_TEXT_UP 10
68 #define MULTI_PXO_TEXT_DOWN 11
69 #define MULTI_PXO_EXIT 12
70 #define MULTI_PXO_HELP 13
71 #define MULTI_PXO_GAMES 14
72 
73 
75  { // GR_640
76  ui_button_info( "PXB_00", 1, 104, -1, -1, 0 ), // scroll player list up
77  ui_button_info( "PXB_01", 1, 334, -1, -1, 1 ), // scroll player list down
78  ui_button_info( "PXB_02", 18, 385, -1, -1, 2 ), // rankings webpage
79  ui_button_info( "PXB_03", 71, 385, -1, -1, 3 ), // pilot info
80  ui_button_info( "PXB_04", 115, 385, -1, -1, 4 ), // find player
81  ui_button_info( "PXB_05", 1, 443, -1, -1, 5 ), // motd
82  ui_button_info( "PXB_06", 330, 96, -1, -1, 6 ), // join channel
83  ui_button_info( "PXB_07", 330, 131, -1, -1, 7 ), // join private channel
84  ui_button_info( "PXB_08", 618, 92, -1, -1, 8 ), // scroll channels up
85  ui_button_info( "PXB_09", 618, 128, -1, -1, 9 ), // scroll channels down
86  ui_button_info( "PXB_10", 615, 171, -1, -1, 10 ), // scroll text up
87  ui_button_info( "PXB_11", 615, 355, -1, -1, 11 ), // scroll text down
88  ui_button_info( "PXB_12", 482, 435, -1, -1, 12 ), // exit
89  ui_button_info( "PXB_13", 533, 432, -1, -1, 13 ), // help
90  ui_button_info( "PXB_14", 573, 432, -1, -1, 14 ), // games list
91  },
92  { // GR_1024
93  ui_button_info( "2_PXB_00", 2, 166, -1, -1, 0 ), // scroll player list up
94  ui_button_info( "2_PXB_01", 2, 534, -1, -1, 1 ), // scroll player list down
95  ui_button_info( "2_PXB_02", 29, 616, -1, -1, 2 ), // rankings webpage
96  ui_button_info( "2_PXB_03", 114, 616, -1, -1, 3 ), // pilot info
97  ui_button_info( "2_PXB_04", 184, 616, -1, -1, 4 ), // find player
98  ui_button_info( "2_PXB_05", 2, 709, -1, -1, 5 ), // motd
99  ui_button_info( "2_PXB_06", 528, 119, -1, -1, 6 ), // join channel
100  ui_button_info( "2_PXB_07", 528, 175, -1, -1, 7 ), // join private channel
101  ui_button_info( "2_PXB_08", 989, 112, -1, -1, 8 ), // scroll channels up
102  ui_button_info( "2_PXB_09", 989, 170, -1, -1, 9 ), // scroll channels down
103  ui_button_info( "2_PXB_10", 984, 240, -1, -1, 10 ), // scroll text up
104  ui_button_info( "2_PXB_11", 984, 568, -1, -1, 11 ), // scroll text down
105  ui_button_info( "2_PXB_12", 771, 696, -1, -1, 12 ), // exit
106  ui_button_info( "2_PXB_13", 853, 691, -1, -1, 13 ), // help
107  ui_button_info( "2_PXB_14", 917, 691, -1, -1, 14 ), // games list
108  },
109 };
110 
111 #define MULTI_PXO_NUM_TEXT 16
113  { // GR_640
114  {"Web", 1313, 20, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_RANKINGS].button},
115  {"Ranking", 1314, 6, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_RANKINGS].button},
116  {"Pilot", 1310, 68, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_PINFO].button},
117  {"Info", 1311, 72, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_PINFO].button},
118  {"Find", 1315, 119, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_FIND].button},
119  {"Motd", 1316, 36, 456, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_MOTD].button},
120  {"Join", 1505, 291, 100, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN].button},
121  {"Channel", 1317, 266, 112, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN].button},
122  {"Join", 1506, 291, 134, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN_PRIV].button},
123  {"Private", 1318, 273, 146, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN_PRIV].button},
124  {"Exit", 1416, 493, 424, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_EXIT].button},
125  {"Help", 928, 535, 416, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_HELP].button},
126  {"Games", 1319, 579, 416, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_GAMES].button},
127  {"Players", 1269, 29, 102, UI_XSTR_COLOR_GREEN, -1, NULL},
128  {"Players", 1269, 507, 90, UI_XSTR_COLOR_GREEN, -1, NULL},
129  {"Games", 1319, 568, 90, UI_XSTR_COLOR_GREEN, -1, NULL}
130  },
131  { // GR_1024
132  {"Web", 1313, 32, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_RANKINGS].button},
133  {"Ranking", 1314, 9, 674, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_RANKINGS].button},
134  {"Pilot", 1310, 109, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_PINFO].button},
135  {"Info", 1311, 115, 674, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_PINFO].button},
136  {"Find", 1315, 190, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_FIND].button},
137  {"Motd", 1316, 58, 729, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_MOTD].button},
138  {"Join", 1505, 488, 129, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN].button},
139  {"Channel", 1317, 461, 139, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN].button},
140  {"Join", 1506, 487, 184, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN_PRIV].button},
141  {"Private", 1318, 467, 194, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN_PRIV].button},
142  {"Exit", 1416, 789, 678, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_EXIT].button},
143  {"Help", 928, 857, 667, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_HELP].button},
144  {"Games", 1319, 917, 667, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_GAMES].button},
145  {"Players", 1269, 47, 163, UI_XSTR_COLOR_GREEN, -1, NULL},
146  {"Players", 1269, 852, 109, UI_XSTR_COLOR_GREEN, -1, NULL},
147  {"Games", 1319, 926, 109, UI_XSTR_COLOR_GREEN, -1, NULL}
148  }
149 };
150 
152  "PXOChat",
153  "2_PXOChat"
154 };
156  "PXOChat-M",
157  "2_PXOChat-M"
158 };
159 
163 
164 
165 // pxo animation
166 #define MULTI_PXO_ANIM_FNAME "pxologo"
167 #define MULTI_PXO_ANIM_X 0
168 #define MULTI_PXO_ANIM_Y 4
170 
171 // rankings last clicked time
172 #define MULTI_PXO_RANK_TIME (5.0f)
174 
175 // chat api vars
176 int Multi_pxo_must_connect = 0; // if we still need to connect
177 int Multi_pxo_connected = 0; // if we are connected
178 int Multi_pxo_must_validate = 0; // if we need to validate on the tracker
179 int Multi_pxo_must_autojoin = 1; // still need to autojoin a channel
180 
181 // mode
182 #define MULTI_PXO_MODE_NORMAL 0 // normal mode
183 #define MULTI_PXO_MODE_PRIVATE 1 // private channel popup
184 #define MULTI_PXO_MODE_FIND 2 // find player popup
186 
187 // our nick for this session
189 
190 // check for button presses
192 
193 // handle a button press
194 void multi_pxo_button_pressed(int n);
195 
196 // condition function for popup_do_with_condition for connected to Parallax Online
197 // return 10 : on successful connect
199 
200 // attempt to connect to Parallax Online, return success or fail
201 int multi_pxo_connect();
202 
203 // run the networking functions for the PXO API
204 void multi_pxo_api_process();
205 
206 // process a "nick" change event
208 
209 // run normally (no popups)
210 void multi_pxo_do_normal();
211 
212 // blit everything on the "normal" screen
213 void multi_pxo_blit_all();
214 
215 // process common stuff
217 
218 // get selected player information
219 void multi_pxo_get_data(char *name);
220 
221 // handle being kicked
222 void multi_pxo_handle_kick();
223 
224 // handle being disconnected
226 
227 // return string2, which is the first substring of string 1 without a space
228 // it is safe to pass the same pointer for both parameters
229 void multi_pxo_strip_space(char *string1,char *string2);
230 
231 // fire up the given URL
232 void multi_pxo_url(char *url);
233 
234 // load/set the palette
236 
237 // unload the palette
239 
240 // if we're currently on a private channel
242 
243 // convert string 1 into string 2, substituting underscores for spaces
244 void multi_pxo_underscore_nick(char *string1,char *string2);
245 
246 // if the command is a potential "nick" command
247 int multi_pxo_is_nick_command(char *msg);
248 
249 
250 // status bar stuff -----------------------------------------------
252  { // GR_640
253  95, 467, 354, 12
254  },
255  { // GR_1024
256  152, 750, 570, 12
257  },
258 };
259 
260 // the status text itself
262 
263 // set the status text
264 void multi_pxo_set_status_text(const char *txt);
265 
266 // blit the status text
268 
269 
270 // channel related stuff -------------------------------------------
271 #define MAX_CHANNEL_NAME_LEN 32
272 #define MAX_CHANNEL_DESCRIPT_LEN 120
273 
274 // some convenient macros
275 #define SWITCHING_CHANNELS() (Multi_pxo_channel_switch.num_users != -1)
276 #define ON_CHANNEL() (Multi_pxo_channel_current.num_users != -1)
277 
278 typedef struct pxo_channel {
279  pxo_channel *next,*prev; // next and previous items in the list
280  char name[MAX_CHANNEL_NAME_LEN+1]; // name
281  char desc[MAX_CHANNEL_DESCRIPT_LEN+1]; // description
282  short num_users; // # users, or -1 if not in use
283  short num_servers; // the # of servers registered on this channel
284 } pxo_channel;
285 
286 // last channel we were on before going to the game list screen
289 
290 // all channels which are prefixed with this are "lobby" channels
291 #define MULTI_PXO_AUTOJOIN_PREFIX "#lobby"
292 
293 // join this channel to get put in an appropriate lobby channel
294 #define MULTI_PXO_AUTOJOIN_CHANNEL "#autoselect"
295 
297  { // GR_640
298  369, 101, 241, 60
299  },
300  { // GR_1024
301  593, 124, 386, 100
302  },
303 };
304 
305 // this is the offset from the RIGHT side of the channel box
306 #define CHAN_PLAYERS_COLUMN 0
307 #define CHAN_GAMES_COLUMN 1
308 static int Multi_pxo_chan_column_offsets[GR_NUM_RESOLUTIONS][2] = {
309  { 81, 26 },
310  { 103, 35 }
311 };
312 
313 #define CHANNEL_REFRESH_TIME (75.0f)
315 
316 #define CHANNEL_SERVER_REFRESH_TIME (35.0f)
318 
320  6, // GR_640
321  10 // GR_1024
322 };
323 
325 
326 // head of the list of available (displayed) channels
329 
330 // item we're going to start displaying at
333 
334 // items we've currently got selected
336 
337 // channel we're currently connected to, num_users == -1, if we're not connected
339 
340 // channel we're currently trying to change to, num_users == -1, if we're not trying to change channels
342 
343 // get a list of channels on the server (clear any old list as well)
345 
346 // clear the old channel list
348 
349 // parse the input string and make a list of new channels
350 void multi_pxo_make_channels(char *chan_str);
351 
352 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
354 
355 // lookup a channel with the specified name
357 
358 // process the channel list (select, etc)
360 
361 // display the channel list
363 
364 // scroll channel list up
366 
367 // scroll channel list down
369 
370 // attempt to join a channel
372 
373 // handle any processing details if we're currently trying to join a channel
375 
376 // autojoin an appropriate channel
377 void multi_pxo_autojoin();
378 
379 // does the string match the "autojoin" prefic
380 int multi_pxo_is_autojoin(char *name);
381 
382 // send a request to refresh our channel server counts
384 
385 // refresh current channel server count
387 
388 
389 // player related stuff -------------------------------------------
390 #define MAX_PLAYER_NAME_LEN 32
391 
392 typedef struct player_list {
395 } player_list;
396 
397 // channel list region
399  { // GR_640
400  27, 121, 141, 261
401  },
402  { // GR_1024
403  43, 194, 154, 417
404  },
405 };
406 
408  25, // GR_640
409  41 // GR_1024
410 };
412 
413 // slider coords
415  { // GR_640
416  1, 139, 21, 192
417  },
418  { // GR_1024
419  2, 219, 33, 314
420  }
421 };
423  "slider", // GR_640
424  "2_slider" // GR_1024
425 };
426 
427 // head of the list of players in this channel
430 
431 // item we're going to start displaying at
433 // int Multi_pxo_player_start_index = -1;
434 
435 // items we've currently got selected
437 
438 // clear the old player list
440 
441 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
443 
444 // remove a player with the given name
445 void multi_pxo_del_player(char *name);
446 
447 // try and find a player with the given name, return a pointer to his entry (or NULL)
449 
450 // process the player list (select, etc)
452 
453 // display the player list
455 
456 // scroll player list up
458 
459 // scroll player list down
461 
462 // get the absolute index of the displayed items which our currently selected one is
464 
465 DCF(players, "Adds the specified number of bogus players to the PXO listing (Multiplayer)")
466 {
467  char name[512] = "";
468  int i;
469  // add a bunch of bogus players
470  dc_stuff_int(&i);
471 
472  for(int idx = 0; idx < i; idx++){
473  sprintf(name, "bogus player %d", idx);
474  multi_pxo_add_player(name);
475  }
476 }
477 
478 // chat text stuff -----------------------------------------
479 #define MAX_CHAT_LINES 60
480 #define MAX_CHAT_LINE_LEN 256
481 
483  181, // GR_640
484  253 // GR_1024
485 };
486 
488  { // GR_640
489  196, 197, 412, 185
490  },
491  { // GR_1024
492  314, 271, 665, 330
493  }
494 };
495 
497  { // GR_640
498  196, 386, 407, 24
499  },
500  { // GR_1024
501  314, 617, 660, 38
502  }
503 };
504 
506  17, // GR_640
507  32 // GR_1024
508 };
509 
510 // all messages from the server are prefixed with this
511 #define MULTI_PXO_SERVER_PREFIX "*** "
512 
513 // the "has left" message from the server
514 #define MULTI_PXO_HAS_LEFT "has left"
515 
516 // chat flags
517 #define CHAT_MODE_NORMAL 0 // normal chat from someone
518 #define CHAT_MODE_SERVER 1 // is from the server, display appropriately
519 #define CHAT_MODE_CARRY 2 // is a carryover from a previous line
520 #define CHAT_MODE_PRIVATE 3 // is a private message
521 #define CHAT_MODE_CHANNEL_SWITCH 4 // "switching channels" message - draw in red
522 #define CHAT_MODE_MOTD 5 // message of the day from the chat server
523 
524 typedef struct chat_line {
527  int mode;
528 } chat_line;
529 
530 // the chat linked list itself
532 
533 // the current add line
535 
536 // the current line to start displaying from
539 
540 // input box for text
542 
543 // slider for chat
545 
547  { // GR_640
548  620, 206, 21, 147
549  },
550  { // GR_1024
551  990, 295, 34, 269
552  }
553 };
554 
556  "slider",
557  "2_slider"
558 };
559 
560 // how many chat lines we have
562 
563 // extra delay time when switching channels
564 #define MULTI_PXO_SWITCH_DELAY_TIME 2000
566 
567 // initialize and create the chat text linked list
568 void multi_pxo_chat_init();
569 
570 // free up all chat list stuff
571 void multi_pxo_chat_free();
572 
573 // clear all lines of chat text in the chat area
574 void multi_pxo_chat_clear();
575 
576 // blit the chat text
577 void multi_pxo_chat_blit();
578 
579 // add a line of text
580 void multi_pxo_chat_add_line(const char *txt,int mode);
581 
582 // process an incoming line of text
583 void multi_pxo_chat_process_incoming(const char *txt, int mode = CHAT_MODE_NORMAL);
584 
585 // scroll to the very bottom of the chat area
586 void multi_pxo_goto_bottom();
587 
588 // check whether we can scroll down or not
590 
591 static int Can_scroll_down = 0;
592 
593 // scroll the text up
595 
596 // scroll the text down
598 
599 // process chat controls
601 
602 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
603 const char *multi_pxo_chat_is_private(const char *txt);
604 
605 // if the text came from the server
606 int multi_pxo_is_server_text(const char *txt);
607 
608 // if the text is message of the day text
609 int multi_pxo_is_motd_text(const char *txt);
610 
611 // if the text is the end of motd text
612 int multi_pxo_is_end_of_motd_text(const char *txt);
613 
614 // if the text is a "has left message" from the server
615 int multi_pxo_chat_is_left_message(const char *txt);
616 
617 // recalculate the chat start index, and adjust the slider properly
619 
620 
621 // motd stuff ---------------------------------------------------------
622 #define MAX_PXO_MOTD_LEN 1024
623 #define PXO_MOTD_BLINK_TIME 500
624 char Pxo_motd[1024] = "";
625 int Pxo_motd_end = 0;
630 
631 // initialize motd when going into this screen
632 void multi_pxo_motd_init();
633 
634 // set the motd text
635 void multi_pxo_motd_add_text(const char *text);
636 
637 // set end of motd
639 
640 // display the motd dialog
641 void multi_pxo_motd_dialog();
642 
643 // call to maybe blink the motd button
645 
646 
647 // common dialog stuff ------------------------------------------------
649  "PXOPop",
650  "2_PXOPop"
651 };
653  "PXOPop-m",
654  "2_PXOPop-m"
655 };
656 
657 // popup coords
659  { // GR_640
660  38, 129
661  },
662  { // GR_1024
663  61, 207
664  }
665 };
666 
667 // input box coords
669  { // GR_640
670  53, 233, 448, 25
671  },
672  { // GR_1024
673  85, 372, 716, 40
674  }
675 };
676 
677 #define MULTI_PXO_COM_NUM_BUTTONS 2
678 #define MULTI_PXO_COM_CANCEL 0
679 #define MULTI_PXO_COM_OK 1
680 
682  { // GR_640
683  ui_button_info("PXP_00", 573, 192, -1, -1, 0),
684  ui_button_info("PXP_01", 573, 226, -1, -1, 1)
685  },
686  { // GR_1024
687  ui_button_info("2_PXP_00", 917, 308, -1, -1, 0),
688  ui_button_info("2_PXP_01", 917, 361, -1, -1, 1)
689  }
690 };
691 
692 #define MULTI_PXO_COM_NUM_TEXT 2
694  { // GR_640
695  { "&Cancel", 645, 510, 204, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_CANCEL].button },
696  { "&Ok", 669, 548, 233, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_OK].button }
697  },
698  { // GR_1024
699  { "&Cancel", 645, 847, 327, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_CANCEL].button },
700  { "&Ok", 669, 877, 372, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_OK].button }
701  }
702 };
703 
707 
708 // text on the "top" half of the dialog display area
710 
711 // text on the "middle" portion of the dialog display area
713 
714 // text on the "bottom" half of the dialog display area
716 
718  { // GR_640
719  58, 152
720  },
721  { // GR_1024
722  91, 227
723  }
724 };
726  172, // GR_640
727  280 // GR_1024
728 };
730  192, // GR_640
731  326 // GR_1024
732 };
733 
734 // initialize the common dialog with the passed max input length
735 void multi_pxo_com_init();
736 
737 // close down the common dialog
738 void multi_pxo_com_close();
739 
740 // blit all text lines, top, middle, bottoms
742 
743 // set the top text, shortening as necessary
744 void multi_pxo_com_set_top_text(const char *txt);
745 
746 // set the middle text, shortening as necessary
747 void multi_pxo_com_set_middle_text(const char *txt);
748 
749 // set the bottom text, shortening as necessary
750 void multi_pxo_com_set_bottom_text(const char *txt);
751 
752 
753 // private channel join stuff -----------------------------------------
754 #define MULTI_PXO_PRIV_MAX_TEXT_LEN 30
755 
756 // max private channel name length
758 
759 // return code, set to something other than -1 if we're supposed to return
761 
762 // initialize the popup
763 void multi_pxo_priv_init();
764 
765 // close down the popup
766 void multi_pxo_priv_close();
767 
768 // run the popup, 0 if still running, -1 if cancel, 1 if ok
770 
771 // process button presses
773 
774 // handle a button press
776 
777 // process the inputbox
779 
780 
781 // find player stuff -----------------------------------------
782 
784 
785 // return code, set to something other than -1 if we're supposed to return
787 
788 // initialize the popup
789 void multi_pxo_find_init();
790 
791 // close down the popup
792 void multi_pxo_find_close();
793 
794 // run the popup, 0 if still running, -1 if cancel, 1 if ok
796 
797 // process button presses
799 
800 // handle a button press
802 
803 // process the inputbox
805 
806 // process search mode if applicable
808 
809 
810 // player info stuff -----------------------------------------
812  "PilotInfo2",
813  "2_PilotInfo2"
814 };
816  "PilotInfo2-M",
817  "2_PilotInfo2-M"
818 };
819 
820 // medals
821 #define MULTI_PXO_PINFO_NUM_BUTTONS 2
822 #define MULTI_PXO_PINFO_MEDALS 0
823 #define MULTI_PXO_PINFO_OK 1
824 
826  { // GR_640
827  ui_button_info("PI2_00", 328, 446, 319, 433, 0),
828  ui_button_info("PI2_01", 376, 446, 382, 433, 1),
829  },
830  { // GR_1024
831  ui_button_info("2_PI2_00", 525, 714, 510, 695, 0),
832  ui_button_info("2_PI2_01", 601, 714, 611, 695, 1),
833  }
834 };
835 
836 // text
837 #define MULTI_PXO_PINFO_NUM_TEXT 2
839  { // GR_640
840  { "Medals", 1037, 319, 433, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_MEDALS].button },
841  { "Ok", 345, 382, 433, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_OK].button },
842  },
843  { // GR_1024
844  { "Medals", 1037, 510, 695, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_MEDALS].button },
845  { "Ok", 345, 611, 695, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_OK].button },
846  }
847 };
848 
851 
852 
854 
856 
859 
860 // stats label stuff
861 #define MULTI_PXO_PINFO_NUM_LABELS 18
862 
864  { // GR_640
865  37, 142, 377, 289
866  },
867  { // GR_640
868  54, 227, 602, 462
869  },
870 };
872  230, // GR_640
873  310 // GR_1024
874 };
875 
877 
879 
881  10,20,10,10,20,10,10,20,10,10,20,10,10,20,10,20,10,0
882 };
883 
884 // popup conditional functions, returns 10 on successful get of stats
886 
887 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
888 int multi_pxo_pinfo_get(char *name);
889 
890 // fire up the stats view popup
891 void multi_pxo_pinfo_show();
892 
893 // build the stats labels values
895 
896 // initialize the popup
897 void multi_pxo_pinfo_init();
898 
899 // do frame
900 int multi_pxo_pinfo_do();
901 
902 // close
903 void multi_pxo_pinfo_close();
904 
905 // blit all the stats on this screen
906 void multi_pxo_pinfo_blit();
907 
908 // run the medals screen
909 void multi_pxo_run_medals();
910 
911 // notify stuff stuff -----------------------------------------
912 #define MULTI_PXO_NOTIFY_TIME 4000
913 #define MULTI_PXO_NOTIFY_Y 435
914 
917 
918 // add a notification string
919 void multi_pxo_notify_add(const char *txt);
920 
921 // blit and process the notification string
922 void multi_pxo_notify_blit();
923 
924 
925 // help screen stuff -----------------------------------------
926 //XSTR:OFF
928  "PXHelp",
929  "2_PXHelp"
930 };
932  "PXOHelp-M",
933  "2_PXOHelp-M"
934 };
935 
936 #define MULTI_PXO_HELP_NUM_BUTTONS 3
937 #define MULTI_PXO_HELP_PREV 0
938 #define MULTI_PXO_HELP_NEXT 1
939 #define MULTI_PXO_HELP_CONTINUE 2
940 
942  { // GR_640
943  ui_button_info("PXH_00", 15, 389, -1, -1, 0),
944  ui_button_info("PXH_01", 60, 389, -1, -1, 1),
945  ui_button_info("PXH_02", 574, 431, 571, 413, 2),
946  },
947  { // GR_1024
948  ui_button_info("2_PXH_00", 24, 622, -1, -1, 0),
949  ui_button_info("2_PXH_01", 96, 622, -1, -1, 1),
950  ui_button_info("2_PXH_02", 919, 689, 928, 663, 2),
951  }
952 };
953 
954 #define MULTI_PXO_HELP_NUM_TEXT 1
956  { // GR_640
957  {"Continue", 1069, 571, 413, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[0][MULTI_PXO_HELP_CONTINUE].button },
958  },
959  { // GR_1024
960  {"Continue", 1069, 928, 663, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[1][MULTI_PXO_HELP_CONTINUE].button },
961  },
962 };
963 
964 // help text
965 #define MULTI_PXO_HELP_FILE "pxohelp.txt"
966 #define MULTI_PXO_MAX_LINES_PP 57
967 #define MULTI_PXO_MAX_PAGES 5
968 
970  { // GR_640
971  40, 40
972  },
973  { // GR_1024
974  60, 40
975  }
976 };
977 
979  130, // GR_640
980  130 // GR_1024
981 };
982 
984  32, // GR_640
985  57 // GR_1024
986 };
987 
988 // help text pages
989 typedef struct help_page {
992 } help_page;
993 
995 
997 
1000 
1001 // current page we're on
1003 
1004 // load the help file up
1005 void multi_pxo_help_load();
1006 
1007 // blit the current page
1009 
1010 // process button presses
1012 
1013 // button pressed
1015 
1016 
1017 // http banner stuff ---------------------------------------------
1019 
1020 // banners file
1021 #define PXO_BANNERS_CONFIG_FILE "pxobanners.cfg"
1022 
1023 // coords to display banners at
1025  { // GR_640
1026  149, 3, 475, 75
1027  },
1028  { // GR_1024
1029  524, 3, 475, 75
1030  }
1031 };
1032 
1033 // http modes
1034 #define PXO_BAN_MODE_LIST_STARTUP 0 // start downloading list
1035 #define PXO_BAN_MODE_LIST 1 // downloading list
1036 #define PXO_BAN_MODE_IMAGES_STARTUP 2 // start downloading images
1037 #define PXO_BAN_MODE_IMAGES 3 // downloading images
1038 #define PXO_BAN_MODE_IMAGES_DONE 4 // done downloading everything - now maybe load an image
1039 #define PXO_BAN_MODE_IDLE 5 // done with everything - doing nothing
1040 #define PXO_BAN_MODE_CHOOSE_RANDOM 6 // choose a bitmap we've already downloaded at random
1041 
1042 // interface button for detecting clicks
1044 
1045 // banners
1046 typedef struct pxo_banner {
1047  char ban_file[MAX_FILENAME_LEN+1]; // base filename of the banner
1048  char ban_file_url[MULTI_OPTIONS_STRING_LEN+1]; // full url of the file to get (convenient)
1049  char ban_url[MULTI_OPTIONS_STRING_LEN+1]; // url to go to when clicked
1050  int ban_bitmap; // banner bitmap
1051 } pxo_banner;
1052 
1053 // active pxo banner
1055 
1056 // mode
1058 
1059 // init
1060 void multi_pxo_ban_init();
1061 
1062 // process http download details
1063 void multi_pxo_ban_process();
1064 
1065 // close
1066 void multi_pxo_ban_close();
1067 
1068 // parse the banners file and maybe fill in Multi_pxo_dl_file[]
1069 void multi_pxo_ban_parse_banner_file(int choose_existing);
1070 
1071 // any bitmap or info or whatever
1072 void multi_pxo_ban_draw();
1073 
1074 // called when the URL button is clicked
1075 void multi_pxo_ban_clicked();
1076 
1077 
1078 // ----------------------------------------------------------------------------------------------------
1079 // PXO FUNCTIONS
1080 //
1081 
1082 // initialize the PXO screen
1083 void multi_pxo_init(int use_last_channel)
1084 {
1085  int idx;
1086 
1087  // load the background bitmap
1089  if(Multi_pxo_bitmap < 0){
1090  // we failed to load the bitmap - this is very bad
1091  Int3();
1092  }
1093 
1094  // load up the private channel bitmap
1095  Multi_pxo_com_bitmap = bm_load(Multi_pxo_com_fname[gr_screen.res]);
1096  Assert(Multi_pxo_com_bitmap != -1);
1097 
1098  // create the interface window
1099  Multi_pxo_window.create(0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled, 0);
1100  Multi_pxo_window.set_mask_bmap(Multi_pxo_mask_fname[gr_screen.res]);
1101 
1102  // multiplayer screen common palettes
1104 
1105  // create the interface buttons
1106  for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
1107  // create the object
1108  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);
1109 
1110  // set the sound to play when highlighted
1112 
1113  // set the ani for the button
1114  Multi_pxo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_buttons[gr_screen.res][idx].filename);
1115 
1116  // set the hotspot
1117  Multi_pxo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_buttons[gr_screen.res][idx].hotspot);
1118  }
1119 
1120  // add all xstrs
1121  for(idx=0; idx<MULTI_PXO_NUM_TEXT; idx++){
1122  Multi_pxo_window.add_XSTR(&Multi_pxo_text[gr_screen.res][idx]);
1123  }
1124 
1125  if(use_last_channel && strlen(Multi_pxo_channel_last)){
1127  } else {
1130  }
1131 
1132  // make all scrolling buttons repeatable
1133  Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_UP].button.repeatable(1);
1134  Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_DOWN].button.repeatable(1);
1135  Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_UP].button.repeatable(1);
1136  Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_DOWN].button.repeatable(1);
1137  Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_UP].button.repeatable(1);
1138  Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_DOWN].button.repeatable(1);
1139 
1140  // set the mouseover cursor if it loaded ok
1141  if (Web_cursor_bitmap > 0) {
1143  }
1144 
1145  // create the channel list select button and hide it
1146  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);
1147  Multi_pxo_channel_button.hide();
1148 
1149  // create the player list select button and hide it
1151  Multi_pxo_player_button.hide();
1152 
1153  // create the chat input box
1154  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);
1155  Multi_pxo_chat_input.set_focus();
1156 
1157  // create the banner button and hide it
1158  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);
1159  Multi_pxo_ban_button.hide();
1160 
1161  // create the chat slider
1162  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);
1163 
1164  // set our connection status so that we do the right stuff next frame
1167  Multi_pxo_connected = 0;
1168 
1169  // channel we're currently connected to
1170  memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
1171  Multi_pxo_channel_current.num_users = -1;
1172 
1173  // channel we're currently trying to change to, or NULL if nont
1174  memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
1175  Multi_pxo_channel_switch.num_users = -1;
1176 
1177  // last time clicked the url button (so we don't have repeats)
1178  Multi_pxo_ranking_last = -1.0f;
1179 
1180  // channel switching extra time delay stamp
1181  Multi_pxo_switch_delay = -1;
1182 
1183  // our nick for this session
1185 
1186  // clear the channel list
1188 
1189  // clear the player list
1191 
1192  // initialize the chat system
1194 
1195  // initialize http
1197 
1198  // load the animation up
1199  if (gr_screen.res == GR_1024) {
1200  char anim_filename[32] = "2_";
1201  strcat_s(anim_filename, MULTI_PXO_ANIM_FNAME);
1202  generic_anim_init(&Multi_pxo_anim, anim_filename);
1203  Multi_pxo_anim.ani.bg_type = bm_get_type(Multi_pxo_bitmap);
1204 
1205  // if hi-res is not there, fallback to low
1206  if (generic_anim_stream(&Multi_pxo_anim) == -1) {
1207  generic_anim_init(&Multi_pxo_anim, MULTI_PXO_ANIM_FNAME);
1208  generic_anim_stream(&Multi_pxo_anim);
1209  }
1210  } else {
1211  generic_anim_init(&Multi_pxo_anim, MULTI_PXO_ANIM_FNAME);
1212  Multi_pxo_anim.ani.bg_type = bm_get_type(Multi_pxo_bitmap);
1213  generic_anim_stream(&Multi_pxo_anim);
1214  }
1215 
1216  // clear the status text
1218 
1219  // last refresh time
1221 
1222  // server count last refresh time
1224 
1225  // set our mode
1227 
1228  // init motd
1230 
1231  // make sure we autojoin
1233 
1234  // clear all tracker channel related strings
1235  memset(Multi_fs_tracker_channel, 0, MAX_PATH);
1236  memset(Multi_fs_tracker_filter, 0, MAX_PATH);
1237 
1239 }
1240 
1241 // do frame for the PXO screen
1243 {
1244  pxo_channel priv_chan;
1245 
1246  // run api stuff
1247  if(Multi_pxo_connected) {
1249  }
1250 
1251  // process common stuff
1253 
1254  switch(Multi_pxo_mode){
1255  // private channel join mode
1257  switch(multi_pxo_priv_popup()){
1258  // still running
1259  case 0:
1260  break;
1261 
1262  // user hit "cancel"
1263  case -1:
1264  // return to normal mode
1266  break;
1267 
1268  // user hit "ok"
1269  case 1 :
1270  // setup some information
1271  memset(&priv_chan, 0, sizeof(pxo_channel));
1272  priv_chan.num_users = 0;
1273  strcpy_s(priv_chan.name, Multi_pxo_priv_chan);
1274 
1275  // see if we know about this channel already
1276  multi_pxo_join_channel(&priv_chan);
1277 
1278  // return to normal mode
1280  break;
1281  }
1282  break;
1283 
1284  // find player mode
1285  case MULTI_PXO_MODE_FIND:
1286  switch(multi_pxo_find_popup()){
1287  // still running
1288  case 0:
1289  break;
1290 
1291  // user hit "cancel"
1292  case -1:
1293  // return to normal mode
1295  break;
1296 
1297  // user hit "ok"
1298  case 1 :
1299  // return to normal mode
1301 
1302  // if there is a valid channel name try and join it
1303  if(strlen(Multi_pxo_find_channel) && !SWITCHING_CHANNELS()){
1304  pxo_channel join;
1305 
1306  // setup the info
1307  memset(&join,0,sizeof(pxo_channel));
1308  join.num_users = 0;
1309  strcpy_s(join.name,Multi_pxo_find_channel);
1310 
1311  // try and join
1312  multi_pxo_join_channel(&join);
1313  }
1314  break;
1315  }
1316  break;
1317  // normal mode
1318  case MULTI_PXO_MODE_NORMAL:
1320  break;
1321  }
1322 }
1323 //XSTR:ON
1324 // close the PXO screen
1326 {
1327  // unload any bitmaps
1329  bm_release(Multi_pxo_com_bitmap);
1330 
1331  // record the last channel we were on, if any
1332  memset(Multi_fs_tracker_channel, 0, MAX_PATH);
1333  memset(Multi_fs_tracker_filter, 0, MAX_PATH);
1334 
1335  if ( ON_CHANNEL() && strlen(Multi_pxo_channel_current.name) ) {
1336  // channel name
1337  strcpy(Multi_fs_tracker_channel, Multi_pxo_channel_current.name);
1338 
1339  // filter name
1340  strcpy(Multi_fs_tracker_filter, Multi_pxo_channel_current.name);
1341  }
1342 
1343  // disconnect from the server
1345  Multi_pxo_connected = 0;
1346 
1347  // unload the animation
1348  if(Multi_pxo_anim.num_frames > 0){
1349  generic_anim_unload(&Multi_pxo_anim);
1350  }
1351 
1352  // unload the palette for this screen
1354 
1355  // destroy the UI_WINDOW
1356  Multi_pxo_window.destroy();
1357 
1358  // clear the channel list
1360 
1361  // close the chat system
1363 
1364  // close http stuff
1366 }
1367 
1368 // run normally (no popups)
1370 {
1371  int k = Multi_pxo_window.process();
1372 
1373  // process any keypresses
1374  switch (k)
1375  {
1376  case KEY_ESC:
1379  break;
1380  }
1381 
1382  // check for button presses
1384 
1385  // if we're not in a chatroom, disable and hide the chat input box
1386  if ( !ON_CHANNEL() ) {
1387  Multi_pxo_chat_input.hide();
1388  Multi_pxo_chat_input.disable();
1389  } else {
1390  Multi_pxo_chat_input.enable();
1391  Multi_pxo_chat_input.unhide();
1392  }
1393 
1394  // blit everything
1395  multi_pxo_blit_all();
1396 
1397  // flip the page
1398  gr_flip();
1399 
1400  // if we need to get tracker info for ourselves, do so
1402  // validate the current player with the master tracker (will create the pilot on the MT if necessary)
1403  bool validate_code = fs2netd_login();
1404 
1405  if ( !validate_code ) {
1406  // go back to the main hall
1408 
1411  }
1412  // now we have to conenct to PXO
1413  else {
1416  }
1417  }
1418 
1419  // if we need to connect, do so now
1420  if (Multi_pxo_must_connect) {
1421  // for now, just try once
1423 
1424  // if we successfully connected, send a request for a list of channels on the server
1425  if(Multi_pxo_connected){
1427 
1428  // set our status
1429  multi_pxo_set_status_text(XSTR("Retrieving Public Channels",939));
1430  } else {
1431  // set our status
1432  multi_pxo_set_status_text(XSTR("Failed to connect to Parallax Online",940));
1433  }
1434 
1435  // no longer need to connect
1437  }
1438 }
1439 
1440 // blit everything on the "normal" screen
1442 {
1443  // draw the background, etc
1444  gr_reset_clip();
1445  // GR_MAYBE_CLEAR_RES(Multi_pxo_bitmap);
1446  int bmap = Multi_pxo_bitmap;
1447  do {
1448  int bmw = -1;
1449  int bmh = -1;
1450  if(bmap != -1){
1451  bm_get_info( bmap, &bmw, &bmh);
1452  if((bmw != gr_screen.max_w_unscaled) || (bmh != gr_screen.max_h_unscaled)){
1453  gr_clear();
1454  }
1455  } else {
1456  gr_clear();
1457  }
1458  } while(0);
1459  if(Multi_pxo_bitmap != -1){
1462  }
1463  Multi_pxo_window.draw();
1464 
1465  // display the channel list
1467 
1468  // display the player list
1470 
1471  // blit the chat text
1473 
1474  // blit the status text
1476 
1477  // blit and process the notification string
1479 
1480  // any bitmap or info or whatever
1482 
1483  // draw any motd stuff
1485 
1486  // if we have a valid animation handle, play it
1487  // display the mission start countdown timer (if any)
1488  //anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
1489  if(gameseq_get_state() == GS_STATE_PXO && Multi_pxo_anim.num_frames > 0)
1491 }
1492 
1493 // process common stuff
1495 {
1496  // process the channel list (select, etc)
1498 
1499  // process the player list (select, etc)
1501 
1502  // process chat controls
1504 
1505  // process http download details
1507 }
1508 
1509 // get selected player information
1510 void multi_pxo_get_data(char *name)
1511 {
1512 }
1513 
1514 // handle being kicked
1516 {
1517  // remove ourselves from the room
1518  memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
1519  Multi_pxo_channel_current.num_users = -1;
1520 
1521  // clear text
1523 
1524  // clear the old player list
1526 
1527  // add a notification string
1528  multi_pxo_notify_add(XSTR("You have been kicked",941));
1529 }
1530 
1531 // handle being disconnected
1533 {
1534  ml_printf("PXO: Got DISCONNECT from server!");
1535 
1536  if ( popup_active() ) {
1537  popup_change_text( XSTR("You have been disconnected from the server", 942) );
1538  } else {
1539  popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You have been disconnected from the server", 942));
1541  }
1542 }
1543 
1544 // return string2, which is the first substring of string 1 without a space
1545 // it is safe to pass the same pointer for both parameters
1546 void multi_pxo_strip_space(char *string1,char *string2)
1547 {
1548  char midway[MAX_PXO_TEXT_LEN];
1549  char *tok;
1550 
1551  // copy the original
1552  strcpy_s(midway,string1);
1553  tok = strtok(midway," ");
1554  if(tok != NULL){
1555  strcpy(string2,tok);
1556  } else {
1557  strcpy(string2,"");
1558  }
1559 }
1560 
1561 // fire up the given URL
1562 void multi_pxo_url(char *url)
1563 {
1564 #if 0
1565  // execute the shell command
1566  int r = (int) ShellExecute(NULL, NOX("open"), url, NULL, NULL, SW_SHOW);
1567  if (r < 32) {
1568  switch (r) {
1569  case 0:
1570  case ERROR_BAD_FORMAT:
1571  case SE_ERR_ACCESSDENIED:
1572  case SE_ERR_ASSOCINCOMPLETE:
1573  case SE_ERR_DDEBUSY:
1574  case SE_ERR_DDEFAIL:
1575  case SE_ERR_DDETIMEOUT:
1576  case SE_ERR_DLLNOTFOUND:
1577  case SE_ERR_OOM:
1578  case SE_ERR_SHARE:
1579  case SE_ERR_NOASSOC:
1580  case ERROR_FILE_NOT_FOUND:
1581  case ERROR_PATH_NOT_FOUND:
1582  popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,XSTR("Warning\nCould not locate/launch default Internet Browser",943));
1583  break;
1584  }
1585  }
1586 #endif
1587 }
1588 
1589 // load/set the palette
1591 {
1592  // use the palette
1593 #ifndef HARDWARE_ONLY
1595 #endif
1596 }
1597 
1602 {
1603  // unload the palette if it exists
1604  if(Multi_pxo_palette != -1){
1606  Multi_pxo_palette = -1;
1607  }
1608 }
1609 
1614 {
1615  // if we're connected to a channel with the "+" symbol on front
1616  if(ON_CHANNEL() && (Multi_pxo_channel_current.name[0] == '+')){
1617  return 1;
1618  }
1619 
1620  // otherwise return falos
1621  return 0;
1622 }
1623 
1627 void multi_pxo_underscore_nick(char *string1,char *string2)
1628 {
1629  char nick_temp[512];
1630  char *tok;
1631 
1632  // don't do anything if we have bogus string
1633  if((string1 == NULL) || (string2 == NULL)){
1634  return;
1635  }
1636 
1637  // copy the nickname
1638  memset(nick_temp,0,512);
1639  strcpy_s(nick_temp,string1);
1640 
1641  // get the first token
1642  tok = strtok(nick_temp," ");
1643  if(tok != NULL){
1644  strcpy(string2,tok);
1645 
1646  // get the next token
1647  tok = strtok(NULL," ");
1648  while(tok != NULL){
1649  if(tok != NULL){
1650  strcat(string2,"_");
1651  strcat(string2,tok);
1652  }
1653 
1654  tok = strtok(NULL," ");
1655  }
1656  } else {
1657  strcpy(string2,string1);
1658  }
1659 }
1660 
1665 {
1666  char *tok;
1667  char tmp[512];
1668 
1669  // get the first token in the message
1670  memset(tmp,0,512);
1671  strcpy_s(tmp,msg);
1672  tok = strtok(tmp," ");
1673  if(tok == NULL){
1674  // can't be a nick message
1675  return 0;
1676  }
1677 
1678  return !stricmp(tok,NOX("/nick"));
1679 }
1680 
1685 {
1686  int idx;
1687 
1688  // go through all buttons
1689  for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
1690  if(Multi_pxo_buttons[gr_screen.res][idx].button.pressed()){
1692  break;
1693  }
1694  }
1695 }
1696 
1701 {
1702  switch(n){
1703  case MULTI_PXO_EXIT:
1706  break;
1707 
1708  case MULTI_PXO_CHAN_UP:
1710  break;
1711 
1712  case MULTI_PXO_CHAN_DOWN:
1714  break;
1715 
1716  case MULTI_PXO_TEXT_UP:
1718  break;
1719 
1720  case MULTI_PXO_TEXT_DOWN:
1722  break;
1723 
1724  case MULTI_PXO_PLIST_UP:
1727  break;
1728 
1729  case MULTI_PXO_PLIST_DOWN:
1732  break;
1733 
1734  case MULTI_PXO_JOIN:
1735  // if there are no channels to join, let the user know
1736  if((Multi_pxo_channel_count == 0) || (Multi_pxo_channels == NULL)){
1738  multi_pxo_notify_add(XSTR("No channels!",944));
1739  break;
1740  }
1741 
1742  // if we're not already trying to join, allow this
1743  if(!SWITCHING_CHANNELS() && (Multi_pxo_channel_select != NULL)){
1745  multi_pxo_join_channel(Multi_pxo_channel_select);
1746  } else {
1747  multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
1749  }
1750  break;
1751 
1752  case MULTI_PXO_GAMES:
1753  // move to the join game screen as normally (temporary!)
1755  break;
1756 
1757  case MULTI_PXO_JOIN_PRIV:
1758  // if we're not already trying to join, allow this
1759  if(!SWITCHING_CHANNELS()){
1761 
1762  // fire up the private join popup
1764  } else {
1765  multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
1767  }
1768  break;
1769 
1770  case MULTI_PXO_FIND:
1772 
1773  // fire up the find join popup
1775  break;
1776 
1777  case MULTI_PXO_HELP:
1780  break;
1781 
1782  case MULTI_PXO_PINFO:
1783  char stats[MAX_PXO_TEXT_LEN];
1784 
1785  // if we have a guy selected, try and get his info
1786  if(Multi_pxo_player_select != NULL){
1787  // if we successfully got info for this guy
1788  if(multi_pxo_pinfo_get(Multi_pxo_player_select->name)){
1789  // show the stats
1791  }
1792  // if we didn't get stats for this guy.
1793  else {
1794  memset(stats,0,MAX_PXO_TEXT_LEN);
1795  sprintf(stats,XSTR("Could not get stats for %s\n(May not be a registered pilot)",946),Multi_pxo_player_select->name);
1797  }
1798  } else {
1800  }
1801  break;
1802 
1803  case MULTI_PXO_RANKINGS:
1804  // make sure he doesn't click it too many times
1807 
1808  // fire up the url
1810 
1811  // mark the time down
1813  } else {
1815  }
1816  break;
1817 
1818  case MULTI_PXO_MOTD:
1819  // maybe fire up the pxo motd dialog
1821  break;
1822  }
1823 }
1824 
1828 int mpxo_failed = 0;
1830 {
1831  int ret_code;
1832  char id_string[MAX_PXO_TEXT_LEN] = "";
1833  char ip_string[MAX_PXO_TEXT_LEN] = "";
1834 
1835  // if we already tried and failed, sit around until the user presses cancel
1836  if(!mpxo_failed){
1837  // try and connect to the server
1838  Assert(Player);
1839 
1840  // build the tracker id string
1841  memset(id_string, 0, MAX_PXO_TEXT_LEN);
1842  sprintf(id_string, "%s %s", Multi_tracker_id_string, Player->callsign);
1843 
1844  // build the ip string
1845  memset(ip_string, 0, MAX_PXO_TEXT_LEN);
1846  sprintf(ip_string, "%s:%d", Multi_options_g.pxo_ip, PXO_CHAT_PORT);
1847 
1848  // connect to the server
1849  ret_code = ConnectToChatServer(ip_string, Multi_pxo_nick, id_string);
1850 
1851  // give some time to the pxo api.
1853 
1854  switch(ret_code){
1855  // already connected, return success
1856  case -2:
1857  return 10;
1858 
1859  // failed to connect, return fail
1860  case -1 :
1861  mpxo_failed = 1;
1862  return 1;
1863 
1864  // connected, return success
1865  case 1 :
1866  return 10;
1867 
1868  // still connecting
1869  case 0 :
1870  return 0;
1871  }
1872  }
1873 
1874  return 0;
1875 }
1876 
1883 {
1884  pxo_channel last_channel;
1885 
1886  // if we need to autojoin, do so now
1889 
1890  // if we're supposed to be using a (valid) "last" channel, do so
1892  // setup the data
1893  memset(&last_channel, 0, sizeof(pxo_channel));
1894  last_channel.num_users = 0;
1895  strcpy_s(last_channel.name, Multi_pxo_channel_last);
1896 
1897  // join the channel
1898  multi_pxo_join_channel(&last_channel);
1899 
1900  nprintf(("Network","PXO : using last channel\n"));
1901  } else {
1903 
1904  nprintf(("Network","PXO : using autojoin channel\n"));
1905  }
1906 
1908  }
1909 
1910  // give some time to the pxo api.
1913 
1914  // next value is not -1 when actually switching channels, so keep processing by returning 0.
1915  if ( SWITCHING_CHANNELS() )
1916  return 0;
1917 
1918  // couldn't switch channel for some reason. bail out with -1
1919  if ( !ON_CHANNEL() )
1920  return -1;
1921 
1922  // return success
1923  return 1;
1924 }
1925 
1930 {
1931  char join_str[256];
1932  char join_fail_str[256];
1933 
1934  // intiialize chat api
1935  ChatInit();
1936 
1937  // set us to "must autojoin"
1939 
1940  // run the connect dialog/popup
1941  mpxo_failed = 0;
1942 
1943  if ( popup_till_condition(multi_pxo_connect_do, XSTR("&Cancel", 779), XSTR("Logging into Parallax Online", 949)) == 10 ) {
1944  int rval;
1945 
1946  memset(join_str, 0, 256);
1947  memset(join_fail_str, 0, 256);
1948 
1949  // if we're going to use the "last" channel
1951  strcpy_s(join_str, XSTR("Joining last channel (", 982));
1952  strcat_s(join_str, Multi_pxo_channel_last + 1);
1953  strcat_s(join_str, ")");
1954 
1955  strcpy_s(join_fail_str, XSTR("Unable to join last channel", 983));
1956  } else {
1957  strcpy_s(join_str, XSTR("Autojoining public channel", 984));
1958  strcpy_s(join_fail_str, XSTR("Unable to autojoin public channel", 985));
1959  }
1960 
1961  // once connected, we should do an autojoin before allowing the guy to continue.
1962  rval = popup_till_condition( multi_pxo_autojoin_do, XSTR("&Cancel", 779), join_str );
1963 
1964  if ( rval == 1 )
1965  return 1;
1966 
1967  popup(PF_USE_AFFIRMATIVE_ICON, 1, XSTR("OK", 1492), join_fail_str);
1968  }
1969 
1970  // otherwise disconnect just to be safe
1972 
1973  // we failed to connect, so give a nice popup about that
1974  if (mpxo_failed) {
1975  popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Failed to connect to Parallax Online!", 947));
1976  }
1977 
1978  // if we are coming from the mainhall then fail to the join game screen rather
1979  // than keeping the user constantly at the mainhall
1982  } else {
1984  }
1985 
1986  // did not successfully connect
1987  return 0;
1988 }
1989 
1994 {
1995  char *p;
1996  char msg_str[512];
1997  Chat_command *cmd;
1998  pxo_channel *lookup;
1999 
2000  // give some time to psnet
2002 
2003  // give some time to FS2NetD
2004  fs2netd_do_frame();
2005 
2006  // get any incoming text
2007  do
2008  {
2009  p = GetChatText();
2010 
2011  if (p) {
2012  // process the chat line
2014  }
2015  } while(p);
2016 
2017  // get any incoming channel list stuff
2018  p = GetChannelList();
2019 
2020  if (p) {
2022  }
2023 
2024  // process any chat commands
2025  cmd = GetChatCommand();
2026 
2027  while (cmd) {
2028  switch (cmd->command)
2029  {
2030  case CC_USER_JOINING:
2031  // add a user, if he doesn't already exist
2032  if (multi_pxo_find_player(cmd->data) == NULL)
2033  multi_pxo_add_player(cmd->data);
2034 
2035  // increase the player count
2036  if (ON_CHANNEL() ) {
2037  lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name, Multi_pxo_channels);
2038 
2039  if (lookup != NULL)
2040  lookup->num_users++;
2041  }
2042  break;
2043 
2044  case CC_USER_LEAVING:
2045  // delete a user
2046  multi_pxo_del_player(cmd->data);
2047 
2048  // add a text message
2049  memset(msg_str, 0, 512);
2050  sprintf(msg_str, XSTR("*** %s has left", 950), cmd->data);
2052 
2053  // decrease the player count
2054  if ( ON_CHANNEL() ) {
2055  lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2056 
2057  if (lookup != NULL)
2058  lookup->num_users--;
2059  }
2060  break;
2061 
2062  case CC_DISCONNECTED:
2064  break;
2065 
2066  case CC_KICKED:
2068  break;
2069 
2070  case CC_NICKCHANGED:
2071  // process a nick change
2073  break;
2074 
2075  case CC_YOURCHANNEL:
2076  // copy the current channel info, and unset the switching status
2077  memset( &Multi_pxo_channel_current, 0, sizeof(pxo_channel) );
2078  Multi_pxo_channel_switch.num_users = -1;
2079 
2080  SetNewChatChannel(NULL);
2081 
2082  strcpy_s(Multi_pxo_channel_current.name, cmd->data);
2083 
2084  // if we don't already have this guy on the list, add him
2085  lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name, Multi_pxo_channels);
2086 
2087  if (lookup == NULL) {
2088  // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2089  lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name, &Multi_pxo_channels);
2090  }
2091 
2092  // set the user count to be 0
2093  if (lookup != NULL)
2094  lookup->num_users = 0;
2095 
2096  // set our "last" channel to be this one
2097  strcpy_s(Multi_pxo_channel_last, Multi_pxo_channel_current.name);
2098 
2099  // refresh current channel server count
2101 
2102  break;
2103 
2104  default:
2105  Int3();
2106  }
2107 
2108  cmd = GetChatCommand();
2109  }
2110 
2111  // handle any processing details if we're currently trying to join a channel
2113 }
2114 
2119 {
2120  char *from, *to;
2121  player_list *lookup;
2122 
2123  // get the new string
2124  from = strtok(data," ");
2125  to = strtok(NULL,"");
2126  if((from != NULL) && (to != NULL)){
2127  lookup = multi_pxo_find_player(from);
2128  if(lookup != NULL){
2129  strcpy_s(lookup->name,to);
2130 
2131  // if this is also my nick, change it
2132  if(!stricmp(Multi_pxo_nick,from)){
2134  }
2135  }
2136  }
2137 }
2138 
2143 {
2144  pxo_channel sw;
2145 
2146  memset( &sw, 0, sizeof(pxo_channel) );
2147  sw.num_users = 0;
2149 
2150  // if we found a valid room, attempt to join it
2151  multi_pxo_join_channel(&sw);
2152 }
2153 
2157 int multi_pxo_is_autojoin(char *name)
2158 {
2159  // check to see if the name is long enough
2160  if ( strlen(name) < strlen(MULTI_PXO_AUTOJOIN_PREFIX) )
2161  return 0;
2162 
2163  // check to see if the first n chars match
2165 }
2166 
2171 {
2172  pxo_channel *lookup;
2173 
2174  // lookup the channel name on the normal list
2175  lookup = multi_pxo_find_channel(name,Multi_pxo_channels);
2176 
2177  if (lookup != NULL) {
2178  lookup->num_servers = (ushort)count;
2179 
2180  nprintf(("Network","PXO : updated channel %s server count to %d\n",name,count));
2181  ml_printf("PXO : updated channel %s server count to %d", name, count);
2182  } else {
2183  ml_printf("PXO : unable to locate channel when trying to update count for %s", name);
2184  }
2185 }
2186 
2187 // status bar stuff -----------------------------------------------
2188 
2192 void multi_pxo_set_status_text(const char *txt)
2193 {
2194  // copy in the text
2196  strncpy(Multi_pxo_status_text, txt, MAX_PXO_TEXT_LEN-1);
2197 
2198  // make sure it fits properly
2200 }
2201 
2206 {
2207  int w;
2208 
2209  // center and draw the text
2210  if(strlen(Multi_pxo_status_text)) {
2214  }
2215 }
2216 
2217 
2218 // channel related stuff -------------------------------------------
2219 
2224 {
2225  SendChatString(NOX("/list"));
2226 }
2227 
2232 {
2233  pxo_channel *moveup,*backup;
2234 
2235  // only clear a non-null list
2236  if(Multi_pxo_channels != NULL){
2237  // otherwise
2238  moveup = Multi_pxo_channels;
2239  backup = NULL;
2240  if(moveup != NULL){
2241  do {
2242  backup = moveup;
2243  moveup = moveup->next;
2244 
2245  // free the struct itself
2246  vm_free(backup);
2247  backup = NULL;
2248  } while(moveup != Multi_pxo_channels);
2249  Multi_pxo_channels = NULL;
2250  }
2251 
2252  // head of the list of available channels
2253  Multi_pxo_channels = NULL;
2255 
2256  // item we're going to start displaying at
2257  Multi_pxo_channel_start = NULL;
2259 
2260  // items we've currently got selected
2261  Multi_pxo_channel_select = NULL;
2262  }
2263 }
2264 
2268 void multi_pxo_make_channels(char *chan_str)
2269 {
2270  char *name_tok,*user_tok,*desc_tok;
2271  pxo_channel *res;
2272  pxo_channel *lookup;
2273  int num_users;
2274 
2275  nprintf(("Network","Making some channels!\n"));
2276 
2277  // set the last get time
2279 
2280  name_tok = strtok(chan_str," ");
2281  if(name_tok == NULL){
2282  return;
2283  }
2284  name_tok += 1;
2285  do {
2286  // parse the user count token
2287  user_tok = strtok(NULL," ");
2288 
2289  // parse the channel description token
2290  desc_tok = strtok(NULL,"$");
2291 
2292  // something invalid in the data, return here.....
2293  if((name_tok == NULL) || (user_tok == NULL) || (desc_tok == NULL)){
2294  return;
2295  }
2296 
2297  // get the # of users
2298  num_users = (ubyte)atoi(user_tok);
2299 
2300  // if the # of users is > 0, or its not an autojoin, place it on the display list
2301  if((num_users > 0) || !multi_pxo_is_autojoin(name_tok)){
2302  // see if it exists already, and if so, just update the user count
2303  lookup = multi_pxo_find_channel(name_tok,Multi_pxo_channels);
2304 
2305  if(lookup != NULL){
2306  lookup->num_users = (short)num_users;
2307  }
2308  // add the channel
2309  else {
2310  res = multi_pxo_add_channel(name_tok,&Multi_pxo_channels);
2311  if(res != NULL){
2312  res->num_users = (short)num_users;
2313  strcpy_s(res->desc,desc_tok);
2314  }
2315  }
2316  }
2317 
2318  // get the next name token
2319  name_tok = strtok(NULL," ");
2320  } while(name_tok != NULL);
2321 
2322  // refresh channels
2323  multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
2324 
2325  // if we haven't refreshed server counts yet, do it now
2328  }
2329 
2330  // if we don't already have this guy on the list, add him
2331  if(ON_CHANNEL()){
2332  lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2333  if(lookup == NULL){
2334  // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2335  multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2336  }
2337  }
2338 }
2339 
2344 {
2345  pxo_channel *new_channel;
2346 
2347  // try and allocate a new pxo_channel struct
2348  new_channel = (pxo_channel *)vm_malloc(sizeof(pxo_channel));
2349  if ( new_channel == NULL ) {
2350  nprintf(("Network", "Cannot allocate space for new pxo_channel structure\n"));
2351  return NULL;
2352  }
2353  memset(new_channel,0,sizeof(pxo_channel));
2354  // try and allocate a string for the channel name
2355  strncpy(new_channel->name,name,MAX_CHANNEL_NAME_LEN);
2356 
2357  // insert it on the list
2358  if ( *list != NULL ) {
2359  new_channel->next = (*list)->next;
2360  new_channel->next->prev = new_channel;
2361  (*list)->next = new_channel;
2362  new_channel->prev = *list;
2363  } else {
2364  *list = new_channel;
2365  (*list)->next = (*list)->prev = *list;
2366  }
2367 
2369  return new_channel;
2370 }
2371 
2376 {
2377  pxo_channel *moveup;
2378 
2379  // look the sucker up
2380  moveup = list;
2381  if(moveup == NULL){
2382  return NULL;
2383  }
2384  do {
2385  if(!stricmp(name,moveup->name)){
2386  return moveup;
2387  }
2388 
2389  moveup = moveup->next;
2390  } while((moveup != list) && (moveup != NULL));
2391 
2392  return NULL;
2393 }
2394 
2399 {
2400  int item_index,my;
2401  int idx;
2402 
2403  // if we don't have a start item, but the list is non-null
2404  if((Multi_pxo_channel_start == NULL) && (Multi_pxo_channels != NULL)){
2405  Multi_pxo_channel_start = Multi_pxo_channels;
2407  }
2408 
2409  // if we don't have a selected item, but the list is non-null
2410  if((Multi_pxo_channel_select == NULL) && (Multi_pxo_channels != NULL)){
2411  Multi_pxo_channel_select = Multi_pxo_channels;
2412 
2413  // set the text
2414  multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2415  }
2416 
2417  // if the "switch" delay timestamp is set, see if it has expired
2418  if((Multi_pxo_switch_delay != -1) && timestamp_elapsed(Multi_pxo_switch_delay)){
2419  Multi_pxo_switch_delay = -1;
2420  }
2421 
2422  // see if we have a mouse click on the channel region
2423  if(Multi_pxo_channel_button.pressed()){
2424  Multi_pxo_channel_button.get_mouse_pos(NULL,&my);
2425 
2426  // index from the top
2427  item_index = my / (gr_get_font_height() + 1);
2428 
2429  // select the item if possible
2431  Multi_pxo_channel_select = Multi_pxo_channel_start;
2432  for(idx=0;idx<item_index;idx++){
2433  Multi_pxo_channel_select = Multi_pxo_channel_select->next;
2434  }
2435 
2436  // set the text
2437  multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2438  }
2439  }
2440 
2441  // last refresh time
2443  // refresh channels
2444  multi_pxo_set_status_text(XSTR("Refreshing Public Channel List",952));
2445 
2446  // get a list of channels on the server (clear any old list as well)
2448 
2449  // refresh
2451 
2452  nprintf(("Network","Refreshing channels\n"));
2453  }
2454 
2455  // if we haven't updated our server channel counts in a while, do so again
2456  // last refresh time
2458 
2459  // do it _NOW_ I"M RIGHT HERE KILL ME WHAT ARE YOU WAITING FOR DO IT KILL ME DO IT NOW!
2461  }
2462 }
2463 
2468 {
2469  pxo_channel *lookup;
2470 
2471  // traverse the list of existing channels we know about and query the game tracker about them
2472  lookup = Multi_pxo_channels;
2473 
2474  if (lookup == NULL) {
2475  return;
2476  }
2477 
2478  do {
2479  if ( strlen(lookup->name) ) {
2480  // send the request
2482  }
2483 
2484  // next item
2485  lookup = lookup->next;
2486  } while ( (lookup != NULL) && (lookup != Multi_pxo_channels) );
2487 
2488  // record the time
2490 }
2491 
2496 {
2497  // send a request for a server count on this channel
2498  if ( strlen(Multi_pxo_channel_current.name) ) {
2499  // send the request
2500  fs2netd_update_game_count(Multi_pxo_channel_current.name);
2501  }
2502 }
2503 
2508 {
2509  pxo_channel *moveup;
2510  char chan_name[MAX_PXO_TEXT_LEN];
2511  char chan_users[15];
2512  char chan_servers[15];
2513  int user_w,server_w;
2514  int disp_count,y_start;
2515  int line_height = gr_get_font_height() + 1;
2516 
2517  // blit as many channels as we can
2518  disp_count = 0;
2519  y_start = Multi_pxo_chan_coords[gr_screen.res][1];
2520  moveup = Multi_pxo_channel_start;
2521  if(moveup == NULL){
2522  return;
2523  }
2524  do {
2525  // if this is the currently selected item, highlight it
2526  if(moveup == Multi_pxo_channel_select){
2528  }
2529  // otherwise draw it normally
2530  else {
2532  }
2533 
2534  // get the # of users on the channel
2535  memset(chan_users, 0, 15);
2536  sprintf(chan_users, "%d", moveup->num_users);
2537 
2538  // get the width of the user count string
2539  gr_get_string_size(&user_w, NULL, chan_users);
2540 
2541  // get the # of servers on the channel
2542  memset(chan_servers,0,15);
2543  sprintf(chan_servers, "%d", moveup->num_servers);
2544 
2545  // get the width of the user count string
2546  gr_get_string_size(&server_w, NULL, chan_servers);
2547 
2548  // make sure the name fits
2549  memset(chan_name, 0, MAX_PXO_TEXT_LEN);
2550  Assert(moveup->name);
2551  strcpy_s(chan_name,moveup->name);
2552  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]);
2553 
2554  // blit the strings
2555  gr_string(Multi_pxo_chan_coords[gr_screen.res][0], y_start, chan_name + 1, GR_RESIZE_MENU);
2556  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);
2558  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);
2559 
2560  // increment the displayed count
2561  disp_count++;
2562  y_start += line_height;
2563 
2564  // next item
2565  moveup = moveup->next;
2566  } while((moveup != Multi_pxo_channels) && (disp_count < Multi_pxo_max_chan_display[gr_screen.res]));
2567 }
2568 
2573 {
2574  // if we're already at the head of the list, do nothing
2575  if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start == Multi_pxo_channels)){
2577  return;
2578  }
2579 
2580  // otherwise move up one
2581  Multi_pxo_channel_start = Multi_pxo_channel_start->prev;
2584 }
2585 
2590 {
2591  // if we're already at the tail of the list, do nothing
2592  if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start->next == Multi_pxo_channels)){
2594  return;
2595  }
2596 
2597  // if we can't scroll further without going past the end of the viewable list, don't
2600  return;
2601  }
2602 
2603  // otherwise move down one
2604  Multi_pxo_channel_start = Multi_pxo_channel_start->next;
2607 }
2608 
2613 {
2614  char switch_msg[256];
2615 
2616  // if we're already on this channel, do nothing
2617  if(ON_CHANNEL() && !stricmp(chan->name, Multi_pxo_channel_current.name)){
2618  return;
2619  }
2620 
2621  // if we're already trying to join a channel, do nothing
2622  if(SWITCHING_CHANNELS()){
2623  return;
2624  }
2625 
2626  // try and join the channel
2627  switch(SetNewChatChannel(chan->name)){
2628  case -1 :
2629  Int3();
2630  break;
2631 
2632  case 0 :
2633  // decrement the count of our current channel
2634  pxo_channel *lookup;
2635  lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2636  if(lookup != NULL){
2637  lookup->num_users--;
2638  }
2639 
2640  // set our current channel as none
2641  memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2642  Multi_pxo_channel_current.num_users = -1;
2643 
2644  multi_pxo_set_status_text(XSTR("Switching channels",953));
2645 
2646  // copy the channel
2647  memcpy(&Multi_pxo_channel_switch,chan,sizeof(pxo_channel));
2648 
2649  // clear the player list
2651 
2652  // display a line of text indicating that we're switching channels
2653  memset(switch_msg,0,256);
2654 
2655  if(strlen(Multi_pxo_channel_switch.name) > 1){
2656  sprintf(switch_msg, "[Switching to channel %s]", Multi_pxo_channel_switch.name + 1);
2657  } else {
2658  sprintf(switch_msg, "[Switching to channel %s]", Multi_pxo_channel_switch.name);
2659  }
2660 
2662  break;
2663 
2664  case 1 :
2665  Int3();
2666  }
2667 }
2668 
2673 {
2674  // if we're not switching channels, do nothing
2675  if(!SWITCHING_CHANNELS()){
2676  return;
2677  }
2678 
2679  // if we are, check the status
2680  switch(SetNewChatChannel(NULL)){
2681  // failed to switch
2682  case -1 :
2683  // unset our switching struct
2684  memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
2685  Multi_pxo_channel_switch.num_users = -1;
2686 
2687  // notify of error
2688  multi_pxo_set_status_text(XSTR("No channel (error while switching)",954));
2689  break;
2690 
2691  // still switching
2692  case 0:
2693  break;
2694 
2695  // successfully changed
2696  case 1:
2697  // copy the current channel info, and unset the switching status
2698  memcpy(&Multi_pxo_channel_current,&Multi_pxo_channel_switch,sizeof(pxo_channel));
2699  Multi_pxo_channel_switch.num_users = -1;
2700 
2701  // set our "last" channel
2702  strcpy_s(Multi_pxo_channel_last, Multi_pxo_channel_current.name);
2703 
2704  // notify the user
2705  multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
2706 
2707  // if we don't already have this guy on the list, add him
2708  pxo_channel *lookup;
2709  lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2710  if(lookup == NULL){
2711  // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2712  lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2713  }
2714 
2715  // set the user count to be 1 (just me)
2716  if(lookup != NULL){
2717  lookup->num_users = 1;
2718  }
2719 
2720  // set the "switch" delay timestamp
2721  Multi_pxo_switch_delay = timestamp(MULTI_PXO_SWITCH_DELAY_TIME);
2722 
2723  // refresh current channel server count
2725  break;
2726  }
2727 }
2728 
2729 
2730 // player related stuff -------------------------------------------
2731 
2736 {
2737  player_list *moveup,*backup;
2738 
2739  // if the list is null, don't free it up
2740  if(Multi_pxo_players != NULL){
2741  // otherwise
2742  moveup = Multi_pxo_players;
2743  backup = NULL;
2744  if(moveup != NULL){
2745  do {
2746  backup = moveup;
2747  moveup = moveup->next;
2748 
2749  // free the struct itself
2750  vm_free(backup);
2751  backup = NULL;
2752  } while(moveup != Multi_pxo_players);
2753  Multi_pxo_players = NULL;
2754  }
2755  }
2756 
2757  Multi_pxo_player_start = NULL;
2758  Multi_pxo_player_select = NULL;
2759 }
2760 
2765 {
2766  player_list *new_player;
2767 
2768  // try and allocate a new player_list struct
2769  new_player = (player_list *)vm_malloc(sizeof(player_list));
2770  if ( new_player == NULL ) {
2771  nprintf(("Network", "Cannot allocate space for new player_list structure\n"));
2772  return NULL;
2773  }
2774  // try and allocate a string for the channel name
2775  strncpy(new_player->name, name, MAX_PLAYER_NAME_LEN);
2776 
2777  // insert it on the list
2778  if ( Multi_pxo_players != NULL ) {
2779  new_player->next = Multi_pxo_players->next;
2780  new_player->next->prev = new_player;
2781  Multi_pxo_players->next = new_player;
2782  new_player->prev = Multi_pxo_players;
2783  } else {
2784  Multi_pxo_players = new_player;
2785  Multi_pxo_players->next = Multi_pxo_players->prev = Multi_pxo_players;
2786  }
2787 
2788  // new player
2790 
2791  return new_player;
2792 }
2793 
2797 void multi_pxo_del_player(char *name)
2798 {
2799  player_list *lookup;
2800 
2801  // try and find this guy
2802  lookup = Multi_pxo_players;
2803  if(lookup == NULL){
2804  return;
2805  }
2806  do {
2807  // if we found a match, delete it
2808  if(!stricmp(name,lookup->name)){
2809  // if this is the only item on the list, free stuff up
2810  if(lookup->next == lookup){
2811  Assert(lookup == Multi_pxo_players);
2812  vm_free(lookup);
2813  Multi_pxo_players = NULL;
2815  }
2816  // otherwise, just delete it
2817  else {
2818  lookup->next->prev = lookup->prev;
2819  lookup->prev->next = lookup->next;
2820 
2821  // if this was our selected item, unselect it
2822  if((lookup == Multi_pxo_player_select) && (Multi_pxo_player_select != NULL)){
2823  Multi_pxo_player_select = Multi_pxo_player_select->next;
2824  }
2825 
2826  // if this was our point to start viewing from, select another
2827  if(lookup == Multi_pxo_player_start){
2828  // if this is the head of the list, move up one
2829  if(Multi_pxo_players == lookup){
2830  Multi_pxo_player_start = Multi_pxo_player_start->next;
2831  // Multi_pxo_player_start_index = 0;
2832  }
2833  // otherwise move back
2834  else {
2835  Multi_pxo_player_start = Multi_pxo_player_start->prev;
2836  }
2837  }
2838 
2839  // if this is the head of the list, move it up
2840  if(lookup == Multi_pxo_players){
2841  Multi_pxo_players = Multi_pxo_players->next;
2842  }
2843 
2844  // free the item up
2845  lookup->next = NULL;
2846  lookup->prev = NULL;
2847  vm_free(lookup);
2848  }
2849 
2850  // new player
2853 
2854  // we're done now
2855  return;
2856  }
2857 
2858  // next item
2859  lookup = lookup->next;
2860  } while((lookup != NULL) && (lookup != Multi_pxo_players));
2861 }
2862 
2867 {
2868  player_list *lookup;
2869 
2870  // look through all players
2871  lookup = Multi_pxo_players;
2872  if(lookup == NULL){
2873  return NULL;
2874  }
2875  do {
2876  if(!stricmp(name,lookup->name)){
2877  return lookup;
2878  }
2879 
2880  lookup = lookup->next;
2881  } while((lookup != NULL) && (lookup != Multi_pxo_players));
2882 
2883  // return NULL
2884  return NULL;
2885 }
2886 
2891 {
2892  int item_index,my;
2893  player_list *lookup;
2894 
2895  // if we don't have a start item, but the list is non-null
2896  if((Multi_pxo_player_start == NULL) && (Multi_pxo_players != NULL)){
2897  Multi_pxo_player_start = Multi_pxo_players;
2898  }
2899 
2900  // if we don't have a selected item, but the list is non-null
2901  if((Multi_pxo_player_select == NULL) && (Multi_pxo_players != NULL)){
2902  Multi_pxo_player_select = Multi_pxo_players;
2903  }
2904 
2905  // see if we have a mouse click on the channel region
2906  if(Multi_pxo_player_button.pressed()){
2907  Multi_pxo_player_button.get_mouse_pos(NULL,&my);
2908 
2909  // index from the top
2910  item_index = my / (gr_get_font_height() + 1);
2911 
2912  // select the item if possible
2913  lookup = Multi_pxo_player_start;
2914  if(lookup == NULL){
2915  return;
2916  }
2917  // select item 0
2918  if(item_index == 0){
2919  Multi_pxo_player_select = Multi_pxo_player_start;
2920  return;
2921  }
2922  do {
2923  // move to the next item
2924  lookup = lookup->next;
2925  item_index--;
2926 
2927  // if this item is our guy
2928  if((item_index == 0) && (lookup != Multi_pxo_players)){
2929  Multi_pxo_player_select = lookup;
2930  return;
2931  }
2932  } while((lookup != Multi_pxo_players) && (item_index > 0));
2933  }
2934 }
2935 
2940 {
2941  player_list *moveup;
2942  char player_name[MAX_PXO_TEXT_LEN];
2943  int disp_count,y_start;
2944  int line_height = gr_get_font_height() + 1;
2945 
2946  // blit as many channels as we can
2947  disp_count = 0;
2948  y_start = Multi_pxo_player_coords[gr_screen.res][1];
2949  moveup = Multi_pxo_player_start;
2950  if(moveup == NULL){
2951  return;
2952  }
2953  do {
2954  // if this is the currently selected item, highlight it
2955  if(moveup == Multi_pxo_player_select){
2957  }
2958  // otherwise draw it normally
2959  else {
2961  }
2962 
2963  // make sure the string fits
2964  strcpy_s(player_name,moveup->name);
2966 
2967  // blit the string
2968  gr_string(Multi_pxo_player_coords[gr_screen.res][0], y_start, player_name, GR_RESIZE_MENU);
2969 
2970  // increment the displayed count
2971  disp_count++;
2972  y_start += line_height;
2973 
2974  // next item
2975  moveup = moveup->next;
2976  } while((moveup != Multi_pxo_players) && (disp_count < Multi_pxo_max_player_display[gr_screen.res]));
2977 }
2978 
2983 {
2984  // if we're already at the head of the list, do nothing
2985  if((Multi_pxo_player_start == NULL) || (Multi_pxo_player_start == Multi_pxo_players)){
2987  return;
2988  }
2989 
2990  // otherwise move up one
2991  Multi_pxo_player_start = Multi_pxo_player_start->prev;
2992 
2994 }
2995 
3000 {
3001  player_list *lookup;
3002  int count = 0;
3003 
3004  // see if its okay to scroll down
3005  lookup = Multi_pxo_player_start;
3006  if(lookup == NULL ){
3008  return;
3009  }
3010  count = 0;
3011  while(lookup->next != Multi_pxo_players){
3012  lookup = lookup->next;
3013  count++;
3014  }
3015 
3016  // if we can move down
3018  Multi_pxo_player_start = Multi_pxo_player_start->next;
3020  } else {
3022  }
3023 }
3024 
3025 
3026 // chat text stuff -----------------------------------------
3027 
3032 {
3033  int idx;
3034  chat_line *new_line;
3035 
3036  // no chat lines
3037  Multi_pxo_chat = NULL;
3038  Multi_pxo_chat_add = NULL;
3039  Multi_pxo_chat_start = NULL;
3040  Multi_pxo_chat_start_index = -1;
3041 
3042  // create the lines in a non-circular doubly linked list
3043  for(idx=0;idx<MAX_CHAT_LINES;idx++){
3044  new_line = (chat_line*)vm_malloc(sizeof(chat_line));
3045 
3046  // clear the line out
3047  Assert(new_line != NULL);
3048  if(new_line == NULL){
3049  return;
3050  }
3051  memset(new_line,0,sizeof(chat_line));
3052  new_line->prev = NULL;
3053  new_line->next = NULL;
3054 
3055  // insert it into the (empty) list
3056  if(Multi_pxo_chat == NULL){
3057  Multi_pxo_chat = new_line;
3058  }
3059  // insert it onto the (non-empty) list
3060  else {
3061  Multi_pxo_chat->prev = new_line;
3062  new_line->next = Multi_pxo_chat;
3063  Multi_pxo_chat = new_line;
3064  }
3065  }
3066 
3067  // start adding chat lines at the beginning of the list
3068  Multi_pxo_chat_add = Multi_pxo_chat;
3069 }
3070 
3075 {
3076  chat_line *moveup, *backup;
3077 
3078  // free all items up
3079  moveup = Multi_pxo_chat;
3080  while(moveup != NULL){
3081  backup = moveup;
3082  moveup = moveup->next;
3083 
3084  vm_free(backup);
3085  }
3086 
3087  // no chat lines
3088  Multi_pxo_chat = NULL;
3089  Multi_pxo_chat_add = NULL;
3090  Multi_pxo_chat_start = NULL;
3091  Multi_pxo_chat_start_index = -1;
3092  Multi_pxo_chat_count = 0;
3093  Multi_pxo_chat_slider.set_numberItems(0);
3094 }
3095 
3100 {
3101  chat_line *moveup;
3102 
3103  // clear the text in all the lines
3104  moveup = Multi_pxo_chat;
3105  while(moveup != NULL){
3106  memset(moveup->text,0,MAX_CHAT_LINE_LEN+1);
3107  moveup = moveup->next;
3108  }
3109 
3110  // how many chat lines we have
3111  Multi_pxo_chat_count = 0;
3112 
3113  // start adding chat lines at the beginning of the list
3114  Multi_pxo_chat_add = Multi_pxo_chat;
3115 }
3116 
3120 void multi_pxo_chat_add_line(const char *txt, int mode)
3121 {
3122  chat_line *temp;
3123 
3124  // copy in the text
3125  Assert(Multi_pxo_chat_add != NULL);
3126  strncpy(Multi_pxo_chat_add->text, txt, MAX_CHAT_LINE_LEN);
3127  Multi_pxo_chat_add->mode = mode;
3128 
3129  // if we're at the end of the list, move the front item down
3130  if(Multi_pxo_chat_add->next == NULL) {
3131  // store the new "head" of the list
3132  temp = Multi_pxo_chat->next;
3133 
3134  // move the current head to the end of the list
3135  Multi_pxo_chat_add->next = Multi_pxo_chat;
3136  temp->prev = NULL;
3137  Multi_pxo_chat->prev = Multi_pxo_chat_add;
3138  Multi_pxo_chat->next = NULL;
3139 
3140  // reset the head of the list
3141  Multi_pxo_chat = temp;
3142 
3143  // set the new add line
3144  Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3145  memset(Multi_pxo_chat_add->text, 0, MAX_CHAT_LINE_LEN+1);
3146  Multi_pxo_chat_add->mode = CHAT_MODE_NORMAL;
3147  }
3148  // if we're not at the end of the list, just move up by one
3149  else {
3150  // set the new add line
3151  Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3152  }
3153 
3154  // if we've reached max chat lines, don't increment
3155  if(Multi_pxo_chat_count < MAX_CHAT_LINES) {
3156  Multi_pxo_chat_count++;
3157  }
3158 
3159  // set the count
3160  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
3161 
3163 }
3164 
3168 void multi_pxo_chat_process_incoming(const char *txt,int mode)
3169 {
3170  char msg_total[512],line[512];
3171  int n_lines,idx;
3172  int n_chars[20];
3173  const char *p_str[20]; // the initial line (unindented)
3174  const char *priv_ptr;
3175 
3176  // filter out "has left" channel messages, when switching channels
3177  if((SWITCHING_CHANNELS() || ((Multi_pxo_switch_delay != -1) && !timestamp_elapsed(Multi_pxo_switch_delay))) &&
3179  return;
3180  }
3181 
3182  // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3183  priv_ptr = multi_pxo_chat_is_private(txt);
3184  if(priv_ptr != NULL){
3185  strcpy_s(msg_total, priv_ptr);
3186  } else {
3187  strcpy_s(msg_total, txt);
3188  }
3189 
3190  // determine what mode to display this text in
3191 
3192  // if this is private chat
3193  if(priv_ptr != NULL){
3194  mode = CHAT_MODE_PRIVATE;
3195  }
3196  // all other chat
3197  else {
3198  // if this is a server message
3199  if(multi_pxo_is_server_text(txt)){
3200  mode = CHAT_MODE_SERVER;
3201  }
3202  // if this is a MOTD
3203  else if(multi_pxo_is_motd_text(txt)){
3205  return;
3206  }
3207  // if this is the end of motd text
3208  else if(multi_pxo_is_end_of_motd_text(txt)){
3210  return;
3211  }
3212  }
3213 
3214  // split the text up into as many lines as necessary
3215  n_lines = split_str(msg_total, Multi_pxo_chat_coords[gr_screen.res][2] - 5, n_chars, p_str, 3);
3216  Assert((n_lines != -1) && (n_lines <= 20));
3217  if((n_lines < 0) || (n_lines > 20)) {
3218  return;
3219  }
3220 
3221  // if the string fits on one line
3222  if(n_lines == 1) {
3223  multi_pxo_chat_add_line(msg_total,mode);
3224  }
3225  // if the string was split into multiple lines
3226  else {
3227  // add the first line
3228  memcpy(line,p_str[0],n_chars[0]);
3229  line[n_chars[0]] = '\0';
3230  multi_pxo_chat_add_line(line,mode);
3231 
3232  // copy the rest of the lines
3233  for(idx=1; idx<n_lines; idx++){
3234  memcpy(line,p_str[idx],n_chars[idx]);
3235  line[n_chars[idx]] = '\0';
3236 
3237  // unless the current mode is server or "switching channels", make all these CHAT_MODE_CARRY
3238  if((mode != CHAT_MODE_SERVER) && (mode != CHAT_MODE_CHANNEL_SWITCH)){
3239  mode = CHAT_MODE_CARRY;
3240  }
3241  multi_pxo_chat_add_line(line, mode);
3242  }
3243  }
3244 }
3245 
3250 {
3251  int y_start, line_height;
3252  int disp_count,token_width;
3253  char piece[100];
3254  char title[MAX_PXO_TEXT_LEN];
3255  char *tok;
3256  chat_line *moveup;
3257 
3258  // blit the title line
3259  memset(title,0,MAX_PXO_TEXT_LEN);
3260  if(ON_CHANNEL()){
3261  if(strlen(Multi_pxo_channel_current.name) > 1){
3262  sprintf(title, XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name+1); // [[ <who> on <channel> ]]
3263  } else {
3264  sprintf(title, XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name); // [[ <who> on <channel> ]]
3265  }
3266  } else {
3267  strcpy_s(title,XSTR("Parallax Online - No Channel", 956));
3268  }
3269  gr_force_fit_string(title, MAX_PXO_TEXT_LEN-1, Multi_pxo_chat_coords[gr_screen.res][2] - 10);
3270  gr_get_string_size(&token_width,NULL,title);
3272  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);
3273 
3274  // blit all active lines of text
3275  moveup = Multi_pxo_chat_start;
3276  disp_count = 0;
3277  y_start = Multi_pxo_chat_coords[gr_screen.res][1];
3278  line_height = gr_get_font_height() + 1;
3279  while((moveup != NULL) && (moveup != Multi_pxo_chat_add) && (disp_count < (Multi_pxo_max_chat_display[gr_screen.res]))){
3280  switch(moveup->mode){
3281  // if this is text from the server, display it all "bright"
3282  case CHAT_MODE_SERVER:
3284  gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text, GR_RESIZE_MENU);
3285  break;
3286 
3287  // if this is motd, display it all "bright"
3288  case CHAT_MODE_MOTD:
3290  gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text, GR_RESIZE_MENU);
3291  break;
3292 
3293  // normal mode, just highlight the server
3294  case CHAT_MODE_PRIVATE:
3295  case CHAT_MODE_NORMAL:
3296  strcpy_s(piece,moveup->text);
3297  tok = strtok(piece," ");
3298  if(tok != NULL){
3299  // get the width of just the first "piece"
3300  gr_get_string_size(&token_width, NULL, tok);
3301 
3302  // draw it brightly
3304  gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, tok, GR_RESIZE_MENU);
3305 
3306  // draw the rest of the string normally
3307  tok = strtok(NULL,"");
3308  if(tok != NULL){
3310  gr_string(Multi_pxo_chat_coords[gr_screen.res][0] + token_width + 6, y_start, tok, GR_RESIZE_MENU);
3311  }
3312  }
3313  break;
3314 
3315  // carry mode, display with no highlight
3316  case CHAT_MODE_CARRY:
3318  gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text, GR_RESIZE_MENU);
3319  break;
3320 
3321  // "switching channels mode", display it bright
3324  gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text, GR_RESIZE_MENU);
3325  break;
3326  }
3327 
3328  // next chat line
3329  moveup = moveup->next;
3330  disp_count++;
3331  y_start += line_height;
3332  }
3333 
3334  if ((moveup != Multi_pxo_chat_add) && (moveup != NULL)) {
3335  Can_scroll_down = 1;
3336  } else {
3337  Can_scroll_down = 0;
3338  }
3339 }
3340 
3345 {
3346  chat_line *backup;
3347  int idx;
3348 
3349  if (Multi_pxo_chat == NULL) {
3350  return;
3351  }
3352 
3353  // if we have less than the displayable amount of lines, do nothing
3354  if(Multi_pxo_chat_count <= Multi_pxo_max_chat_display[gr_screen.res]){
3355  Multi_pxo_chat_start = Multi_pxo_chat;
3356 
3357  // nothing to do for the slider
3358  Multi_pxo_chat_slider.set_numberItems(0);
3359  return;
3360  }
3361 
3362  if (!Can_scroll_down)
3363  {
3364  // otherwise move back the right # of items
3365  backup = Multi_pxo_chat_add;
3366  for(idx=0; idx<Multi_pxo_max_chat_display[gr_screen.res]; idx++){
3367  Assert(backup->prev != NULL);
3368  backup = backup->prev;
3369  }
3370 
3371  Multi_pxo_chat_start = backup;
3372 
3373  // fixup the start index
3375  }
3376 }
3377 
3382 {
3383  // if we're already at the top of the list, don't do anything
3384  if ((Multi_pxo_chat_start == NULL) || (Multi_pxo_chat_start == Multi_pxo_chat)) {
3386  return;
3387  }
3388 
3389  // otherwise move up one
3390  Multi_pxo_chat_start = Multi_pxo_chat_start->prev;
3391 
3393 
3395 }
3396 
3401 {
3402  chat_line *lookup;
3403  int count = 0;
3404 
3405  // see if its okay to scroll down
3406  lookup = Multi_pxo_chat_start;
3407  if (lookup == NULL) {
3408  return 0;
3409  }
3410  count = 0;
3411  while (lookup != Multi_pxo_chat_add) {
3412  lookup = lookup->next;
3413  count++;
3414  }
3415 
3416  // check if we can move down, return accordingly
3417  if (count > Multi_pxo_max_chat_display[gr_screen.res]) {
3418  return 1;
3419  } else {
3420  return 0;
3421  }
3422 }
3423 
3428 {
3429  // if we can move down
3430  if (multi_pxo_can_scroll_down()) {
3431  Multi_pxo_chat_start = Multi_pxo_chat_start->next;
3434  } else {
3436  }
3437 }
3438 
3443 {
3444  char *remainder;
3445  const char *result;
3446  char msg[512];
3447  int msg_pixel_width;
3448 
3449  // if the chat line is getting too long, fire off the message, putting the last
3450  // word on the next input line.
3451  memset(msg, 0, 512);
3452  Multi_pxo_chat_input.get_text(msg);
3453  remainder = "";
3454 
3455  // determine if the width of the string in pixels is > than the inputbox width -- if so,
3456  // then send the message
3457  gr_get_string_size(&msg_pixel_width, NULL, msg);
3458  if ( msg_pixel_width >= (Multi_pxo_input_coords[gr_screen.res][2])) {
3459  remainder = strrchr(msg, ' ');
3460  if ( remainder ) {
3461  *remainder = '\0';
3462  remainder++;
3463  } else {
3464  remainder = "";
3465  }
3466 
3467  // if we're connected to a channel, send the chat to the server
3468  if(ON_CHANNEL()){
3469  result = SendChatString(msg,1);
3470  if(result != NULL){
3472  }
3473 
3474  // display any remainder of text on the next line
3475  Multi_pxo_chat_input.set_text(remainder);
3476  } else {
3477  Multi_pxo_chat_input.set_text("");
3478  }
3479  } else if((Multi_pxo_chat_input.pressed() && (msg[0] != '\0')) || (strlen(msg) >= MAX_CHAT_LINE_LEN)) {
3480  // tack on the null terminator in the boundary case
3481  int x = strlen(msg);
3482  if(x >= MAX_CHAT_LINE_LEN){
3483  msg[MAX_CHAT_LINE_LEN-1] = '\0';
3484  }
3485 
3486  // ignore "/nick" commands
3487  if(multi_pxo_is_nick_command(msg)){
3488  Multi_pxo_chat_input.set_text("");
3489  return;
3490  }
3491 
3492  // send the chat to the server
3493  // if we're connected to a channel, send the chat to the server
3494  if(ON_CHANNEL()){
3495  result = SendChatString(msg,1);
3496  if(result != NULL){
3498  }
3499 
3500  // display any remainder of text on the next line
3501  Multi_pxo_chat_input.set_text(remainder);
3502  } else {
3503  Multi_pxo_chat_input.set_text("");
3504  }
3505  }
3506 }
3507 
3508 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3509 //XSTR:OFF
3510 
3511 // NOTE : DO NOT LOCALIZE THESE STRINGS!!!! THEY ARE CONSTANTS WHICH ARE CHECKED AGAINST
3512 // PXO CHAT SERVER DATA. THEY CANNOT CHANGE!!!
3513 #define PMSG_FROM "private message from "
3514 #define PMSG_TO "private message to "
3515 const char *multi_pxo_chat_is_private(const char *txt)
3516 {
3517  // quick check
3518  size_t from_len = strlen( PMSG_FROM );
3519  if( strlen(txt) > from_len ){
3520  // otherwise do a comparison
3521  if( !strnicmp(txt, PMSG_FROM, from_len) ){
3522  return &txt[from_len];
3523  }
3524  }
3525 
3526  // quick check
3527  size_t to_len = strlen( PMSG_TO );
3528  if( strlen(txt) > to_len ){
3529  // otherwise do a comparison
3530  if( !strnicmp(txt, PMSG_TO, to_len) ){
3531  return &txt[to_len];
3532  }
3533  }
3534 
3535  return NULL;
3536 }
3537 //XSTR:ON
3538 
3539 static const size_t pxo_prefix_len = strlen(MULTI_PXO_SERVER_PREFIX);
3540 
3544 int multi_pxo_is_server_text(const char *txt)
3545 {
3546  // if the message is prefaced by a ***
3547  if((strlen(txt) >= pxo_prefix_len) && !strncmp(txt, MULTI_PXO_SERVER_PREFIX, pxo_prefix_len)){
3548  return 1;
3549  }
3550 
3551  return 0;
3552 }
3553 
3554 static const size_t motd_prefix_len = strlen(PXO_CHAT_MOTD_PREFIX);
3555 
3559 int multi_pxo_is_motd_text(const char *txt)
3560 {
3561  // if we're not on a channel, and this is not a channel switching message assume its coming from a server
3562  if((strlen(txt) >= motd_prefix_len) && !strncmp(txt, PXO_CHAT_MOTD_PREFIX, motd_prefix_len)){
3563  return 1;
3564  }
3565 
3566  return 0;
3567 }
3568 
3569 static const size_t end_motd_prefix_len = strlen(PXO_CHAT_END_OF_MOTD_PREFIX);
3570 
3574 int multi_pxo_is_end_of_motd_text(const char *txt)
3575 {
3576  // if we're not on a channel, and this is not a channel switching message assume its coming from a server
3577  if((strlen(txt) >= end_motd_prefix_len) && !strncmp(txt, PXO_CHAT_END_OF_MOTD_PREFIX, end_motd_prefix_len)){
3578  return 1;
3579  }
3580 
3581  return 0;
3582 }
3583 
3588 {
3589  // if the text is not server text
3590  if(!multi_pxo_is_server_text(txt)){
3591  return 0;
3592  }
3593 
3594  // check to see if the last portion is the correct wording
3595  if((strlen(txt) > strlen(MULTI_PXO_HAS_LEFT)) && !strcmp(&txt[strlen(txt) - strlen(MULTI_PXO_HAS_LEFT)], MULTI_PXO_HAS_LEFT)){
3596  return 1;
3597  }
3598 
3599  // check the end of the line
3600  return 0;
3601 }
3602 
3607 {
3608  chat_line *moveup;
3609 
3610  // if we have no chat
3611  if (Multi_pxo_chat == NULL) {
3612  Multi_pxo_chat_start_index = -1;
3613  return;
3614  }
3615 
3616  // traverse
3617  Multi_pxo_chat_start_index = 0;
3618  moveup = Multi_pxo_chat;
3619  while((moveup != Multi_pxo_chat_start) && (moveup != NULL)){
3620  Multi_pxo_chat_start_index++;
3621  moveup = moveup->next;
3622  }
3623 
3624  // set the slider index
3625  Multi_pxo_chat_slider.force_currentItem(Multi_pxo_chat_start_index);
3626 }
3627 
3628 // motd stuff ---------------------------------------------------------
3629 
3634 {
3635  // zero the motd string
3636  strcpy_s(Pxo_motd, "");
3637 
3638  // haven't gotten it yet
3639  Pxo_motd_end = 0;
3640 
3641  // haven't read it yet either
3642  Pxo_motd_read = 0;
3643 }
3644 
3648 void multi_pxo_motd_add_text(const char *text)
3649 {
3650  int cur_len = strlen(Pxo_motd);
3651  int new_len;
3652 
3653  // sanity
3654  if(text == NULL){
3655  return;
3656  }
3657 
3658  // make sure its motd text
3660  if(!multi_pxo_is_motd_text(text)){
3661  return;
3662  }
3663 
3664  // if its a 0 line motd
3665  if(strlen(text) <= motd_prefix_len){
3666  return;
3667  }
3668 
3669  // add text to the motd
3670  new_len = strlen(text + motd_prefix_len) - 1;
3671  if((cur_len + new_len + 1) < MAX_PXO_MOTD_LEN){
3672  strcat_s(Pxo_motd, text + motd_prefix_len + 1);
3673  strcat_s(Pxo_motd, "\n");
3674  mprintf(("MOTD ADD : %s\n", Pxo_motd));
3675  }
3676 }
3677 
3682 {
3683  int blink = 1;
3684 
3685  Pxo_motd_end = 1;
3686  mprintf(("MOTD ALL : %s\n", Pxo_motd));
3687 
3688  Pxo_motd_read = 0;
3689 
3690  // do we have an old MOTD file laying around? If so, read it in and see if its the same
3691  uint old_chksum;
3692  uint new_chksum;
3693 
3694  // checksum the current motd
3695  new_chksum = cf_add_chksum_long(0, (ubyte*)Pxo_motd, strlen(Pxo_motd));
3696 
3697  // checksum the old motd if its lying around
3698  CFILE *in = cfopen("oldmotd.txt", "rb");
3699  if(in != NULL){
3700  // read the old checksum
3701  cfread(&old_chksum, sizeof(old_chksum), 1, in);
3702  cfclose(in);
3703 
3704  // same checksum? no blink
3705  if(new_chksum == old_chksum){
3706  blink = 0;
3707  }
3708  }
3709 
3710  // write out the motd for next time
3711  if(strlen(Pxo_motd)){
3712  CFILE *out = cfopen("oldmotd.txt", "wb", CFILE_NORMAL, CF_TYPE_DATA);
3713  if(out != NULL){
3714  // write all the text
3715  cfwrite(&new_chksum, sizeof(new_chksum), 1, out);
3716 
3717  // close the outfile
3718  cfclose(out);
3719  }
3720  }
3721 
3722  // set the blink stamp
3723  Pxo_motd_blink_stamp = -1;
3724  if(blink){
3725  Pxo_motd_blink_on = 0;
3726  if(!Pxo_motd_blinked_already){
3727  Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
3728  Pxo_motd_blink_on = 1;
3729  }
3730  }
3731 
3732  Pxo_motd_blinked_already = 1;
3733 }
3734 
3739 {
3740  // mark the motd as read
3741  Pxo_motd_read = 1;
3742 
3743  // simple popup, with a slider
3744  popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, Pxo_motd);
3745 }
3746 
3751 {
3752  // if we got the end of the motd, and he hasn't read it yet
3753  if(Pxo_motd_end && !Pxo_motd_read && (Pxo_motd_blink_stamp != -1)){
3754  // if the timestamp elapsed, flip the blink flag
3755  if(timestamp_elapsed(Pxo_motd_blink_stamp)){
3756  Pxo_motd_blink_on = !Pxo_motd_blink_on;
3757  Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
3758  }
3759 
3760  // draw properly
3761  if(Pxo_motd_blink_on){
3762  Multi_pxo_buttons[gr_screen.res][MULTI_PXO_MOTD].button.draw_forced(2);
3763  }
3764  }
3765 }
3766 
3767 
3768 // common dialog stuff ------------------------------------------------
3769 
3771 
3775 void multi_pxo_com_init(int input_len)
3776 {
3777  int idx;
3778 
3779  // create the interface window
3780  Multi_pxo_com_window.create(0, 0, gr_screen.max_w_unscaled,gr_screen.max_h_unscaled, 0);
3781  Multi_pxo_com_window.set_mask_bmap(Multi_pxo_com_mask_fname[gr_screen.res]);
3782 
3783  // create the interface buttons
3784  for(idx=0; idx<MULTI_PXO_COM_NUM_BUTTONS; idx++){
3785  // create the object
3786  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);
3787 
3788  // set the sound to play when highlighted
3790 
3791  // set the ani for the button
3792  Multi_pxo_com_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_com_buttons[gr_screen.res][idx].filename);
3793 
3794  // set the hotspot
3795  Multi_pxo_com_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_com_buttons[gr_screen.res][idx].hotspot);
3796  }
3797 
3798  // add xstrs
3799  for(idx=0; idx<MULTI_PXO_COM_NUM_TEXT; idx++){
3800  Multi_pxo_com_window.add_XSTR(&Multi_pxo_com_text[gr_screen.res][idx]);
3801  }
3802 
3803  // create the input box
3804  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);
3805  Multi_pxo_com_input.set_focus();
3806 
3807  // clear all text lines
3808  memset(Multi_pxo_com_bottom_text, 0, MAX_PXO_TEXT_LEN);
3809  memset(Multi_pxo_com_middle_text, 0, MAX_PXO_TEXT_LEN);
3810  memset(Multi_pxo_com_top_text, 0, MAX_PXO_TEXT_LEN);
3811 }
3812 
3817 {
3818  // destroy the UI_WINDOW
3819  Multi_pxo_com_window.destroy();
3820 }
3821 
3826 {
3827  // blit top, middle and bottom text if possible
3828  if(Multi_pxo_com_top_text[0] != '\0'){
3830  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);
3831  }
3832  if(Multi_pxo_com_middle_text[0] != '\0'){
3834  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);
3835  }
3836  if(Multi_pxo_com_bottom_text[0] != '\0'){
3838  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);
3839  }
3840 }
3841 
3845 void multi_pxo_com_set_top_text(const char *txt)
3846 {
3847  if((txt != NULL) && strlen(txt)){
3848  strcpy_s(Multi_pxo_com_top_text,txt);
3849  gr_force_fit_string(Multi_pxo_com_top_text, MAX_PXO_TEXT_LEN-1, Multi_pxo_com_input_coords[gr_screen.res][2]);
3850  }
3851 }
3852 
3856 void multi_pxo_com_set_middle_text(const char *txt)
3857 {
3858  if((txt != NULL) && strlen(txt)){
3859  strcpy_s(Multi_pxo_com_middle_text,txt);
3860  gr_force_fit_string(Multi_pxo_com_middle_text, MAX_PXO_TEXT_LEN-1, Multi_pxo_com_input_coords[gr_screen.res][2]);
3861  }
3862 }
3863 
3867 void multi_pxo_com_set_bottom_text(const char *txt)
3868 {
3869  if((txt != NULL) && strlen(txt)){
3870  strcpy_s(Multi_pxo_com_bottom_text,txt);
3871  gr_force_fit_string(Multi_pxo_com_bottom_text, MAX_PXO_TEXT_LEN-1, Multi_pxo_com_input_coords[gr_screen.res][2]);
3872  }
3873 }
3874 
3875 
3876 // private channel join stuff -----------------------------------------
3877 
3882 {
3884 
3885  // initialize the common dialog with the passed max input length
3887 
3888  // initialize the return code
3889  Multi_pxo_priv_return_code = -1;
3890 
3891  // mark us as running
3893 
3894  // set some text
3895  multi_pxo_com_set_middle_text(XSTR("Type the name of the channel to join/create",961));
3896 }
3897 
3902 {
3903  // close down the common dialog
3905 
3906  // mark us as not running any more
3908 }
3909 
3914 {
3915  int k;
3916 
3917  // if we're not already running, initialize stuff
3919  // intialize
3921 
3922  // return "still running"
3923  return 0;
3924  }
3925 
3926  k = Multi_pxo_com_window.process();
3927 
3928  // process keypresses
3929  switch(k){
3930  // like hitting the cancel button
3931  case KEY_ESC:
3932  Multi_pxo_priv_return_code = 0;
3933  break;
3934  }
3935 
3936  // process button presses
3938 
3939  // process the inputbox
3941 
3942  // blit the background
3943  multi_pxo_blit_all();
3944 
3945  // blit my stuff
3946  gr_reset_clip();
3947  gr_set_bitmap(Multi_pxo_com_bitmap);
3948  gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1], GR_RESIZE_MENU);
3949  Multi_pxo_com_window.draw();
3950 
3951  // blit all text lines, top, middle, bottoms
3953 
3954  gr_flip();
3955 
3956  // check the return code
3957  switch(Multi_pxo_priv_return_code){
3958  // still in progress
3959  case -1 :
3960  return 0;
3961 
3962  // user hit cancel
3963  case 0 :
3965  return -1;
3966 
3967  // user hit ok
3968  case 1 :
3970  return 1;
3971  }
3972 
3973  return 0;
3974 }
3975 
3980 {
3981  int idx;
3982 
3983  // check all buttons
3984  for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
3985  if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
3987  return;
3988  }
3989  }
3990 }
3991 
3996 {
3997  char priv_chan_name[128];
3998 
3999  switch(n){
4000  case MULTI_PXO_COM_CANCEL:
4001  Multi_pxo_priv_return_code = 0;
4002  break;
4003 
4004  case MULTI_PXO_COM_OK:
4005  Multi_pxo_com_input.get_text(priv_chan_name);
4006  multi_pxo_strip_space(priv_chan_name,priv_chan_name);
4007 
4008  // if its a 0 length string, interpret as a cancel
4009  if(strlen(priv_chan_name) <= 0){
4010  Multi_pxo_priv_return_code = 0;
4011  return;
4012  }
4013 
4014  Multi_pxo_priv_return_code = 1;
4015  break;
4016  }
4017 }
4018 
4023 {
4024  char priv_chan_name[128];
4025 
4026  // see if the user has pressed enter
4027  if(Multi_pxo_com_input.pressed()){
4028  Multi_pxo_com_input.get_text(priv_chan_name);
4029  multi_pxo_strip_space(priv_chan_name,priv_chan_name);
4030 
4031  // if its a 0 length string, interpret as a cancel
4032  if(strlen(priv_chan_name) <= 0){
4033  Multi_pxo_priv_return_code = 0;
4034  return;
4035  }
4036 
4037  // otherwise interpret as "accept"
4038  Multi_pxo_priv_return_code = 1;
4039 
4040  // add in the "+" which indicates a private room
4041  strcpy_s(Multi_pxo_priv_chan,"+");
4042  strcat_s(Multi_pxo_priv_chan, priv_chan_name);
4043  }
4044 }
4045 
4046 // find player stuff -----------------------------------------
4047 
4049 
4054 {
4056 
4057  // initialize the common dialog with the passed max input length
4059 
4060  // return code, set to something other than -1 if we're supposed to return
4061  Multi_pxo_find_return_code = -1;
4062 
4063  // mark us as running
4065 
4066  // not searching yet
4067  Multi_pxo_searching = 0;
4068 
4069  // set the top text
4070  multi_pxo_com_set_top_text(XSTR("Enter user to be found",962));
4071 
4072  // 0 length
4073  strcpy_s(Multi_pxo_find_channel,"");
4074 
4075  // 0 length
4076  strcpy_s(name_lookup,"");
4077 }
4078 
4083 {
4084  // close down the common dialog
4086 
4087  // mark us as not running any more
4089 }
4090 
4095 {
4096  int k;
4097 
4098  // if we're not already running, initialize stuff
4100  // intialize
4102 
4103  // return "still running"
4104  return 0;
4105  }
4106 
4107  k = Multi_pxo_com_window.process();
4108 
4109  // process keypresses
4110  switch(k){
4111  // like hitting the cancel button
4112  case KEY_ESC:
4113  Multi_pxo_find_return_code = 0;
4114  break;
4115  }
4116 
4117  // process button presses
4119 
4120  // process the inputbox
4122 
4123  // process search mode if applicable
4125 
4126  // blit the background
4127  multi_pxo_blit_all();
4128 
4129  // blit my stuff
4130  gr_reset_clip();
4131  gr_set_bitmap(Multi_pxo_com_bitmap);
4132  gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1], GR_RESIZE_MENU);
4133  Multi_pxo_com_window.draw();
4134 
4135  // blit any text lines
4137 
4138  gr_flip();
4139 
4140  // check the return code
4141  switch(Multi_pxo_find_return_code){
4142  // still in progress
4143  case -1 :
4144  return 0;
4145 
4146  // user hit cancel
4147  case 0 :
4148  // close the popup down
4150  return -1;
4151 
4152  // user hit ok
4153  case 1 :
4154  // close the popup down
4156 
4157  // if we have a channel, join it now if possible
4158  if(Multi_pxo_find_channel[0] != '\0'){
4159  pxo_channel *lookup;
4160  lookup = multi_pxo_find_channel(Multi_pxo_find_channel,Multi_pxo_channels);
4161 
4162  // if we couldn't find it, don't join
4163  if(lookup != NULL){
4164  multi_pxo_join_channel(lookup);
4165  }
4166  }
4167  return 1;
4168  }
4169 
4170  return 0;
4171 }
4172 
4177 {
4178  int idx;
4179 
4180  // check all buttons
4181  for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4182  if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4184  return;
4185  }
4186  }
4187 }
4188 
4193 {
4194  switch(n){
4195  case MULTI_PXO_COM_CANCEL:
4196  Multi_pxo_find_return_code = 0;
4197  break;
4198 
4199  case MULTI_PXO_COM_OK:
4200  Multi_pxo_find_return_code = 1;
4201  break;
4202  }
4203 }
4204 
4209 {
4210  // see if the user has pressed enter
4211  if(Multi_pxo_com_input.pressed()){
4212  // if we're not already in search mode
4213  if(!Multi_pxo_searching){
4214  // clear all text
4215  memset(Multi_pxo_com_middle_text,0,MAX_PXO_TEXT_LEN);
4216  memset(Multi_pxo_com_bottom_text,0,MAX_PXO_TEXT_LEN);
4217 
4218  Multi_pxo_com_input.get_text(name_lookup);
4219  multi_pxo_strip_space(name_lookup,name_lookup);
4220 
4221  // never search with a zero length string
4222  if(name_lookup[0] != '\0'){
4223  char search_text[512];
4224 
4225  // put us in search mode
4226  Multi_pxo_searching = 1;
4227 
4228  // look for the guy
4229  GetChannelByUser(name_lookup);
4230 
4231  // set the top text
4232  memset(search_text,0,512);
4233  sprintf(search_text,XSTR("Searching for %s",963),name_lookup);
4234  multi_pxo_com_set_top_text(search_text);
4235  }
4236  // clear everything
4237  else {
4238  memset(Multi_pxo_com_top_text,0,MAX_PXO_TEXT_LEN);
4239  }
4240  }
4241  }
4242 }
4243 
4248 {
4249  char *channel;
4250 
4251  // if we're not searching for anything, return
4252  if(!Multi_pxo_searching){
4253  return;
4254  }
4255 
4256  // otherwise check to see if we've found him
4257  channel = GetChannelByUser(NULL);
4258 
4259  // if we've got a result, let the user know
4260  if(channel){
4261  // if he couldn't be found
4262  if((ptr_s)channel == -1){
4263  multi_pxo_com_set_middle_text(XSTR("User not found",964));
4264  strcpy_s(Multi_pxo_find_channel,"");
4265  } else {
4266  if(channel[0] == '*'){
4267  multi_pxo_com_set_middle_text(XSTR("Player is logged in but is not on a channel",965));
4268  strcpy_s(Multi_pxo_find_channel,"");
4269  } else {
4270  char p_text[512];
4271  memset(p_text,0,512);
4272 
4273  // if this guy is on a public channel, display which one
4274  if(channel[0] == '#'){
4275  sprintf(p_text,XSTR("Found %s on :",966),name_lookup);
4276 
4277  // display the results
4279  multi_pxo_com_set_bottom_text(channel+1);
4280 
4281  // mark down the channel name so we know where to find him
4282  strcpy_s(Multi_pxo_find_channel,channel);
4283  // strip out trailing whitespace
4284  if(Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] == ' '){
4285  Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] = '\0';
4286  }
4287  }
4288  // if this is a private channel
4289  else if(channel[0] == '+'){
4290  sprintf(p_text,XSTR("Found %s on a private channel",967),name_lookup);
4292 
4293  strcpy_s(Multi_pxo_find_channel,"");
4294  }
4295  }
4296  }
4297 
4298  // unset search mode
4299  Multi_pxo_searching = 0;
4300 
4301  // clear the inputbox
4302  Multi_pxo_com_input.set_text("");
4303  }
4304 }
4305 
4306 
4307 // player info stuff -----------------------------------------
4308 
4313 {
4314  // process common stuff
4316 
4317  // run the networking functions for the PXO API
4319 
4320  // process depending on what mode we're in
4321  switch (Multi_pxo_retrieve_mode)
4322  {
4323  // we don't need to do anything extra here, just move on to mode 1
4324  case 0:
4325  {
4326  char *ret_string;
4327  char temp_string[MAX_PXO_TEXT_LEN];
4328  char *tok;
4329 
4330  // if the thing is non-null, do something
4331  ret_string = GetTrackerIdByUser(Multi_pxo_retrieve_name);
4332 
4333  if (ret_string != NULL) {
4334  // user not-online/not found
4335  if ( (int)ret_string[0] == -1) {
4336  return 1;
4337  }
4338 
4339  // user not a tracker pilot
4340  if ( !stricmp(ret_string,"-1") ) {
4341  return 1;
4342  }
4343 
4344  // otherwise parse into his id and callsign
4345  strcpy_s(temp_string, ret_string);
4346  tok = strtok(temp_string, " ");
4347 
4348  // get tracker id
4349  if (tok != NULL) {
4350  strcpy_s(Multi_pxo_retrieve_id, tok);
4351 
4352  // get the callsign
4353  tok = strtok(NULL, "");
4354 
4355  if (tok != NULL) {
4356  strcpy_s(Multi_pxo_retrieve_name, tok);
4357  }
4358  // failure
4359  else {
4360  return 1;
4361  }
4362  }
4363  // failure of some kind or another
4364  else {
4365  return 1;
4366  }
4367 
4368  Multi_pxo_retrieve_mode = 1;
4369 
4370  return 0;
4371  }
4372 
4373  break;
4374  }
4375 
4376  // initial call to get his stats
4377  case 1:
4378  {
4379  // change the popup text
4380  popup_change_text(XSTR("Getting player stats",968));
4381 
4382  switch ( fs2netd_get_pilot_info(Multi_pxo_retrieve_name, &Multi_pxo_pinfo_player, true) )
4383  {
4384  // there was some failure
4385  case -2:
4386  return 2;
4387 
4388  // still processing
4389  case -1:
4390  Multi_pxo_retrieve_mode = 2;
4391  break;
4392 
4393  // we got the data
4394  case 0:
4395  return 10;
4396  }
4397 
4398  break;
4399  }
4400 
4401  // busy retrieving his stats
4402  case 2:
4403  {
4404  switch ( fs2netd_get_pilot_info(Multi_pxo_retrieve_name, &Multi_pxo_pinfo_player, false) )
4405  {
4406  // there was some failure
4407  case -2:
4408  return 2;
4409 
4410  // still processing
4411  case -1:
4412  break;
4413 
4414  // we got the data
4415  case 0:
4416  return 10;
4417  }
4418 
4419  break;
4420  }
4421  }
4422 
4423  // return not done yet
4424  return 0;
4425 }
4426 
4430 int multi_pxo_pinfo_get(char *name)
4431 {
4432  // run the popup
4433  Multi_pxo_retrieve_mode = 0;
4434  strcpy_s(Multi_pxo_retrieve_name, name);
4435 
4436  switch ( popup_till_condition(multi_pxo_pinfo_cond, XSTR("&Cancel", 779), XSTR("Retrieving player tracker id", 969)) )
4437  {
4438  // success
4439  case 10 :
4440  return 1;
4441 
4442  // failed to get his tracker id
4443  case 1 :
4444  return 0;
4445 
4446  // failed to get his stats
4447  case 2 :
4448  return 0;
4449  }
4450 
4451  // we didn't get the stats
4452  return 0;
4453 }
4454 
4459 {
4460  // initialize the popup
4462 
4463  // run the popup
4464  do {
4466  } while ( !multi_pxo_pinfo_do() );
4467 
4468  // close down the popup
4470 }
4471 
4476 {
4478 
4479  // pilot name
4480  memset(Multi_pxo_pinfo_vals[0], 0, 50);
4481  strcpy_s(Multi_pxo_pinfo_vals[0], fs->callsign);
4482  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]));
4483 
4484  // rank
4485  memset(Multi_pxo_pinfo_vals[1], 0, 50);
4486  multi_sg_rank_build_name(Ranks[fs->stats.rank].name, Multi_pxo_pinfo_vals[1]);
4487  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]));
4488 
4489  // kills
4490  memset(Multi_pxo_pinfo_vals[2], 0, 50);
4491  sprintf(Multi_pxo_pinfo_vals[2], "%d", fs->stats.kill_count);
4492 
4493  // assists
4494  memset(Multi_pxo_pinfo_vals[3], 0, 50);
4495  sprintf(Multi_pxo_pinfo_vals[3], "%d", fs->stats.assists);
4496 
4497  // friendly kills
4498  memset(Multi_pxo_pinfo_vals[4], 0, 50);
4499  sprintf(Multi_pxo_pinfo_vals[4], "%d", fs->stats.kill_count - fs->stats.kill_count_ok);
4500 
4501  // missions flown
4502  memset(Multi_pxo_pinfo_vals[5], 0, 50);
4503  sprintf(Multi_pxo_pinfo_vals[5], "%d", (int)fs->stats.missions_flown);
4504 
4505  // flight time
4506  memset(Multi_pxo_pinfo_vals[6], 0, 50);
4507  game_format_time( fl2f((float)fs->stats.flight_time), Multi_pxo_pinfo_vals[6] );
4508 
4509  // last flown
4510  memset(Multi_pxo_pinfo_vals[7], 0, 50);
4511  if (fs->stats.last_flown == 0) {
4512  strcpy_s(Multi_pxo_pinfo_vals[7], XSTR("No missions flown", 970) );
4513  } else {
4514  time_t tmp_lf = fs->stats.last_flown; // don't cast a pointer to a type that can be either 32 or 64bit
4515  tm *tmr = gmtime( &tmp_lf );
4516 
4517  if (tmr != NULL)
4518  strftime(Multi_pxo_pinfo_vals[7], 30, "%m/%d/%y %H:%M", tmr);
4519  else
4520  strcpy_s(Multi_pxo_pinfo_vals[7], "");
4521  }
4522 
4523  // primary shots fired
4524  memset(Multi_pxo_pinfo_vals[8], 0, 50);
4525  sprintf(Multi_pxo_pinfo_vals[8], "%d", (int)fs->stats.p_shots_fired);
4526 
4527  // primary shots hit
4528  memset(Multi_pxo_pinfo_vals[9],0,50);
4529  sprintf(Multi_pxo_pinfo_vals[9], "%d", (int)fs->stats.p_shots_hit);
4530 
4531  // primary hit pct
4532  memset(Multi_pxo_pinfo_vals[10], 0, 50);
4533  if (fs->stats.p_shots_fired > 0) {
4534  sprintf(Multi_pxo_pinfo_vals[10], "%d%%", (int)((float)fs->stats.p_shots_hit / (float)fs->stats.p_shots_fired * 100.0f));
4535  } else {
4536  strcpy_s(Multi_pxo_pinfo_vals[10], "0%");
4537  }
4538 
4539  // secondary shots fired
4540  memset(Multi_pxo_pinfo_vals[11], 0, 50);
4541  sprintf(Multi_pxo_pinfo_vals[11], "%d", (int)fs->stats.s_shots_fired);
4542 
4543  // secondary shots hit
4544  memset(Multi_pxo_pinfo_vals[12], 0, 50);
4545  sprintf(Multi_pxo_pinfo_vals[12], "%d", (int)fs->stats.s_shots_hit);
4546 
4547  // secondary hit pct
4548  memset(Multi_pxo_pinfo_vals[13], 0, 50);
4549  if (fs->stats.s_shots_fired > 0) {
4550  sprintf(Multi_pxo_pinfo_vals[13], "%d%%", (int)((float)fs->stats.s_shots_hit / (float)fs->stats.s_shots_fired * 100.0f));
4551  } else {
4552  strcpy_s(Multi_pxo_pinfo_vals[13], "0%");
4553  }
4554 
4555  // primary friendly hits
4556  memset(Multi_pxo_pinfo_vals[14], 0, 50);
4557  sprintf(Multi_pxo_pinfo_vals[14], "%u", fs->stats.p_bonehead_hits);
4558 
4559  // primary friendly hit %
4560  memset(Multi_pxo_pinfo_vals[15], 0, 50);
4561  if (fs->stats.p_shots_fired > 0) {
4562  sprintf(Multi_pxo_pinfo_vals[15], "%d%%", (int)((float)100.0f*((float)fs->stats.p_bonehead_hits/(float)fs->stats.p_shots_fired)));
4563  } else {
4564  strcpy_s(Multi_pxo_pinfo_vals[15], "0%");
4565  }
4566 
4567  // secondary friendly hits
4568  memset(Multi_pxo_pinfo_vals[16], 0, 50);
4569  sprintf(Multi_pxo_pinfo_vals[16], "%u", fs->stats.s_bonehead_hits);
4570 
4571  // secondary friendly hit %
4572  memset(Multi_pxo_pinfo_vals[17], 0, 50);
4573  if (fs->stats.s_shots_fired > 0) {
4574  sprintf(Multi_pxo_pinfo_vals[17], "%d%%", (int)((float)100.0f*((float)fs->stats.s_bonehead_hits/(float)fs->stats.s_shots_fired)));
4575  } else {
4576  strcpy_s(Multi_pxo_pinfo_vals[17], "0%");
4577  }
4578 }
4579 
4584 {
4585  int idx;
4586 
4587  // create the interface window
4588  Multi_pxo_pinfo_window.create(0,0,gr_screen.max_w_unscaled,gr_screen.max_h_unscaled,0);
4589  Multi_pxo_pinfo_window.set_mask_bmap(Multi_pxo_pinfo_mask_fname[gr_screen.res]);
4590 
4591  Multi_pxo_pinfo_bitmap = bm_load(Multi_pxo_pinfo_fname[gr_screen.res]);
4592  Assert(Multi_pxo_pinfo_bitmap != -1);
4593 
4594  // create the interface buttons
4595  for(idx=0; idx<MULTI_PXO_PINFO_NUM_BUTTONS; idx++){
4596  // create the object
4597  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);
4598 
4599  // set the sound to play when highlighted
4601 
4602  // set the ani for the button
4603  Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_pinfo_buttons[gr_screen.res][idx].filename);
4604 
4605  // set the hotspot
4606  Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_pinfo_buttons[gr_screen.res][idx].hotspot);
4607  }
4608 
4609  // add xstrs
4610  for(idx=0; idx<MULTI_PXO_PINFO_NUM_TEXT; idx++){
4611  Multi_pxo_pinfo_window.add_XSTR(&Multi_pxo_pinfo_text[gr_screen.res][idx]);
4612  }
4613 
4614  // set up the stats labels
4615  Multi_pxo_pinfo_stats_labels[0] = vm_strdup(XSTR("Name", 1532));
4616  Multi_pxo_pinfo_stats_labels[1] = vm_strdup(XSTR("Rank", 1533));
4617  Multi_pxo_pinfo_stats_labels[2] = vm_strdup(XSTR("Kills", 1534));
4618  Multi_pxo_pinfo_stats_labels[3] = vm_strdup(XSTR("Assists", 1535));
4619  Multi_pxo_pinfo_stats_labels[4] = vm_strdup(XSTR("Friendly kills", 1536));
4620  Multi_pxo_pinfo_stats_labels[5] = vm_strdup(XSTR("Missions flown", 1537));
4621  Multi_pxo_pinfo_stats_labels[6] = vm_strdup(XSTR("Flight time", 1538));
4622  Multi_pxo_pinfo_stats_labels[7] = vm_strdup(XSTR("Last flown", 1539));
4623  Multi_pxo_pinfo_stats_labels[8] = vm_strdup(XSTR("Primary shots fired", 1540));
4624  Multi_pxo_pinfo_stats_labels[9] = vm_strdup(XSTR("Primary shots hit", 1541));
4625  Multi_pxo_pinfo_stats_labels[10] = vm_strdup(XSTR("Primary hit %", 1542));
4626  Multi_pxo_pinfo_stats_labels[11] = vm_strdup(XSTR("Secondary shots fired", 1543));
4627  Multi_pxo_pinfo_stats_labels[12] = vm_strdup(XSTR("Secondary shots hit", 1544));
4628  Multi_pxo_pinfo_stats_labels[13] = vm_strdup(XSTR("Secondary hit %", 1545));
4629  Multi_pxo_pinfo_stats_labels[14] = vm_strdup(XSTR("Primary friendly hits", 1546));
4630  Multi_pxo_pinfo_stats_labels[15] = vm_strdup(XSTR("Primary friendly hit %", 1547));
4631  Multi_pxo_pinfo_stats_labels[16] = vm_strdup(XSTR("Secondary friendly hits", 1548));
4632  Multi_pxo_pinfo_stats_labels[17] = vm_strdup(XSTR("Secondary friendly hit %", 1549));
4633 
4634  // build the stats labels values
4636 }
4637 
4642 {
4643  int k = Multi_pxo_pinfo_window.process();
4644 
4645  // process common stuff
4647 
4648  // run the networking functions for the PXO API
4650 
4651  // check to see if he pressed escp
4652  if(k == KEY_ESC){
4653  return 1;
4654  }
4655 
4656  // if he pressed the ok button
4657  if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_OK].button.pressed()){
4658  return 1;
4659  }
4660 
4661  // if he pressed the medals buttons, run the medals screen
4662  if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_MEDALS].button.pressed()){
4664  }
4665 
4666  // draw stuff
4667 
4668  // blit everything on the "normal" screen
4670 
4671  // blit our own stuff
4672  gr_reset_clip();
4673  gr_set_bitmap(Multi_pxo_pinfo_bitmap);
4674  gr_bitmap(0, 0, GR_RESIZE_MENU);
4675  Multi_pxo_pinfo_window.draw();
4676 
4677  // blit the stats themselves
4679 
4680  // flip the page
4681  gr_flip();
4682 
4683  // not done yet
4684  return 0;
4685 }
4686 
4691 {
4692  int i;
4693 
4694  // destroy the UI_WINDOW
4695  Multi_pxo_pinfo_window.destroy();
4696 
4697  // unload the bitmap
4698  if(Multi_pxo_pinfo_bitmap != -1){
4699  bm_unload(Multi_pxo_pinfo_bitmap);
4700  }
4701 
4702  // free the stats labels strings
4703  for (i=0; i<MULTI_PXO_PINFO_NUM_LABELS; i++) {
4704  vm_free(Multi_pxo_pinfo_stats_labels[i]);
4705  }
4706 }
4707 
4712 {
4713  int idx;
4714  int y_start;
4715 
4716  // blit all the labels
4717  y_start = Multi_pxo_pinfo_coords[gr_screen.res][1];
4718  for(idx=0; idx<MULTI_PXO_PINFO_NUM_LABELS; idx++){
4719  // blit the label
4721  gr_string(Multi_pxo_pinfo_coords[gr_screen.res][0], y_start, Multi_pxo_pinfo_stats_labels[idx], GR_RESIZE_MENU);
4722 
4723  // blit the label's value
4725  gr_string(Multi_pxo_pinfo_val_x[gr_screen.res], y_start, Multi_pxo_pinfo_vals[idx], GR_RESIZE_MENU);
4726 
4727  // spacing
4728  y_start += Multi_pxo_pinfo_stats_spacing[idx];
4729  }
4730 }
4731 
4736 {
4737  int ret_code;
4738 
4739  // process common stuff
4741 
4742  // run the networking functions for the PXO API
4744 
4745  // initialize the medals screen
4746  medal_main_init(&Multi_pxo_pinfo_player, MM_POPUP);
4747 
4748  // run the medals screen until it says that it should be closed
4749  do {
4750  // set frametime and run common functions
4751  game_set_frametime(-1);
4753 
4754  // run the medals screen
4755  ret_code = medal_main_do();
4756  } while(ret_code);
4757 
4758  // close the medals screen down
4759  medal_main_close();
4760 
4761  // reset the palette
4763 }
4764 
4765 
4766 // notify stuff stuff -----------------------------------------
4767 
4771 void multi_pxo_notify_add(const char *txt)
4772 {
4773  // copy the text
4774  strcpy_s(Multi_pxo_notify_text, txt);
4775 
4776  // set the timestamp
4777  Multi_pxo_notify_stamp = timestamp(MULTI_PXO_NOTIFY_TIME);
4778 }
4779 
4784 {
4785  int w;
4786 
4787  // if the timestamp is -1, do nothing
4788  if(Multi_pxo_notify_stamp == -1){
4789  return;
4790  }
4791 
4792  // if it has expired, do nothing
4793  if(timestamp_elapsed(Multi_pxo_notify_stamp)){
4794  Multi_pxo_notify_stamp = -1;
4795  }
4796 
4797  // otherwise blit the text
4799  gr_get_string_size(&w,NULL,Multi_pxo_notify_text);
4800  gr_string((gr_screen.max_w_unscaled - w)/2,MULTI_PXO_NOTIFY_Y,Multi_pxo_notify_text,GR_RESIZE_MENU);
4801 }
4802 
4803 
4808 {
4809  int idx;
4810 
4811  // load the background bitmap
4812  Multi_pxo_help_bitmap = bm_load(Multi_pxo_help_fname[gr_screen.res]);
4813  if(Multi_pxo_help_bitmap < 0){
4814  // we failed to load the bitmap - this is very bad
4815  Int3();
4816  }
4817 
4818  // create the interface window
4819  Multi_pxo_help_window.create(0,0,gr_screen.max_w_unscaled,gr_screen.max_h_unscaled,0);
4820  Multi_pxo_help_window.set_mask_bmap(Multi_pxo_help_mask_fname[gr_screen.res]);
4821 
4822  // create the interface buttons
4823  for(idx=0; idx<MULTI_PXO_HELP_NUM_BUTTONS; idx++){
4824  // create the object
4825  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);
4826 
4827  // set the sound to play when highlighted
4829 
4830  // set the ani for the button
4831  Multi_pxo_help_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_help_buttons[gr_screen.res][idx].filename);
4832 
4833  // set the hotspot
4834  Multi_pxo_help_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_help_buttons[gr_screen.res][idx].hotspot);
4835  }
4836 
4837  // add xstrs
4838  for(idx=0; idx<MULTI_PXO_HELP_NUM_TEXT; idx++){
4839  Multi_pxo_help_window.add_XSTR(&Multi_pxo_help_text[gr_screen.res][idx]);
4840  }
4841 
4843 
4844  // set the current page to 0
4845  Multi_pxo_help_cur = 0;
4846 }
4847 
4852 {
4853  // run api stuff
4854  if(Multi_pxo_connected){
4856  }
4857 
4858  // process common stuff
4860 
4861  int k = Multi_pxo_help_window.process();
4862 
4863  // process any keypresses
4864  switch(k){
4865  case KEY_ESC:
4868  break;
4869  }
4870 
4871  // process button presses
4873 
4874  // draw the background, etc
4875  gr_reset_clip();
4876  GR_MAYBE_CLEAR_RES(Multi_pxo_help_bitmap);
4877  if(Multi_pxo_help_bitmap != -1){
4878  gr_set_bitmap(Multi_pxo_help_bitmap);
4880  }
4881  Multi_pxo_help_window.draw();
4882 
4883  // blit the current page
4885 
4886  // page flip
4887  gr_flip();
4888 }
4889 
4894 {
4895  int idx, idx2;
4896 
4897  // unload any bitmaps
4898  bm_unload(Multi_pxo_help_bitmap);
4899 
4900  // destroy the UI_WINDOW
4901  Multi_pxo_help_window.destroy();
4902 
4903  // free all pages
4904  for(idx=0; idx<Multi_pxo_help_num_pages; idx++){
4905  for(idx2=0; idx2<Multi_pxo_help_pages[idx].num_lines; idx2++){
4906  // maybe free
4907  if(Multi_pxo_help_pages[idx].text[idx2] != NULL){
4908  vm_free(Multi_pxo_help_pages[idx].text[idx2]);
4909  Multi_pxo_help_pages[idx].text[idx2] = NULL;
4910  }
4911  }
4912  }
4913 }
4914 
4919 {
4920  CFILE *in;
4921  help_page *cp;
4922 
4923  // read in the text file
4924  in = NULL;
4926  Assert(in != NULL);
4927  if(in == NULL){
4928  return;
4929  }
4930 
4931  Multi_pxo_help_num_pages = 0;
4932 
4933  // blast all the help pages clear
4934  memset(Multi_pxo_help_pages, 0, sizeof(help_page) * MULTI_PXO_MAX_PAGES);
4935  Multi_pxo_help_num_pages = 0;
4936  cp = &Multi_pxo_help_pages[0];
4937 
4938  while(!cfeof(in)){
4939  // malloc the line
4940  cp->text[cp->num_lines] = (char*)vm_malloc(Multi_pxo_chars_per_line[gr_screen.res]);
4941  if(cp->text[cp->num_lines] == NULL){
4942  break;
4943  }
4944 
4945  // read in the next line
4946  cfgets(cp->text[cp->num_lines++], Multi_pxo_chars_per_line[gr_screen.res], in);
4947 
4948  // skip to the next page if necessary
4949  if(cp->num_lines == Multi_pxo_lines_pp[gr_screen.res]){
4950  Multi_pxo_help_num_pages++;
4951  Assert(Multi_pxo_help_num_pages < MULTI_PXO_MAX_PAGES);
4952  if(Multi_pxo_help_num_pages >= MULTI_PXO_MAX_PAGES){
4953  Multi_pxo_help_num_pages--;
4954  break;
4955  }
4956  cp = &Multi_pxo_help_pages[Multi_pxo_help_num_pages];
4957  }
4958  }
4959 
4960  // close the file
4961  cfclose(in);
4962 }
4963 
4968 {
4969  int idx;
4970  int start_pos;
4971  int y_start, line_height;
4972  help_page *cp = &Multi_pxo_help_pages[Multi_pxo_help_cur];
4973 
4974  // blit each line
4975  y_start = Multi_pxo_help_coords[gr_screen.res][1];
4976  line_height = gr_get_font_height() + 1;
4977  for(idx=0;idx<cp->num_lines;idx++){
4978  // if the first symbol is "@", highlight the line
4979  if(cp->text[idx][0] == '@'){
4981  start_pos = 1;
4982  } else {
4984  start_pos = 0;
4985  }
4986 
4987  // blit the line
4988  gr_string(Multi_pxo_help_coords[gr_screen.res][0], y_start, cp->text[idx] + start_pos, GR_RESIZE_MENU);
4989 
4990  // increment the y location
4991  y_start += line_height;
4992  }
4993 }
4994 
4999 {
5000  int idx;
5001 
5002  // process all buttons
5003  for(idx=0;idx<MULTI_PXO_HELP_NUM_BUTTONS;idx++){
5004  if(Multi_pxo_help_buttons[gr_screen.res][idx].button.pressed()){
5006  return;
5007  }
5008  }
5009 }
5010 
5015 {
5016  switch(n){
5017  case MULTI_PXO_HELP_PREV:
5018  // if we're already at page 0, do nothing
5019  if(Multi_pxo_help_cur == 0){
5021  } else {
5022  Multi_pxo_help_cur--;
5024  }
5025  break;
5026 
5027  case MULTI_PXO_HELP_NEXT:
5028  // if we're already at max pages, do nothing
5029  if(Multi_pxo_help_cur == Multi_pxo_help_num_pages){
5031  } else {
5032  Multi_pxo_help_cur++;
5034  }
5035  break;
5036 
5040  break;
5041  }
5042 }
5043 
5044 // http banner stuff ---------------------------------------------
5045 
5050 {
5051  // zero the active banner bitmap
5052  Multi_pxo_banner.ban_bitmap = -1;
5053 
5054  // are we doing banners at all?
5055  if ( os_config_read_uint(NULL, "PXOBanners", 1) ) {
5056  // if we're already in idle mode, we're done downloading for this instance of freespace. pick a random image we already have
5057  if(Multi_pxo_ban_mode == PXO_BAN_MODE_IDLE){
5058  Multi_pxo_ban_mode = PXO_BAN_MODE_CHOOSE_RANDOM;
5059  return;
5060  }
5061 
5062  // set ourselves to startup mode
5063  Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
5064  Multi_pxo_ban_get = NULL;
5065  } else {
5066  // set ourselves to idle mode
5067  Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5068  Multi_pxo_ban_get = NULL;
5069  }
5070 
5071  // zero the active banner bitmap
5072  Multi_pxo_banner.ban_bitmap = -1;
5073  strcpy_s(Multi_pxo_banner.ban_file, "");
5074  strcpy_s(Multi_pxo_banner.ban_file_url, "");
5075  strcpy_s(Multi_pxo_banner.ban_url, "");
5076 }
5077 
5082 {
5083  char url_string[512] = "";
5084  char local_file[512] = "";
5085 
5086  // process stuff
5087  switch(Multi_pxo_ban_mode){
5088  // start downloading list
5090  // remote file
5091  sprintf(url_string, "http://www.pxo.net/files/%s", PXO_BANNERS_CONFIG_FILE);
5092 
5093  // local file
5094  cf_create_default_path_string(local_file, sizeof(local_file) - 1, CF_TYPE_MULTI_CACHE, PXO_BANNERS_CONFIG_FILE);
5095 
5096  // try creating the file get object
5097  Multi_pxo_ban_get = NULL;
5098 
5099  // bad
5100  if (Multi_pxo_ban_get == NULL) {
5101  Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5102  break;
5103  }
5104 
5105  // go to the downloading list mode
5106  Multi_pxo_ban_mode = PXO_BAN_MODE_LIST;
5107  break;
5108 
5109  // downloading list
5110  case PXO_BAN_MODE_LIST:
5111  // error
5112  if ( Multi_pxo_ban_get->IsFileError() ) {
5113  delete Multi_pxo_ban_get;
5114  Multi_pxo_ban_get = NULL;
5115  Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5116  break;
5117  }
5118 
5119  // connecting, receiving
5120  if ( Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving() )
5121  break;
5122 
5123  // done!
5124  if ( Multi_pxo_ban_get->IsFileReceived() ) {
5125  delete Multi_pxo_ban_get;
5126  Multi_pxo_ban_get = NULL;
5127  Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_STARTUP;
5128  }
5129  break;
5130 
5131  // start downloading files
5133  // first thing - parse the banners file and pick a file
5135 
5136  // if we have no active file, we're done
5137  if ( (strlen(Multi_pxo_banner.ban_file) <= 0) || (strlen(Multi_pxo_banner.ban_file_url) <= 0) ) {
5138  Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5139  break;
5140  }
5141 
5142  // if the file already exists, we're done
5143  if ( cf_exists(Multi_pxo_banner.ban_file, CF_TYPE_MULTI_CACHE) ) {
5144  Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5145  break;
5146  }
5147 
5148  // otherwise try and download it
5149  cf_create_default_path_string(local_file, sizeof(local_file) - 1, CF_TYPE_MULTI_CACHE, Multi_pxo_banner.ban_file);
5150 
5151  // try creating the file get object
5152  Multi_pxo_ban_get = NULL;
5153 
5154  // bad
5155  if (Multi_pxo_ban_get == NULL) {
5156  Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5157  break;
5158  }
5159 
5160  // go to the downloading images mode
5161  Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES;
5162  break;
5163 
5164  // downloading files
5165  case PXO_BAN_MODE_IMAGES:
5166  // error
5167  if ( Multi_pxo_ban_get->IsFileError() ) {
5168  delete Multi_pxo_ban_get;
5169  Multi_pxo_ban_get = NULL;
5170  Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5171  break;
5172  }
5173 
5174  // connecting, receiving
5175  if ( Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving() )
5176  break;
5177 
5178  // done!
5179  if ( Multi_pxo_ban_get->IsFileReceived() ) {
5180  delete Multi_pxo_ban_get;
5181  Multi_pxo_ban_get = NULL;
5182  Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5183  }
5184  break;
5185 
5186  // done downloading - maybe load an image
5188  // make sure we have a valid filename
5189  if (Multi_pxo_banner.ban_file[0] != '\0')
5190  Multi_pxo_banner.ban_bitmap = bm_load(Multi_pxo_banner.ban_file);
5191 
5192  // now we're idle
5193  Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5194  break;
5195 
5196  // idle (done with EVERYTHING)
5197  case PXO_BAN_MODE_IDLE:
5198  // if the banner button was clicked
5199  if ( Multi_pxo_ban_button.pressed() ) {
5201  }
5202  break;
5203 
5205  // first thing - parse the banners file and pick a file
5207  Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5208  break;
5209  }
5210 }
5211 
5216 {
5217  // if we have a currently active transfer
5218  if(Multi_pxo_ban_get != NULL){
5219  Multi_pxo_ban_get->AbortGet();
5220  delete Multi_pxo_ban_get;
5221  Multi_pxo_ban_get = NULL;
5222  }
5223 
5224  // if we have a loaded bitmap, unload it
5225  if(Multi_pxo_banner.ban_bitmap != -1){
5226  bm_unload(Multi_pxo_banner.ban_bitmap);
5227  Multi_pxo_banner.ban_bitmap = -1;
5228  }
5229 }
5230 
5234 void multi_pxo_ban_parse_banner_file(int choose_existing)
5235 {
5236  char file_url[512] = "";
5237  char banners[10][512];
5238  char urls[10][512];
5239  int exists[10];
5240  int exist_count;
5241  int num_banners, idx;
5243 
5244  Multi_pxo_banner.ban_bitmap = -1;
5245  strcpy_s(Multi_pxo_banner.ban_file, "");
5246  strcpy_s(Multi_pxo_banner.ban_file_url, "");
5247  strcpy_s(Multi_pxo_banner.ban_url, "");
5248 
5249  // bad
5250  if(in == NULL){
5251  return;
5252  }
5253 
5254  // clear all strings
5255  for(idx=0; idx<10; idx++){
5256  strcpy_s(banners[idx], "");
5257  strcpy_s(urls[idx], "");
5258  }
5259 
5260  // get the global banner url
5261  if(cfgets(file_url, 254, in) == NULL){
5262  cfclose(in);
5264  return;
5265  }
5266  drop_leading_white_space(file_url);
5267  drop_trailing_white_space(file_url);
5268 
5269  // otherwise read in
5270  num_banners = 0;
5271  while(num_banners < 10){
5272  // try and get the pcx
5273  if(cfgets(banners[num_banners], 254, in) == NULL){
5274  break;
5275  }
5276  // try and get the url
5277  if(cfgets(urls[num_banners], 254, in) == NULL){
5278  break;
5279  }
5280 
5281  // strip off trailing and leading whitespace
5282  drop_leading_white_space(banners[num_banners]);
5283  drop_trailing_white_space(banners[num_banners]);
5284  drop_leading_white_space(urls[num_banners]);
5285  drop_trailing_white_space(urls[num_banners]);
5286 
5287  // got one
5288  num_banners++;
5289  }
5290 
5291  // close the file
5292  cfclose(in);
5293 
5294  // no banners
5295  if(num_banners <= 0){
5296  return;
5297  }
5298 
5299  // if we're only selecting files which already exist (previously downloaded)
5300  if(choose_existing){
5301  // non exist
5302  for(idx=0; idx<10; idx++){
5303  exists[idx] = 0;
5304  }
5305 
5306  // build a list of existing files
5307  exist_count = 0;
5308  for (idx = 0; idx < num_banners; idx++) {
5309  if ( cf_exists(banners[idx], CF_TYPE_MULTI_CACHE) ) {
5310  exists[idx] = 1;
5311  exist_count++;
5312  }
5313  }
5314 
5315  // bogus
5316  if(exist_count <= 0){
5317  return;
5318  }
5319 
5320  // select one
5321  int select = (int)frand_range(0.0f, (float)exist_count);
5322  if(select >= exist_count){
5323  select = exist_count - 1;
5324  }
5325  if(select < 0){
5326  select = 0;
5327  }
5328  for(idx=0; idx<exist_count; idx++){
5329  if(select == 0){
5330  break;
5331  }
5332  if(exists[idx]){
5333  select--;
5334  }
5335  }
5336 
5337  // valid?
5338  if(idx < exist_count){
5339  // base filename
5340  strncpy(Multi_pxo_banner.ban_file, banners[idx], MAX_FILENAME_LEN);
5341 
5342  // get the full file url
5343  strncpy(Multi_pxo_banner.ban_file_url, file_url, MULTI_OPTIONS_STRING_LEN);
5344  strncat(Multi_pxo_banner.ban_file_url, banners[idx], MULTI_OPTIONS_STRING_LEN);
5345 
5346  // url of where to go to when clicked
5347  strncpy(Multi_pxo_banner.ban_url, urls[idx], MULTI_OPTIONS_STRING_LEN);
5348  }
5349  }
5350  // randomly pick a file for download
5351  else {
5352  idx = (int)frand_range(0.0f, (float)num_banners);
5353 
5354  if(idx >= num_banners){
5355  idx = num_banners - 1;
5356  }
5357  if(idx < 0){
5358  idx = 0;
5359  }
5360 
5361  // base filename
5362  strncpy(Multi_pxo_banner.ban_file, banners[idx], MAX_FILENAME_LEN);
5363 
5364  // get the full file url
5365  strncpy(Multi_pxo_banner.ban_file_url, file_url, MULTI_OPTIONS_STRING_LEN);
5366  strncat(Multi_pxo_banner.ban_file_url, banners[idx], MULTI_OPTIONS_STRING_LEN);
5367 
5368  // url of where to go to when clicked
5369  strncpy(Multi_pxo_banner.ban_url, urls[idx], MULTI_OPTIONS_STRING_LEN);
5370  }
5371 }
5372 
5377 {
5378  // if we have a valid bitmap
5379  if(Multi_pxo_banner.ban_bitmap >= 0){
5380  // if the mouse is over the banner button, highlight with a rectangle
5381  if(Multi_pxo_ban_button.is_mouse_on()){
5383  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);
5384  }
5385 
5386  // draw the bitmap itself
5387  gr_set_bitmap(Multi_pxo_banner.ban_bitmap);
5388  gr_bitmap(Pxo_ban_coords[gr_screen.res][0], Pxo_ban_coords[gr_screen.res][1], GR_RESIZE_MENU);
5389  }
5390 }
5391 
5396 {
5397  // if we have a valid bitmap and URL, launch the URL
5398  if((Multi_pxo_banner.ban_bitmap >= 0) && (Multi_pxo_banner.ban_url[0] != '\0')){
5399  multi_pxo_url(Multi_pxo_banner.ban_url);
5400  }
5401 }
void gr_rect(int x, int y, int w, int h, int resize_mode)
Definition: 2d.cpp:2068
player Multi_pxo_pinfo_player
Definition: multi_pxo.cpp:853
#define PXO_BAN_MODE_CHOOSE_RANDOM
Definition: multi_pxo.cpp:1040
GLuint64EXT * result
Definition: Glext.h:10775
#define MULTI_PXO_TEXT_DOWN
Definition: multi_pxo.cpp:68
void set_text(const char *in)
Definition: inputbox.cpp:490
int Multi_pxo_pinfo_coords[GR_NUM_RESOLUTIONS][4]
Definition: multi_pxo.cpp:863
#define MAX_FILENAME_LEN
Definition: pstypes.h:324
void set_highlight_action(void(*_user_function)(void))
Definition: button.cpp:375
void multi_pxo_notify_add(const char *txt)
Definition: multi_pxo.cpp:4771
int timestamp(int delta_ms)
Definition: timer.cpp:226
void medal_main_close()
Definition: medals.cpp:736
pxo_channel Multi_pxo_channel_current
Definition: multi_pxo.cpp:338
pxo_channel * multi_pxo_add_channel(char *name, pxo_channel **list)
Definition: multi_pxo.cpp:2343
#define CFILE_NORMAL
Definition: cfile.h:89
int gameseq_get_previous_state()
float Multi_pxo_channel_server_refresh
Definition: multi_pxo.cpp:317
#define UI_INPUTBOX_FLAG_EAT_USED
Definition: ui.h:40
int i
Definition: multi_pxo.cpp:466
#define vm_free(ptr)
Definition: pstypes.h:548
InetGetFile * Multi_pxo_ban_get
Definition: multi_pxo.cpp:1018
int kill_count_ok
Definition: scoring.h:90
struct chat_line chat_line
uint os_config_read_uint(const char *section, const char *name, uint default_value)
Definition: osregistry.cpp:372
#define PXO_MOTD_BLINK_TIME
Definition: multi_pxo.cpp:623
void multi_pxo_priv_button_pressed(int n)
Definition: multi_pxo.cpp:3995
void add_XSTR(char *string, int _xstr_id, int _x, int _y, UI_GADGET *_assoc, int _color_type, int _font_id=-1)
Definition: window.cpp:476
player_list * next
Definition: multi_pxo.cpp:393
#define gr_clear
Definition: 2d.h:749
char Multi_fs_tracker_channel[MAX_PATH]
#define MULTI_PXO_PINFO
Definition: multi_pxo.cpp:60
void multi_pxo_handle_kick()
Definition: multi_pxo.cpp:1515
#define MULTI_PXO_HELP_NUM_TEXT
Definition: multi_pxo.cpp:954
int cf_delete(const char *filename, int path_type)
Delete the specified file.
Definition: cfile.cpp:483
#define PXO_BAN_MODE_IMAGES_STARTUP
Definition: multi_pxo.cpp:1036
#define CHANNEL_SERVER_REFRESH_TIME
Definition: multi_pxo.cpp:316
help_page Multi_pxo_help_pages[MULTI_PXO_MAX_PAGES]
Definition: multi_pxo.cpp:994
#define CC_DISCONNECTED
Definition: chat_api.h:25
UI_WINDOW Multi_pxo_help_window
Definition: multi_pxo.cpp:999
void multi_pxo_pinfo_blit()
Definition: multi_pxo.cpp:4711
void game_do_state_common(int, int)
Definition: fredstubs.cpp:195
void game_set_frametime(int)
Definition: fredstubs.cpp:196
void multi_pxo_load_palette()
Definition: multi_pxo.cpp:1590
void multi_sg_rank_build_name(char *in, char *out)
Definition: multiui.cpp:2915
void gr_flip()
Definition: 2d.cpp:2113
void multi_pxo_chat_process_incoming(const char *txt, int mode=CHAT_MODE_NORMAL)
Definition: multi_pxo.cpp:3168
int x
Definition: ui.h:658
float frand_range(float min, float max)
Return a floating point number in the range min..max.
Definition: floating.cpp:50
char Multi_pxo_mask_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN]
Definition: multi_pxo.cpp:155
int is_mouse_on()
Definition: gadget.cpp:371
#define GR_RESIZE_MENU
Definition: 2d.h:684
char ban_url[MULTI_OPTIONS_STRING_LEN+1]
Definition: multi_pxo.cpp:1049
ui_button_info Multi_pxo_help_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_BUTTONS]
Definition: multi_pxo.cpp:941
chat_line * Multi_pxo_chat
Definition: multi_pxo.cpp:531
int cfread(void *buf, int elsize, int nelem, CFILE *fp)
INT64 INT64 INT64 remainder
Definition: wglext.h:660
void drop_trailing_white_space(char *str)
Definition: parselo.cpp:93
float flFrametime
Definition: fredstubs.cpp:22
char * Multi_pxo_com_mask_fname[GR_NUM_RESOLUTIONS]
Definition: multi_pxo.cpp:652
int y
Definition: ui.h:658
#define MAX_PATH
void multi_pxo_help_init()
Definition: multi_pxo.cpp:4807
int Multi_pxo_palette
Definition: multi_pxo.cpp:162
void multi_pxo_clear_players()
Definition: multi_pxo.cpp:2735
int Multi_pxo_switch_delay
Definition: multi_pxo.cpp:565
int Multi_pxo_notify_stamp
Definition: multi_pxo.cpp:916
int Web_cursor_bitmap
Definition: 2d.cpp:60
#define MULTI_PXO_HELP_NEXT
Definition: multi_pxo.cpp:938
int Multi_pxo_help_cur
Definition: multi_pxo.cpp:1002
void multi_pxo_check_buttons()
Definition: multi_pxo.cpp:1684
int Multi_pxo_chat_start_index
Definition: multi_pxo.cpp:538
void multi_pxo_channel_refresh_current()
Definition: multi_pxo.cpp:2495
chat_line * prev
Definition: multi_pxo.cpp:525
virtual void unhide()
Definition: gadget.cpp:217
int gameseq_get_state(void)
Definition: fredstubs.cpp:60
char Pxo_motd[1024]
Definition: multi_pxo.cpp:624
Assert(pm!=NULL)
void multi_pxo_pinfo_init()
Definition: multi_pxo.cpp:4583
#define GR_NUM_RESOLUTIONS
Definition: 2d.h:651
void multi_pxo_autojoin()
Definition: multi_pxo.cpp:2142
int Multi_pxo_max_chat_display[GR_NUM_RESOLUTIONS]
Definition: multi_pxo.cpp:505
#define mprintf(args)
Definition: pstypes.h:238
#define MULTI_PXO_COM_NUM_TEXT
Definition: multi_pxo.cpp:692
__inline void gr_string(int x, int y, const char *string, int resize_mode=GR_RESIZE_FULL)
Definition: 2d.h:769
void multi_pxo_help_button_pressed(int n)
Definition: multi_pxo.cpp:5014
int multi_pxo_is_nick_command(char *msg)
Definition: multi_pxo.cpp:1664
int Multi_pxo_must_autojoin
Definition: multi_pxo.cpp:179
void multi_pxo_chat_clear()
Definition: multi_pxo.cpp:3099
void multi_pxo_com_set_bottom_text(const char *txt)
Definition: multi_pxo.cpp:3867
#define PXO_CHAT_PORT
Definition: chat_api.h:16
general failure sound for any event
Definition: gamesnd.h:297
pxo_channel * multi_pxo_find_channel(char *name, pxo_channel *list)
Definition: multi_pxo.cpp:2375
int medal_main_do()
Definition: medals.cpp:670
#define MAX_PLAYER_NAME_LEN
Definition: multi_pxo.cpp:390
int Multi_pxo_mode
Definition: multi_pxo.cpp:185
int res
Definition: 2d.h:370
float Multi_pxo_channel_last_refresh
Definition: multi_pxo.cpp:314
void multi_pxo_com_set_middle_text(const char *txt)
Definition: multi_pxo.cpp:3856
int max_h_unscaled
Definition: 2d.h:361
void multi_pxo_scroll_chat_up()
Definition: multi_pxo.cpp:3381
int bm_get_info(int handle, int *w, int *h, ubyte *flags, int *nframes, int *fps)
Gets info on the bitmap indexed by handle.
Definition: bmpman.cpp:769
chat_line * next
Definition: multi_pxo.cpp:525
int Multi_pxo_com_bottom_text_y[GR_NUM_RESOLUTIONS]
Definition: multi_pxo.cpp:729
int Multi_pxo_must_validate
Definition: multi_pxo.cpp:178
void set_focus()
Definition: gadget.cpp:321
GLclampf f
Definition: Glext.h:7097
_fs_time_t last_flown
Definition: scoring.h:103
#define PXO_BANNERS_CONFIG_FILE
Definition: multi_pxo.cpp:1021
#define PXO_BAN_MODE_IMAGES
Definition: multi_pxo.cpp:1037
void multi_pxo_chat_adjust_start()
Definition: multi_pxo.cpp:3606
void multi_pxo_get_data(char *name)
Definition: multi_pxo.cpp:1510
int kill_count
Definition: scoring.h:89
void multi_pxo_motd_maybe_blit()
Definition: multi_pxo.cpp:3750
char * GetChatText()
Definition: chat_api.cpp:292
Definition: cfile.h:28
int Multi_pxo_pinfo_stats_spacing[MULTI_PXO_PINFO_NUM_LABELS]
Definition: multi_pxo.cpp:880
void multi_pxo_button_pressed(int n)
Definition: multi_pxo.cpp:1700
#define f2fl(fx)
Definition: floating.h:37
#define CHAT_MODE_SERVER
Definition: multi_pxo.cpp:518
#define CC_YOURCHANNEL
Definition: chat_api.h:28
#define GR_MAYBE_CLEAR_RES(bmap)
Definition: 2d.h:639
int mpxo_failed
Definition: multi_pxo.cpp:1828
#define MULTI_PXO_PRIV_MAX_TEXT_LEN
Definition: multi_pxo.cpp:754
int fs2netd_get_pilot_info(const char *callsign, player *out_plr, bool first_call)
GLenum mode
Definition: Glext.h:5794
#define MAX_CHANNEL_DESCRIPT_LEN
Definition: multi_pxo.cpp:272
void multi_pxo_init(int use_last_channel)
Definition: multi_pxo.cpp:1083
virtual void hide(int n)
Definition: gadget.cpp:207
void gr_set_color_fast(color *dst)
Definition: 2d.cpp:1197
UI_WINDOW Multi_pxo_com_window
Definition: multi_pxo.cpp:705
Definition: ui.h:195
int cf_create_default_path_string(char *path, uint path_max, int pathtype, const char *filename, bool localize)
player_list * multi_pxo_find_player(char *name)
Definition: multi_pxo.cpp:2866
unsigned int s_shots_fired
Definition: scoring.h:92
void multi_pxo_priv_process_buttons()
Definition: multi_pxo.cpp:3979
void gr_set_bitmap(int bitmap_num, int alphablend_mode, int bitblt_mode, float alpha)
Definition: 2d.cpp:2105
int max_w_unscaled
Definition: 2d.h:361
#define Int3()
Definition: pstypes.h:292
void multi_pxo_find_close()
Definition: multi_pxo.cpp:4082
void multi_pxo_set_end_of_motd()
Definition: multi_pxo.cpp:3681
struct channel channel
void multi_pxo_chat_add_line(const char *txt, int mode)
Definition: multi_pxo.cpp:3120
int multi_pxo_chat_is_left_message(const char *txt)
Definition: multi_pxo.cpp:3587
GLuint in
Definition: Glext.h:9087
int Multi_pxo_chat_title_y[GR_NUM_RESOLUTIONS]
Definition: multi_pxo.cpp:482
#define MULTI_PXO_PINFO_NUM_BUTTONS
Definition: multi_pxo.cpp:821
int bm_release(int handle, int clear_render_targets)
Frees both a bitmap's data and it's associated slot.
Definition: bmpman.cpp:2603
int Multi_pxo_status_coords[GR_NUM_RESOLUTIONS][4]
Definition: multi_pxo.cpp:251
int Multi_pxo_channel_count
Definition: multi_pxo.cpp:328
#define PMSG_FROM
Definition: multi_pxo.cpp:3513
bool IsConnecting()
#define CF_TYPE_MULTI_CACHE
Definition: cfile.h:73
char callsign[CALLSIGN_LEN+1]
Definition: player.h:91
#define CC_USER_JOINING
Definition: chat_api.h:23
#define PMSG_TO
Definition: multi_pxo.cpp:3514
void multi_pxo_help_close()
Definition: multi_pxo.cpp:4893
#define MAX_CHAT_LINES
Definition: multi_pxo.cpp:479
void generic_anim_unload(generic_anim *ga)
Definition: generic.cpp:291
#define DCF(function_name, help_text)
The potent DCF macro, used to define new debug commands for the console.
Definition: console.h:60
UI_WINDOW Multi_pxo_window
Definition: multi_pxo.cpp:160
char name_lookup[MAX_PXO_TEXT_LEN]
Definition: multi_pxo.cpp:4048
int Multi_pxo_must_connect
Definition: multi_pxo.cpp:176
int Multi_pxo_priv_return_code
Definition: multi_pxo.cpp:760
void multi_pxo_ban_clicked()
Definition: multi_pxo.cpp:5395
void fs2netd_update_game_count(const char *chan_name)
UI_XSTR Multi_pxo_pinfo_text[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_TEXT]
Definition: multi_pxo.cpp:838
void multi_pxo_run_medals()
Definition: multi_pxo.cpp:4735
void multi_pxo_motd_add_text(const char *text)
Definition: multi_pxo.cpp:3648
int Multi_pxo_help_coords[GR_NUM_RESOLUTIONS][2]
Definition: multi_pxo.cpp:969
pxo_banner Multi_pxo_banner
Definition: multi_pxo.cpp:1054
float Multi_pxo_ranking_last
Definition: multi_pxo.cpp:173
void multi_pxo_pinfo_show()
Definition: multi_pxo.cpp:4458
void draw_forced(int frame_num)
Definition: button.cpp:104
#define MULTI_PXO_JOIN
Definition: multi_pxo.cpp:63
void multi_pxo_blit_channels()
Definition: multi_pxo.cpp:2507
void destroy()
Definition: window.cpp:189
void multi_pxo_help_blit_page()
Definition: multi_pxo.cpp:4967
void multi_pxo_notify_blit()
Definition: multi_pxo.cpp:4783
#define MULTI_PXO_MAX_PAGES
Definition: multi_pxo.cpp:967
int num_frames
Definition: generic.h:20
typedef int(SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version *)
#define gr_reset_clip
Definition: 2d.h:745
unsigned int missions_flown
Definition: scoring.h:101
void set_mask_bmap(char *fname)
Definition: window.cpp:75
char ban_file[MAX_FILENAME_LEN+1]
Definition: multi_pxo.cpp:1047
int set_bmaps(char *ani_filename, int nframes=3, int start_frame=1)
Definition: gadget.cpp:71
void game_format_time(long, char *)
Definition: fredstubs.cpp:204
Definition: player.h:85
void multi_pxo_ban_close()
Definition: multi_pxo.cpp:5215
#define MULTI_PXO_NUM_TEXT
Definition: multi_pxo.cpp:111
int Multi_pxo_com_bitmap
Definition: multi_pxo.cpp:704
void medal_main_init(player *pl, int mode)
Definition: medals.cpp:525
void multi_pxo_ban_draw()
Definition: multi_pxo.cpp:5376
bool fs2netd_login()
#define MULTI_PXO_HAS_LEFT
Definition: multi_pxo.cpp:514
void set_numberItems(int _numberItems, int _reset=1)
Definition: slider2.cpp:217
int Pxo_motd_blink_stamp
Definition: multi_pxo.cpp:627
#define MULTI_PXO_JOIN_PRIV
Definition: multi_pxo.cpp:64
GLdouble GLdouble GLdouble r
Definition: Glext.h:5337
char Multi_pxo_bitmap_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN]
Definition: multi_pxo.cpp:151
bool IsFileReceived()
color Color_bright
Definition: alphacolors.cpp:28
unsigned int uint
Definition: pstypes.h:64
char Multi_pxo_status_text[MAX_PXO_TEXT_LEN]
Definition: multi_pxo.cpp:261
void ml_printf(const char *format,...)
Definition: multi_log.cpp:112
#define cfopen(...)
Definition: cfile.h:134
void multi_pxo_pinfo_close()
Definition: multi_pxo.cpp:4690
char Multi_pxo_notify_text[MAX_PXO_TEXT_LEN]
Definition: multi_pxo.cpp:915
#define MULTI_PXO_PLIST_DOWN
Definition: multi_pxo.cpp:58
#define nprintf(args)
Definition: pstypes.h:239
short num_servers
Definition: multi_pxo.cpp:283
bool IsReceiving()
int pressed()
Definition: inputbox.cpp:479
bool IsFileError()
#define MULTI_PXO_FIND
Definition: multi_pxo.cpp:61
char * cfgets(char *buf, int n, CFILE *cfile)
Definition: cfile.cpp:1571
unsigned int p_shots_fired
Definition: scoring.h:91
int multi_pxo_autojoin_do()
Definition: multi_pxo.cpp:1882
player_list * Multi_pxo_player_select
Definition: multi_pxo.cpp:436
void multi_pxo_scroll_channels_down()
Definition: multi_pxo.cpp:2589
void create(UI_WINDOW *wnd, int _x, int _y, int _w, int _h, int _numberItems, char *_bitmapSliderControl, void(*_upCallback)(), void(*_downCallback)(), void(*_captureCallback)())
Definition: slider2.cpp:21
void multi_pxo_priv_init()
Definition: multi_pxo.cpp:3881
#define PXO_CHAT_END_OF_MOTD_PREFIX
Definition: chat_api.