2 * Copyright (C) Volition, Inc. 2005. All rights reserved.
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
11 * $Logfile: /Freespace2/code/Network/multi_pxo.cpp $
13 * $Date: 10/25/99 5:52p $
16 * $Log: /Freespace2/code/Network/multi_pxo.cpp $
18 * 42 10/25/99 5:52p Jefff
19 * fixed some non-localized text
21 * 41 10/14/99 2:51p Jefff
24 * 40 10/13/99 3:29p Jefff
25 * fixed unnumbered XSTRs
27 * 39 9/13/99 11:30a Dave
28 * Added checkboxes and functionality for disabling PXO banners as well as
29 * disabling d3d zbuffer biasing.
31 * 38 8/30/99 5:01p Dave
32 * Made d3d do less state changing in the nebula. Use new chat server for
35 * 37 8/20/99 2:09p Dave
38 * 36 8/17/99 3:31p Jefff
39 * fixed too-big chat area in 640
41 * 35 8/16/99 9:51a Jefff
42 * webcursor over html links
44 * 34 8/11/99 12:19p Jefff
45 * no longer force pxo chatbox to snap to bottom when new text is added
47 * 33 8/10/99 5:53p Jefff
48 * fixed 640 chatbox width
50 * 32 8/06/99 1:49p Jefff
51 * added hi-res pxo logo
53 * 31 8/06/99 12:29a Dave
56 * 30 8/05/99 4:17p Dave
57 * Tweaks to client interpolation.
59 * 29 8/05/99 4:11p Jefff
61 * 28 8/04/99 10:59a Dave
62 * Removed "squad" text.
64 * 27 8/03/99 5:55p Jefff
65 * column headings in channel window, some general scrubbing
67 * 26 7/26/99 11:14a Andsager
68 * Disable medals in demo multiplayer
70 * 25 7/19/99 2:13p Dave
71 * Added some new strings for Heiko.
73 * 24 6/29/99 7:39p Dave
74 * Lots of small bug fixes.
76 * 23 6/18/99 5:16p Dave
77 * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
78 * dialog to PXO screen.
80 * 22 6/16/99 4:55p Dave
81 * New pxo pilot info popup.
83 * 21 6/07/99 9:51p Dave
84 * Consolidated all multiplayer ports into one.
86 * 20 6/01/99 3:52p Dave
87 * View footage screen. Fixed xstrings to not display the & symbol. Popup,
88 * dead popup, pxo find player popup, pxo private room popup.
90 * 19 5/21/99 6:45p Dave
91 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
92 * start game screen, multi password, and multi pxo-help screen.
94 * 18 5/19/99 4:07p Dave
95 * Moved versioning code into a nice isolated common place. Fixed up
96 * updating code on the pxo screen. Fixed several stub problems.
98 * 17 5/17/99 9:25a Dave
99 * Updated PXO screen. Still needs popups.
101 * 16 4/30/99 12:18p Dave
102 * Several minor bug fixes.
104 * 15 4/24/99 8:03p Dave
105 * Finalized banner code on PXO screen.
107 * 14 4/21/99 11:29p Dave
108 * Improved the banner stuff. Will display bitmaps, launcher URLs and
109 * highlight the banner bitmap.
111 * 13 4/20/99 6:39p Dave
112 * Almost done with artillery targeting. Added support for downloading
113 * images on the PXO screen.
115 * 12 2/17/99 2:11p Dave
116 * First full run of squad war. All freespace and tracker side stuff
119 * 11 2/08/99 5:07p Dave
120 * FS2 chat server support. FS2 specific validated missions.
122 * 10 2/04/99 6:29p Dave
123 * First full working rev of FS2 PXO support. Fixed Glide lighting
126 * 9 2/03/99 6:06p Dave
127 * Groundwork for FS2 PXO usertracker support. Gametracker support next.
129 * 8 1/30/99 5:08p Dave
130 * More new hi-res stuff.Support for nice D3D textures.
132 * 7 12/18/98 1:13a Dave
133 * Rough 1024x768 support for Direct3D. Proper detection and usage through
136 * 6 11/30/98 1:07p Dave
137 * 16 bit conversion, first run.
139 * 5 11/19/98 4:19p Dave
140 * Put IPX sockets back in psnet. Consolidated all multiplayer config
143 * 4 11/05/98 5:55p Dave
144 * Big pass at reducing #includes
146 * 3 10/13/98 9:29a Dave
147 * Started neatening up freespace.h. Many variables renamed and
148 * reorganized. Added AlphaColors.[h,cpp]
150 * 2 10/07/98 10:53a Dave
153 * 1 10/07/98 10:50a Dave
155 * 43 9/18/98 2:22a Dave
156 * Fixed freespace-side PXO api to correctly handle length 10 id strings.
157 * Fixed team select screen to handle alpha/beta/gamma ships which are not
158 * marked as OF_PLAYER_SHIP
160 * 42 9/17/98 3:08p Dave
161 * PXO to non-pxo game warning popup. Player icon stuff in create and join
162 * game screens. Upped server count refresh time in PXO to 35 secs (from
165 * 41 9/09/98 5:53p Dave
166 * Put in new tracker packets in API. Change cfile to be able to checksum
167 * portions of a file.
169 * 40 7/13/98 10:30a Lawrance
170 * add index numbers for newly localized strings
172 * 39 7/13/98 10:12a Dave
173 * Remove improperly localized strings.
175 * 38 7/10/98 2:03p Dave
176 * Additional PXO changes.
178 * 37 7/09/98 6:01p Dave
179 * Firsts full version of PXO updater. Put in stub for displaying
182 * 36 7/09/98 4:51p Dave
183 * First revision of PXO autoupdate check system.
185 * 35 7/08/98 8:15p Dave
186 * Fixed bandwidth capping for new system, and fixed pxo join button bug.
188 * 34 7/08/98 5:28p Dave
189 * Display channel name when returning to previous PXO channel from
192 * 33 7/08/98 4:54p Dave
193 * Join last used channel when returning from the games list.
195 * 32 6/17/98 10:56a Dave
196 * Put in debug code for detecting potential tracker stats update
199 * 31 6/16/98 10:39a Allender
202 * 30 6/13/98 6:01p Hoffoss
203 * Externalized all new (or forgot to be added) strings to all the code.
205 * 29 6/13/98 4:42p Hoffoss
206 * Fixed code to work properly with the XSTR program.
208 * 28 6/13/98 3:19p Hoffoss
209 * NOX()ed out a bunch of strings that shouldn't be translated.
211 * 27 6/13/98 1:45p Sandeep
213 * 26 6/05/98 3:08p Dave
214 * Fixed broken version checking. Fixed pxo chat scroll-down bug.
216 * 25 6/04/98 3:52p Dave
217 * Fixed pxo chat bug. Used physics compression code to compress observer
218 * updates. Put in rate limiting for observer updates.
220 * 24 5/27/98 1:15p Dave
221 * Change pxo login failure popup text. Put in message display of player
222 * who is currently doing voice.
224 * 23 5/26/98 11:25a Dave
225 * Check for NULL when scroll chat area up.
227 * 22 5/24/98 8:15p Dave
228 * Tweaked pxo some more.
230 * 21 5/24/98 3:45a Dave
231 * Minor object update fixes. Justify channel information on PXO. Add a
232 * bunch of configuration stuff for the standalone.
234 * 20 5/24/98 12:14a Dave
237 * 19 5/23/98 3:31p Dave
238 * Tweaked pxo code. Fixed observer HUD stuff.
240 * 18 5/23/98 3:02a Dave
243 * 17 5/21/98 9:45p Dave
244 * Lengthened tracker polling times. Put in initial support for PXO
245 * servers with channel filters. Fixed several small UI bugs.
247 * 16 5/21/98 1:52a Dave
248 * Remove obsolete command line functions. Reduce shield explosion packets
249 * drastically. Tweak PXO screen even more. Fix file xfer system so that
250 * we can guarantee file uniqueness.
252 * 15 5/20/98 12:12p Dave
253 * Channel name fixes.
255 * 14 5/20/98 2:24a Dave
256 * Fixed server side voice muting. Tweaked multi debrief/endgame
257 * sequencing a bit. Much friendlier for stats tossing/accepting now.
259 * 13 5/19/98 8:35p Dave
260 * Revamp PXO channel listing system. Send campaign goals/events to
261 * clients for evaluation. Made lock button pressable on all screens.
263 * 12 5/19/98 1:35a Dave
264 * Tweaked pxo interface. Added rankings url to pxo.cfg. Make netplayer
265 * local options update dynamically in netgames.
267 * 11 5/18/98 9:15p Dave
268 * Put in network config file support.
270 * 10 5/17/98 1:43a Dave
271 * Eradicated chatbox problems. Remove speed match for observers. Put in
272 * help screens for PXO. Fix messaging and end mission privelges. Fixed
273 * team select screen bugs. Misc UI fixes.
275 * 9 5/15/98 9:52p Dave
276 * Added new stats for freespace. Put in artwork for viewing stats on PXO.
278 * 8 5/15/98 1:41p Dave
279 * Update api so everyone can test.
281 * 7 5/15/98 12:09a Dave
282 * New tracker api code. New game tracker code. Finished up first run of
283 * the PXO screen. Fixed a few game server list exceptions.
285 * 6 5/14/98 2:41p Johnson
286 * Fixed a nick switching bug.
288 * 5 5/14/98 12:40a Dave
289 * Still more additions to the PXO screen. Updated tracker code.
291 * 4 5/13/98 6:54p Dave
292 * More sophistication to PXO interface. Changed respawn checking so
293 * there's no window for desynchronization between the server and the
296 * 3 5/12/98 11:59p Dave
297 * Put in some more functionality for Parallax Online.
299 * 2 5/12/98 2:46a Dave
300 * Rudimentary communication between Parallax Online and freespace. Can
301 * get and store channel lists.
303 * 1 5/11/98 11:47p Dave
311 #include <netinet/in.h>
314 #include "multi_pxo.h"
315 #include "animplay.h"
321 #include "gamesequence.h"
323 #include "chat_api.h"
325 #include "freespace.h"
329 #include "multi_fstracker.h"
333 #include "multi_update.h"
334 #include "alphacolors.h"
336 #include "inetgetfile.h"
337 #include "cfilesystem.h"
338 #include "osregistry.h"
340 // ----------------------------------------------------------------------------------------------------
344 // button definitions
345 #define MULTI_PXO_NUM_BUTTONS 15
346 #define MULTI_PXO_PLIST_UP 0
347 #define MULTI_PXO_PLIST_DOWN 1
348 #define MULTI_PXO_RANKINGS 2
349 #define MULTI_PXO_PINFO 3
350 #define MULTI_PXO_FIND 4
351 #define MULTI_PXO_MOTD 5
352 #define MULTI_PXO_JOIN 6
353 #define MULTI_PXO_JOIN_PRIV 7
354 #define MULTI_PXO_CHAN_UP 8
355 #define MULTI_PXO_CHAN_DOWN 9
356 #define MULTI_PXO_TEXT_UP 10
357 #define MULTI_PXO_TEXT_DOWN 11
358 #define MULTI_PXO_EXIT 12
359 #define MULTI_PXO_HELP 13
360 #define MULTI_PXO_GAMES 14
363 ui_button_info Multi_pxo_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_NUM_BUTTONS] = {
365 ui_button_info( "PXB_00", 1, 104, -1, -1, 0 ), // scroll player list up
366 ui_button_info( "PXB_01", 1, 334, -1, -1, 1 ), // scroll player list down
367 ui_button_info( "PXB_02", 18, 385, -1, -1, 2 ), // rankings webpage
368 ui_button_info( "PXB_03", 71, 385, -1, -1, 3 ), // pilot info
369 ui_button_info( "PXB_04", 115, 385, -1, -1, 4 ), // find player
370 ui_button_info( "PXB_05", 1, 443, -1, -1, 5 ), // motd
371 ui_button_info( "PXB_06", 330, 96, -1, -1, 6 ), // join channel
372 ui_button_info( "PXB_07", 330, 131, -1, -1, 7 ), // join private channel
373 ui_button_info( "PXB_08", 618, 92, -1, -1, 8 ), // scroll channels up
374 ui_button_info( "PXB_09", 618, 128, -1, -1, 9 ), // scroll channels down
375 ui_button_info( "PXB_10", 615, 171, -1, -1, 10 ), // scroll text up
376 ui_button_info( "PXB_11", 615, 355, -1, -1, 11 ), // scroll text down
377 ui_button_info( "PXB_12", 482, 435, -1, -1, 12 ), // exit
378 ui_button_info( "PXB_13", 533, 432, -1, -1, 13 ), // help
379 ui_button_info( "PXB_14", 573, 432, -1, -1, 14 ), // games list
382 ui_button_info( "2_PXB_00", 2, 166, -1, -1, 0 ), // scroll player list up
383 ui_button_info( "2_PXB_01", 2, 534, -1, -1, 1 ), // scroll player list down
384 ui_button_info( "2_PXB_02", 29, 616, -1, -1, 2 ), // rankings webpage
385 ui_button_info( "2_PXB_03", 114, 616, -1, -1, 3 ), // pilot info
386 ui_button_info( "2_PXB_04", 184, 616, -1, -1, 4 ), // find player
387 ui_button_info( "2_PXB_05", 2, 709, -1, -1, 5 ), // motd
388 ui_button_info( "2_PXB_06", 528, 119, -1, -1, 6 ), // join channel
389 ui_button_info( "2_PXB_07", 528, 175, -1, -1, 7 ), // join private channel
390 ui_button_info( "2_PXB_08", 989, 112, -1, -1, 8 ), // scroll channels up
391 ui_button_info( "2_PXB_09", 989, 170, -1, -1, 9 ), // scroll channels down
392 ui_button_info( "2_PXB_10", 984, 240, -1, -1, 10 ), // scroll text up
393 ui_button_info( "2_PXB_11", 984, 568, -1, -1, 11 ), // scroll text down
394 ui_button_info( "2_PXB_12", 771, 696, -1, -1, 12 ), // exit
395 ui_button_info( "2_PXB_13", 853, 691, -1, -1, 13 ), // help
396 ui_button_info( "2_PXB_14", 917, 691, -1, -1, 14 ), // games list
400 // define MULTI_PXO_NUM_TEXT 18
401 #define MULTI_PXO_NUM_TEXT 16
402 UI_XSTR Multi_pxo_text[GR_NUM_RESOLUTIONS][MULTI_PXO_NUM_TEXT] = {
404 {"Web", 1313, 20, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_RANKINGS].button},
405 {"Ranking", 1314, 6, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_RANKINGS].button},
406 {"Pilot", 1310, 68, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_PINFO].button},
407 {"Info", 1311, 72, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_PINFO].button},
408 {"Find", 1315, 119, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_FIND].button},
409 {"Motd", 1316, 36, 456, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_MOTD].button},
410 {"Join", 1505, 291, 100, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN].button},
411 {"Channel", 1317, 266, 112, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN].button},
412 {"Join", 1506, 291, 134, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN_PRIV].button},
413 {"Private", 1318, 273, 146, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN_PRIV].button},
414 {"Exit", 1416, 493, 424, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_EXIT].button},
415 {"Help", 928, 535, 416, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_HELP].button},
416 {"Games", 1319, 579, 416, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_GAMES].button},
417 {"Players", 1269, 29, 102, UI_XSTR_COLOR_GREEN, -1, NULL},
418 // {"Squad", 1320, 110, 101, UI_XSTR_COLOR_GREEN, -1, NULL},
419 // {"Channels", 1321, 369, 76, UI_XSTR_COLOR_GREEN, -1, NULL},
420 {"Players", 1269, 507, 90, UI_XSTR_COLOR_GREEN, -1, NULL},
421 {"Games", 1319, 568, 90, UI_XSTR_COLOR_GREEN, -1, NULL}
424 {"Web", 1313, 32, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_RANKINGS].button},
425 {"Ranking", 1314, 9, 674, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_RANKINGS].button},
426 {"Pilot", 1310, 109, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_PINFO].button},
427 {"Info", 1311, 115, 674, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_PINFO].button},
428 {"Find", 1315, 190, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_FIND].button},
429 {"Motd", 1316, 58, 729, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_MOTD].button},
430 {"Join", 1505, 488, 129, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN].button},
431 {"Channel", 1317, 461, 139, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN].button},
432 {"Join", 1506, 487, 184, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN_PRIV].button},
433 {"Private", 1318, 467, 194, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN_PRIV].button},
434 {"Exit", 1416, 789, 678, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_EXIT].button},
435 {"Help", 928, 857, 667, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_HELP].button},
436 {"Games", 1319, 917, 667, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_GAMES].button},
437 {"Players", 1269, 47, 163, UI_XSTR_COLOR_GREEN, -1, NULL},
438 // {"Squad", 1320, 176, 163, UI_XSTR_COLOR_GREEN, -1, NULL},
439 // {"Channels", 1321, 591, 86, UI_XSTR_COLOR_GREEN, -1, NULL},
440 {"Players", 1269, 852, 109, UI_XSTR_COLOR_GREEN, -1, NULL},
441 {"Games", 1319, 926, 109, UI_XSTR_COLOR_GREEN, -1, NULL}
445 char Multi_pxo_bitmap_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
449 char Multi_pxo_mask_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
454 UI_WINDOW Multi_pxo_window;
455 int Multi_pxo_bitmap = -1;
456 int Multi_pxo_palette = -1;
460 #define MULTI_PXO_ANIM_FNAME "pxologo"
461 #define MULTI_PXO_ANIM_X 0
462 #define MULTI_PXO_ANIM_Y 4
463 anim *Multi_pxo_anim = NULL;
464 anim_instance *Multi_pxo_anim_instance = NULL;
466 // rankings last clicked time
467 #define MULTI_PXO_RANK_TIME (5.0f)
468 float Multi_pxo_ranking_last = -1.0f;
471 int Multi_pxo_must_connect = 0; // if we still need to connect
472 int Multi_pxo_connected = 0; // if we are connected
473 int Multi_pxo_must_validate = 0; // if we need to validate on the tracker
474 int Multi_pxo_must_autojoin = 1; // still need to autojoin a channel
475 int Multi_pxo_must_verify_version = 1; // only do it once per instance of freespace
478 #define MULTI_PXO_MODE_NORMAL 0 // normal mode
479 #define MULTI_PXO_MODE_PRIVATE 1 // private channel popup
480 #define MULTI_PXO_MODE_FIND 2 // find player popup
481 int Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
483 // our nick for this session
485 char Multi_pxo_nick[NAME_LENGTH+1];
487 // check for button presses
488 void multi_pxo_check_buttons();
490 // handle a button press
491 void multi_pxo_button_pressed(int n);
493 // condition function for popup_do_with_condition for connected to Parallax Online
494 // return 10 : on successful connect
495 int multi_pxo_connect_do();
497 // attempt to connect to Parallax Online, return success or fail
498 int multi_pxo_connect();
500 // run the networking functions for the PXO API
501 void multi_pxo_api_process();
503 // process a "nick" change event
504 void multi_pxo_process_nick_change(char *data);
506 // run normally (no popups)
507 void multi_pxo_do_normal();
509 // blit everything on the "normal" screen
510 void multi_pxo_blit_all();
512 // process common stuff
513 void multi_pxo_process_common();
515 // get selected player information
516 void multi_pxo_get_data(char *name);
518 // handle being kicked
519 void multi_pxo_handle_kick();
521 // handle being disconnected
522 void multi_pxo_handle_disconnect();
524 // return string2, which is the first substring of string 1 without a space
525 // it is safe to pass the same pointer for both parameters
526 void multi_pxo_strip_space(char *string1,char *string2, const int str2_len);
528 // fire up the given URL
529 void multi_pxo_url(char *url);
531 // load/set the palette
532 void multi_pxo_load_palette();
534 // unload the palette
535 void multi_pxo_unload_palette();
537 // if we're currently on a private channel
538 int multi_pxo_on_private_channel();
540 // convert string 1 into string 2, substituting underscores for spaces
541 void multi_pxo_underscore_nick(char *string1, char *string2, const int str2_len);
543 // if the command is a potential "nick" command
544 int multi_pxo_is_nick_command(char *msg);
547 // status bar stuff -----------------------------------------------
548 int Multi_pxo_status_coords[GR_NUM_RESOLUTIONS][4] = {
557 // the status text itself
558 char Multi_pxo_status_text[255];
560 // set the status text
561 void multi_pxo_set_status_text(const char *txt);
563 // blit the status text
564 void multi_pxo_blit_status_text();
567 // channel related stuff -------------------------------------------
568 #define MAX_CHANNEL_NAME_LEN 32
569 #define MAX_CHANNEL_DESCRIPT_LEN 120
571 // some convenient macros
572 #define SWITCHING_CHANNELS() (Multi_pxo_channel_switch.num_users != -1)
573 #define ON_CHANNEL() (Multi_pxo_channel_current.num_users != -1)
575 typedef struct pxo_channel {
576 pxo_channel *next,*prev; // next and previous items in the list
577 char name[MAX_CHANNEL_NAME_LEN+1]; // name
578 char desc[MAX_CHANNEL_DESCRIPT_LEN+1]; // description
579 short num_users; // # users, or -1 if not in use
580 short num_servers; // the # of servers registered on this channel
583 // last channel we were on before going to the game list screen
584 char Multi_pxo_channel_last[MAX_CHANNEL_NAME_LEN+1] = "";
585 int Multi_pxo_use_last_channel = 0;
587 // all channels which are prefixed with this are "lobby" channels
588 #define MULTI_PXO_AUTOJOIN_PREFIX "#lobby"
590 // join this channel to get put in an appropriate lobby channel
591 #define MULTI_PXO_AUTOJOIN_CHANNEL "#autoselect"
593 int Multi_pxo_chan_coords[GR_NUM_RESOLUTIONS][4] = {
602 // this is the offset from the RIGHT side of the channel box
603 #define CHAN_PLAYERS_COLUMN 0
604 #define CHAN_GAMES_COLUMN 1
605 static int Multi_pxo_chan_column_offsets[GR_NUM_RESOLUTIONS][2] = {
610 #define CHANNEL_REFRESH_TIME (75.0f)
611 float Multi_pxo_channel_last_refresh = -1.0f;
613 #define CHANNEL_SERVER_REFRESH_TIME (35.0f)
614 float Multi_pxo_channel_server_refresh = -1.0f;
616 int Multi_pxo_max_chan_display[GR_NUM_RESOLUTIONS] = {
621 UI_BUTTON Multi_pxo_channel_button;
623 // head of the list of available (displayed) channels
624 pxo_channel *Multi_pxo_channels = NULL;
625 int Multi_pxo_channel_count = 0;
627 // item we're going to start displaying at
628 pxo_channel *Multi_pxo_channel_start = NULL;
629 int Multi_pxo_channel_start_index = -1;
631 // items we've currently got selected
632 pxo_channel *Multi_pxo_channel_select = NULL;
634 // channel we're currently connected to, num_users == -1, if we're not connected
635 pxo_channel Multi_pxo_channel_current;
637 // channel we're currently trying to change to, num_users == -1, if we're not trying to change channels
638 pxo_channel Multi_pxo_channel_switch;
640 // get a list of channels on the server (clear any old list as well)
641 void multi_pxo_get_channels();
643 // clear the old channel list
644 void multi_pxo_clear_channels();
646 // parse the input string and make a list of new channels
647 void multi_pxo_make_channels(char *chan_str);
649 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
650 pxo_channel *multi_pxo_add_channel(char *name, pxo_channel **list);
652 // lookup a channel with the specified name
653 pxo_channel *multi_pxo_find_channel(char *name, pxo_channel *list);
655 // process the channel list (select, etc)
656 void multi_pxo_process_channels();
658 // display the channel list
659 void multi_pxo_blit_channels();
661 // scroll channel list up
662 void multi_pxo_scroll_channels_up();
664 // scroll channel list down
665 void multi_pxo_scroll_channels_down();
667 // attempt to join a channel
668 void multi_pxo_join_channel(pxo_channel *chan);
670 // handle any processing details if we're currently trying to join a channel
671 void multi_pxo_handle_channel_change();
673 // autojoin an appropriate channel
674 void multi_pxo_autojoin();
676 // does the string match the "autojoin" prefic
677 int multi_pxo_is_autojoin(char *name);
679 // send a request to refresh our channel server counts
680 void multi_pxo_channel_refresh_servers();
682 // refresh current channel server count
683 void multi_pxo_channel_refresh_current();
686 // player related stuff -------------------------------------------
687 #define MAX_PLAYER_NAME_LEN 32
689 typedef struct player_list {
690 player_list *next,*prev;
691 char name[MAX_PLAYER_NAME_LEN+1];
694 // channel list region
695 int Multi_pxo_player_coords[GR_NUM_RESOLUTIONS][4] = {
704 int Multi_pxo_max_player_display[GR_NUM_RESOLUTIONS] = {
708 UI_BUTTON Multi_pxo_player_button;
710 // UI_SLIDER2 Multi_pxo_player_slider;
713 int Multi_pxo_player_slider_coords[GR_NUM_RESOLUTIONS][4] = {
721 const char *Multi_pxo_player_slider_name[GR_NUM_RESOLUTIONS] = {
723 "2_slider" // GR_1024
726 // head of the list of players in this channel
727 player_list *Multi_pxo_players = NULL;
728 int Multi_pxo_player_count = 0;
730 // item we're going to start displaying at
731 player_list *Multi_pxo_player_start = NULL;
732 // int Multi_pxo_player_start_index = -1;
734 // items we've currently got selected
735 player_list *Multi_pxo_player_select = NULL;
737 // clear the old player list
738 void multi_pxo_clear_players();
740 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
741 player_list *multi_pxo_add_player(char *name);
743 // remove a player with the given name
744 void multi_pxo_del_player(char *name);
746 // try and find a player with the given name, return a pointer to his entry (or NULL)
747 player_list *multi_pxo_find_player(char *name);
749 // process the player list (select, etc)
750 void multi_pxo_process_players();
752 // display the player list
753 void multi_pxo_blit_players();
755 // scroll player list up
756 void multi_pxo_scroll_players_up();
758 // scroll player list down
759 void multi_pxo_scroll_players_down();
761 // get the absolute index of the displayed items which our currently selected one is
762 int multi_pxo_get_select_index();
768 // add a bunch of bogus players
770 for(int idx=0; idx<Dc_arg_int; idx++){
771 SDL_snprintf(name, SDL_arraysize(name), "player %d", idx);
772 multi_pxo_add_player(name);
776 // chat text stuff -----------------------------------------
777 #define MAX_CHAT_LINES 60
778 #define MAX_CHAT_LINE_LEN 256
780 int Multi_pxo_chat_title_y[GR_NUM_RESOLUTIONS] = {
785 int Multi_pxo_chat_coords[GR_NUM_RESOLUTIONS][4] = {
794 int Multi_pxo_input_coords[GR_NUM_RESOLUTIONS][4] = {
803 int Multi_pxo_max_chat_display[GR_NUM_RESOLUTIONS] = {
808 // all messages from the server are prefixed with this
809 #define MULTI_PXO_SERVER_PREFIX "*** "
811 // the "has left" message from the server
812 #define MULTI_PXO_HAS_LEFT "has left"
815 #define CHAT_MODE_NORMAL 0 // normal chat from someone
816 #define CHAT_MODE_SERVER 1 // is from the server, display appropriately
817 #define CHAT_MODE_CARRY 2 // is a carryover from a previous line
818 #define CHAT_MODE_PRIVATE 3 // is a private message
819 #define CHAT_MODE_CHANNEL_SWITCH 4 // "switching channels" message - draw in red
820 #define CHAT_MODE_MOTD 5 // message of the day from the chat server
822 typedef struct chat_line {
823 chat_line *next,*prev;
824 char text[MAX_CHAT_LINE_LEN+1];
828 // the chat linked list itself
829 chat_line *Multi_pxo_chat = NULL;
831 // the current add line
832 chat_line *Multi_pxo_chat_add = NULL;
834 // the current line to start displaying from
835 chat_line *Multi_pxo_chat_start = NULL;
836 int Multi_pxo_chat_start_index = -1;
838 // input box for text
839 UI_INPUTBOX Multi_pxo_chat_input;
842 UI_SLIDER2 Multi_pxo_chat_slider;
844 int Multi_pxo_chat_slider_coords[GR_NUM_RESOLUTIONS][4] = {
853 const char *Multi_pxo_chat_slider_name[GR_NUM_RESOLUTIONS] = {
858 // how many chat lines we have
859 int Multi_pxo_chat_count = 0;
861 // extra delay time when switching channels
862 #define MULTI_PXO_SWITCH_DELAY_TIME 2000
863 int Multi_pxo_switch_delay = -1;
865 // initialize and create the chat text linked list
866 void multi_pxo_chat_init();
868 // free up all chat list stuff
869 void multi_pxo_chat_free();
871 // clear all lines of chat text in the chat area
872 void multi_pxo_chat_clear();
874 // blit the chat text
875 void multi_pxo_chat_blit();
877 // add a line of text
878 void multi_pxo_chat_add_line(char *txt,int mode);
880 // process an incoming line of text
881 void multi_pxo_chat_process_incoming(const char *txt,int mode = CHAT_MODE_NORMAL);
883 // scroll to the very bottom of the chat area
884 void multi_pxo_goto_bottom();
886 // check whether we can scroll down or not
887 int multi_pxo_can_scroll_down();
889 static int Can_scroll_down = 0;
891 // scroll the text up
892 void multi_pxo_scroll_chat_up();
894 // scroll the text down
895 void multi_pxo_scroll_chat_down();
897 // process chat controls
898 void multi_pxo_chat_process();
900 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
901 const char *multi_pxo_chat_is_private(const char *txt);
903 // if the text came from the server
904 int multi_pxo_is_server_text(const char *txt);
906 // if the text is message of the day text
907 int multi_pxo_is_motd_text(const char *txt);
909 // if the text is the end of motd text
910 int multi_pxo_is_end_of_motd_text(const char *txt);
912 // if the text is a "has left message" from the server
913 int multi_pxo_chat_is_left_message(const char *txt);
915 // recalculate the chat start index, and adjust the slider properly
916 void multi_pxo_chat_adjust_start();
919 // motd stuff ---------------------------------------------------------
920 #define MAX_PXO_MOTD_LEN 1024
921 #define PXO_MOTD_BLINK_TIME 500
922 char Pxo_motd[1024] = "";
923 int Pxo_motd_end = 0;
924 int Pxo_motd_read = 0;
925 int Pxo_motd_blink_stamp = -1;
926 int Pxo_motd_blink_on = 0;
927 int Pxo_motd_blinked_already = 0;
929 // initialize motd when going into this screen
930 void multi_pxo_motd_init();
933 void multi_pxo_motd_add_text(const char *text);
936 void multi_pxo_set_end_of_motd();
938 // display the motd dialog
939 void multi_pxo_motd_dialog();
941 // call to maybe blink the motd button
942 void multi_pxo_motd_maybe_blit();
945 // common dialog stuff ------------------------------------------------
946 const char *Multi_pxo_com_fname[GR_NUM_RESOLUTIONS] = {
950 const char *Multi_pxo_com_mask_fname[GR_NUM_RESOLUTIONS] = {
956 int Multi_pxo_com_coords[GR_NUM_RESOLUTIONS][2] = {
966 int Multi_pxo_com_input_coords[GR_NUM_RESOLUTIONS][4] = {
975 #define MULTI_PXO_COM_NUM_BUTTONS 2
976 #define MULTI_PXO_COM_CANCEL 0
977 #define MULTI_PXO_COM_OK 1
979 ui_button_info Multi_pxo_com_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_BUTTONS] = {
981 ui_button_info("PXP_00", 573, 192, -1, -1, 0),
982 ui_button_info("PXP_01", 573, 226, -1, -1, 1)
985 ui_button_info("2_PXP_00", 917, 308, -1, -1, 0),
986 ui_button_info("2_PXP_01", 917, 361, -1, -1, 1)
990 #define MULTI_PXO_COM_NUM_TEXT 2
991 UI_XSTR Multi_pxo_com_text[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_TEXT] = {
993 { "&Cancel", 645, 510, 204, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_CANCEL].button },
994 { "&Ok", 669, 548, 233, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_OK].button }
997 { "&Cancel", 645, 847, 327, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_CANCEL].button },
998 { "&Ok", 669, 877, 372, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_OK].button }
1002 int Multi_pxo_com_bitmap = -1;
1003 UI_WINDOW Multi_pxo_com_window;
1004 UI_INPUTBOX Multi_pxo_com_input;
1006 // text on the "top" half of the dialog display area
1007 char Multi_pxo_com_top_text[255];
1009 // text on the "middle" portion of the dialog display area
1010 char Multi_pxo_com_middle_text[255];
1012 // text on the "bottom" half of the dialog display area
1013 char Multi_pxo_com_bottom_text[255];
1015 int Multi_pxo_com_top_text_coords[GR_NUM_RESOLUTIONS][2] = {
1023 int Multi_pxo_com_middle_text_y[GR_NUM_RESOLUTIONS] = {
1027 int Multi_pxo_com_bottom_text_y[GR_NUM_RESOLUTIONS] = {
1032 // initialize the common dialog with the passed max input length
1033 void multi_pxo_com_init();
1035 // close down the common dialog
1036 void multi_pxo_com_close();
1038 // blit all text lines, top, middle, bottoms
1039 void multi_pxo_com_blit_text();
1041 // set the top text, shortening as necessary
1042 void multi_pxo_com_set_top_text(const char *txt);
1044 // set the middle text, shortening as necessary
1045 void multi_pxo_com_set_middle_text(const char *txt);
1047 // set the bottom text, shortening as necessary
1048 void multi_pxo_com_set_bottom_text(const char *txt);
1051 // private channel join stuff -----------------------------------------
1052 #define MULTI_PXO_PRIV_MAX_TEXT_LEN 30
1054 // max private channel name length
1055 char Multi_pxo_priv_chan[MULTI_PXO_PRIV_MAX_TEXT_LEN+100];
1057 // return code, set to something other than -1 if we're supposed to return
1058 int Multi_pxo_priv_return_code = -1;
1060 // initialize the popup
1061 void multi_pxo_priv_init();
1063 // close down the popup
1064 void multi_pxo_priv_close();
1066 // run the popup, 0 if still running, -1 if cancel, 1 if ok
1067 int multi_pxo_priv_popup();
1069 // process button presses
1070 void multi_pxo_priv_process_buttons();
1072 // handle a button press
1073 void multi_pxo_priv_button_pressed(int n);
1075 // process the inputbox
1076 void multi_pxo_priv_process_input();
1079 // find player stuff -----------------------------------------
1081 char Multi_pxo_find_channel[MAX_CHANNEL_NAME_LEN+1];
1083 // return code, set to something other than -1 if we're supposed to return
1084 int Multi_pxo_find_return_code = -1;
1086 // initialize the popup
1087 void multi_pxo_find_init();
1089 // close down the popup
1090 void multi_pxo_find_close();
1092 // run the popup, 0 if still running, -1 if cancel, 1 if ok
1093 int multi_pxo_find_popup();
1095 // process button presses
1096 void multi_pxo_find_process_buttons();
1098 // handle a button press
1099 void multi_pxo_find_button_pressed(int n);
1101 // process the inputbox
1102 void multi_pxo_find_process_input();
1104 // process search mode if applicable
1105 void multi_pxo_find_search_process();
1108 // player info stuff -----------------------------------------
1109 const char *Multi_pxo_pinfo_fname[GR_NUM_RESOLUTIONS] = {
1113 const char *Multi_pxo_pinfo_mask_fname[GR_NUM_RESOLUTIONS] = {
1119 #define MULTI_PXO_PINFO_NUM_BUTTONS 2
1120 #define MULTI_PXO_PINFO_MEDALS 0
1121 #define MULTI_PXO_PINFO_OK 1
1123 ui_button_info Multi_pxo_pinfo_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_BUTTONS] = {
1125 ui_button_info("PI2_00", 328, 446, 319, 433, 0),
1126 ui_button_info("PI2_01", 376, 446, 382, 433, 1),
1129 ui_button_info("2_PI2_00", 525, 714, 510, 695, 0),
1130 ui_button_info("2_PI2_01", 601, 714, 611, 695, 1),
1135 #define MULTI_PXO_PINFO_NUM_TEXT 2
1136 UI_XSTR Multi_pxo_pinfo_text[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_TEXT] = {
1138 { "Medals", 1037, 319, 433, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_MEDALS].button },
1139 { "Ok", 345, 382, 433, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_OK].button },
1142 { "Medals", 1037, 510, 695, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_MEDALS].button },
1143 { "Ok", 345, 611, 695, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_OK].button },
1147 int Multi_pxo_pinfo_bitmap = -1;
1148 UI_WINDOW Multi_pxo_pinfo_window;
1150 vmt_freespace2_struct Multi_pxo_pinfo;
1151 player Multi_pxo_pinfo_player;
1153 int Multi_pxo_retrieve_mode = -1;
1155 char Multi_pxo_retrieve_name[MAX_PLAYER_NAME_LEN+1];
1156 char Multi_pxo_retrieve_id[128];
1158 // stats label stuff
1159 #define MULTI_PXO_PINFO_NUM_LABELS 18
1161 int Multi_pxo_pinfo_coords[GR_NUM_RESOLUTIONS][4] = {
1169 int Multi_pxo_pinfo_val_x[GR_NUM_RESOLUTIONS] = {
1174 char *Multi_pxo_pinfo_stats_labels[MULTI_PXO_PINFO_NUM_LABELS];
1185 "Primary shots fired",
1186 "Primary shots hit",
1188 "Secondary shots fired",
1189 "Secondary shots hit",
1191 "Primary friendly hits",
1192 "Primary friendly hit %",
1193 "Secondary friendly hits",
1194 "Secondary friendly hit %"
1199 char Multi_pxo_pinfo_vals[MULTI_PXO_PINFO_NUM_LABELS][50];
1201 int Multi_pxo_pinfo_stats_spacing[MULTI_PXO_PINFO_NUM_LABELS] = {
1202 10,20,10,10,20,10,10,20,10,10,20,10,10,20,10,20,10,0
1205 // popup conditional functions, returns 10 on successful get of stats
1206 int multi_pxo_pinfo_cond();
1208 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
1209 int multi_pxo_pinfo_get(char *name);
1211 // fire up the stats view popup
1212 void multi_pxo_pinfo_show();
1214 // build the stats labels values
1215 void multi_pxo_pinfo_build_vals();
1217 // initialize the popup
1218 void multi_pxo_pinfo_init();
1221 int multi_pxo_pinfo_do();
1224 void multi_pxo_pinfo_close();
1226 // blit all the stats on this screen
1227 void multi_pxo_pinfo_blit();
1229 // run the medals screen
1230 void multi_pxo_run_medals();
1232 // notify stuff stuff -----------------------------------------
1233 #define MULTI_PXO_NOTIFY_TIME 4000
1234 #define MULTI_PXO_NOTIFY_Y 435
1236 char Multi_pxo_notify_text[255];
1237 int Multi_pxo_notify_stamp = -1;
1239 // add a notification string
1240 void multi_pxo_notify_add(const char *txt);
1242 // blit and process the notification string
1243 void multi_pxo_notify_blit();
1246 // help screen stuff -----------------------------------------
1248 const char *Multi_pxo_help_fname[GR_NUM_RESOLUTIONS] = {
1252 const char *Multi_pxo_help_mask_fname[GR_NUM_RESOLUTIONS] = {
1257 #define MULTI_PXO_HELP_NUM_BUTTONS 3
1258 #define MULTI_PXO_HELP_PREV 0
1259 #define MULTI_PXO_HELP_NEXT 1
1260 #define MULTI_PXO_HELP_CONTINUE 2
1262 ui_button_info Multi_pxo_help_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_BUTTONS] = {
1264 ui_button_info("PXH_00", 15, 389, -1, -1, 0),
1265 ui_button_info("PXH_01", 60, 389, -1, -1, 1),
1266 ui_button_info("PXH_02", 574, 431, 571, 413, 2),
1269 ui_button_info("2_PXH_00", 24, 622, -1, -1, 0),
1270 ui_button_info("2_PXH_01", 96, 622, -1, -1, 1),
1271 ui_button_info("2_PXH_02", 919, 689, 928, 663, 2),
1275 #define MULTI_PXO_HELP_NUM_TEXT 1
1276 UI_XSTR Multi_pxo_help_text[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_TEXT] = {
1278 {"Continue", 1069, 571, 413, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[0][MULTI_PXO_HELP_CONTINUE].button },
1281 {"Continue", 1069, 928, 663, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[1][MULTI_PXO_HELP_CONTINUE].button },
1286 #define MULTI_PXO_HELP_FILE "pxohelp.txt"
1287 #define MULTI_PXO_MAX_LINES_PP 57
1288 #define MULTI_PXO_MAX_PAGES 3
1290 int Multi_pxo_help_coords[GR_NUM_RESOLUTIONS][2] = {
1299 int Multi_pxo_chars_per_line[GR_NUM_RESOLUTIONS] = {
1304 int Multi_pxo_lines_pp[GR_NUM_RESOLUTIONS] = {
1310 typedef struct help_page {
1311 char *text[MULTI_PXO_MAX_LINES_PP];
1315 help_page Multi_pxo_help_pages[MULTI_PXO_MAX_PAGES];
1316 // int Multi_pxo_help_loaded = 0;
1318 int Multi_pxo_help_num_pages = 0;
1320 int Multi_pxo_help_bitmap = -1;
1321 UI_WINDOW Multi_pxo_help_window;
1323 // current page we're on
1324 int Multi_pxo_help_cur = 0;
1326 // load the help file up
1327 void multi_pxo_help_load();
1329 // blit the current page
1330 void multi_pxo_help_blit_page();
1332 // process button presses
1333 void multi_pxo_help_process_buttons();
1336 void multi_pxo_help_button_pressed(int n);
1339 // http banner stuff ---------------------------------------------
1340 InetGetFile *Multi_pxo_ban_get = NULL;
1343 #define PXO_BANNERS_CONFIG_FILE "pxobanners.cfg"
1345 // coords to display banners at
1346 int Pxo_ban_coords[GR_NUM_RESOLUTIONS][4] = {
1356 #define PXO_BAN_MODE_LIST_STARTUP 0 // start downloading list
1357 #define PXO_BAN_MODE_LIST 1 // downloading list
1358 #define PXO_BAN_MODE_IMAGES_STARTUP 2 // start downloading images
1359 #define PXO_BAN_MODE_IMAGES 3 // downloading images
1360 #define PXO_BAN_MODE_IMAGES_DONE 4 // done downloading everything - now maybe load an image
1361 #define PXO_BAN_MODE_IDLE 5 // done with everything - doing nothing
1362 #define PXO_BAN_MODE_CHOOSE_RANDOM 6 // choose a bitmap we've already downloaded at random
1364 // interface button for detecting clicks
1365 UI_BUTTON Multi_pxo_ban_button;
1368 typedef struct pxo_banner {
1369 char ban_file[MAX_FILENAME_LEN+1]; // base filename of the banner
1370 char ban_file_url[MULTI_OPTIONS_STRING_LEN+1]; // full url of the file to get (convenient)
1371 char ban_url[MULTI_OPTIONS_STRING_LEN+1]; // url to go to when clicked
1372 int ban_bitmap; // banner bitmap
1375 // active pxo banner
1376 pxo_banner Multi_pxo_banner;
1379 int Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
1382 void multi_pxo_ban_init();
1384 // process http download details
1385 void multi_pxo_ban_process();
1388 void multi_pxo_ban_close();
1390 // parse the banners file and maybe fill in Multi_pxo_dl_file[]
1391 void multi_pxo_ban_parse_banner_file(int choose_existing);
1393 // any bitmap or info or whatever
1394 void multi_pxo_ban_draw();
1396 // called when the URL button is clicked
1397 void multi_pxo_ban_clicked();
1400 // ----------------------------------------------------------------------------------------------------
1404 // initialize the PXO screen
1405 void multi_pxo_init(int use_last_channel)
1409 // load the background bitmap
1410 Multi_pxo_bitmap = bm_load(Multi_pxo_bitmap_fname[gr_screen.res]);
1411 if(Multi_pxo_bitmap < 0){
1412 // we failed to load the bitmap - this is very bad
1416 // load up the private channel bitmap
1417 Multi_pxo_com_bitmap = bm_load(Multi_pxo_com_fname[gr_screen.res]);
1418 SDL_assert(Multi_pxo_com_bitmap != -1);
1420 // create the interface window
1421 Multi_pxo_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1422 Multi_pxo_window.set_mask_bmap(Multi_pxo_mask_fname[gr_screen.res]);
1424 // multiplayer screen common palettes
1425 multi_pxo_load_palette();
1427 // create the interface buttons
1428 for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
1429 // create the object
1430 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);
1432 // set the sound to play when highlighted
1433 Multi_pxo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1435 // set the ani for the button
1436 Multi_pxo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_buttons[gr_screen.res][idx].filename);
1439 Multi_pxo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_buttons[gr_screen.res][idx].hotspot);
1443 for(idx=0; idx<MULTI_PXO_NUM_TEXT; idx++){
1444 Multi_pxo_window.add_XSTR(&Multi_pxo_text[gr_screen.res][idx]);
1447 if(use_last_channel && strlen(Multi_pxo_channel_last)){
1448 Multi_pxo_use_last_channel = 1;
1450 SDL_zero(Multi_pxo_channel_last);
1451 Multi_pxo_use_last_channel = 0;
1454 // make all scrolling buttons repeatable
1455 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_UP].button.repeatable(1);
1456 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_DOWN].button.repeatable(1);
1457 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_UP].button.repeatable(1);
1458 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_DOWN].button.repeatable(1);
1459 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_UP].button.repeatable(1);
1460 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_DOWN].button.repeatable(1);
1462 // set the mouseover cursor if it loaded ok
1463 if (Web_cursor_bitmap > 0) {
1464 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_RANKINGS].button.set_custom_cursor_bmap(Web_cursor_bitmap);
1467 // create the channel list select button and hide it
1468 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);
1469 Multi_pxo_channel_button.hide();
1471 // create the player list select button and hide it
1472 Multi_pxo_player_button.create(&Multi_pxo_window, "", Multi_pxo_player_coords[gr_screen.res][0], Multi_pxo_player_coords[gr_screen.res][1], Multi_pxo_player_coords[gr_screen.res][2], Multi_pxo_player_coords[gr_screen.res][3], 0, 1);
1473 Multi_pxo_player_button.hide();
1475 // create the chat input box
1476 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);
1477 Multi_pxo_chat_input.set_focus();
1479 // create the banner button and hide it
1480 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);
1481 Multi_pxo_ban_button.hide();
1483 // create the player list slider
1484 // Multi_pxo_player_slider.create(&Multi_pxo_window, Multi_pxo_player_slider_coords[gr_screen.res][0], Multi_pxo_player_slider_coords[gr_screen.res][1], Multi_pxo_player_slider_coords[gr_screen.res][2], Multi_pxo_player_slider_coords[gr_screen.res][3], 0, Multi_pxo_player_slider_name[gr_screen.res], multi_pxo_scroll_players_up, multi_pxo_scroll_players_down, NULL);
1486 // create the chat slider
1487 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);
1489 // set our connection status so that we do the right stuff next frame
1490 Multi_pxo_must_validate = 1;
1491 Multi_pxo_must_connect = 0;
1492 Multi_pxo_connected = 0;
1494 // channel we're currently connected to
1495 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
1496 Multi_pxo_channel_current.num_users = -1;
1498 // channel we're currently trying to change to, or NULL if nont
1499 memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
1500 Multi_pxo_channel_switch.num_users = -1;
1502 // last time clicked the url button (so we don't have repeats)
1503 Multi_pxo_ranking_last = -1.0f;
1505 // channel switching extra time delay stamp
1506 Multi_pxo_switch_delay = -1;
1508 // our nick for this session
1509 multi_pxo_underscore_nick(Player->callsign, Multi_pxo_nick, SDL_arraysize(Multi_pxo_nick));
1511 // clear the channel list
1512 multi_pxo_clear_channels();
1514 // clear the player list
1515 multi_pxo_clear_players();
1517 // initialize the chat system
1518 multi_pxo_chat_init();
1521 multi_pxo_ban_init();
1523 // load the animation up
1524 if (gr_screen.res == GR_1024) {
1525 char anim_filename[32] = "2_";
1526 SDL_strlcat(anim_filename, MULTI_PXO_ANIM_FNAME, SDL_arraysize(anim_filename));
1527 Multi_pxo_anim = anim_load(anim_filename);
1529 // if hi-res is not there, fallback to low
1530 if (Multi_pxo_anim == NULL) {
1531 Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME);
1534 Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME);
1537 // clear the status text
1538 multi_pxo_set_status_text("");
1540 // last refresh time
1541 Multi_pxo_channel_last_refresh = -1.0f;
1543 // server count last refresh time
1544 Multi_pxo_channel_server_refresh = -1.0f;
1547 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1550 multi_pxo_motd_init();
1552 // make sure we autojoin
1553 Multi_pxo_must_autojoin = 1;
1555 // clear all tracker channel related strings
1556 SDL_zero(Multi_fs_tracker_channel);
1557 SDL_zero(Multi_fs_tracker_filter);
1560 // do frame for the PXO screen
1563 pxo_channel priv_chan;
1566 if(Multi_pxo_connected) {
1567 multi_pxo_api_process();
1570 // process common stuff
1571 multi_pxo_process_common();
1573 switch(Multi_pxo_mode){
1574 // private channel join mode
1575 case MULTI_PXO_MODE_PRIVATE:
1576 switch(multi_pxo_priv_popup()){
1581 // user hit "cancel"
1583 // return to normal mode
1584 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1589 // setup some information
1590 memset(&priv_chan,0,sizeof(pxo_channel));
1591 priv_chan.num_users = 0;
1592 SDL_strlcpy(priv_chan.name, Multi_pxo_priv_chan, SDL_arraysize(priv_chan.name));
1594 // see if we know about this channel already
1595 multi_pxo_join_channel(&priv_chan);
1597 // return to normal mode
1598 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1604 case MULTI_PXO_MODE_FIND:
1605 switch(multi_pxo_find_popup()){
1610 // user hit "cancel"
1612 // return to normal mode
1613 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1618 // return to normal mode
1619 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1621 // if there is a valid channel name try and join it
1622 if(strlen(Multi_pxo_find_channel) && !SWITCHING_CHANNELS()){
1626 memset(&join,0,sizeof(pxo_channel));
1628 SDL_strlcpy(join.name, Multi_pxo_find_channel, SDL_arraysize(join.name));
1631 multi_pxo_join_channel(&join);
1637 case MULTI_PXO_MODE_NORMAL:
1638 multi_pxo_do_normal();
1643 // close the PXO screen
1644 void multi_pxo_close()
1646 // unload any bitmaps
1647 bm_unload(Multi_pxo_bitmap);
1648 bm_unload(Multi_pxo_com_bitmap);
1650 // record the last channel we were on, if any
1651 SDL_zero(Multi_fs_tracker_channel);
1652 SDL_zero(Multi_fs_tracker_filter);
1654 if( ON_CHANNEL() && strlen(Multi_pxo_channel_current.name) ){
1656 SDL_strlcpy(Multi_fs_tracker_channel, Multi_pxo_channel_current.name, SDL_arraysize(Multi_fs_tracker_channel));
1659 SDL_strlcpy(Multi_fs_tracker_filter, Multi_pxo_channel_current.name, SDL_arraysize(Multi_fs_tracker_filter));
1662 // disconnect from the server
1663 DisconnectFromChatServer();
1664 Multi_pxo_connected = 0;
1666 // unload the animation
1667 anim_release_all_instances(GS_STATE_PXO);
1668 Multi_pxo_anim_instance = NULL;
1669 if(Multi_pxo_anim != NULL){
1670 anim_free(Multi_pxo_anim);
1671 Multi_pxo_anim = NULL;
1674 // unload the palette for this screen
1675 multi_pxo_unload_palette();
1677 // destroy the UI_WINDOW
1678 Multi_pxo_window.destroy();
1680 // clear the channel list
1681 multi_pxo_clear_channels();
1683 // close the chat system
1684 multi_pxo_chat_free();
1687 multi_pxo_ban_close();
1690 // run normally (no popups)
1691 void multi_pxo_do_normal()
1694 int k = Multi_pxo_window.process();
1696 // if the animation isn't playing, start it up
1697 if((Multi_pxo_anim_instance == NULL) && (Multi_pxo_anim != NULL)){
1698 anim_play_struct aps;
1700 // fire up the animation
1701 anim_play_init(&aps, Multi_pxo_anim, MULTI_PXO_ANIM_X, MULTI_PXO_ANIM_Y);
1702 aps.screen_id = GS_STATE_PXO;
1703 aps.framerate_independent = 1;
1705 Multi_pxo_anim_instance = anim_play(&aps);
1708 // process any keypresses
1711 gamesnd_play_iface(SND_USER_SELECT);
1712 gameseq_post_event(GS_EVENT_MAIN_MENU);
1716 // check for button presses
1717 multi_pxo_check_buttons();
1719 // if we're not in a chatroom, disable and hide the chat input box
1721 Multi_pxo_chat_input.hide();
1722 Multi_pxo_chat_input.disable();
1724 Multi_pxo_chat_input.enable();
1725 Multi_pxo_chat_input.unhide();
1729 multi_pxo_blit_all();
1734 // verify version # now (only once per Freespace instance)
1735 if(Multi_pxo_must_verify_version){
1736 switch(multi_update_gobaby()){
1737 // everything is cool. Move along
1738 case MULTI_UPDATE_CONTINUE:
1739 Multi_pxo_must_verify_version = 0;
1742 // go back to the main menu
1743 case MULTI_UPDATE_MAIN_MENU:
1744 gameseq_post_event(GS_EVENT_MAIN_MENU);
1746 // unset these so we don't do anything else PXO related
1747 Multi_pxo_must_validate = 0;
1748 Multi_pxo_must_connect = 0;
1751 // freespace will be shutting down shortly
1752 case MULTI_UPDATE_SHUTTING_DOWN:
1757 // if we need to get tracker info for ourselves, do so
1758 if(Multi_pxo_must_validate){
1759 // initialize the master tracker API for Freespace
1760 multi_fs_tracker_init();
1762 // validate the current player with the master tracker (will create the pilot on the MT if necessary)
1763 validate_code = multi_fs_tracker_validate(0);
1764 if(validate_code != 1){
1765 // show an error popup if it failed (not cancelled by the user)
1766 if (validate_code == 0) {
1767 switch (popup(PF_USE_AFFIRMATIVE_ICON | PF_WEB_CURSOR_1 | PF_WEB_CURSOR_2, 3, POPUP_CANCEL,XSTR("&Create Acct",936), XSTR("&Verify Acct",937), XSTR("PXO Login not accepted. You may visit the Parallax Online website to create or verify your login. Or you may click Cancel to play without using the Parallax Online service. (You may switch back to Parallax Online from the Options Menu under the Multi tab.)",938))) {
1769 nprintf(("Network","PXO CANCEL\n"));
1771 // flip his "pxo" bit temporarily and push him to the join game screen
1772 Multi_options_g.pxo = 0;
1773 // Net_game_tcp_mode = NET_TCP;
1774 gameseq_post_event(GS_EVENT_MULTI_JOIN_GAME);
1778 nprintf(("Network","PXO CREATE\n"));
1779 // fire up the given URL
1780 multi_pxo_url(Multi_options_g.pxo_create_url);
1784 nprintf(("Network","PXO VERIFY\n"));
1785 // fire up the given URL
1786 multi_pxo_url(Multi_options_g.pxo_verify_url);
1791 // go back to the main hall
1792 gameseq_post_event(GS_EVENT_MAIN_MENU);
1794 Multi_pxo_must_connect = 0;
1795 Multi_pxo_must_validate = 0;
1797 // now we have to conenct to PXO
1799 Multi_pxo_must_connect = 1;
1800 Multi_pxo_must_validate = 0;
1804 // if we need to connect, do so now
1805 if(Multi_pxo_must_connect){
1806 // for now, just try once
1807 Multi_pxo_connected = multi_pxo_connect();
1809 // if we successfully connected, send a request for a list of channels on the server
1810 if(Multi_pxo_connected){
1811 multi_pxo_get_channels();
1814 multi_pxo_set_status_text(XSTR("Retrieving Public Channels",939));
1817 multi_pxo_set_status_text(XSTR("Failed to connect to Parallax Online",940));
1820 // no longer need to connect
1821 Multi_pxo_must_connect = 0;
1825 // blit everything on the "normal" screen
1826 void multi_pxo_blit_all()
1828 // draw the background, etc
1830 // GR_MAYBE_CLEAR_RES(Multi_pxo_bitmap);
1831 int bmap = Multi_pxo_bitmap;
1836 bm_get_info( bmap, &bmw, &bmh);
1837 if((bmw != gr_screen.max_w) || (bmh != gr_screen.max_h)){
1844 if(Multi_pxo_bitmap != -1){
1845 gr_set_bitmap(Multi_pxo_bitmap);
1848 Multi_pxo_window.draw();
1850 // display the channel list
1851 multi_pxo_blit_channels();
1853 // display the player list
1854 multi_pxo_blit_players();
1856 // blit the chat text
1857 multi_pxo_chat_blit();
1859 // blit the status text
1860 multi_pxo_blit_status_text();
1862 // blit and process the notification string
1863 multi_pxo_notify_blit();
1865 // any bitmap or info or whatever
1866 multi_pxo_ban_draw();
1868 // draw any motd stuff
1869 multi_pxo_motd_maybe_blit();
1871 // if we have a valid animation handle, play it
1872 if(Multi_pxo_anim_instance != NULL){
1873 anim_render_all(GS_STATE_PXO,flFrametime);
1877 // process common stuff
1878 void multi_pxo_process_common()
1880 // process the channel list (select, etc)
1881 multi_pxo_process_channels();
1883 // process the player list (select, etc)
1884 multi_pxo_process_players();
1886 // process chat controls
1887 multi_pxo_chat_process();
1889 // process http download details
1890 multi_pxo_ban_process();
1893 // get selected player information
1894 void multi_pxo_get_data(char *name)
1898 // handle being kicked
1899 void multi_pxo_handle_kick()
1901 // remove ourselves from the room
1902 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
1903 Multi_pxo_channel_current.num_users = -1;
1906 multi_pxo_chat_clear();
1908 // clear the old player list
1909 multi_pxo_clear_players();
1911 // add a notification string
1912 multi_pxo_notify_add(XSTR("You have been kicked",941));
1915 // handle being disconnected
1916 void multi_pxo_handle_disconnect()
1918 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been disconnected from the server",942));
1919 gameseq_post_event(GS_EVENT_MAIN_MENU);
1922 // return string2, which is the first substring of string 1 without a space
1923 // it is safe to pass the same pointer for both parameters
1924 void multi_pxo_strip_space(char *string1, char *string2, const int str2_len)
1929 // copy the original
1930 SDL_strlcpy(midway, string1, SDL_arraysize(midway));
1931 tok = strtok(midway," ");
1933 SDL_strlcpy(string2, tok, str2_len);
1935 SDL_strlcpy(string2, "", str2_len);
1939 // fire up the given URL
1940 void multi_pxo_url(char *url)
1945 // execute the shell command
1946 int r = (int) ShellExecute(NULL, NOX("open"), url, NULL, NULL, SW_SHOW);
1950 case ERROR_BAD_FORMAT:
1951 case SE_ERR_ACCESSDENIED:
1952 case SE_ERR_ASSOCINCOMPLETE:
1953 case SE_ERR_DDEBUSY:
1954 case SE_ERR_DDEFAIL:
1955 case SE_ERR_DDETIMEOUT:
1956 case SE_ERR_DLLNOTFOUND:
1959 case SE_ERR_NOASSOC:
1960 case ERROR_FILE_NOT_FOUND:
1961 case ERROR_PATH_NOT_FOUND:
1962 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,XSTR("Warning\nCould not locate/launch default Internet Browser",943));
1969 // load/set the palette
1970 void multi_pxo_load_palette()
1973 #ifndef HARDWARE_ONLY
1974 palette_use_bm_palette(Multi_pxo_palette);
1978 // unload the palette
1979 void multi_pxo_unload_palette()
1981 // unload the palette if it exists
1982 if(Multi_pxo_palette != -1){
1983 bm_release(Multi_pxo_palette);
1984 Multi_pxo_palette = -1;
1988 // if we're currently on a private channel
1989 int multi_pxo_on_private_channel()
1991 // if we're connected to a channel with the "+" symbol on front
1992 if(ON_CHANNEL() && (Multi_pxo_channel_current.name[0] == '+')){
1996 // otherwise return falos
2000 // convert string 1 into string 2, substituting underscores for spaces
2001 void multi_pxo_underscore_nick(char *string1, char *string2, const int str2_len)
2003 char nick_temp[512];
2006 // don't do anything if we have bogus string
2007 if((string1 == NULL) || (string2 == NULL)){
2011 // copy the nickname
2012 SDL_strlcpy(nick_temp, string1, SDL_arraysize(nick_temp));
2014 // get the first token
2015 tok = strtok(nick_temp, " ");
2017 SDL_strlcpy(string2, tok, str2_len);
2019 // get the next token
2020 tok = strtok(NULL," ");
2023 SDL_strlcat(string2, "_", str2_len);
2024 SDL_strlcat(string2, tok, str2_len);
2027 tok = strtok(NULL," ");
2030 SDL_strlcpy(string2, string1, str2_len);
2034 // if the command is a potential "nick" command
2035 int multi_pxo_is_nick_command(char *msg)
2040 // get the first token in the message
2041 SDL_strlcpy(tmp, msg, SDL_arraysize(tmp));
2042 tok = strtok(tmp," ");
2044 // can't be a nick message
2048 return !SDL_strcasecmp(tok,NOX("/nick"));
2051 // check for button presses
2052 void multi_pxo_check_buttons()
2056 // go through all buttons
2057 for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
2058 if(Multi_pxo_buttons[gr_screen.res][idx].button.pressed()){
2059 multi_pxo_button_pressed(idx);
2065 // handle a button press
2066 void multi_pxo_button_pressed(int n)
2069 case MULTI_PXO_EXIT:
2070 gamesnd_play_iface(SND_USER_SELECT);
2071 gameseq_post_event(GS_EVENT_MAIN_MENU);
2074 case MULTI_PXO_CHAN_UP:
2075 multi_pxo_scroll_channels_up();
2078 case MULTI_PXO_CHAN_DOWN:
2079 multi_pxo_scroll_channels_down();
2082 case MULTI_PXO_TEXT_UP:
2083 multi_pxo_scroll_chat_up();
2086 case MULTI_PXO_TEXT_DOWN:
2087 multi_pxo_scroll_chat_down();
2090 case MULTI_PXO_PLIST_UP:
2091 multi_pxo_scroll_players_up();
2092 multi_pxo_chat_adjust_start();
2095 case MULTI_PXO_PLIST_DOWN:
2096 multi_pxo_scroll_players_down();
2097 multi_pxo_chat_adjust_start();
2100 case MULTI_PXO_JOIN:
2101 // if there are no channels to join, let the user know
2102 if((Multi_pxo_channel_count == 0) || (Multi_pxo_channels == NULL)){
2103 gamesnd_play_iface(SND_GENERAL_FAIL);
2104 multi_pxo_notify_add(XSTR("No channels!",944));
2108 // if we're not already trying to join, allow this
2109 if(!SWITCHING_CHANNELS() && (Multi_pxo_channel_select != NULL)){
2110 gamesnd_play_iface(SND_USER_SELECT);
2111 multi_pxo_join_channel(Multi_pxo_channel_select);
2113 multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2114 gamesnd_play_iface(SND_GENERAL_FAIL);
2118 case MULTI_PXO_GAMES:
2119 // move to the join game screen as normally (temporary!)
2120 gameseq_post_event( GS_EVENT_MULTI_JOIN_GAME );
2123 case MULTI_PXO_JOIN_PRIV:
2124 // if we're not already trying to join, allow this
2125 if(!SWITCHING_CHANNELS()){
2126 gamesnd_play_iface(SND_USER_SELECT);
2128 // fire up the private join popup
2129 multi_pxo_priv_popup();
2131 multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2132 gamesnd_play_iface(SND_GENERAL_FAIL);
2136 case MULTI_PXO_FIND:
2137 gamesnd_play_iface(SND_USER_SELECT);
2139 // fire up the find join popup
2140 multi_pxo_find_popup();
2143 case MULTI_PXO_HELP:
2144 gamesnd_play_iface(SND_USER_SELECT);
2145 gameseq_post_event(GS_EVENT_PXO_HELP);
2148 case MULTI_PXO_PINFO:
2151 // if we have a guy selected, try and get his info
2152 if(Multi_pxo_player_select != NULL){
2153 // if we successfully got info for this guy
2154 if(multi_pxo_pinfo_get(Multi_pxo_player_select->name)){
2156 multi_pxo_pinfo_show();
2158 // if we didn't get stats for this guy.
2160 SDL_snprintf(stats, SDL_arraysize(stats), XSTR("Could not get stats for %s\n(May not be a registered pilot)", 946), Multi_pxo_player_select->name);
2161 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,stats);
2164 gamesnd_play_iface(SND_GENERAL_FAIL);
2168 case MULTI_PXO_RANKINGS:
2169 // make sure he doesn't click it too many times
2170 if((Multi_pxo_ranking_last < 0.0f) || ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_ranking_last) > MULTI_PXO_RANK_TIME) ){
2171 gamesnd_play_iface(SND_USER_SELECT);
2174 multi_pxo_url(Multi_options_g.pxo_rank_url);
2176 // mark the time down
2177 Multi_pxo_ranking_last = f2fl(timer_get_fixed_seconds());
2179 gamesnd_play_iface(SND_GENERAL_FAIL);
2183 case MULTI_PXO_MOTD:
2184 // maybe fire up the pxo motd dialog
2185 multi_pxo_motd_dialog();
2190 // condition function for popup_do_with_condition for connected to Parallax Online
2191 int mpxo_failed = 0;
2192 int multi_pxo_connect_do()
2195 char id_string[255] = "";
2196 char ip_string[255] = "";
2198 // if we already tried and failed, sit around until the user presses cancel
2200 // try and connect to the server
2203 // build the tracker id string
2204 SDL_snprintf(id_string, SDL_arraysize(id_string), "%s %s", Multi_tracker_id_string, Player->callsign);
2206 // build the ip string
2207 SDL_snprintf(ip_string, SDL_arraysize(ip_string), "%s:%d", Multi_options_g.pxo_ip, PXO_CHAT_PORT);
2209 // connect to the server
2210 ret_code = ConnectToChatServer(ip_string, Multi_pxo_nick, id_string);
2212 // give some time to the pxo api.
2213 multi_pxo_api_process();
2216 // already connected, return success
2220 // failed to connect, return fail
2223 popup_change_text(XSTR("Failed to connect to Parallax Online!", 947));
2226 // connected, return success
2239 // popup loop which does an autojoin of a public channel. Returns when the autojoin process is complete
2240 int multi_pxo_autojoin_do()
2242 pxo_channel last_channel;
2244 // if we need to autojoin, do so now
2245 if(Multi_pxo_must_autojoin){
2246 Multi_pxo_must_autojoin = 0;
2248 // if we're supposed to be using a (valid) "last" channel, do so
2249 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){
2251 memset(&last_channel, 0, sizeof(pxo_channel));
2252 last_channel.num_users = 0;
2253 SDL_strlcpy(last_channel.name, Multi_pxo_channel_last, SDL_arraysize(last_channel.name));
2256 multi_pxo_join_channel(&last_channel);
2258 nprintf(("Network","PXO : using last channel\n"));
2260 multi_pxo_autojoin();
2262 nprintf(("Network","PXO : using autojoin channel\n"));
2264 multi_pxo_get_channels();
2267 // give some time to the pxo api.
2268 multi_pxo_api_process();
2269 multi_pxo_process_common();
2271 // next value is not -1 when actually switching channels, so keep processing by returning 0.
2272 if ( SWITCHING_CHANNELS() ){
2276 // couldn't switch channel for some reason. bail out with -1
2277 if ( !ON_CHANNEL() ){
2285 // attempt to connect to Parallax Online, return success or fail
2286 int multi_pxo_connect()
2289 char join_fail_str[256];
2291 // intiialize chat api
2294 // set us to "must autojoin"
2295 Multi_pxo_must_autojoin = 1;
2297 // run the connect dialog/popup
2299 if(popup_till_condition(multi_pxo_connect_do, XSTR("&Cancel", 779), XSTR("Logging into Parallax Online",949)) == 10){
2302 // if we're going to use the "last" channel
2303 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){
2304 SDL_strlcpy(join_str, XSTR("Joining last channel (",982), SDL_arraysize(join_str));
2305 SDL_strlcat(join_str, Multi_pxo_channel_last + 1, SDL_arraysize(join_str));
2306 SDL_strlcat(join_str, ")", SDL_arraysize(join_str));
2308 SDL_strlcpy(join_fail_str, XSTR("Unable to join last channel", 983), SDL_arraysize(join_fail_str));
2310 SDL_strlcpy(join_str, XSTR("Autojoining public channel", 984), SDL_arraysize(join_str));
2311 SDL_strlcpy(join_fail_str, XSTR("Unable to autojoin public channel", 985), SDL_arraysize(join_fail_str));
2314 // once connected, we should do an autojoin before allowing the guy to continue.
2315 rval = popup_till_condition( multi_pxo_autojoin_do, XSTR("&Cancel", 779), join_str );
2320 popup( PF_USE_AFFIRMATIVE_ICON, 1, XSTR("OK", 1492), join_fail_str);
2323 // otherwise disconnect just to be safe
2324 DisconnectFromChatServer();
2325 gameseq_post_event(GS_EVENT_MAIN_MENU);
2327 // did not successfully connect
2331 // run the networking functions for the PXO API
2332 void multi_pxo_api_process()
2337 pxo_channel *lookup;
2339 // give some time to psnet
2340 PSNET_TOP_LAYER_PROCESS();
2342 // give some time to the game tracker API
2345 // give some time to the user tracker API
2348 // get any incoming text
2354 // process the chat line
2355 multi_pxo_chat_process_incoming(p);
2359 // get any incoming channel list stuff
2360 p = GetChannelList();
2363 // nprintf(("Network","%s\n",p));
2364 multi_pxo_make_channels(p);
2367 // process any chat commands
2368 cmd = GetChatCommand();
2371 switch(cmd->command)
2373 case CC_USER_JOINING:
2374 // add a user, if he doesn't already exist
2375 if(multi_pxo_find_player(cmd->data) == NULL){
2376 multi_pxo_add_player(cmd->data);
2379 // increase the player count
2381 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2383 lookup->num_users++;
2388 case CC_USER_LEAVING:
2390 multi_pxo_del_player(cmd->data);
2392 // add a text message
2393 SDL_snprintf(msg_str, SDL_arraysize(msg_str), XSTR("*** %s has left", 950), cmd->data);
2394 multi_pxo_chat_process_incoming(msg_str);
2396 // decrease the player count
2398 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2400 lookup->num_users--;
2405 case CC_DISCONNECTED:
2406 multi_pxo_handle_disconnect();
2410 multi_pxo_handle_kick();
2413 case CC_NICKCHANGED:
2414 // process a nick change
2415 multi_pxo_process_nick_change(cmd->data);
2418 case CC_YOURCHANNEL:
2419 // copy the current channel info, and unset the switching status
2420 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2421 Multi_pxo_channel_switch.num_users = -1;
2423 SetNewChatChannel(NULL);
2425 SDL_strlcpy(Multi_pxo_channel_current.name, cmd->data, SDL_arraysize(Multi_pxo_channel_current.name));
2427 // if we don't already have this guy on the list, add him
2428 pxo_channel *lookup;
2429 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2431 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2432 lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2435 // set the user count to be 0
2437 lookup->num_users = 0;
2440 // set our "last" channel to be this one
2441 SDL_strlcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name, SDL_arraysize(Multi_pxo_channel_last));
2443 // refresh current channel server count
2444 multi_pxo_channel_refresh_current();
2446 // clear the chat area
2447 // multi_pxo_chat_clear();
2454 cmd = GetChatCommand();
2457 // handle any processing details if we're currently trying to join a channel
2458 multi_pxo_handle_channel_change();
2461 // process a "nick" change event
2462 void multi_pxo_process_nick_change(char *data)
2465 player_list *lookup;
2467 // get the new string
2468 from = strtok(data," ");
2469 to = strtok(NULL,"");
2470 if((from != NULL) && (to != NULL)){
2471 lookup = multi_pxo_find_player(from);
2473 SDL_strlcpy(lookup->name, to, SDL_arraysize(lookup->name));
2475 // if this is also my nick, change it
2476 if(!SDL_strcasecmp(Multi_pxo_nick,from)){
2477 SDL_strlcpy(Multi_pxo_nick, to, SDL_arraysize(Multi_pxo_nick));
2483 // autojoin an appropriate channel
2484 void multi_pxo_autojoin()
2488 memset(&sw,0,sizeof(pxo_channel));
2490 SDL_strlcpy(sw.name, MULTI_PXO_AUTOJOIN_CHANNEL, SDL_arraysize(sw.name));
2492 // if we found a valid room, attempt to join it
2493 multi_pxo_join_channel(&sw);
2496 // does the string match the "autojoin" prefic
2497 int multi_pxo_is_autojoin(char *name)
2499 // check to see if the name is long enough
2500 if(strlen(name) < strlen(MULTI_PXO_AUTOJOIN_PREFIX)){
2504 // check to see if the first n chars match
2505 return !SDL_strncasecmp(name,MULTI_PXO_AUTOJOIN_PREFIX,strlen(MULTI_PXO_AUTOJOIN_PREFIX));
2508 // called from the game tracker API - server count update for a channel
2509 void multi_pxo_channel_count_update(char *name,int count)
2511 pxo_channel *lookup;
2513 // lookup the channel name on the normal list
2515 lookup = multi_pxo_find_channel(name,Multi_pxo_channels);
2517 lookup->num_servers = (ushort)count;
2519 nprintf(("Network","PXO : updated channel %s server count to %d\n",name,count));
2523 // status bar stuff -----------------------------------------------
2525 // set the status text
2526 void multi_pxo_set_status_text(const char *txt)
2529 SDL_strlcpy(Multi_pxo_status_text, txt, SDL_arraysize(Multi_pxo_status_text));
2531 // make sure it fits properly
2532 gr_force_fit_string(Multi_pxo_status_text, 254, Multi_pxo_status_coords[gr_screen.res][2]);
2535 // blit the status text
2536 void multi_pxo_blit_status_text()
2540 // center and draw the text
2541 if(strlen(Multi_pxo_status_text)) {
2542 gr_set_color_fast(&Color_bright);
2543 gr_get_string_size(&w, NULL, Multi_pxo_status_text);
2544 gr_string(Multi_pxo_status_coords[gr_screen.res][0] + ((Multi_pxo_status_coords[gr_screen.res][2] - w)/2), Multi_pxo_status_coords[gr_screen.res][1], Multi_pxo_status_text);
2549 // channel related stuff -------------------------------------------
2551 // get a list of channels on the server
2552 void multi_pxo_get_channels()
2554 SendChatString(NOX("/list"));
2557 // clear the old channel list
2558 void multi_pxo_clear_channels()
2560 pxo_channel *moveup,*backup;
2562 // only clear a non-null list
2563 if(Multi_pxo_channels != NULL){
2565 moveup = Multi_pxo_channels;
2570 moveup = moveup->next;
2572 // free the struct itself
2575 } while(moveup != Multi_pxo_channels);
2576 Multi_pxo_channels = NULL;
2579 // head of the list of available channels
2580 Multi_pxo_channels = NULL;
2581 Multi_pxo_channel_count = 0;
2583 // item we're going to start displaying at
2584 Multi_pxo_channel_start = NULL;
2585 Multi_pxo_channel_start_index = -1;
2587 // items we've currently got selected
2588 Multi_pxo_channel_select = NULL;
2592 // parse the input string and make a list of new channels
2593 void multi_pxo_make_channels(char *chan_str)
2595 char *name_tok,*user_tok,*desc_tok;
2597 pxo_channel *lookup;
2600 nprintf(("Network","Making some channels!\n"));
2602 // clear the channel list
2603 // multi_pxo_clear_channels();
2605 // set the last get time
2606 Multi_pxo_channel_last_refresh = f2fl(timer_get_fixed_seconds());
2608 name_tok = strtok(chan_str," ");
2609 if(name_tok == NULL){
2614 // parse the user count token
2615 user_tok = strtok(NULL," ");
2617 // parse the channel description token
2618 desc_tok = strtok(NULL,"$");
2620 // something invalid in the data, return here.....
2621 if((name_tok == NULL) || (user_tok == NULL) || (desc_tok == NULL)){
2625 // get the # of users
2627 num_users = (ubyte)atoi(user_tok);
2629 // if the # of users is > 0, or its not an autojoin, place it on the display list
2630 if((num_users > 0) || !multi_pxo_is_autojoin(name_tok)){
2631 // see if it exists already, and if so, just update the user count
2632 lookup = multi_pxo_find_channel(name_tok,Multi_pxo_channels);
2635 lookup->num_users = (short)num_users;
2639 res = multi_pxo_add_channel(name_tok,&Multi_pxo_channels);
2641 //Multi_pxo_channel_count++;
2642 res->num_users = (short)num_users;
2643 SDL_strlcpy(res->desc, desc_tok, SDL_arraysize(res->desc));
2648 // get the next name token
2649 name_tok = strtok(NULL," ");
2650 } while(name_tok != NULL);
2652 // if we need to autojoin, do so now
2653 //if(Multi_pxo_must_autojoin){
2654 // Multi_pxo_must_autojoin = 0;
2656 // multi_pxo_autojoin();
2660 multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
2662 // if we haven't refreshed server counts yet, do it now
2663 if(Multi_pxo_channel_server_refresh < 0.0f){
2664 multi_pxo_channel_refresh_servers();
2667 // if we don't already have this guy on the list, add him
2669 pxo_channel *lookup;
2670 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2672 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2673 multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2678 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2679 pxo_channel *multi_pxo_add_channel(char *name,pxo_channel **list)
2681 pxo_channel *new_channel;
2683 // try and allocate a new pxo_channel struct
2684 new_channel = (pxo_channel *)malloc(sizeof(pxo_channel));
2685 if ( new_channel == NULL ) {
2686 nprintf(("Network", "Cannot allocate space for new pxo_channel structure\n"));
2689 memset(new_channel,0,sizeof(pxo_channel));
2690 // try and allocate a string for the channel name
2691 SDL_strlcpy(new_channel->name, name, SDL_arraysize(new_channel->name));
2693 // insert it on the list
2694 if ( *list != NULL ) {
2695 new_channel->next = (*list)->next;
2696 new_channel->next->prev = new_channel;
2697 (*list)->next = new_channel;
2698 new_channel->prev = *list;
2700 *list = new_channel;
2701 (*list)->next = (*list)->prev = *list;
2704 Multi_pxo_channel_count++;
2708 // lookup a channel with the specified name
2709 pxo_channel *multi_pxo_find_channel(char *name,pxo_channel *list)
2711 pxo_channel *moveup;
2713 // look the sucker up
2719 if(!SDL_strcasecmp(name,moveup->name)){
2723 moveup = moveup->next;
2724 } while((moveup != list) && (moveup != NULL));
2729 // process the channel list (select, etc)
2730 void multi_pxo_process_channels()
2735 // if we don't have a start item, but the list is non-null
2736 if((Multi_pxo_channel_start == NULL) && (Multi_pxo_channels != NULL)){
2737 Multi_pxo_channel_start = Multi_pxo_channels;
2738 Multi_pxo_channel_start_index = 0;
2741 // if we don't have a selected item, but the list is non-null
2742 if((Multi_pxo_channel_select == NULL) && (Multi_pxo_channels != NULL)){
2743 Multi_pxo_channel_select = Multi_pxo_channels;
2746 multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2749 // if the "switch" delay timestamp is set, see if it has expired
2750 if((Multi_pxo_switch_delay != -1) && timestamp_elapsed(Multi_pxo_switch_delay)){
2751 Multi_pxo_switch_delay = -1;
2754 // see if we have a mouse click on the channel region
2755 if(Multi_pxo_channel_button.pressed()){
2756 Multi_pxo_channel_button.get_mouse_pos(NULL,&my);
2758 // index from the top
2759 item_index = my / 10;
2761 // select the item if possible
2762 if((item_index + Multi_pxo_channel_start_index) < Multi_pxo_channel_count){
2763 Multi_pxo_channel_select = Multi_pxo_channel_start;
2764 for(idx=0;idx<item_index;idx++){
2765 Multi_pxo_channel_select = Multi_pxo_channel_select->next;
2769 multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2773 // last refresh time
2774 if((Multi_pxo_channel_last_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_last_refresh) > CHANNEL_REFRESH_TIME) ){
2776 multi_pxo_set_status_text(XSTR("Refreshing Public Channel List",952));
2778 // get a list of channels on the server (clear any old list as well)
2779 multi_pxo_get_channels();
2782 Multi_pxo_channel_last_refresh = -1.0f;
2784 nprintf(("Network","Refreshing channels\n"));
2787 // if we haven't updated our server channel counts in a while, do so again
2788 // last refresh time
2789 if((Multi_pxo_channel_server_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_server_refresh) > CHANNEL_SERVER_REFRESH_TIME) ){
2790 // refresh server counts
2791 // multi_pxo_set_status_text("Refreshing Public Channel Server Counts");
2793 // do it _NOW_ I"M RIGHT HERE KILL ME WHAT ARE YOU WAITING FOR DO IT KILL ME DO IT NOW!
2794 multi_pxo_channel_refresh_servers();
2798 // send a request to refresh our channel server counts
2799 void multi_pxo_channel_refresh_servers()
2801 pxo_channel *lookup;
2802 filter_game_list_struct filter;
2804 // traverse the list of existing channels we know about and query the game tracker about them
2805 lookup = Multi_pxo_channels;
2810 if(strlen(lookup->name)){
2812 memset(&filter,0,sizeof(filter_game_list_struct));
2813 SDL_strlcpy(filter.channel, lookup->name, SDL_arraysize(filter.channel));
2816 RequestGameCountWithFilter(&filter);
2820 lookup = lookup->next;
2821 } while((lookup != NULL) && (lookup != Multi_pxo_channels));
2824 Multi_pxo_channel_server_refresh = f2fl(timer_get_fixed_seconds());
2827 // refresh current channel server count
2828 void multi_pxo_channel_refresh_current()
2830 // send a request for a server count on this channel
2831 if(strlen(Multi_pxo_channel_current.name)){
2833 filter_game_list_struct filter;
2834 memset(&filter,0,sizeof(filter_game_list_struct));
2835 SDL_strlcpy(filter.channel, Multi_pxo_channel_current.name, SDL_arraysize(filter.channel));
2838 RequestGameCountWithFilter(&filter);
2842 // display the channel list
2843 void multi_pxo_blit_channels()
2845 pxo_channel *moveup;
2846 char chan_name[255];
2847 char chan_users[15];
2848 char chan_servers[15];
2849 int user_w,server_w;
2850 int disp_count,y_start;
2852 // blit as many channels as we can
2854 y_start = Multi_pxo_chan_coords[gr_screen.res][1];
2855 moveup = Multi_pxo_channel_start;
2860 // if this is the currently selected item, highlight it
2861 if(moveup == Multi_pxo_channel_select){
2862 gr_set_color_fast(&Color_bright);
2864 // otherwise draw it normally
2866 gr_set_color_fast(&Color_normal);
2869 // get the # of users on the channel
2870 SDL_snprintf(chan_users, SDL_arraysize(chan_users), "%d", moveup->num_users);
2872 // get the width of the user count string
2873 gr_get_string_size(&user_w, NULL, chan_users);
2875 // get the # of servers on the channel
2876 SDL_snprintf(chan_servers, SDL_arraysize(chan_servers), "%d", moveup->num_servers);
2878 // get the width of the user count string
2879 gr_get_string_size(&server_w, NULL, chan_servers);
2881 // make sure the name fits
2882 SDL_assert(moveup->name);
2883 SDL_strlcpy(chan_name, moveup->name, SDL_arraysize(chan_name));
2884 gr_force_fit_string(chan_name, 254, Multi_pxo_chan_coords[gr_screen.res][2] - Multi_pxo_chan_column_offsets[gr_screen.res][CHAN_PLAYERS_COLUMN]);
2887 gr_string(Multi_pxo_chan_coords[gr_screen.res][0], y_start, chan_name + 1);
2888 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);
2889 gr_set_color_fast(&Color_bright);
2890 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);
2892 // increment the displayed count
2897 moveup = moveup->next;
2898 } while((moveup != Multi_pxo_channels) && (disp_count < Multi_pxo_max_chan_display[gr_screen.res]));
2901 // scroll channel list up
2902 void multi_pxo_scroll_channels_up()
2904 // if we're already at the head of the list, do nothing
2905 if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start == Multi_pxo_channels)){
2906 gamesnd_play_iface(SND_GENERAL_FAIL);
2910 // otherwise move up one
2911 Multi_pxo_channel_start = Multi_pxo_channel_start->prev;
2912 Multi_pxo_channel_start_index--;
2913 gamesnd_play_iface(SND_USER_SELECT);
2916 // scroll channel list down
2917 void multi_pxo_scroll_channels_down()
2919 // if we're already at the tail of the list, do nothing
2920 if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start->next == Multi_pxo_channels)){
2921 gamesnd_play_iface(SND_GENERAL_FAIL);
2925 // if we can't scroll further without going past the end of the viewable list, don't
2926 if((Multi_pxo_channel_start_index + Multi_pxo_max_chan_display[gr_screen.res]) >= Multi_pxo_channel_count){
2927 gamesnd_play_iface(SND_GENERAL_FAIL);
2931 // otherwise move down one
2932 Multi_pxo_channel_start = Multi_pxo_channel_start->next;
2933 Multi_pxo_channel_start_index++;
2934 gamesnd_play_iface(SND_USER_SELECT);
2937 // attempt to join a channel
2938 void multi_pxo_join_channel(pxo_channel *chan)
2940 char switch_msg[256];
2942 // if we're already on this channel, do nothing
2943 if(ON_CHANNEL() && !SDL_strcasecmp(chan->name,Multi_pxo_channel_current.name)){
2947 // if we're already trying to join a channel, do nothing
2948 if(SWITCHING_CHANNELS()){
2952 // try and join the channel
2953 switch(SetNewChatChannel(chan->name)){
2959 // decrement the count of our current channel
2960 pxo_channel *lookup;
2961 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2963 lookup->num_users--;
2966 // set our current channel as none
2967 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2968 Multi_pxo_channel_current.num_users = -1;
2970 multi_pxo_set_status_text(XSTR("Switching channels",953));
2973 memcpy(&Multi_pxo_channel_switch,chan,sizeof(pxo_channel));
2975 // clear the player list
2976 multi_pxo_clear_players();
2978 // display a line of text indicating that we're switching channels
2979 if(strlen(Multi_pxo_channel_switch.name) > 1){
2980 SDL_snprintf(switch_msg, SDL_arraysize(switch_msg), "[Switching to channel %s]", Multi_pxo_channel_switch.name + 1);
2982 SDL_snprintf(switch_msg, SDL_arraysize(switch_msg), "[Switching to channel %s]", Multi_pxo_channel_switch.name);
2984 multi_pxo_chat_process_incoming(switch_msg, CHAT_MODE_CHANNEL_SWITCH);
2992 // handle any processing details if we're currently trying to join a channel
2993 void multi_pxo_handle_channel_change()
2995 // if we're not switching channels, do nothing
2996 if(!SWITCHING_CHANNELS()){
3000 // if we are, check the status
3001 switch(SetNewChatChannel(NULL)){
3004 // unset our switching struct
3005 memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
3006 Multi_pxo_channel_switch.num_users = -1;
3009 multi_pxo_set_status_text(XSTR("No channel (error while switching)",954));
3016 // successfully changed
3018 // copy the current channel info, and unset the switching status
3019 memcpy(&Multi_pxo_channel_current,&Multi_pxo_channel_switch,sizeof(pxo_channel));
3020 Multi_pxo_channel_switch.num_users = -1;
3022 // set our "last" channel
3023 SDL_strlcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name, SDL_arraysize(Multi_pxo_channel_last));
3026 multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
3028 // if we don't already have this guy on the list, add him
3029 pxo_channel *lookup;
3030 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
3032 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
3033 lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
3036 // set the user count to be 1 (just me)
3038 lookup->num_users = 1;
3041 // clear the chat area
3042 // multi_pxo_chat_clear();
3044 // set the "switch" delay timestamp
3045 Multi_pxo_switch_delay = timestamp(MULTI_PXO_SWITCH_DELAY_TIME);
3047 // refresh current channel server count
3048 multi_pxo_channel_refresh_current();
3054 // player related stuff -------------------------------------------
3056 // clear the old player list
3057 void multi_pxo_clear_players()
3059 player_list *moveup,*backup;
3061 // if the list is null, don't free it up
3062 if(Multi_pxo_players != NULL){
3064 moveup = Multi_pxo_players;
3069 moveup = moveup->next;
3071 // free the struct itself
3074 } while(moveup != Multi_pxo_players);
3075 Multi_pxo_players = NULL;
3079 Multi_pxo_player_start = NULL;
3080 // Multi_pxo_player_start_index = -1;
3081 Multi_pxo_player_select = NULL;
3084 // Multi_pxo_player_slider.set_numberItems(0);
3086 // add a bunch of bogus players
3089 for(int idx=0;idx<30;idx++){
3090 sprintf(str,"player%d",idx);
3091 multi_pxo_add_player(str);
3096 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
3097 player_list *multi_pxo_add_player(char *name)
3099 player_list *new_player;
3101 // try and allocate a new player_list struct
3102 new_player = (player_list *)malloc(sizeof(player_list));
3103 if ( new_player == NULL ) {
3104 nprintf(("Network", "Cannot allocate space for new player_list structure\n"));
3107 // try and allocate a string for the channel name
3108 SDL_strlcpy(new_player->name, name, SDL_arraysize(new_player->name));
3110 // insert it on the list
3111 if ( Multi_pxo_players != NULL ) {
3112 new_player->next = Multi_pxo_players->next;
3113 new_player->next->prev = new_player;
3114 Multi_pxo_players->next = new_player;
3115 new_player->prev = Multi_pxo_players;
3117 Multi_pxo_players = new_player;
3118 Multi_pxo_players->next = Multi_pxo_players->prev = Multi_pxo_players;
3121 // increment the start position
3122 if(Multi_pxo_player_start != NULL){
3123 // Multi_pxo_player_start_index++;
3127 Multi_pxo_player_count++;
3129 // update the count on the slider
3130 // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3135 // remove a player with the given name
3136 void multi_pxo_del_player(char *name)
3138 player_list *lookup;
3140 // try and find this guy
3141 lookup = Multi_pxo_players;
3146 // if we found a match, delete it
3147 if(!SDL_strcasecmp(name,lookup->name)){
3148 // if this is the only item on the list, free stuff up
3149 if(lookup->next == lookup){
3150 SDL_assert(lookup == Multi_pxo_players);
3152 Multi_pxo_players = NULL;
3153 multi_pxo_clear_players();
3155 // otherwise, just delete it
3157 lookup->next->prev = lookup->prev;
3158 lookup->prev->next = lookup->next;
3160 // if this was our selected item, unselect it
3161 if((lookup == Multi_pxo_player_select) && (Multi_pxo_player_select != NULL)){
3162 Multi_pxo_player_select = Multi_pxo_player_select->next;
3165 // if this was our point to start viewing from, select another
3166 if(lookup == Multi_pxo_player_start){
3167 // if this is the head of the list, move up one
3168 if(Multi_pxo_players == lookup){
3169 Multi_pxo_player_start = Multi_pxo_player_start->next;
3170 // Multi_pxo_player_start_index = 0;
3172 // otherwise move back
3174 Multi_pxo_player_start = Multi_pxo_player_start->prev;
3175 // Multi_pxo_player_start_index++;
3179 // if this is the head of the list, move it up
3180 if(lookup == Multi_pxo_players){
3181 Multi_pxo_players = Multi_pxo_players->next;
3185 lookup->next = NULL;
3186 lookup->prev = NULL;
3191 Multi_pxo_player_count--;
3192 SDL_assert(Multi_pxo_player_count >= 0);
3194 // update the count on the slider
3195 // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3196 // Multi_pxo_player_slider.force_currentItem(Multi_pxo_player_start_index);
3203 lookup = lookup->next;
3204 } while((lookup != NULL) && (lookup != Multi_pxo_players));
3207 // try and find a player with the given name, return a pointer to his entry (or NULL)
3208 player_list *multi_pxo_find_player(char *name)
3210 player_list *lookup;
3212 // look through all players
3213 lookup = Multi_pxo_players;
3218 if(!SDL_strcasecmp(name,lookup->name)){
3222 lookup = lookup->next;
3223 } while((lookup != NULL) && (lookup != Multi_pxo_players));
3229 // process the player list (select, etc)
3230 void multi_pxo_process_players()
3233 player_list *lookup;
3235 // if we don't have a start item, but the list is non-null
3236 if((Multi_pxo_player_start == NULL) && (Multi_pxo_players != NULL)){
3237 Multi_pxo_player_start = Multi_pxo_players;
3238 // Multi_pxo_player_start_index = 0;
3240 // update the slider
3241 // Multi_pxo_player_slider.set_currentItem(Multi_pxo_player_start_index);
3244 // if we don't have a selected item, but the list is non-null
3245 if((Multi_pxo_player_select == NULL) && (Multi_pxo_players != NULL)){
3246 Multi_pxo_player_select = Multi_pxo_players;
3249 // see if we have a mouse click on the channel region
3250 if(Multi_pxo_player_button.pressed()){
3251 Multi_pxo_player_button.get_mouse_pos(NULL,&my);
3253 // index from the top
3254 item_index = my / 10;
3256 // select the item if possible
3257 lookup = Multi_pxo_player_start;
3262 if(item_index == 0){
3263 Multi_pxo_player_select = Multi_pxo_player_start;
3267 // move to the next item
3268 lookup = lookup->next;
3271 // if this item is our guy
3272 if((item_index == 0) && (lookup != Multi_pxo_players)){
3273 Multi_pxo_player_select = lookup;
3276 } while((lookup != Multi_pxo_players) && (item_index > 0));
3280 // display the player list
3281 void multi_pxo_blit_players()
3283 player_list *moveup;
3284 char player_name[255];
3285 int disp_count,y_start;
3287 // blit as many channels as we can
3289 y_start = Multi_pxo_player_coords[gr_screen.res][1];
3290 moveup = Multi_pxo_player_start;
3295 // if this is the currently selected item, highlight it
3296 if(moveup == Multi_pxo_player_select){
3297 gr_set_color_fast(&Color_bright);
3299 // otherwise draw it normally
3301 gr_set_color_fast(&Color_normal);
3304 // make sure the string fits
3305 SDL_strlcpy(player_name, moveup->name, SDL_arraysize(player_name));
3306 gr_force_fit_string(player_name, 254, Multi_pxo_player_coords[gr_screen.res][2]);
3309 gr_string(Multi_pxo_player_coords[gr_screen.res][0], y_start, player_name);
3311 // increment the displayed count
3316 moveup = moveup->next;
3317 } while((moveup != Multi_pxo_players) && (disp_count < Multi_pxo_max_player_display[gr_screen.res]));
3320 // scroll player list up
3321 void multi_pxo_scroll_players_up()
3323 // if we're already at the head of the list, do nothing
3324 if((Multi_pxo_player_start == NULL) || (Multi_pxo_player_start == Multi_pxo_players)){
3325 gamesnd_play_iface(SND_GENERAL_FAIL);
3329 // otherwise move up one
3330 Multi_pxo_player_start = Multi_pxo_player_start->prev;
3331 // Multi_pxo_player_start_index--;
3332 // SDL_assert(Multi_pxo_player_start_index >= 0);
3334 gamesnd_play_iface(SND_USER_SELECT);
3337 // scroll player list down
3338 void multi_pxo_scroll_players_down()
3340 player_list *lookup;
3343 // see if its okay to scroll down
3344 lookup = Multi_pxo_player_start;
3345 if(lookup == NULL ){
3346 gamesnd_play_iface(SND_GENERAL_FAIL);
3350 while(lookup->next != Multi_pxo_players){
3351 lookup = lookup->next;
3355 // if we can move down
3356 if(count >= Multi_pxo_max_player_display[gr_screen.res]){
3357 Multi_pxo_player_start = Multi_pxo_player_start->next;
3359 // Multi_pxo_player_start_index++;
3361 gamesnd_play_iface(SND_USER_SELECT);
3363 gamesnd_play_iface(SND_GENERAL_FAIL);
3368 // chat text stuff -----------------------------------------
3370 // initialize and create the chat text linked list
3371 void multi_pxo_chat_init()
3374 chat_line *new_line;
3377 Multi_pxo_chat = NULL;
3378 Multi_pxo_chat_add = NULL;
3379 Multi_pxo_chat_start = NULL;
3380 Multi_pxo_chat_start_index = -1;
3382 // create the lines in a non-circular doubly linked list
3383 for(idx=0;idx<MAX_CHAT_LINES;idx++){
3384 new_line = (chat_line*)malloc(sizeof(chat_line));
3386 // clear the line out
3387 SDL_assert(new_line != NULL);
3388 if(new_line == NULL){
3391 memset(new_line,0,sizeof(chat_line));
3392 new_line->prev = NULL;
3393 new_line->next = NULL;
3395 // insert it into the (empty) list
3396 if(Multi_pxo_chat == NULL){
3397 Multi_pxo_chat = new_line;
3399 // insert it onto the (non-empty) list
3401 Multi_pxo_chat->prev = new_line;
3402 new_line->next = Multi_pxo_chat;
3403 Multi_pxo_chat = new_line;
3407 // start adding chat lines at the beginning of the list
3408 Multi_pxo_chat_add = Multi_pxo_chat;
3411 // free up all chat list stuff
3412 void multi_pxo_chat_free()
3414 chat_line *moveup, *backup;
3416 // free all items up
3417 moveup = Multi_pxo_chat;
3418 while(moveup != NULL){
3420 moveup = moveup->next;
3426 Multi_pxo_chat = NULL;
3427 Multi_pxo_chat_add = NULL;
3428 Multi_pxo_chat_start = NULL;
3429 Multi_pxo_chat_start_index = -1;
3430 Multi_pxo_chat_count = 0;
3431 Multi_pxo_chat_slider.set_numberItems(0);
3434 // clear all lines of chat text in the chat area
3435 void multi_pxo_chat_clear()
3439 // clear the text in all the lines
3440 moveup = Multi_pxo_chat;
3441 while(moveup != NULL){
3442 SDL_zero(moveup->text);
3443 moveup = moveup->next;
3446 // how many chat lines we have
3447 Multi_pxo_chat_count = 0;
3449 // start adding chat lines at the beginning of the list
3450 Multi_pxo_chat_add = Multi_pxo_chat;
3453 // add a line of text
3454 void multi_pxo_chat_add_line(char *txt, int mode)
3459 SDL_assert(Multi_pxo_chat_add != NULL);
3460 SDL_strlcpy(Multi_pxo_chat_add->text, txt, SDL_arraysize(Multi_pxo_chat_add->text));
3461 Multi_pxo_chat_add->mode = mode;
3463 // if we're at the end of the list, move the front item down
3464 if(Multi_pxo_chat_add->next == NULL) {
3465 // store the new "head" of the list
3466 temp = Multi_pxo_chat->next;
3468 // move the current head to the end of the list
3469 Multi_pxo_chat_add->next = Multi_pxo_chat;
3471 Multi_pxo_chat->prev = Multi_pxo_chat_add;
3472 Multi_pxo_chat->next = NULL;
3474 // reset the head of the list
3475 Multi_pxo_chat = temp;
3477 // set the new add line
3478 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3479 SDL_zero(Multi_pxo_chat_add->text);
3480 Multi_pxo_chat_add->mode = CHAT_MODE_NORMAL;
3482 // if we're not at the end of the list, just move up by one
3484 // set the new add line
3485 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3488 // if we've reached max chat lines, don't increment
3489 if(Multi_pxo_chat_count < MAX_CHAT_LINES) {
3490 Multi_pxo_chat_count++;
3494 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
3496 // force the position, in case we arent at the bottom of the list
3499 // move to the bottom of the chat area
3502 multi_pxo_goto_bottom();
3505 // if we have more than the # of lines displayable
3506 if(Multi_pxo_chat_count >= MULTI_PXO_MAX_CHAT_DISPLAY){
3508 multi_pxo_goto_bottom();
3511 multi_pxo_goto_bottom();
3514 // process an incoming line of text
3515 void multi_pxo_chat_process_incoming(const char *txt,int mode)
3517 char msg_total[512],line[512];
3520 char *p_str[20]; // the initial line (unindented)
3521 const char *priv_ptr = NULL;
3523 // filter out "has left" channel messages, when switching channels
3524 if((SWITCHING_CHANNELS() || ((Multi_pxo_switch_delay != -1) && !timestamp_elapsed(Multi_pxo_switch_delay))) &&
3525 multi_pxo_chat_is_left_message(txt)){
3529 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3530 priv_ptr = multi_pxo_chat_is_private(txt);
3531 if(priv_ptr != NULL){
3532 SDL_strlcpy(msg_total, priv_ptr, SDL_arraysize(msg_total));
3534 SDL_strlcpy(msg_total, txt, SDL_arraysize(msg_total));
3537 // determine what mode to display this text in
3539 // if this is private chat
3540 if(priv_ptr != NULL){
3541 mode = CHAT_MODE_PRIVATE;
3545 // if this is a server message
3546 if(multi_pxo_is_server_text(txt)){
3547 mode = CHAT_MODE_SERVER;
3549 // if this is a MOTD
3550 else if(multi_pxo_is_motd_text(txt)){
3551 // mode = CHAT_MODE_MOTD;
3553 multi_pxo_motd_add_text(txt);
3556 // if this is the end of motd text
3557 else if(multi_pxo_is_end_of_motd_text(txt)){
3558 multi_pxo_set_end_of_motd();
3563 // split the text up into as many lines as necessary
3564 n_lines = split_str(msg_total, Multi_pxo_chat_coords[gr_screen.res][2] - 5, n_chars, p_str, 3);
3565 SDL_assert((n_lines != -1) && (n_lines <= 20));
3566 if((n_lines < 0) || (n_lines > 20)) {
3570 // if the string fits on one line
3572 multi_pxo_chat_add_line(msg_total,mode);
3574 // don't pad with extra spaces if its from the server
3576 if(mode != CHAT_MODE_SERVER){
3577 multi_pxo_chat_add_line("",CHAT_MODE_NORMAL);
3581 // if the string was split into multiple lines
3583 // add the first line
3584 memcpy(line,p_str[0],n_chars[0]);
3585 line[n_chars[0]] = '\0';
3586 multi_pxo_chat_add_line(line,mode);
3588 // copy the rest of the lines
3589 for(idx=1; idx<n_lines; idx++){
3590 memcpy(line,p_str[idx],n_chars[idx]);
3591 line[n_chars[idx]] = '\0';
3593 // unless the current mode is server or "switching channels", make all these CHAT_MODE_CARRY
3594 if((mode != CHAT_MODE_SERVER) && (mode != CHAT_MODE_CHANNEL_SWITCH)){
3595 mode = CHAT_MODE_CARRY;
3597 multi_pxo_chat_add_line(line, mode);
3602 // blit the chat text
3603 void multi_pxo_chat_blit()
3606 int disp_count,token_width;
3612 // blit the title line
3614 if(strlen(Multi_pxo_channel_current.name) > 1){
3615 SDL_snprintf(title, SDL_arraysize(title), XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name+1); // [[ <who> on <channel> ]]
3617 SDL_snprintf(title, SDL_arraysize(title), XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name); // [[ <who> on <channel> ]]
3620 SDL_strlcpy(title, XSTR("Parallax Online - No Channel", 956), SDL_arraysize(title));
3622 gr_force_fit_string(title, 254, Multi_pxo_chat_coords[gr_screen.res][2] - 10);
3623 gr_get_string_size(&token_width,NULL,title);
3624 gr_set_color_fast(&Color_normal);
3625 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);
3627 // blit all active lines of text
3628 moveup = Multi_pxo_chat_start;
3630 y_start = Multi_pxo_chat_coords[gr_screen.res][1];
3631 while((moveup != NULL) && (moveup != Multi_pxo_chat_add) && (disp_count < (Multi_pxo_max_chat_display[gr_screen.res]))){
3632 switch(moveup->mode){
3633 // if this is text from the server, display it all "bright"
3634 case CHAT_MODE_SERVER:
3635 gr_set_color_fast(&Color_bright);
3636 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3639 // if this is motd, display it all "bright"
3640 case CHAT_MODE_MOTD:
3641 gr_set_color_fast(&Color_bright_white);
3642 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3645 // normal mode, just highlight the server
3646 case CHAT_MODE_PRIVATE:
3647 case CHAT_MODE_NORMAL:
3648 SDL_strlcpy(piece, moveup->text, SDL_arraysize(piece));
3649 tok = strtok(piece," ");
3651 // get the width of just the first "piece"
3652 gr_get_string_size(&token_width, NULL, tok);
3655 gr_set_color_fast(&Color_bright);
3656 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, tok);
3658 // draw the rest of the string normally
3659 tok = strtok(NULL,"");
3661 gr_set_color_fast(&Color_normal);
3662 gr_string(Multi_pxo_chat_coords[gr_screen.res][0] + token_width + 6, y_start, tok);
3667 // carry mode, display with no highlight
3668 case CHAT_MODE_CARRY:
3669 gr_set_color_fast(&Color_normal);
3670 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3673 // "switching channels mode", display it bright
3674 case CHAT_MODE_CHANNEL_SWITCH:
3675 gr_set_color_fast(&Color_bright);
3676 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3681 moveup = moveup->next;
3686 if ((moveup != Multi_pxo_chat_add) && (moveup != NULL)) {
3687 Can_scroll_down = 1;
3689 Can_scroll_down = 0;
3693 // scroll to the very bottom of the chat area
3694 void multi_pxo_goto_bottom()
3699 if (Multi_pxo_chat == NULL) {
3703 // if we have less than the displayable amount of lines, do nothing
3704 if(Multi_pxo_chat_count <= Multi_pxo_max_chat_display[gr_screen.res]){
3705 Multi_pxo_chat_start = Multi_pxo_chat;
3707 // nothing to do for the slider
3708 Multi_pxo_chat_slider.set_numberItems(0);
3712 if (!Can_scroll_down)
3714 // otherwise move back the right # of items
3715 backup = Multi_pxo_chat_add;
3716 for(idx=0; idx<Multi_pxo_max_chat_display[gr_screen.res]; idx++){
3717 SDL_assert(backup->prev != NULL);
3718 backup = backup->prev;
3721 Multi_pxo_chat_start = backup;
3723 // fixup the start index
3724 multi_pxo_chat_adjust_start();
3728 // scroll the text up
3729 void multi_pxo_scroll_chat_up()
3731 // if we're already at the top of the list, don't do anything
3732 if ((Multi_pxo_chat_start == NULL) || (Multi_pxo_chat_start == Multi_pxo_chat)) {
3733 gamesnd_play_iface(SND_GENERAL_FAIL);
3737 // otherwise move up one
3738 Multi_pxo_chat_start = Multi_pxo_chat_start->prev;
3740 multi_pxo_chat_adjust_start();
3742 gamesnd_play_iface(SND_USER_SELECT);
3745 // returns 1 if we can scroll down, 0 otherwise
3746 int multi_pxo_can_scroll_down()
3751 // see if its okay to scroll down
3752 lookup = Multi_pxo_chat_start;
3753 if (lookup == NULL) {
3754 // gamesnd_play_iface(SND_GENERAL_FAIL);
3758 while (lookup != Multi_pxo_chat_add) {
3759 lookup = lookup->next;
3763 // check if we can move down, return accordingly
3764 if (count > Multi_pxo_max_chat_display[gr_screen.res]) {
3771 // scroll the text down
3772 void multi_pxo_scroll_chat_down()
3774 // if we can move down
3775 if (multi_pxo_can_scroll_down()) {
3776 Multi_pxo_chat_start = Multi_pxo_chat_start->next;
3777 multi_pxo_chat_adjust_start();
3778 gamesnd_play_iface(SND_USER_SELECT);
3780 gamesnd_play_iface(SND_GENERAL_FAIL);
3784 // process chat controls
3785 void multi_pxo_chat_process()
3787 char *remainder = NULL;
3788 const char *result = NULL;
3790 int msg_pixel_width;
3792 // if the chat line is getting too long, fire off the message, putting the last
3793 // word on the next input line.
3795 Multi_pxo_chat_input.get_text(msg);
3797 // determine if the width of the string in pixels is > than the inputbox width -- if so,
3798 // then send the message
3799 gr_get_string_size(&msg_pixel_width, NULL, msg);
3800 // if ( msg_pixel_width >= (Chatbox_inputbox_w - Player->short_callsign_width) ) {
3801 if ( msg_pixel_width >= (Multi_pxo_input_coords[gr_screen.res][2])) {
3802 remainder = strrchr(msg, ' ');
3808 // if we're connected to a channel, send the chat to the server
3810 result = SendChatString(msg,1);
3812 multi_pxo_chat_process_incoming(result);
3815 // display any remainder of text on the next line
3816 Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3818 Multi_pxo_chat_input.set_text("");
3820 } else if((Multi_pxo_chat_input.pressed() && (strlen(msg) > 0)) || (strlen(msg) >= MAX_CHAT_LINE_LEN)) {
3821 // tack on the null terminator in the boundary case
3822 int x = strlen(msg);
3823 if(x >= MAX_CHAT_LINE_LEN){
3824 msg[MAX_CHAT_LINE_LEN-1] = '\0';
3827 // ignore "/nick" commands
3828 if(multi_pxo_is_nick_command(msg)){
3829 Multi_pxo_chat_input.set_text("");
3833 // send the chat to the server
3834 // if we're connected to a channel, send the chat to the server
3836 result = SendChatString(msg,1);
3838 multi_pxo_chat_process_incoming(result);
3841 // display any remainder of text on the next line
3842 Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3844 Multi_pxo_chat_input.set_text("");
3849 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3852 // NOTE : DO NOT LOCALIZE THESE STRINGS!!!! THEY ARE CONSTANTS WHICH ARE CHECKED AGAINST
3853 // PXO CHAT SERVER DATA. THEY CANNOT CHANGE!!!
3854 #define PMSG_FROM "private message from "
3855 #define PMSG_TO "private message to "
3856 const char *multi_pxo_chat_is_private(const char *txt)
3859 if( strlen(txt) > strlen( PMSG_FROM ) ){
3860 // otherwise do a comparison
3861 if(!SDL_strncasecmp( txt, PMSG_FROM, strlen(PMSG_FROM) )){
3862 return &txt[strlen( PMSG_FROM )];
3867 if(strlen(txt) > strlen( PMSG_TO )){
3868 // otherwise do a comparison
3869 if(!SDL_strncasecmp(txt,PMSG_TO,strlen(PMSG_TO))){
3870 return &txt[strlen(PMSG_TO)];
3878 // if the text came from the server
3879 int multi_pxo_is_server_text(const char *txt)
3881 // if the message is prefaced by a ***
3882 if((strlen(txt) >= strlen(MULTI_PXO_SERVER_PREFIX)) && !strncmp(txt, MULTI_PXO_SERVER_PREFIX, strlen(MULTI_PXO_SERVER_PREFIX))){
3889 // if the text is message of the day text
3890 int multi_pxo_is_motd_text(const char *txt)
3892 // if we're not on a channel, and this is not a channel switching message assume its coming from a server
3893 if((strlen(txt) >= strlen(PXO_CHAT_MOTD_PREFIX)) && !strncmp(txt, PXO_CHAT_MOTD_PREFIX, strlen(PXO_CHAT_MOTD_PREFIX))){
3900 // if the text is the end of motd text
3901 int multi_pxo_is_end_of_motd_text(const char *txt)
3903 // if we're not on a channel, and this is not a channel switching message assume its coming from a server
3904 if((strlen(txt) >= strlen(PXO_CHAT_END_OF_MOTD_PREFIX)) && !strncmp(txt, PXO_CHAT_END_OF_MOTD_PREFIX, strlen(PXO_CHAT_END_OF_MOTD_PREFIX))){
3911 // if the text is a "has left message" from the server
3912 int multi_pxo_chat_is_left_message(const char *txt)
3914 char last_portion[100];
3916 // if the text is not server text
3917 if(!multi_pxo_is_server_text(txt)){
3921 // check to see if the last portion is the correct wording
3922 SDL_zero(last_portion);
3923 if((strlen(txt) > strlen(MULTI_PXO_HAS_LEFT)) && !strcmp(&txt[strlen(txt) - strlen(MULTI_PXO_HAS_LEFT)], MULTI_PXO_HAS_LEFT)){
3927 // check the end of the line
3931 // recalculate the chat start index, and adjust the slider properly
3932 void multi_pxo_chat_adjust_start()
3936 // if we have no chat
3937 if (Multi_pxo_chat == NULL) {
3938 Multi_pxo_chat_start_index = -1;
3943 Multi_pxo_chat_start_index = 0;
3944 moveup = Multi_pxo_chat;
3945 while((moveup != Multi_pxo_chat_start) && (moveup != NULL)){
3946 Multi_pxo_chat_start_index++;
3947 moveup = moveup->next;
3950 // set the slider index
3951 Multi_pxo_chat_slider.force_currentItem(Multi_pxo_chat_start_index);
3954 // motd stuff ---------------------------------------------------------
3956 // initialize motd when going into this screen
3957 void multi_pxo_motd_init()
3959 // zero the motd string
3960 SDL_strlcpy(Pxo_motd, "", SDL_arraysize(Pxo_motd));
3962 // haven't gotten it yet
3965 // haven't read it yet either
3969 // set the motd text
3970 void multi_pxo_motd_add_text(const char *text)
3972 int cur_len = strlen(Pxo_motd);
3980 // make sure its motd text
3981 SDL_assert(multi_pxo_is_motd_text(text));
3982 if(!multi_pxo_is_motd_text(text)){
3986 // if its a 0 line motd
3987 if(strlen(text) <= strlen(PXO_CHAT_MOTD_PREFIX)){
3991 // add text to the motd
3992 new_len = strlen(text + strlen(PXO_CHAT_MOTD_PREFIX)) - 1;
3993 if((cur_len + new_len + 1) < MAX_PXO_MOTD_LEN){
3994 SDL_strlcat(Pxo_motd, text + strlen(PXO_CHAT_MOTD_PREFIX) + 1, SDL_arraysize(Pxo_motd));
3995 SDL_strlcat(Pxo_motd, "\n", SDL_arraysize(Pxo_motd));
3996 mprintf(("MOTD ADD : %s\n", Pxo_motd));
4001 void multi_pxo_set_end_of_motd()
4006 mprintf(("MOTD ALL : %s\n", Pxo_motd));
4010 // do we have an old MOTD file laying around? If so, read it in and see if its the same
4011 unsigned int old_chksum;
4012 unsigned long new_chksum;
4014 // checksum the current motd
4015 new_chksum = cf_add_chksum_long(0, Pxo_motd, strlen(Pxo_motd));
4017 // checksum the old motd if its lying around
4018 CFILE *in = cfopen("oldmotd.txt", "rb");
4020 // read the old checksum
4021 cfread(&old_chksum, sizeof(old_chksum), 1, in);
4024 // same checksum? no blink
4025 if(new_chksum == old_chksum){
4030 // write out the motd for next time
4031 if(strlen(Pxo_motd)){
4032 CFILE *out = cfopen("oldmotd.txt", "wb", CFILE_NORMAL, CF_TYPE_DATA);
4034 // write all the text
4035 cfwrite(&new_chksum, sizeof(new_chksum), 1, out);
4037 // close the outfile
4042 // set the blink stamp
4043 Pxo_motd_blink_stamp = -1;
4045 Pxo_motd_blink_on = 0;
4046 if(!Pxo_motd_blinked_already){
4047 Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4048 Pxo_motd_blink_on = 1;
4052 Pxo_motd_blinked_already = 1;
4055 // display the motd dialog
4056 void multi_pxo_motd_dialog()
4058 // mark the motd as read
4061 // simple popup, with a slider
4062 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, Pxo_motd);
4065 // call to maybe blink the motd button
4066 void multi_pxo_motd_maybe_blit()
4068 // if we got the end of the motd, and he hasn't read it yet
4069 if(Pxo_motd_end && !Pxo_motd_read && (Pxo_motd_blink_stamp != -1)){
4070 // if the timestamp elapsed, flip the blink flag
4071 if(timestamp_elapsed(Pxo_motd_blink_stamp)){
4072 Pxo_motd_blink_on = !Pxo_motd_blink_on;
4073 Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4077 if(Pxo_motd_blink_on){
4078 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_MOTD].button.draw_forced(2);
4084 // common dialog stuff ------------------------------------------------
4086 int Multi_pxo_searching = 0;
4088 // initialize the common dialog with the passed max input length
4089 void multi_pxo_com_init(int input_len)
4093 // create the interface window
4094 Multi_pxo_com_window.create(0, 0, gr_screen.max_w,gr_screen.max_h, 0);
4095 Multi_pxo_com_window.set_mask_bmap(Multi_pxo_com_mask_fname[gr_screen.res]);
4097 // create the interface buttons
4098 for(idx=0; idx<MULTI_PXO_COM_NUM_BUTTONS; idx++){
4099 // create the object
4100 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);
4102 // set the sound to play when highlighted
4103 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4105 // set the ani for the button
4106 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_com_buttons[gr_screen.res][idx].filename);
4109 Multi_pxo_com_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_com_buttons[gr_screen.res][idx].hotspot);
4113 for(idx=0; idx<MULTI_PXO_COM_NUM_TEXT; idx++){
4114 Multi_pxo_com_window.add_XSTR(&Multi_pxo_com_text[gr_screen.res][idx]);
4117 // create the input box
4118 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);
4119 Multi_pxo_com_input.set_focus();
4121 // clear all text lines
4122 SDL_zero(Multi_pxo_com_bottom_text);
4123 SDL_zero(Multi_pxo_com_middle_text);
4124 SDL_zero(Multi_pxo_com_top_text);
4127 // close down the common dialog
4128 void multi_pxo_com_close()
4130 // destroy the UI_WINDOW
4131 Multi_pxo_com_window.destroy();
4134 // blit all text lines, top, middle, bottoms
4135 void multi_pxo_com_blit_text()
4137 // blit top, middle and bottom text if possible
4138 if(strlen(Multi_pxo_com_top_text) > 0){
4139 gr_set_color_fast(&Color_bright);
4140 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);
4142 if(strlen(Multi_pxo_com_middle_text) > 0){
4143 gr_set_color_fast(&Color_bright);
4144 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);
4146 if(strlen(Multi_pxo_com_bottom_text) > 0){
4147 gr_set_color_fast(&Color_bright);
4148 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);
4152 // set the top text, shortening as necessary
4153 void multi_pxo_com_set_top_text(const char *txt)
4155 if((txt != NULL) && strlen(txt)){
4156 SDL_strlcpy(Multi_pxo_com_top_text, txt, SDL_arraysize(Multi_pxo_com_top_text));
4157 gr_force_fit_string(Multi_pxo_com_top_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4161 // set the middle text, shortening as necessary
4162 void multi_pxo_com_set_middle_text(const char *txt)
4164 if((txt != NULL) && strlen(txt)){
4165 SDL_strlcpy(Multi_pxo_com_middle_text, txt, SDL_arraysize(Multi_pxo_com_middle_text));
4166 gr_force_fit_string(Multi_pxo_com_middle_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4170 // set the bottom text, shortening as necessary
4171 void multi_pxo_com_set_bottom_text(const char *txt)
4173 if((txt != NULL) && strlen(txt)){
4174 SDL_strlcpy(Multi_pxo_com_bottom_text, txt, SDL_arraysize(Multi_pxo_com_bottom_text));
4175 gr_force_fit_string(Multi_pxo_com_bottom_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4180 // private channel join stuff -----------------------------------------
4182 // initialize the popup
4183 void multi_pxo_priv_init()
4185 SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE);
4187 // initialize the common dialog with the passed max input length
4188 multi_pxo_com_init(MULTI_PXO_PRIV_MAX_TEXT_LEN);
4190 // initialize the return code
4191 Multi_pxo_priv_return_code = -1;
4193 // mark us as running
4194 Multi_pxo_mode = MULTI_PXO_MODE_PRIVATE;
4197 multi_pxo_com_set_middle_text(XSTR("Type the name of the channel to join/create",961));
4200 // close down the popup
4201 void multi_pxo_priv_close()
4203 // close down the common dialog
4204 multi_pxo_com_close();
4206 // mark us as not running any more
4207 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4210 // run the popup, 0 if still running, -1 if cancel, 1 if ok
4211 int multi_pxo_priv_popup()
4215 // if we're not already running, initialize stuff
4216 if(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE){
4218 multi_pxo_priv_init();
4220 // return "still running"
4224 k = Multi_pxo_com_window.process();
4226 // process keypresses
4228 // like hitting the cancel button
4230 Multi_pxo_priv_return_code = 0;
4234 // process button presses
4235 multi_pxo_priv_process_buttons();
4237 // process the inputbox
4238 multi_pxo_priv_process_input();
4240 // blit the background
4241 multi_pxo_blit_all();
4245 gr_set_bitmap(Multi_pxo_com_bitmap);
4246 gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4247 Multi_pxo_com_window.draw();
4249 // blit all text lines, top, middle, bottoms
4250 multi_pxo_com_blit_text();
4254 // check the return code
4255 switch(Multi_pxo_priv_return_code){
4256 // still in progress
4262 multi_pxo_priv_close();
4267 multi_pxo_priv_close();
4274 // process button presses
4275 void multi_pxo_priv_process_buttons()
4279 // check all buttons
4280 for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4281 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4282 multi_pxo_priv_button_pressed(idx);
4288 // handle a button press
4289 void multi_pxo_priv_button_pressed(int n)
4291 char priv_chan_name[128];
4294 case MULTI_PXO_COM_CANCEL:
4295 Multi_pxo_priv_return_code = 0;
4298 case MULTI_PXO_COM_OK:
4299 Multi_pxo_com_input.get_text(priv_chan_name);
4300 multi_pxo_strip_space(priv_chan_name, priv_chan_name, SDL_arraysize(priv_chan_name));
4302 // if its a 0 length string, interpret as a cancel
4303 if(strlen(priv_chan_name) <= 0){
4304 Multi_pxo_priv_return_code = 0;
4308 Multi_pxo_priv_return_code = 1;
4313 // process the inputbox
4314 void multi_pxo_priv_process_input()
4316 char priv_chan_name[128];
4318 // see if the user has pressed enter
4319 if(Multi_pxo_com_input.pressed()){
4320 Multi_pxo_com_input.get_text(priv_chan_name);
4321 multi_pxo_strip_space(priv_chan_name, priv_chan_name, SDL_arraysize(priv_chan_name));
4323 // if its a 0 length string, interpret as a cancel
4324 if(strlen(priv_chan_name) <= 0){
4325 Multi_pxo_priv_return_code = 0;
4329 // otherwise interpret as "accept"
4330 Multi_pxo_priv_return_code = 1;
4332 // add in the "+" which indicates a private room
4333 SDL_strlcpy(Multi_pxo_priv_chan, "+", SDL_arraysize(Multi_pxo_priv_chan));
4334 SDL_strlcat(Multi_pxo_priv_chan, priv_chan_name, SDL_arraysize(Multi_pxo_priv_chan));
4338 // find player stuff -----------------------------------------
4340 char name_lookup[255];
4342 // initialize the popup
4343 void multi_pxo_find_init()
4345 SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_FIND);
4347 // initialize the common dialog with the passed max input length
4348 multi_pxo_com_init(MAX_PLAYER_NAME_LEN);
4350 // return code, set to something other than -1 if we're supposed to return
4351 Multi_pxo_find_return_code = -1;
4353 // mark us as running
4354 Multi_pxo_mode = MULTI_PXO_MODE_FIND;
4356 // not searching yet
4357 Multi_pxo_searching = 0;
4360 multi_pxo_com_set_top_text(XSTR("Enter user to be found",962));
4363 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4366 SDL_strlcpy(name_lookup, "", SDL_arraysize(name_lookup));
4369 // close down the popup
4370 void multi_pxo_find_close()
4372 // close down the common dialog
4373 multi_pxo_com_close();
4375 // mark us as not running any more
4376 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4379 // run the popup, 0 if still running, -1 if cancel, 1 if ok
4380 int multi_pxo_find_popup()
4384 // if we're not already running, initialize stuff
4385 if(Multi_pxo_mode != MULTI_PXO_MODE_FIND){
4387 multi_pxo_find_init();
4389 // return "still running"
4393 k = Multi_pxo_com_window.process();
4395 // process keypresses
4397 // like hitting the cancel button
4399 Multi_pxo_find_return_code = 0;
4403 // process button presses
4404 multi_pxo_find_process_buttons();
4406 // process the inputbox
4407 multi_pxo_find_process_input();
4409 // process search mode if applicable
4410 multi_pxo_find_search_process();
4412 // blit the background
4413 multi_pxo_blit_all();
4417 gr_set_bitmap(Multi_pxo_com_bitmap);
4418 gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4419 Multi_pxo_com_window.draw();
4421 // blit any text lines
4422 multi_pxo_com_blit_text();
4426 // check the return code
4427 switch(Multi_pxo_find_return_code){
4428 // still in progress
4434 // close the popup down
4435 multi_pxo_find_close();
4440 // close the popup down
4441 multi_pxo_find_close();
4443 // if we have a channel, join it now if possible
4444 if(strlen(Multi_pxo_find_channel) > 0){
4445 pxo_channel *lookup;
4446 lookup = multi_pxo_find_channel(Multi_pxo_find_channel,Multi_pxo_channels);
4448 // if we couldn't find it, don't join
4450 multi_pxo_join_channel(lookup);
4459 // process button presses
4460 void multi_pxo_find_process_buttons()
4464 // check all buttons
4465 for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4466 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4467 multi_pxo_find_button_pressed(idx);
4473 // handle a button press
4474 void multi_pxo_find_button_pressed(int n)
4477 case MULTI_PXO_COM_CANCEL:
4478 Multi_pxo_find_return_code = 0;
4481 case MULTI_PXO_COM_OK:
4482 Multi_pxo_find_return_code = 1;
4487 // process the inputbox
4488 void multi_pxo_find_process_input()
4490 // see if the user has pressed enter
4491 if(Multi_pxo_com_input.pressed()){
4492 // if we're not already in search mode
4493 if(!Multi_pxo_searching){
4495 SDL_zero(Multi_pxo_com_middle_text);
4496 SDL_zero(Multi_pxo_com_bottom_text);
4498 Multi_pxo_com_input.get_text(name_lookup);
4499 multi_pxo_strip_space(name_lookup, name_lookup, SDL_arraysize(name_lookup));
4501 // never search with a zero length string
4502 if(strlen(name_lookup) > 0){
4503 char search_text[512];
4505 // put us in search mode
4506 Multi_pxo_searching = 1;
4509 GetChannelByUser(name_lookup);
4512 SDL_snprintf(search_text, SDL_arraysize(search_text), XSTR("Searching for %s", 963), name_lookup);
4513 multi_pxo_com_set_top_text(search_text);
4517 SDL_zero(Multi_pxo_com_top_text);
4523 // process search mode if applicable
4524 void multi_pxo_find_search_process()
4528 // if we're not searching for anything, return
4529 if(!Multi_pxo_searching){
4533 // otherwise check to see if we've found him
4534 channel = GetChannelByUser(NULL);
4536 // if we've got a result, let the user know
4538 // if he couldn't be found
4539 if(channel == (char *)-1){
4540 multi_pxo_com_set_middle_text(XSTR("User not found",964));
4541 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4543 if(channel[0] == '*'){
4544 multi_pxo_com_set_middle_text(XSTR("Player is logged in but is not on a channel",965));
4545 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4549 // if this guy is on a public channel, display which one
4550 if(channel[0] == '#'){
4551 SDL_snprintf(p_text, SDL_arraysize(p_text), XSTR("Found %s on :", 966), name_lookup);
4553 // display the results
4554 multi_pxo_com_set_middle_text(p_text);
4555 multi_pxo_com_set_bottom_text(channel+1);
4557 // mark down the channel name so we know where to find him
4558 SDL_strlcpy(Multi_pxo_find_channel, channel, SDL_arraysize(Multi_pxo_find_channel));
4559 // strip out trailing whitespace
4560 if(Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] == ' '){
4561 Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] = '\0';
4564 // if this is a private channel
4565 else if(channel[0] == '+'){
4566 SDL_snprintf(p_text, SDL_arraysize(p_text), XSTR("Found %s on a private channel", 967), name_lookup);
4567 multi_pxo_com_set_middle_text(p_text);
4569 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4574 // unset search mode
4575 Multi_pxo_searching = 0;
4577 // clear the inputbox
4578 Multi_pxo_com_input.set_text("");
4583 // player info stuff -----------------------------------------
4585 // popup conditional functions, returns 10 on successful get of stats
4586 int multi_pxo_pinfo_cond()
4589 char temp_string[255];
4592 // process common stuff
4593 multi_pxo_process_common();
4595 // run the networking functions for the PXO API
4596 multi_pxo_api_process();
4598 // process depending on what mode we're in
4599 switch(Multi_pxo_retrieve_mode){
4600 // getting his player tracker id
4602 // if the thing is non-null, do something
4603 ret_string = GetTrackerIdByUser(Multi_pxo_retrieve_name);
4604 if(ret_string != NULL){
4605 // user not-online/not found
4606 if(ret_string == (char *)-1){
4610 // user not a tracker pilot
4611 if(!SDL_strcasecmp(ret_string,"-1")){
4615 // otherwise parse into his id and callsign
4616 SDL_strlcpy(temp_string, ret_string, SDL_arraysize(temp_string));
4617 tok = strtok(temp_string," ");
4621 SDL_strlcpy(Multi_pxo_retrieve_id, tok, SDL_arraysize(Multi_pxo_retrieve_id));
4624 tok = strtok(NULL,"");
4626 SDL_strlcpy(Multi_pxo_retrieve_name, tok, SDL_arraysize(Multi_pxo_retrieve_name));
4633 // failure of some kind or another
4638 Multi_pxo_retrieve_mode = 1;
4643 // initial call to get his stats
4645 // change the popup text
4646 popup_change_text(XSTR("Getting player stats",968));
4649 memset(&Multi_pxo_pinfo, 0, sizeof(vmt_freespace2_struct));
4650 SDL_strlcpy(Multi_pxo_pinfo.pilot_name, Multi_pxo_retrieve_name, SDL_arraysize(Multi_pxo_pinfo.pilot_name));
4651 SDL_strlcpy(Multi_pxo_pinfo.tracker_id, Multi_pxo_retrieve_id, SDL_arraysize(Multi_pxo_pinfo.tracker_id));
4653 // make the initial call to the API
4654 GetFSPilotData((vmt_freespace2_struct*)0xffffffff,NULL,NULL,0);
4655 if(GetFSPilotData(&Multi_pxo_pinfo,Multi_pxo_retrieve_name,Multi_pxo_retrieve_id,0) != 0){
4658 // if the call went through, set the mode to 2
4660 Multi_pxo_retrieve_mode = 2;
4664 // busy retrieving his stats
4666 switch(GetFSPilotData(NULL,NULL,NULL,0)){
4667 // timeout, fail, cancel
4685 // return not done yet
4689 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
4690 int multi_pxo_pinfo_get(char *name)
4693 Multi_pxo_retrieve_mode = 0;
4694 SDL_strlcpy(Multi_pxo_retrieve_name, name, SDL_arraysize(Multi_pxo_retrieve_name));
4695 switch(popup_till_condition(multi_pxo_pinfo_cond,XSTR("&Cancel", 779),XSTR("Retrieving player tracker id",969))){
4700 // failed to get his tracker id
4704 // failed to get his stats
4709 // we didn't get the stats
4713 // fire up the stats view popup
4714 void multi_pxo_pinfo_show()
4716 // initialize the popup
4717 multi_pxo_pinfo_init();
4721 game_set_frametime(GS_STATE_PXO);
4722 } while(!multi_pxo_pinfo_do());
4724 // close down the popup
4725 multi_pxo_pinfo_close();
4728 // build the stats labels values
4729 void multi_pxo_pinfo_build_vals()
4731 vmt_freespace2_struct *fs = &Multi_pxo_pinfo;
4733 SDL_zero(Multi_pxo_pinfo_vals);
4736 SDL_strlcpy(Multi_pxo_pinfo_vals[0], fs->pilot_name, SDL_arraysize(Multi_pxo_pinfo_vals[0]));
4737 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]));
4740 multi_sg_rank_build_name(Ranks[fs->rank].name, Multi_pxo_pinfo_vals[1], SDL_arraysize(Multi_pxo_pinfo_vals[1]));
4741 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]));
4744 SDL_snprintf(Multi_pxo_pinfo_vals[2], SDL_arraysize(Multi_pxo_pinfo_vals[2]), "%d", fs->kill_count);
4747 SDL_snprintf(Multi_pxo_pinfo_vals[3], SDL_arraysize(Multi_pxo_pinfo_vals[3]), "%d", fs->assists);
4750 SDL_snprintf(Multi_pxo_pinfo_vals[4], SDL_arraysize(Multi_pxo_pinfo_vals[4]), "%d", fs->kill_count - fs->kill_count_ok);
4753 SDL_snprintf(Multi_pxo_pinfo_vals[5], SDL_arraysize(Multi_pxo_pinfo_vals[5]), "%d", (int)fs->missions_flown);
4756 game_format_time(fl2f((float)fs->flight_time), Multi_pxo_pinfo_vals[6], SDL_arraysize(Multi_pxo_pinfo_vals[6]));
4759 if(fs->last_flown == 0){
4760 SDL_strlcpy(Multi_pxo_pinfo_vals[7], XSTR("No missions flown", 970), SDL_arraysize(Multi_pxo_pinfo_vals[7]));
4762 tm *tmr = gmtime((time_t*)&fs->last_flown);
4764 strftime(Multi_pxo_pinfo_vals[7],30,"%m/%d/%y %H:%M",tmr);
4766 SDL_strlcpy(Multi_pxo_pinfo_vals[7], "", SDL_arraysize(Multi_pxo_pinfo_vals[7]));
4770 // primary shots fired
4771 SDL_snprintf(Multi_pxo_pinfo_vals[8], SDL_arraysize(Multi_pxo_pinfo_vals[8]), "%d", (int)fs->p_shots_fired);
4773 // primary shots hit
4774 SDL_snprintf(Multi_pxo_pinfo_vals[9], SDL_arraysize(Multi_pxo_pinfo_vals[9]), "%d", (int)fs->p_shots_hit);
4777 if(fs->p_shots_fired > 0){
4778 SDL_snprintf(Multi_pxo_pinfo_vals[10], SDL_arraysize(Multi_pxo_pinfo_vals[10]), "%d%%", (int)((float)fs->p_shots_hit / (float)fs->p_shots_fired * 100.0f));
4780 SDL_strlcpy(Multi_pxo_pinfo_vals[10], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[10]));
4783 // secondary shots fired
4784 SDL_snprintf(Multi_pxo_pinfo_vals[11], SDL_arraysize(Multi_pxo_pinfo_vals[11]), "%d", (int)fs->s_shots_fired);
4786 // secondary shots hit
4787 SDL_snprintf(Multi_pxo_pinfo_vals[12], SDL_arraysize(Multi_pxo_pinfo_vals[12]), "%d", (int)fs->s_shots_hit);
4789 // secondary hit pct
4790 if(fs->s_shots_fired > 0){
4791 SDL_snprintf(Multi_pxo_pinfo_vals[13], SDL_arraysize(Multi_pxo_pinfo_vals[13]), "%d%%", (int)((float)fs->s_shots_hit / (float)fs->s_shots_fired * 100.0f));
4793 SDL_strlcpy(Multi_pxo_pinfo_vals[13], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[13]));
4796 // primary friendly hits
4797 SDL_snprintf(Multi_pxo_pinfo_vals[14], SDL_arraysize(Multi_pxo_pinfo_vals[14]), "%d", fs->p_bonehead_hits);
4799 // primary friendly hit %
4800 if(fs->p_shots_hit > 0){
4801 SDL_snprintf(Multi_pxo_pinfo_vals[15], SDL_arraysize(Multi_pxo_pinfo_vals[15]), "%d%%", (int)((float)100.0f*((float)fs->p_bonehead_hits/(float)fs->p_shots_fired)));
4803 SDL_strlcpy(Multi_pxo_pinfo_vals[15], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[15]));
4806 // secondary friendly hits
4807 SDL_snprintf(Multi_pxo_pinfo_vals[16], SDL_arraysize(Multi_pxo_pinfo_vals[16]), "%d", fs->s_bonehead_hits);
4809 // secondary friendly hit %
4810 if(fs->s_shots_hit > 0){
4811 SDL_snprintf(Multi_pxo_pinfo_vals[17], SDL_arraysize(Multi_pxo_pinfo_vals[17]), "%d%%", (int)((float)100.0f*((float)fs->s_bonehead_hits/(float)fs->s_shots_fired)));
4813 SDL_strlcpy(Multi_pxo_pinfo_vals[17], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[17]));
4817 // initialize the popup
4818 void multi_pxo_pinfo_init()
4822 // create the interface window
4823 Multi_pxo_pinfo_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4824 Multi_pxo_pinfo_window.set_mask_bmap(Multi_pxo_pinfo_mask_fname[gr_screen.res]);
4826 Multi_pxo_pinfo_bitmap = bm_load(Multi_pxo_pinfo_fname[gr_screen.res]);
4827 SDL_assert(Multi_pxo_pinfo_bitmap != -1);
4829 // create the interface buttons
4830 for(idx=0; idx<MULTI_PXO_PINFO_NUM_BUTTONS; idx++){
4831 // create the object
4832 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);
4834 // set the sound to play when highlighted
4835 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4837 // set the ani for the button
4838 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_pinfo_buttons[gr_screen.res][idx].filename);
4841 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_pinfo_buttons[gr_screen.res][idx].hotspot);
4845 for(idx=0; idx<MULTI_PXO_PINFO_NUM_TEXT; idx++){
4846 Multi_pxo_pinfo_window.add_XSTR(&Multi_pxo_pinfo_text[gr_screen.res][idx]);
4849 // set up the stats labels
4850 Multi_pxo_pinfo_stats_labels[0] = strdup(XSTR("Name", 1532));
4851 Multi_pxo_pinfo_stats_labels[1] = strdup(XSTR("Rank", 1533));
4852 Multi_pxo_pinfo_stats_labels[2] = strdup(XSTR("Kills", 1534));
4853 Multi_pxo_pinfo_stats_labels[3] = strdup(XSTR("Assists", 1535));
4854 Multi_pxo_pinfo_stats_labels[4] = strdup(XSTR("Friendly kills", 1536));
4855 Multi_pxo_pinfo_stats_labels[5] = strdup(XSTR("Missions flown", 1537));
4856 Multi_pxo_pinfo_stats_labels[6] = strdup(XSTR("Flight time", 1538));
4857 Multi_pxo_pinfo_stats_labels[7] = strdup(XSTR("Last flown", 1539));
4858 Multi_pxo_pinfo_stats_labels[8] = strdup(XSTR("Primary shots fired", 1540));
4859 Multi_pxo_pinfo_stats_labels[9] = strdup(XSTR("Primary shots hit", 1541));
4860 Multi_pxo_pinfo_stats_labels[10] = strdup(XSTR("Primary hit %", 1542));
4861 Multi_pxo_pinfo_stats_labels[11] = strdup(XSTR("Secondary shots fired", 1543));
4862 Multi_pxo_pinfo_stats_labels[12] = strdup(XSTR("Secondary shots hit", 1544));
4863 Multi_pxo_pinfo_stats_labels[13] = strdup(XSTR("Secondary hit %", 1545));
4864 Multi_pxo_pinfo_stats_labels[14] = strdup(XSTR("Primary friendly hits", 1546));
4865 Multi_pxo_pinfo_stats_labels[15] = strdup(XSTR("Primary friendly hit %", 1547));
4866 Multi_pxo_pinfo_stats_labels[16] = strdup(XSTR("Secondary friendly hits", 1548));
4867 Multi_pxo_pinfo_stats_labels[17] = strdup(XSTR("Secondary friendly hit %", 1549));
4869 // build the stats labels values
4870 multi_pxo_pinfo_build_vals();
4874 int multi_pxo_pinfo_do()
4876 int k = Multi_pxo_pinfo_window.process();
4878 // process common stuff
4879 multi_pxo_process_common();
4881 // run the networking functions for the PXO API
4882 multi_pxo_api_process();
4884 // check to see if he pressed escp
4885 if(k == SDLK_ESCAPE){
4889 // if he pressed the ok button
4890 if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_OK].button.pressed()){
4894 // if he pressed the medals buttons, run the medals screen
4895 if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_MEDALS].button.pressed()){
4897 game_feature_not_in_demo_popup();
4899 multi_pxo_run_medals();
4905 // blit everything on the "normal" screen
4906 multi_pxo_blit_all();
4908 // blit our own stuff
4910 gr_set_bitmap(Multi_pxo_pinfo_bitmap);
4912 Multi_pxo_pinfo_window.draw();
4914 // blit the stats themselves
4915 multi_pxo_pinfo_blit();
4925 void multi_pxo_pinfo_close()
4929 // destroy the UI_WINDOW
4930 Multi_pxo_pinfo_window.destroy();
4932 // unload the bitmap
4933 if(Multi_pxo_pinfo_bitmap != -1){
4934 bm_unload(Multi_pxo_pinfo_bitmap);
4937 // free the stats labels strings
4938 for (i=0; i<MULTI_PXO_PINFO_NUM_LABELS; i++) {
4939 free(Multi_pxo_pinfo_stats_labels[i]);
4943 // blit all the stats on this screen
4944 void multi_pxo_pinfo_blit()
4949 // blit all the labels
4950 y_start = Multi_pxo_pinfo_coords[gr_screen.res][1];
4951 for(idx=0; idx<MULTI_PXO_PINFO_NUM_LABELS; idx++){
4953 gr_set_color_fast(&Color_bright);
4954 gr_string(Multi_pxo_pinfo_coords[gr_screen.res][0], y_start, Multi_pxo_pinfo_stats_labels[idx]);
4956 // blit the label's value
4957 gr_set_color_fast(&Color_normal);
4958 gr_string(Multi_pxo_pinfo_val_x[gr_screen.res], y_start, Multi_pxo_pinfo_vals[idx]);
4961 y_start += Multi_pxo_pinfo_stats_spacing[idx];
4965 // run the medals screen
4966 void multi_pxo_run_medals()
4970 // process common stuff
4971 multi_pxo_process_common();
4973 // run the networking functions for the PXO API
4974 multi_pxo_api_process();
4976 // initialize the freespace data and the player struct
4977 multi_stats_tracker_to_fs(&Multi_pxo_pinfo, &Multi_pxo_pinfo_player.stats);
4978 SDL_strlcpy(Multi_pxo_pinfo_player.callsign, Multi_pxo_pinfo.pilot_name, SDL_arraysize(Multi_pxo_pinfo_player.callsign));
4980 // initialize the medals screen
4981 medal_main_init(&Multi_pxo_pinfo_player, MM_POPUP);
4983 // run the medals screen until it says that it should be closed
4985 // set frametime and run common functions
4986 game_set_frametime(-1);
4987 game_do_state_common(gameseq_get_state());
4989 // run the medals screen
4990 ret_code = medal_main_do();
4993 // close the medals screen down
4996 // reset the palette
4997 multi_pxo_load_palette();
5001 // notify stuff stuff -----------------------------------------
5003 // add a notification string
5004 void multi_pxo_notify_add(const char *txt)
5007 SDL_strlcpy(Multi_pxo_notify_text, txt, SDL_arraysize(Multi_pxo_notify_text));
5009 // set the timestamp
5010 Multi_pxo_notify_stamp = timestamp(MULTI_PXO_NOTIFY_TIME);
5013 // blit and process the notification string
5014 void multi_pxo_notify_blit()
5018 // if the timestamp is -1, do nothing
5019 if(Multi_pxo_notify_stamp == -1){
5023 // if it has expired, do nothing
5024 if(timestamp_elapsed(Multi_pxo_notify_stamp)){
5025 Multi_pxo_notify_stamp = -1;
5028 // otherwise blit the text
5029 gr_set_color_fast(&Color_bright);
5030 gr_get_string_size(&w,NULL,Multi_pxo_notify_text);
5031 gr_string((gr_screen.max_w - w)/2,MULTI_PXO_NOTIFY_Y,Multi_pxo_notify_text);
5035 // initialize the PXO help screen
5036 void multi_pxo_help_init()
5040 // load the background bitmap
5041 Multi_pxo_help_bitmap = bm_load(Multi_pxo_help_fname[gr_screen.res]);
5042 if(Multi_pxo_help_bitmap < 0){
5043 // we failed to load the bitmap - this is very bad
5046 // create the interface window
5047 Multi_pxo_help_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
5048 Multi_pxo_help_window.set_mask_bmap(Multi_pxo_help_mask_fname[gr_screen.res]);
5050 // create the interface buttons
5051 for(idx=0; idx<MULTI_PXO_HELP_NUM_BUTTONS; idx++){
5052 // create the object
5053 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);
5055 // set the sound to play when highlighted
5056 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
5058 // set the ani for the button
5059 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_help_buttons[gr_screen.res][idx].filename);
5062 Multi_pxo_help_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_help_buttons[gr_screen.res][idx].hotspot);
5066 for(idx=0; idx<MULTI_PXO_HELP_NUM_TEXT; idx++){
5067 Multi_pxo_help_window.add_XSTR(&Multi_pxo_help_text[gr_screen.res][idx]);
5070 // if we haven't already loaded in the text, do so
5071 // if(!Multi_pxo_help_loaded){
5072 multi_pxo_help_load();
5075 // set the current page to 0
5076 Multi_pxo_help_cur = 0;
5079 // do frame for PXO help
5080 void multi_pxo_help_do()
5083 if(Multi_pxo_connected){
5084 multi_pxo_api_process();
5087 // process common stuff
5088 multi_pxo_process_common();
5090 int k = Multi_pxo_help_window.process();
5092 // process any keypresses
5095 gamesnd_play_iface(SND_USER_SELECT);
5096 gameseq_post_event(GS_EVENT_PXO);
5100 // process button presses
5101 multi_pxo_help_process_buttons();
5103 // draw the background, etc
5105 GR_MAYBE_CLEAR_RES(Multi_pxo_help_bitmap);
5106 if(Multi_pxo_help_bitmap != -1){
5107 gr_set_bitmap(Multi_pxo_help_bitmap);
5110 Multi_pxo_help_window.draw();
5112 // blit the current page
5113 multi_pxo_help_blit_page();
5119 // close the pxo screen
5120 void multi_pxo_help_close()
5124 // unload any bitmaps
5125 bm_unload(Multi_pxo_help_bitmap);
5127 // destroy the UI_WINDOW
5128 Multi_pxo_help_window.destroy();
5131 for(idx=0; idx<Multi_pxo_help_num_pages; idx++){
5132 for(idx2=0; idx2<Multi_pxo_help_pages[idx].num_lines; idx2++){
5134 if(Multi_pxo_help_pages[idx].text[idx2] != NULL){
5135 free(Multi_pxo_help_pages[idx].text[idx2]);
5136 Multi_pxo_help_pages[idx].text[idx2] = NULL;
5142 // load the help file up
5143 void multi_pxo_help_load()
5148 // if its already loaded, do nothing
5149 // if(Multi_pxo_help_loaded){
5153 // read in the text file
5155 in = cfopen(MULTI_PXO_HELP_FILE,"rt",CFILE_NORMAL,CF_TYPE_DATA);
5156 SDL_assert(in != NULL);
5161 Multi_pxo_help_num_pages = 0;
5163 // blast all the help pages clear
5164 memset(Multi_pxo_help_pages, 0, sizeof(help_page) * MULTI_PXO_MAX_PAGES);
5165 Multi_pxo_help_num_pages = 0;
5166 cp = &Multi_pxo_help_pages[0];
5170 cp->text[cp->num_lines] = (char*)malloc(Multi_pxo_chars_per_line[gr_screen.res]);
5171 if(cp->text[cp->num_lines] == NULL){
5175 // read in the next line
5176 cfgets(cp->text[cp->num_lines++], Multi_pxo_chars_per_line[gr_screen.res], in);
5178 // skip to the next page if necessary
5179 if(cp->num_lines == Multi_pxo_lines_pp[gr_screen.res]){
5180 Multi_pxo_help_num_pages++;
5181 SDL_assert(Multi_pxo_help_num_pages < MULTI_PXO_MAX_PAGES);
5182 if(Multi_pxo_help_num_pages >= MULTI_PXO_MAX_PAGES){
5183 Multi_pxo_help_num_pages--;
5186 cp = &Multi_pxo_help_pages[Multi_pxo_help_num_pages];
5193 // mark the help as having been loaded
5194 // Multi_pxo_help_loaded = 1;
5197 // blit the current page
5198 void multi_pxo_help_blit_page()
5203 help_page *cp = &Multi_pxo_help_pages[Multi_pxo_help_cur];
5206 y_start = Multi_pxo_help_coords[gr_screen.res][1];
5207 for(idx=0;idx<cp->num_lines;idx++){
5208 // if the first symbol is "@", highlight the line
5209 if(cp->text[idx][0] == '@'){
5210 gr_set_color_fast(&Color_bright);
5213 gr_set_color_fast(&Color_normal);
5218 gr_string(Multi_pxo_help_coords[gr_screen.res][0], y_start, cp->text[idx] + start_pos);
5220 // increment the y location
5225 // process button presses
5226 void multi_pxo_help_process_buttons()
5230 // process all buttons
5231 for(idx=0;idx<MULTI_PXO_HELP_NUM_BUTTONS;idx++){
5232 if(Multi_pxo_help_buttons[gr_screen.res][idx].button.pressed()){
5233 multi_pxo_help_button_pressed(idx);
5240 void multi_pxo_help_button_pressed(int n)
5243 case MULTI_PXO_HELP_PREV:
5244 // if we're already at page 0, do nothing
5245 if(Multi_pxo_help_cur == 0){
5246 gamesnd_play_iface(SND_GENERAL_FAIL);
5248 Multi_pxo_help_cur--;
5249 gamesnd_play_iface(SND_USER_SELECT);
5253 case MULTI_PXO_HELP_NEXT:
5254 // if we're already at max pages, do nothing
5255 if(Multi_pxo_help_cur == Multi_pxo_help_num_pages){
5256 gamesnd_play_iface(SND_GENERAL_FAIL);
5258 Multi_pxo_help_cur++;
5259 gamesnd_play_iface(SND_USER_SELECT);
5263 case MULTI_PXO_HELP_CONTINUE:
5264 gamesnd_play_iface(SND_USER_SELECT);
5265 gameseq_post_event(GS_EVENT_PXO);
5270 // http banner stuff ---------------------------------------------
5273 void multi_pxo_ban_init()
5275 // zero the active banner bitmap
5276 Multi_pxo_banner.ban_bitmap = -1;
5278 // are we doing banners at all?
5279 if(os_config_read_uint(NULL, "PXOBanners", 1)){
5280 // if we're already in idle mode, we're done downloading for this instance of freespace. pick a random image we already have
5281 if(Multi_pxo_ban_mode == PXO_BAN_MODE_IDLE){
5282 Multi_pxo_ban_mode = PXO_BAN_MODE_CHOOSE_RANDOM;
5286 // set ourselves to startup mode
5287 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
5288 Multi_pxo_ban_get = NULL;
5290 // set ourselves to idle mode
5291 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5292 Multi_pxo_ban_get = NULL;
5295 // zero the active banner bitmap
5296 SDL_zero(Multi_pxo_banner);
5297 Multi_pxo_banner.ban_bitmap = -1;
5300 // process http download details
5301 void multi_pxo_ban_process()
5303 char url_string[512] = "";
5304 char local_file[MAX_PATH_LEN] = "";
5307 switch(Multi_pxo_ban_mode){
5308 // start downloading list
5309 case PXO_BAN_MODE_LIST_STARTUP:
5311 SDL_snprintf(url_string, SDL_arraysize(url_string), "%s/%s", Multi_options_g.pxo_banner_url, PXO_BANNERS_CONFIG_FILE);
5314 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, PXO_BANNERS_CONFIG_FILE);
5316 // try creating the file get object
5317 Multi_pxo_ban_get = new InetGetFile(url_string, local_file);
5320 if(Multi_pxo_ban_get == NULL){
5321 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5324 // go to the downloading list mode
5325 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST;
5329 case PXO_BAN_MODE_LIST:
5331 if(Multi_pxo_ban_get->IsFileError()){
5332 delete Multi_pxo_ban_get;
5333 Multi_pxo_ban_get = NULL;
5334 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5338 // connecting, receiving
5339 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5344 if(Multi_pxo_ban_get->IsFileReceived()){
5345 delete Multi_pxo_ban_get;
5346 Multi_pxo_ban_get = NULL;
5347 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_STARTUP;
5351 // start downloading files
5352 case PXO_BAN_MODE_IMAGES_STARTUP:
5353 // first thing - parse the banners file and pick a file
5354 multi_pxo_ban_parse_banner_file(0);
5356 // if we have no active file, we're done
5357 if((strlen(Multi_pxo_banner.ban_file) <= 0) || (strlen(Multi_pxo_banner.ban_file_url) <= 0)){
5358 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5362 // if the file already exists, we're done
5363 if(cf_exist(Multi_pxo_banner.ban_file, CF_TYPE_MULTI_CACHE)){
5364 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5368 // otherwise try and download it
5369 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, Multi_pxo_banner.ban_file);
5370 // try creating the file get object
5371 Multi_pxo_ban_get = new InetGetFile(Multi_pxo_banner.ban_file_url, local_file);
5374 if(Multi_pxo_ban_get == NULL){
5375 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5378 // go to the downloading images mode
5379 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES;
5382 // downloading files
5383 case PXO_BAN_MODE_IMAGES:
5385 if(Multi_pxo_ban_get->IsFileError()){
5386 delete Multi_pxo_ban_get;
5387 Multi_pxo_ban_get = NULL;
5388 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5392 // connecting, receiving
5393 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5398 if(Multi_pxo_ban_get->IsFileReceived()){
5399 delete Multi_pxo_ban_get;
5400 Multi_pxo_ban_get = NULL;
5401 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5405 // done downloading - maybe load an image
5406 case PXO_BAN_MODE_IMAGES_DONE:
5407 // make sure we have a valid filename
5408 // SDL_assert(strlen(Multi_pxo_banner.ban_file) > 0);
5409 if(strlen(Multi_pxo_banner.ban_file) > 0){
5410 Multi_pxo_banner.ban_bitmap = bm_load(Multi_pxo_banner.ban_file);
5414 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5417 // idle (done with EVERYTHING)
5418 case PXO_BAN_MODE_IDLE:
5419 // if the banner button was clicked
5420 if(Multi_pxo_ban_button.pressed()){
5421 multi_pxo_ban_clicked();
5425 case PXO_BAN_MODE_CHOOSE_RANDOM:
5426 // first thing - parse the banners file and pick a file
5427 multi_pxo_ban_parse_banner_file(1);
5429 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5435 void multi_pxo_ban_close()
5437 // if we have a currently active transfer
5438 if(Multi_pxo_ban_get != NULL){
5439 Multi_pxo_ban_get->AbortGet();
5440 delete Multi_pxo_ban_get;
5441 Multi_pxo_ban_get = NULL;
5444 // if we have a loaded bitmap, unload it
5445 if(Multi_pxo_banner.ban_bitmap != -1){
5446 bm_unload(Multi_pxo_banner.ban_bitmap);
5447 Multi_pxo_banner.ban_bitmap = -1;
5451 // parse the banners file and maybe fill in Multi_pxo_dl_file
5452 void multi_pxo_ban_parse_banner_file(int choose_existing)
5454 char file_url[MAX_PATH_LEN] = "";
5455 char banners[10][MAX_PATH_LEN];
5456 char urls[10][MAX_PATH_LEN];
5459 int num_banners, idx;
5460 CFILE *in = cfopen(PXO_BANNERS_CONFIG_FILE, "rt", CFILE_NORMAL, CF_TYPE_MULTI_CACHE);
5462 SDL_zero(Multi_pxo_banner);
5463 Multi_pxo_banner.ban_bitmap = -1;
5470 // clear all strings
5474 // get the global banner url
5475 if(cfgets(file_url, SDL_arraysize(file_url), in) == NULL){
5477 cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5480 drop_leading_white_space(file_url);
5481 drop_trailing_white_space(file_url);
5483 // otherwise read in
5485 while(num_banners < 10){
5486 // try and get the pcx
5487 if(cfgets(banners[num_banners], SDL_arraysize(banners[0]), in) == NULL){
5490 // try and get the url
5491 if(cfgets(urls[num_banners], SDL_arraysize(urls[0]), in) == NULL){
5495 // strip off trailing and leading whitespace
5496 drop_leading_white_space(banners[num_banners]);
5497 drop_trailing_white_space(banners[num_banners]);
5498 drop_leading_white_space(urls[num_banners]);
5499 drop_trailing_white_space(urls[num_banners]);
5509 if(num_banners <= 0){
5513 // if we're only selecting files which already exist (previously downloaded)
5514 if(choose_existing){
5516 for(idx=0; idx<10; idx++){
5520 // build a list of existing files
5522 for(idx=0; idx<num_banners; idx++){
5523 if(cf_exist(banners[idx], CF_TYPE_MULTI_CACHE)){
5530 if(exist_count <= 0){
5535 int select = (int)frand_range(0.0f, (float)exist_count);
5536 if(select >= exist_count){
5537 select = exist_count - 1;
5542 for(idx=0; idx<exist_count; idx++){
5552 if(idx < exist_count){
5554 SDL_strlcpy(Multi_pxo_banner.ban_file, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file));
5556 // get the full file url
5557 SDL_strlcpy(Multi_pxo_banner.ban_file_url, file_url, SDL_arraysize(Multi_pxo_banner.ban_file_url));
5558 SDL_strlcat(Multi_pxo_banner.ban_file_url, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file_url));
5560 // url of where to go to when clicked
5561 SDL_strlcpy(Multi_pxo_banner.ban_url, urls[idx], SDL_arraysize(Multi_pxo_banner.ban_url));
5564 // randomly pick a file for download
5566 idx = (int)frand_range(0.0f, (float)num_banners);
5568 if(idx >= num_banners){
5569 idx = num_banners - 1;
5576 SDL_strlcpy(Multi_pxo_banner.ban_file, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file));
5578 // get the full file url
5579 SDL_strlcpy(Multi_pxo_banner.ban_file_url, file_url, SDL_arraysize(Multi_pxo_banner.ban_file_url));
5580 SDL_strlcat(Multi_pxo_banner.ban_file_url, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file_url));
5582 // url of where to go to when clicked
5583 SDL_strlcpy(Multi_pxo_banner.ban_url, urls[idx], SDL_arraysize(Multi_pxo_banner.ban_url));
5586 // delete the banner config file
5587 // cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5590 // any bitmap or info or whatever
5591 void multi_pxo_ban_draw()
5593 // if we have a valid bitmap
5594 if(Multi_pxo_banner.ban_bitmap >= 0){
5595 // if the mouse is over the banner button, highlight with a rectangle
5596 if(Multi_pxo_ban_button.is_mouse_on()){
5597 gr_set_color_fast(&Color_bright_blue);
5598 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);
5601 // draw the bitmap itself
5602 gr_set_bitmap(Multi_pxo_banner.ban_bitmap);
5603 gr_bitmap(Pxo_ban_coords[gr_screen.res][0], Pxo_ban_coords[gr_screen.res][1]);
5607 // called when the URL button is clicked
5608 void multi_pxo_ban_clicked()
5610 // if we have a valid bitmap and URL, launch the URL
5611 if((Multi_pxo_banner.ban_bitmap >= 0) && (strlen(Multi_pxo_banner.ban_url) > 0)){
5612 multi_pxo_url(Multi_pxo_banner.ban_url);