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);
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);
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 sprintf(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 memset(Multi_pxo_channel_last, 0, MAX_CHANNEL_NAME_LEN + 1);
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);
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 strcat(anim_filename, MULTI_PXO_ANIM_FNAME);
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 memset(Multi_fs_tracker_channel,0,255);
1557 memset(Multi_fs_tracker_filter,0,255);
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 strcpy(priv_chan.name,Multi_pxo_priv_chan);
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 strcpy(join.name,Multi_pxo_find_channel);
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 memset(Multi_fs_tracker_channel,0,255);
1652 memset(Multi_fs_tracker_filter,0,255);
1653 if( ON_CHANNEL() && strlen(Multi_pxo_channel_current.name) ){
1655 strcpy(Multi_fs_tracker_channel,Multi_pxo_channel_current.name);
1658 strcpy(Multi_fs_tracker_filter,Multi_pxo_channel_current.name);
1661 // disconnect from the server
1662 DisconnectFromChatServer();
1663 Multi_pxo_connected = 0;
1665 // unload the animation
1666 anim_release_all_instances(GS_STATE_PXO);
1667 Multi_pxo_anim_instance = NULL;
1668 if(Multi_pxo_anim != NULL){
1669 anim_free(Multi_pxo_anim);
1670 Multi_pxo_anim = NULL;
1673 // unload the palette for this screen
1674 multi_pxo_unload_palette();
1676 // destroy the UI_WINDOW
1677 Multi_pxo_window.destroy();
1679 // clear the channel list
1680 multi_pxo_clear_channels();
1682 // close the chat system
1683 multi_pxo_chat_free();
1686 multi_pxo_ban_close();
1689 // run normally (no popups)
1690 void multi_pxo_do_normal()
1693 int k = Multi_pxo_window.process();
1695 // if the animation isn't playing, start it up
1696 if((Multi_pxo_anim_instance == NULL) && (Multi_pxo_anim != NULL)){
1697 anim_play_struct aps;
1699 // fire up the animation
1700 anim_play_init(&aps, Multi_pxo_anim, MULTI_PXO_ANIM_X, MULTI_PXO_ANIM_Y);
1701 aps.screen_id = GS_STATE_PXO;
1702 aps.framerate_independent = 1;
1704 Multi_pxo_anim_instance = anim_play(&aps);
1707 // process any keypresses
1710 gamesnd_play_iface(SND_USER_SELECT);
1711 gameseq_post_event(GS_EVENT_MAIN_MENU);
1715 // check for button presses
1716 multi_pxo_check_buttons();
1718 // if we're not in a chatroom, disable and hide the chat input box
1720 Multi_pxo_chat_input.hide();
1721 Multi_pxo_chat_input.disable();
1723 Multi_pxo_chat_input.enable();
1724 Multi_pxo_chat_input.unhide();
1728 multi_pxo_blit_all();
1733 // verify version # now (only once per Freespace instance)
1734 if(Multi_pxo_must_verify_version){
1735 switch(multi_update_gobaby()){
1736 // everything is cool. Move along
1737 case MULTI_UPDATE_CONTINUE:
1738 Multi_pxo_must_verify_version = 0;
1741 // go back to the main menu
1742 case MULTI_UPDATE_MAIN_MENU:
1743 gameseq_post_event(GS_EVENT_MAIN_MENU);
1745 // unset these so we don't do anything else PXO related
1746 Multi_pxo_must_validate = 0;
1747 Multi_pxo_must_connect = 0;
1750 // freespace will be shutting down shortly
1751 case MULTI_UPDATE_SHUTTING_DOWN:
1756 // if we need to get tracker info for ourselves, do so
1757 if(Multi_pxo_must_validate){
1758 // initialize the master tracker API for Freespace
1759 multi_fs_tracker_init();
1761 // validate the current player with the master tracker (will create the pilot on the MT if necessary)
1762 validate_code = multi_fs_tracker_validate(0);
1763 if(validate_code != 1){
1764 // show an error popup if it failed (not cancelled by the user)
1765 if (validate_code == 0) {
1766 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))) {
1768 nprintf(("Network","PXO CANCEL\n"));
1770 // flip his "pxo" bit temporarily and push him to the join game screen
1771 Multi_options_g.pxo = 0;
1772 // Net_game_tcp_mode = NET_TCP;
1773 gameseq_post_event(GS_EVENT_MULTI_JOIN_GAME);
1777 nprintf(("Network","PXO CREATE\n"));
1778 // fire up the given URL
1779 multi_pxo_url(Multi_options_g.pxo_create_url);
1783 nprintf(("Network","PXO VERIFY\n"));
1784 // fire up the given URL
1785 multi_pxo_url(Multi_options_g.pxo_verify_url);
1790 // go back to the main hall
1791 gameseq_post_event(GS_EVENT_MAIN_MENU);
1793 Multi_pxo_must_connect = 0;
1794 Multi_pxo_must_validate = 0;
1796 // now we have to conenct to PXO
1798 Multi_pxo_must_connect = 1;
1799 Multi_pxo_must_validate = 0;
1803 // if we need to connect, do so now
1804 if(Multi_pxo_must_connect){
1805 // for now, just try once
1806 Multi_pxo_connected = multi_pxo_connect();
1808 // if we successfully connected, send a request for a list of channels on the server
1809 if(Multi_pxo_connected){
1810 multi_pxo_get_channels();
1813 multi_pxo_set_status_text(XSTR("Retrieving Public Channels",939));
1816 multi_pxo_set_status_text(XSTR("Failed to connect to Parallax Online",940));
1819 // no longer need to connect
1820 Multi_pxo_must_connect = 0;
1824 // blit everything on the "normal" screen
1825 void multi_pxo_blit_all()
1827 // draw the background, etc
1829 // GR_MAYBE_CLEAR_RES(Multi_pxo_bitmap);
1830 int bmap = Multi_pxo_bitmap;
1835 bm_get_info( bmap, &bmw, &bmh);
1836 if((bmw != gr_screen.max_w) || (bmh != gr_screen.max_h)){
1843 if(Multi_pxo_bitmap != -1){
1844 gr_set_bitmap(Multi_pxo_bitmap);
1847 Multi_pxo_window.draw();
1849 // display the channel list
1850 multi_pxo_blit_channels();
1852 // display the player list
1853 multi_pxo_blit_players();
1855 // blit the chat text
1856 multi_pxo_chat_blit();
1858 // blit the status text
1859 multi_pxo_blit_status_text();
1861 // blit and process the notification string
1862 multi_pxo_notify_blit();
1864 // any bitmap or info or whatever
1865 multi_pxo_ban_draw();
1867 // draw any motd stuff
1868 multi_pxo_motd_maybe_blit();
1870 // if we have a valid animation handle, play it
1871 if(Multi_pxo_anim_instance != NULL){
1872 anim_render_all(GS_STATE_PXO,flFrametime);
1876 // process common stuff
1877 void multi_pxo_process_common()
1879 // process the channel list (select, etc)
1880 multi_pxo_process_channels();
1882 // process the player list (select, etc)
1883 multi_pxo_process_players();
1885 // process chat controls
1886 multi_pxo_chat_process();
1888 // process http download details
1889 multi_pxo_ban_process();
1892 // get selected player information
1893 void multi_pxo_get_data(char *name)
1897 // handle being kicked
1898 void multi_pxo_handle_kick()
1900 // remove ourselves from the room
1901 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
1902 Multi_pxo_channel_current.num_users = -1;
1905 multi_pxo_chat_clear();
1907 // clear the old player list
1908 multi_pxo_clear_players();
1910 // add a notification string
1911 multi_pxo_notify_add(XSTR("You have been kicked",941));
1914 // handle being disconnected
1915 void multi_pxo_handle_disconnect()
1917 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been disconnected from the server",942));
1918 gameseq_post_event(GS_EVENT_MAIN_MENU);
1921 // return string2, which is the first substring of string 1 without a space
1922 // it is safe to pass the same pointer for both parameters
1923 void multi_pxo_strip_space(char *string1,char *string2)
1928 // copy the original
1929 strcpy(midway,string1);
1930 tok = strtok(midway," ");
1932 strcpy(string2,tok);
1938 // fire up the given URL
1939 void multi_pxo_url(char *url)
1944 // execute the shell command
1945 int r = (int) ShellExecute(NULL, NOX("open"), url, NULL, NULL, SW_SHOW);
1949 case ERROR_BAD_FORMAT:
1950 case SE_ERR_ACCESSDENIED:
1951 case SE_ERR_ASSOCINCOMPLETE:
1952 case SE_ERR_DDEBUSY:
1953 case SE_ERR_DDEFAIL:
1954 case SE_ERR_DDETIMEOUT:
1955 case SE_ERR_DLLNOTFOUND:
1958 case SE_ERR_NOASSOC:
1959 case ERROR_FILE_NOT_FOUND:
1960 case ERROR_PATH_NOT_FOUND:
1961 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,XSTR("Warning\nCould not locate/launch default Internet Browser",943));
1968 // load/set the palette
1969 void multi_pxo_load_palette()
1972 #ifndef HARDWARE_ONLY
1973 palette_use_bm_palette(Multi_pxo_palette);
1977 // unload the palette
1978 void multi_pxo_unload_palette()
1980 // unload the palette if it exists
1981 if(Multi_pxo_palette != -1){
1982 bm_release(Multi_pxo_palette);
1983 Multi_pxo_palette = -1;
1987 // if we're currently on a private channel
1988 int multi_pxo_on_private_channel()
1990 // if we're connected to a channel with the "+" symbol on front
1991 if(ON_CHANNEL() && (Multi_pxo_channel_current.name[0] == '+')){
1995 // otherwise return falos
1999 // convert string 1 into string 2, substituting underscores for spaces
2000 void multi_pxo_underscore_nick(char *string1,char *string2)
2002 char nick_temp[512];
2005 // don't do anything if we have bogus string
2006 if((string1 == NULL) || (string2 == NULL)){
2010 // copy the nickname
2011 memset(nick_temp,0,512);
2012 strcpy(nick_temp,string1);
2014 // get the first token
2015 tok = strtok(nick_temp," ");
2017 strcpy(string2,tok);
2019 // get the next token
2020 tok = strtok(NULL," ");
2023 strcat(string2,"_");
2024 strcat(string2,tok);
2027 tok = strtok(NULL," ");
2030 strcpy(string2,string1);
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
2043 tok = strtok(tmp," ");
2045 // can't be a nick message
2049 return !SDL_strcasecmp(tok,NOX("/nick"));
2052 // check for button presses
2053 void multi_pxo_check_buttons()
2057 // go through all buttons
2058 for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
2059 if(Multi_pxo_buttons[gr_screen.res][idx].button.pressed()){
2060 multi_pxo_button_pressed(idx);
2066 // handle a button press
2067 void multi_pxo_button_pressed(int n)
2070 case MULTI_PXO_EXIT:
2071 gamesnd_play_iface(SND_USER_SELECT);
2072 gameseq_post_event(GS_EVENT_MAIN_MENU);
2075 case MULTI_PXO_CHAN_UP:
2076 multi_pxo_scroll_channels_up();
2079 case MULTI_PXO_CHAN_DOWN:
2080 multi_pxo_scroll_channels_down();
2083 case MULTI_PXO_TEXT_UP:
2084 multi_pxo_scroll_chat_up();
2087 case MULTI_PXO_TEXT_DOWN:
2088 multi_pxo_scroll_chat_down();
2091 case MULTI_PXO_PLIST_UP:
2092 multi_pxo_scroll_players_up();
2093 multi_pxo_chat_adjust_start();
2096 case MULTI_PXO_PLIST_DOWN:
2097 multi_pxo_scroll_players_down();
2098 multi_pxo_chat_adjust_start();
2101 case MULTI_PXO_JOIN:
2102 // if there are no channels to join, let the user know
2103 if((Multi_pxo_channel_count == 0) || (Multi_pxo_channels == NULL)){
2104 gamesnd_play_iface(SND_GENERAL_FAIL);
2105 multi_pxo_notify_add(XSTR("No channels!",944));
2109 // if we're not already trying to join, allow this
2110 if(!SWITCHING_CHANNELS() && (Multi_pxo_channel_select != NULL)){
2111 gamesnd_play_iface(SND_USER_SELECT);
2112 multi_pxo_join_channel(Multi_pxo_channel_select);
2114 multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2115 gamesnd_play_iface(SND_GENERAL_FAIL);
2119 case MULTI_PXO_GAMES:
2120 // move to the join game screen as normally (temporary!)
2121 gameseq_post_event( GS_EVENT_MULTI_JOIN_GAME );
2124 case MULTI_PXO_JOIN_PRIV:
2125 // if we're not already trying to join, allow this
2126 if(!SWITCHING_CHANNELS()){
2127 gamesnd_play_iface(SND_USER_SELECT);
2129 // fire up the private join popup
2130 multi_pxo_priv_popup();
2132 multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2133 gamesnd_play_iface(SND_GENERAL_FAIL);
2137 case MULTI_PXO_FIND:
2138 gamesnd_play_iface(SND_USER_SELECT);
2140 // fire up the find join popup
2141 multi_pxo_find_popup();
2144 case MULTI_PXO_HELP:
2145 gamesnd_play_iface(SND_USER_SELECT);
2146 gameseq_post_event(GS_EVENT_PXO_HELP);
2149 case MULTI_PXO_PINFO:
2152 // if we have a guy selected, try and get his info
2153 if(Multi_pxo_player_select != NULL){
2154 // if we successfully got info for this guy
2155 if(multi_pxo_pinfo_get(Multi_pxo_player_select->name)){
2157 multi_pxo_pinfo_show();
2159 // if we didn't get stats for this guy.
2161 memset(stats,0,255);
2162 sprintf(stats,XSTR("Could not get stats for %s\n(May not be a registered pilot)",946),Multi_pxo_player_select->name);
2163 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,stats);
2166 gamesnd_play_iface(SND_GENERAL_FAIL);
2170 case MULTI_PXO_RANKINGS:
2171 // make sure he doesn't click it too many times
2172 if((Multi_pxo_ranking_last < 0.0f) || ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_ranking_last) > MULTI_PXO_RANK_TIME) ){
2173 gamesnd_play_iface(SND_USER_SELECT);
2176 multi_pxo_url(Multi_options_g.pxo_rank_url);
2178 // mark the time down
2179 Multi_pxo_ranking_last = f2fl(timer_get_fixed_seconds());
2181 gamesnd_play_iface(SND_GENERAL_FAIL);
2185 case MULTI_PXO_MOTD:
2186 // maybe fire up the pxo motd dialog
2187 multi_pxo_motd_dialog();
2192 // condition function for popup_do_with_condition for connected to Parallax Online
2193 int mpxo_failed = 0;
2194 int multi_pxo_connect_do()
2197 char id_string[255] = "";
2198 char ip_string[255] = "";
2200 // if we already tried and failed, sit around until the user presses cancel
2202 // try and connect to the server
2205 // build the tracker id string
2206 memset(id_string, 0, 255);
2207 sprintf(id_string, "%s %s", Multi_tracker_id_string, Player->callsign);
2209 // build the ip string
2210 memset(ip_string, 0, 255);
2211 sprintf(ip_string, "%s:%d", Multi_options_g.pxo_ip, PXO_CHAT_PORT);
2213 // connect to the server
2214 ret_code = ConnectToChatServer(ip_string, Multi_pxo_nick, id_string);
2216 // give some time to the pxo api.
2217 multi_pxo_api_process();
2220 // already connected, return success
2224 // failed to connect, return fail
2227 popup_change_text(XSTR("Failed to connect to Parallax Online!", 947));
2230 // connected, return success
2243 // popup loop which does an autojoin of a public channel. Returns when the autojoin process is complete
2244 int multi_pxo_autojoin_do()
2246 pxo_channel last_channel;
2248 // if we need to autojoin, do so now
2249 if(Multi_pxo_must_autojoin){
2250 Multi_pxo_must_autojoin = 0;
2252 // if we're supposed to be using a (valid) "last" channel, do so
2253 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){
2255 memset(&last_channel, 0, sizeof(pxo_channel));
2256 last_channel.num_users = 0;
2257 strcpy(last_channel.name, Multi_pxo_channel_last);
2260 multi_pxo_join_channel(&last_channel);
2262 nprintf(("Network","PXO : using last channel\n"));
2264 multi_pxo_autojoin();
2266 nprintf(("Network","PXO : using autojoin channel\n"));
2268 multi_pxo_get_channels();
2271 // give some time to the pxo api.
2272 multi_pxo_api_process();
2273 multi_pxo_process_common();
2275 // next value is not -1 when actually switching channels, so keep processing by returning 0.
2276 if ( SWITCHING_CHANNELS() ){
2280 // couldn't switch channel for some reason. bail out with -1
2281 if ( !ON_CHANNEL() ){
2289 // attempt to connect to Parallax Online, return success or fail
2290 int multi_pxo_connect()
2293 char join_fail_str[256];
2295 // intiialize chat api
2298 // set us to "must autojoin"
2299 Multi_pxo_must_autojoin = 1;
2301 // run the connect dialog/popup
2303 if(popup_till_condition(multi_pxo_connect_do, XSTR("&Cancel", 779), XSTR("Logging into Parallax Online",949)) == 10){
2306 memset(join_str,0,256);
2307 memset(join_fail_str,0,256);
2308 // if we're going to use the "last" channel
2309 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){
2310 strcpy(join_str, XSTR("Joining last channel (",982));
2311 strcat(join_str, Multi_pxo_channel_last + 1);
2312 strcat(join_str, ")");
2314 strcpy(join_fail_str, XSTR("Unable to join last channel", 983));
2316 strcpy(join_str, XSTR("Autojoining public channel", 984));
2317 strcpy(join_fail_str, XSTR("Unable to autojoin public channel", 985));
2320 // once connected, we should do an autojoin before allowing the guy to continue.
2321 rval = popup_till_condition( multi_pxo_autojoin_do, XSTR("&Cancel", 779), join_str );
2326 popup( PF_USE_AFFIRMATIVE_ICON, 1, XSTR("OK", 1492), join_fail_str);
2329 // otherwise disconnect just to be safe
2330 DisconnectFromChatServer();
2331 gameseq_post_event(GS_EVENT_MAIN_MENU);
2333 // did not successfully connect
2337 // run the networking functions for the PXO API
2338 void multi_pxo_api_process()
2343 pxo_channel *lookup;
2345 // give some time to psnet
2346 PSNET_TOP_LAYER_PROCESS();
2348 // give some time to the game tracker API
2351 // give some time to the user tracker API
2354 // get any incoming text
2360 // process the chat line
2361 multi_pxo_chat_process_incoming(p);
2365 // get any incoming channel list stuff
2366 p = GetChannelList();
2369 // nprintf(("Network","%s\n",p));
2370 multi_pxo_make_channels(p);
2373 // process any chat commands
2374 cmd = GetChatCommand();
2377 switch(cmd->command)
2379 case CC_USER_JOINING:
2380 // add a user, if he doesn't already exist
2381 if(multi_pxo_find_player(cmd->data) == NULL){
2382 multi_pxo_add_player(cmd->data);
2385 // increase the player count
2387 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2389 lookup->num_users++;
2394 case CC_USER_LEAVING:
2396 multi_pxo_del_player(cmd->data);
2398 // add a text message
2399 memset(msg_str,0,512);
2400 sprintf(msg_str, XSTR("*** %s has left", 950), cmd->data);
2401 multi_pxo_chat_process_incoming(msg_str);
2403 // decrease the player count
2405 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2407 lookup->num_users--;
2412 case CC_DISCONNECTED:
2413 multi_pxo_handle_disconnect();
2417 multi_pxo_handle_kick();
2420 case CC_NICKCHANGED:
2421 // process a nick change
2422 multi_pxo_process_nick_change(cmd->data);
2425 case CC_YOURCHANNEL:
2426 // copy the current channel info, and unset the switching status
2427 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2428 Multi_pxo_channel_switch.num_users = -1;
2430 SetNewChatChannel(NULL);
2432 strcpy(Multi_pxo_channel_current.name,cmd->data);
2434 // if we don't already have this guy on the list, add him
2435 pxo_channel *lookup;
2436 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2438 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2439 lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2442 // set the user count to be 0
2444 lookup->num_users = 0;
2447 // set our "last" channel to be this one
2448 strcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name);
2450 // refresh current channel server count
2451 multi_pxo_channel_refresh_current();
2453 // clear the chat area
2454 // multi_pxo_chat_clear();
2461 cmd = GetChatCommand();
2464 // handle any processing details if we're currently trying to join a channel
2465 multi_pxo_handle_channel_change();
2468 // process a "nick" change event
2469 void multi_pxo_process_nick_change(char *data)
2472 player_list *lookup;
2474 // get the new string
2475 from = strtok(data," ");
2476 to = strtok(NULL,"");
2477 if((from != NULL) && (to != NULL)){
2478 lookup = multi_pxo_find_player(from);
2480 strcpy(lookup->name,to);
2482 // if this is also my nick, change it
2483 if(!SDL_strcasecmp(Multi_pxo_nick,from)){
2484 strcpy(Multi_pxo_nick,to);
2490 // autojoin an appropriate channel
2491 void multi_pxo_autojoin()
2495 memset(&sw,0,sizeof(pxo_channel));
2497 strcpy(sw.name,MULTI_PXO_AUTOJOIN_CHANNEL);
2499 // if we found a valid room, attempt to join it
2500 multi_pxo_join_channel(&sw);
2503 // does the string match the "autojoin" prefic
2504 int multi_pxo_is_autojoin(char *name)
2506 // check to see if the name is long enough
2507 if(strlen(name) < strlen(MULTI_PXO_AUTOJOIN_PREFIX)){
2511 // check to see if the first n chars match
2512 return !SDL_strncasecmp(name,MULTI_PXO_AUTOJOIN_PREFIX,strlen(MULTI_PXO_AUTOJOIN_PREFIX));
2515 // called from the game tracker API - server count update for a channel
2516 void multi_pxo_channel_count_update(char *name,int count)
2518 pxo_channel *lookup;
2520 // lookup the channel name on the normal list
2522 lookup = multi_pxo_find_channel(name,Multi_pxo_channels);
2524 lookup->num_servers = (ushort)count;
2526 nprintf(("Network","PXO : updated channel %s server count to %d\n",name,count));
2530 // status bar stuff -----------------------------------------------
2532 // set the status text
2533 void multi_pxo_set_status_text(const char *txt)
2536 memset(Multi_pxo_status_text, 0, 255);
2537 strncpy(Multi_pxo_status_text, txt, 254);
2539 // make sure it fits properly
2540 gr_force_fit_string(Multi_pxo_status_text, 254, Multi_pxo_status_coords[gr_screen.res][2]);
2543 // blit the status text
2544 void multi_pxo_blit_status_text()
2548 // center and draw the text
2549 if(strlen(Multi_pxo_status_text)) {
2550 gr_set_color_fast(&Color_bright);
2551 gr_get_string_size(&w, NULL, Multi_pxo_status_text);
2552 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);
2557 // channel related stuff -------------------------------------------
2559 // get a list of channels on the server
2560 void multi_pxo_get_channels()
2562 SendChatString(NOX("/list"));
2565 // clear the old channel list
2566 void multi_pxo_clear_channels()
2568 pxo_channel *moveup,*backup;
2570 // only clear a non-null list
2571 if(Multi_pxo_channels != NULL){
2573 moveup = Multi_pxo_channels;
2578 moveup = moveup->next;
2580 // free the struct itself
2583 } while(moveup != Multi_pxo_channels);
2584 Multi_pxo_channels = NULL;
2587 // head of the list of available channels
2588 Multi_pxo_channels = NULL;
2589 Multi_pxo_channel_count = 0;
2591 // item we're going to start displaying at
2592 Multi_pxo_channel_start = NULL;
2593 Multi_pxo_channel_start_index = -1;
2595 // items we've currently got selected
2596 Multi_pxo_channel_select = NULL;
2600 // parse the input string and make a list of new channels
2601 void multi_pxo_make_channels(char *chan_str)
2603 char *name_tok,*user_tok,*desc_tok;
2605 pxo_channel *lookup;
2608 nprintf(("Network","Making some channels!\n"));
2610 // clear the channel list
2611 // multi_pxo_clear_channels();
2613 // set the last get time
2614 Multi_pxo_channel_last_refresh = f2fl(timer_get_fixed_seconds());
2616 name_tok = strtok(chan_str," ");
2617 if(name_tok == NULL){
2622 // parse the user count token
2623 user_tok = strtok(NULL," ");
2625 // parse the channel description token
2626 desc_tok = strtok(NULL,"$");
2628 // something invalid in the data, return here.....
2629 if((name_tok == NULL) || (user_tok == NULL) || (desc_tok == NULL)){
2633 // get the # of users
2635 num_users = (ubyte)atoi(user_tok);
2637 // if the # of users is > 0, or its not an autojoin, place it on the display list
2638 if((num_users > 0) || !multi_pxo_is_autojoin(name_tok)){
2639 // see if it exists already, and if so, just update the user count
2640 lookup = multi_pxo_find_channel(name_tok,Multi_pxo_channels);
2643 lookup->num_users = (short)num_users;
2647 res = multi_pxo_add_channel(name_tok,&Multi_pxo_channels);
2649 //Multi_pxo_channel_count++;
2650 res->num_users = (short)num_users;
2651 strcpy(res->desc,desc_tok);
2656 // get the next name token
2657 name_tok = strtok(NULL," ");
2658 } while(name_tok != NULL);
2660 // if we need to autojoin, do so now
2661 //if(Multi_pxo_must_autojoin){
2662 // Multi_pxo_must_autojoin = 0;
2664 // multi_pxo_autojoin();
2668 multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
2670 // if we haven't refreshed server counts yet, do it now
2671 if(Multi_pxo_channel_server_refresh < 0.0f){
2672 multi_pxo_channel_refresh_servers();
2675 // if we don't already have this guy on the list, add him
2677 pxo_channel *lookup;
2678 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2680 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2681 multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2686 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2687 pxo_channel *multi_pxo_add_channel(char *name,pxo_channel **list)
2689 pxo_channel *new_channel;
2691 // try and allocate a new pxo_channel struct
2692 new_channel = (pxo_channel *)malloc(sizeof(pxo_channel));
2693 if ( new_channel == NULL ) {
2694 nprintf(("Network", "Cannot allocate space for new pxo_channel structure\n"));
2697 memset(new_channel,0,sizeof(pxo_channel));
2698 // try and allocate a string for the channel name
2699 strncpy(new_channel->name,name,MAX_CHANNEL_NAME_LEN);
2701 // insert it on the list
2702 if ( *list != NULL ) {
2703 new_channel->next = (*list)->next;
2704 new_channel->next->prev = new_channel;
2705 (*list)->next = new_channel;
2706 new_channel->prev = *list;
2708 *list = new_channel;
2709 (*list)->next = (*list)->prev = *list;
2712 Multi_pxo_channel_count++;
2716 // lookup a channel with the specified name
2717 pxo_channel *multi_pxo_find_channel(char *name,pxo_channel *list)
2719 pxo_channel *moveup;
2721 // look the sucker up
2727 if(!SDL_strcasecmp(name,moveup->name)){
2731 moveup = moveup->next;
2732 } while((moveup != list) && (moveup != NULL));
2737 // process the channel list (select, etc)
2738 void multi_pxo_process_channels()
2743 // if we don't have a start item, but the list is non-null
2744 if((Multi_pxo_channel_start == NULL) && (Multi_pxo_channels != NULL)){
2745 Multi_pxo_channel_start = Multi_pxo_channels;
2746 Multi_pxo_channel_start_index = 0;
2749 // if we don't have a selected item, but the list is non-null
2750 if((Multi_pxo_channel_select == NULL) && (Multi_pxo_channels != NULL)){
2751 Multi_pxo_channel_select = Multi_pxo_channels;
2754 multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2757 // if the "switch" delay timestamp is set, see if it has expired
2758 if((Multi_pxo_switch_delay != -1) && timestamp_elapsed(Multi_pxo_switch_delay)){
2759 Multi_pxo_switch_delay = -1;
2762 // see if we have a mouse click on the channel region
2763 if(Multi_pxo_channel_button.pressed()){
2764 Multi_pxo_channel_button.get_mouse_pos(NULL,&my);
2766 // index from the top
2767 item_index = my / 10;
2769 // select the item if possible
2770 if((item_index + Multi_pxo_channel_start_index) < Multi_pxo_channel_count){
2771 Multi_pxo_channel_select = Multi_pxo_channel_start;
2772 for(idx=0;idx<item_index;idx++){
2773 Multi_pxo_channel_select = Multi_pxo_channel_select->next;
2777 multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2781 // last refresh time
2782 if((Multi_pxo_channel_last_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_last_refresh) > CHANNEL_REFRESH_TIME) ){
2784 multi_pxo_set_status_text(XSTR("Refreshing Public Channel List",952));
2786 // get a list of channels on the server (clear any old list as well)
2787 multi_pxo_get_channels();
2790 Multi_pxo_channel_last_refresh = -1.0f;
2792 nprintf(("Network","Refreshing channels\n"));
2795 // if we haven't updated our server channel counts in a while, do so again
2796 // last refresh time
2797 if((Multi_pxo_channel_server_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_server_refresh) > CHANNEL_SERVER_REFRESH_TIME) ){
2798 // refresh server counts
2799 // multi_pxo_set_status_text("Refreshing Public Channel Server Counts");
2801 // do it _NOW_ I"M RIGHT HERE KILL ME WHAT ARE YOU WAITING FOR DO IT KILL ME DO IT NOW!
2802 multi_pxo_channel_refresh_servers();
2806 // send a request to refresh our channel server counts
2807 void multi_pxo_channel_refresh_servers()
2809 pxo_channel *lookup;
2810 filter_game_list_struct filter;
2812 // traverse the list of existing channels we know about and query the game tracker about them
2813 lookup = Multi_pxo_channels;
2818 if(strlen(lookup->name)){
2820 memset(&filter,0,sizeof(filter_game_list_struct));
2821 strcpy(filter.channel,lookup->name);
2824 RequestGameCountWithFilter(&filter);
2828 lookup = lookup->next;
2829 } while((lookup != NULL) && (lookup != Multi_pxo_channels));
2832 Multi_pxo_channel_server_refresh = f2fl(timer_get_fixed_seconds());
2835 // refresh current channel server count
2836 void multi_pxo_channel_refresh_current()
2838 // send a request for a server count on this channel
2839 if(strlen(Multi_pxo_channel_current.name)){
2841 filter_game_list_struct filter;
2842 memset(&filter,0,sizeof(filter_game_list_struct));
2843 strcpy(filter.channel,Multi_pxo_channel_current.name);
2846 RequestGameCountWithFilter(&filter);
2850 // display the channel list
2851 void multi_pxo_blit_channels()
2853 pxo_channel *moveup;
2854 char chan_name[255];
2855 char chan_users[15];
2856 char chan_servers[15];
2857 int user_w,server_w;
2858 int disp_count,y_start;
2860 // blit as many channels as we can
2862 y_start = Multi_pxo_chan_coords[gr_screen.res][1];
2863 moveup = Multi_pxo_channel_start;
2868 // if this is the currently selected item, highlight it
2869 if(moveup == Multi_pxo_channel_select){
2870 gr_set_color_fast(&Color_bright);
2872 // otherwise draw it normally
2874 gr_set_color_fast(&Color_normal);
2877 // get the # of users on the channel
2878 memset(chan_users, 0, 15);
2879 sprintf(chan_users, "%d", moveup->num_users);
2881 // get the width of the user count string
2882 gr_get_string_size(&user_w, NULL, chan_users);
2884 // get the # of servers on the channel
2885 memset(chan_servers,0,15);
2886 sprintf(chan_servers, "%d", moveup->num_servers);
2888 // get the width of the user count string
2889 gr_get_string_size(&server_w, NULL, chan_servers);
2891 // make sure the name fits
2892 memset(chan_name, 0, 10);
2893 SDL_assert(moveup->name);
2894 strcpy(chan_name,moveup->name);
2895 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]);
2898 gr_string(Multi_pxo_chan_coords[gr_screen.res][0], y_start, chan_name + 1);
2899 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);
2900 gr_set_color_fast(&Color_bright);
2901 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);
2903 // increment the displayed count
2908 moveup = moveup->next;
2909 } while((moveup != Multi_pxo_channels) && (disp_count < Multi_pxo_max_chan_display[gr_screen.res]));
2912 // scroll channel list up
2913 void multi_pxo_scroll_channels_up()
2915 // if we're already at the head of the list, do nothing
2916 if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start == Multi_pxo_channels)){
2917 gamesnd_play_iface(SND_GENERAL_FAIL);
2921 // otherwise move up one
2922 Multi_pxo_channel_start = Multi_pxo_channel_start->prev;
2923 Multi_pxo_channel_start_index--;
2924 gamesnd_play_iface(SND_USER_SELECT);
2927 // scroll channel list down
2928 void multi_pxo_scroll_channels_down()
2930 // if we're already at the tail of the list, do nothing
2931 if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start->next == Multi_pxo_channels)){
2932 gamesnd_play_iface(SND_GENERAL_FAIL);
2936 // if we can't scroll further without going past the end of the viewable list, don't
2937 if((Multi_pxo_channel_start_index + Multi_pxo_max_chan_display[gr_screen.res]) >= Multi_pxo_channel_count){
2938 gamesnd_play_iface(SND_GENERAL_FAIL);
2942 // otherwise move down one
2943 Multi_pxo_channel_start = Multi_pxo_channel_start->next;
2944 Multi_pxo_channel_start_index++;
2945 gamesnd_play_iface(SND_USER_SELECT);
2948 // attempt to join a channel
2949 void multi_pxo_join_channel(pxo_channel *chan)
2951 char switch_msg[256];
2953 // if we're already on this channel, do nothing
2954 if(ON_CHANNEL() && !SDL_strcasecmp(chan->name,Multi_pxo_channel_current.name)){
2958 // if we're already trying to join a channel, do nothing
2959 if(SWITCHING_CHANNELS()){
2963 // try and join the channel
2964 switch(SetNewChatChannel(chan->name)){
2970 // decrement the count of our current channel
2971 pxo_channel *lookup;
2972 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2974 lookup->num_users--;
2977 // set our current channel as none
2978 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2979 Multi_pxo_channel_current.num_users = -1;
2981 multi_pxo_set_status_text(XSTR("Switching channels",953));
2984 memcpy(&Multi_pxo_channel_switch,chan,sizeof(pxo_channel));
2986 // clear the player list
2987 multi_pxo_clear_players();
2989 // display a line of text indicating that we're switching channels
2990 memset(switch_msg,0,256);
2992 if(strlen(Multi_pxo_channel_switch.name) > 1){
2993 sprintf(switch_msg, "[Switching to channel %s]", Multi_pxo_channel_switch.name + 1);
2995 sprintf(switch_msg, "[Switching to channel %s]", Multi_pxo_channel_switch.name);
2997 multi_pxo_chat_process_incoming(switch_msg, CHAT_MODE_CHANNEL_SWITCH);
3005 // handle any processing details if we're currently trying to join a channel
3006 void multi_pxo_handle_channel_change()
3008 // if we're not switching channels, do nothing
3009 if(!SWITCHING_CHANNELS()){
3013 // if we are, check the status
3014 switch(SetNewChatChannel(NULL)){
3017 // unset our switching struct
3018 memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
3019 Multi_pxo_channel_switch.num_users = -1;
3022 multi_pxo_set_status_text(XSTR("No channel (error while switching)",954));
3029 // successfully changed
3031 // copy the current channel info, and unset the switching status
3032 memcpy(&Multi_pxo_channel_current,&Multi_pxo_channel_switch,sizeof(pxo_channel));
3033 Multi_pxo_channel_switch.num_users = -1;
3035 // set our "last" channel
3036 strcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name);
3039 multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
3041 // if we don't already have this guy on the list, add him
3042 pxo_channel *lookup;
3043 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
3045 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
3046 lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
3049 // set the user count to be 1 (just me)
3051 lookup->num_users = 1;
3054 // clear the chat area
3055 // multi_pxo_chat_clear();
3057 // set the "switch" delay timestamp
3058 Multi_pxo_switch_delay = timestamp(MULTI_PXO_SWITCH_DELAY_TIME);
3060 // refresh current channel server count
3061 multi_pxo_channel_refresh_current();
3067 // player related stuff -------------------------------------------
3069 // clear the old player list
3070 void multi_pxo_clear_players()
3072 player_list *moveup,*backup;
3074 // if the list is null, don't free it up
3075 if(Multi_pxo_players != NULL){
3077 moveup = Multi_pxo_players;
3082 moveup = moveup->next;
3084 // free the struct itself
3087 } while(moveup != Multi_pxo_players);
3088 Multi_pxo_players = NULL;
3092 Multi_pxo_player_start = NULL;
3093 // Multi_pxo_player_start_index = -1;
3094 Multi_pxo_player_select = NULL;
3097 // Multi_pxo_player_slider.set_numberItems(0);
3099 // add a bunch of bogus players
3102 for(int idx=0;idx<30;idx++){
3103 sprintf(str,"player%d",idx);
3104 multi_pxo_add_player(str);
3109 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
3110 player_list *multi_pxo_add_player(char *name)
3112 player_list *new_player;
3114 // try and allocate a new player_list struct
3115 new_player = (player_list *)malloc(sizeof(player_list));
3116 if ( new_player == NULL ) {
3117 nprintf(("Network", "Cannot allocate space for new player_list structure\n"));
3120 // try and allocate a string for the channel name
3121 strncpy(new_player->name, name, MAX_PLAYER_NAME_LEN);
3123 // insert it on the list
3124 if ( Multi_pxo_players != NULL ) {
3125 new_player->next = Multi_pxo_players->next;
3126 new_player->next->prev = new_player;
3127 Multi_pxo_players->next = new_player;
3128 new_player->prev = Multi_pxo_players;
3130 Multi_pxo_players = new_player;
3131 Multi_pxo_players->next = Multi_pxo_players->prev = Multi_pxo_players;
3134 // increment the start position
3135 if(Multi_pxo_player_start != NULL){
3136 // Multi_pxo_player_start_index++;
3140 Multi_pxo_player_count++;
3142 // update the count on the slider
3143 // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3148 // remove a player with the given name
3149 void multi_pxo_del_player(char *name)
3151 player_list *lookup;
3153 // try and find this guy
3154 lookup = Multi_pxo_players;
3159 // if we found a match, delete it
3160 if(!SDL_strcasecmp(name,lookup->name)){
3161 // if this is the only item on the list, free stuff up
3162 if(lookup->next == lookup){
3163 SDL_assert(lookup == Multi_pxo_players);
3165 Multi_pxo_players = NULL;
3166 multi_pxo_clear_players();
3168 // otherwise, just delete it
3170 lookup->next->prev = lookup->prev;
3171 lookup->prev->next = lookup->next;
3173 // if this was our selected item, unselect it
3174 if((lookup == Multi_pxo_player_select) && (Multi_pxo_player_select != NULL)){
3175 Multi_pxo_player_select = Multi_pxo_player_select->next;
3178 // if this was our point to start viewing from, select another
3179 if(lookup == Multi_pxo_player_start){
3180 // if this is the head of the list, move up one
3181 if(Multi_pxo_players == lookup){
3182 Multi_pxo_player_start = Multi_pxo_player_start->next;
3183 // Multi_pxo_player_start_index = 0;
3185 // otherwise move back
3187 Multi_pxo_player_start = Multi_pxo_player_start->prev;
3188 // Multi_pxo_player_start_index++;
3192 // if this is the head of the list, move it up
3193 if(lookup == Multi_pxo_players){
3194 Multi_pxo_players = Multi_pxo_players->next;
3198 lookup->next = NULL;
3199 lookup->prev = NULL;
3204 Multi_pxo_player_count--;
3205 SDL_assert(Multi_pxo_player_count >= 0);
3207 // update the count on the slider
3208 // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3209 // Multi_pxo_player_slider.force_currentItem(Multi_pxo_player_start_index);
3216 lookup = lookup->next;
3217 } while((lookup != NULL) && (lookup != Multi_pxo_players));
3220 // try and find a player with the given name, return a pointer to his entry (or NULL)
3221 player_list *multi_pxo_find_player(char *name)
3223 player_list *lookup;
3225 // look through all players
3226 lookup = Multi_pxo_players;
3231 if(!SDL_strcasecmp(name,lookup->name)){
3235 lookup = lookup->next;
3236 } while((lookup != NULL) && (lookup != Multi_pxo_players));
3242 // process the player list (select, etc)
3243 void multi_pxo_process_players()
3246 player_list *lookup;
3248 // if we don't have a start item, but the list is non-null
3249 if((Multi_pxo_player_start == NULL) && (Multi_pxo_players != NULL)){
3250 Multi_pxo_player_start = Multi_pxo_players;
3251 // Multi_pxo_player_start_index = 0;
3253 // update the slider
3254 // Multi_pxo_player_slider.set_currentItem(Multi_pxo_player_start_index);
3257 // if we don't have a selected item, but the list is non-null
3258 if((Multi_pxo_player_select == NULL) && (Multi_pxo_players != NULL)){
3259 Multi_pxo_player_select = Multi_pxo_players;
3262 // see if we have a mouse click on the channel region
3263 if(Multi_pxo_player_button.pressed()){
3264 Multi_pxo_player_button.get_mouse_pos(NULL,&my);
3266 // index from the top
3267 item_index = my / 10;
3269 // select the item if possible
3270 lookup = Multi_pxo_player_start;
3275 if(item_index == 0){
3276 Multi_pxo_player_select = Multi_pxo_player_start;
3280 // move to the next item
3281 lookup = lookup->next;
3284 // if this item is our guy
3285 if((item_index == 0) && (lookup != Multi_pxo_players)){
3286 Multi_pxo_player_select = lookup;
3289 } while((lookup != Multi_pxo_players) && (item_index > 0));
3293 // display the player list
3294 void multi_pxo_blit_players()
3296 player_list *moveup;
3297 char player_name[255];
3298 int disp_count,y_start;
3300 // blit as many channels as we can
3302 y_start = Multi_pxo_player_coords[gr_screen.res][1];
3303 moveup = Multi_pxo_player_start;
3308 // if this is the currently selected item, highlight it
3309 if(moveup == Multi_pxo_player_select){
3310 gr_set_color_fast(&Color_bright);
3312 // otherwise draw it normally
3314 gr_set_color_fast(&Color_normal);
3317 // make sure the string fits
3318 strcpy(player_name,moveup->name);
3319 gr_force_fit_string(player_name, 254, Multi_pxo_player_coords[gr_screen.res][2]);
3322 gr_string(Multi_pxo_player_coords[gr_screen.res][0], y_start, player_name);
3324 // increment the displayed count
3329 moveup = moveup->next;
3330 } while((moveup != Multi_pxo_players) && (disp_count < Multi_pxo_max_player_display[gr_screen.res]));
3333 // scroll player list up
3334 void multi_pxo_scroll_players_up()
3336 // if we're already at the head of the list, do nothing
3337 if((Multi_pxo_player_start == NULL) || (Multi_pxo_player_start == Multi_pxo_players)){
3338 gamesnd_play_iface(SND_GENERAL_FAIL);
3342 // otherwise move up one
3343 Multi_pxo_player_start = Multi_pxo_player_start->prev;
3344 // Multi_pxo_player_start_index--;
3345 // SDL_assert(Multi_pxo_player_start_index >= 0);
3347 gamesnd_play_iface(SND_USER_SELECT);
3350 // scroll player list down
3351 void multi_pxo_scroll_players_down()
3353 player_list *lookup;
3356 // see if its okay to scroll down
3357 lookup = Multi_pxo_player_start;
3358 if(lookup == NULL ){
3359 gamesnd_play_iface(SND_GENERAL_FAIL);
3363 while(lookup->next != Multi_pxo_players){
3364 lookup = lookup->next;
3368 // if we can move down
3369 if(count >= Multi_pxo_max_player_display[gr_screen.res]){
3370 Multi_pxo_player_start = Multi_pxo_player_start->next;
3372 // Multi_pxo_player_start_index++;
3374 gamesnd_play_iface(SND_USER_SELECT);
3376 gamesnd_play_iface(SND_GENERAL_FAIL);
3381 // chat text stuff -----------------------------------------
3383 // initialize and create the chat text linked list
3384 void multi_pxo_chat_init()
3387 chat_line *new_line;
3390 Multi_pxo_chat = NULL;
3391 Multi_pxo_chat_add = NULL;
3392 Multi_pxo_chat_start = NULL;
3393 Multi_pxo_chat_start_index = -1;
3395 // create the lines in a non-circular doubly linked list
3396 for(idx=0;idx<MAX_CHAT_LINES;idx++){
3397 new_line = (chat_line*)malloc(sizeof(chat_line));
3399 // clear the line out
3400 SDL_assert(new_line != NULL);
3401 if(new_line == NULL){
3404 memset(new_line,0,sizeof(chat_line));
3405 new_line->prev = NULL;
3406 new_line->next = NULL;
3408 // insert it into the (empty) list
3409 if(Multi_pxo_chat == NULL){
3410 Multi_pxo_chat = new_line;
3412 // insert it onto the (non-empty) list
3414 Multi_pxo_chat->prev = new_line;
3415 new_line->next = Multi_pxo_chat;
3416 Multi_pxo_chat = new_line;
3420 // start adding chat lines at the beginning of the list
3421 Multi_pxo_chat_add = Multi_pxo_chat;
3424 // free up all chat list stuff
3425 void multi_pxo_chat_free()
3427 chat_line *moveup, *backup;
3429 // free all items up
3430 moveup = Multi_pxo_chat;
3431 while(moveup != NULL){
3433 moveup = moveup->next;
3439 Multi_pxo_chat = NULL;
3440 Multi_pxo_chat_add = NULL;
3441 Multi_pxo_chat_start = NULL;
3442 Multi_pxo_chat_start_index = -1;
3443 Multi_pxo_chat_count = 0;
3444 Multi_pxo_chat_slider.set_numberItems(0);
3447 // clear all lines of chat text in the chat area
3448 void multi_pxo_chat_clear()
3452 // clear the text in all the lines
3453 moveup = Multi_pxo_chat;
3454 while(moveup != NULL){
3455 memset(moveup->text,0,MAX_CHAT_LINE_LEN+1);
3456 moveup = moveup->next;
3459 // how many chat lines we have
3460 Multi_pxo_chat_count = 0;
3462 // start adding chat lines at the beginning of the list
3463 Multi_pxo_chat_add = Multi_pxo_chat;
3466 // add a line of text
3467 void multi_pxo_chat_add_line(char *txt, int mode)
3472 SDL_assert(Multi_pxo_chat_add != NULL);
3473 strncpy(Multi_pxo_chat_add->text, txt, MAX_CHAT_LINE_LEN);
3474 Multi_pxo_chat_add->mode = mode;
3476 // if we're at the end of the list, move the front item down
3477 if(Multi_pxo_chat_add->next == NULL) {
3478 // store the new "head" of the list
3479 temp = Multi_pxo_chat->next;
3481 // move the current head to the end of the list
3482 Multi_pxo_chat_add->next = Multi_pxo_chat;
3484 Multi_pxo_chat->prev = Multi_pxo_chat_add;
3485 Multi_pxo_chat->next = NULL;
3487 // reset the head of the list
3488 Multi_pxo_chat = temp;
3490 // set the new add line
3491 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3492 memset(Multi_pxo_chat_add->text, 0, MAX_CHAT_LINE_LEN+1);
3493 Multi_pxo_chat_add->mode = CHAT_MODE_NORMAL;
3495 // if we're not at the end of the list, just move up by one
3497 // set the new add line
3498 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3501 // if we've reached max chat lines, don't increment
3502 if(Multi_pxo_chat_count < MAX_CHAT_LINES) {
3503 Multi_pxo_chat_count++;
3507 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
3509 // force the position, in case we arent at the bottom of the list
3512 // move to the bottom of the chat area
3515 multi_pxo_goto_bottom();
3518 // if we have more than the # of lines displayable
3519 if(Multi_pxo_chat_count >= MULTI_PXO_MAX_CHAT_DISPLAY){
3521 multi_pxo_goto_bottom();
3524 multi_pxo_goto_bottom();
3527 // process an incoming line of text
3528 void multi_pxo_chat_process_incoming(const char *txt,int mode)
3530 char msg_total[512],line[512];
3533 char *p_str[20]; // the initial line (unindented)
3534 const char *priv_ptr = NULL;
3536 // filter out "has left" channel messages, when switching channels
3537 if((SWITCHING_CHANNELS() || ((Multi_pxo_switch_delay != -1) && !timestamp_elapsed(Multi_pxo_switch_delay))) &&
3538 multi_pxo_chat_is_left_message(txt)){
3542 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3543 priv_ptr = multi_pxo_chat_is_private(txt);
3544 if(priv_ptr != NULL){
3545 strcpy(msg_total, priv_ptr);
3547 strcpy(msg_total, txt);
3550 // determine what mode to display this text in
3552 // if this is private chat
3553 if(priv_ptr != NULL){
3554 mode = CHAT_MODE_PRIVATE;
3558 // if this is a server message
3559 if(multi_pxo_is_server_text(txt)){
3560 mode = CHAT_MODE_SERVER;
3562 // if this is a MOTD
3563 else if(multi_pxo_is_motd_text(txt)){
3564 // mode = CHAT_MODE_MOTD;
3566 multi_pxo_motd_add_text(txt);
3569 // if this is the end of motd text
3570 else if(multi_pxo_is_end_of_motd_text(txt)){
3571 multi_pxo_set_end_of_motd();
3576 // split the text up into as many lines as necessary
3577 n_lines = split_str(msg_total, Multi_pxo_chat_coords[gr_screen.res][2] - 5, n_chars, p_str, 3);
3578 SDL_assert((n_lines != -1) && (n_lines <= 20));
3579 if((n_lines < 0) || (n_lines > 20)) {
3583 // if the string fits on one line
3585 multi_pxo_chat_add_line(msg_total,mode);
3587 // don't pad with extra spaces if its from the server
3589 if(mode != CHAT_MODE_SERVER){
3590 multi_pxo_chat_add_line("",CHAT_MODE_NORMAL);
3594 // if the string was split into multiple lines
3596 // add the first line
3597 memcpy(line,p_str[0],n_chars[0]);
3598 line[n_chars[0]] = '\0';
3599 multi_pxo_chat_add_line(line,mode);
3601 // copy the rest of the lines
3602 for(idx=1; idx<n_lines; idx++){
3603 memcpy(line,p_str[idx],n_chars[idx]);
3604 line[n_chars[idx]] = '\0';
3606 // unless the current mode is server or "switching channels", make all these CHAT_MODE_CARRY
3607 if((mode != CHAT_MODE_SERVER) && (mode != CHAT_MODE_CHANNEL_SWITCH)){
3608 mode = CHAT_MODE_CARRY;
3610 multi_pxo_chat_add_line(line, mode);
3615 // blit the chat text
3616 void multi_pxo_chat_blit()
3619 int disp_count,token_width;
3625 // blit the title line
3628 if(strlen(Multi_pxo_channel_current.name) > 1){
3629 sprintf(title, XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name+1); // [[ <who> on <channel> ]]
3631 sprintf(title, XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name); // [[ <who> on <channel> ]]
3634 strcpy(title,XSTR("Parallax Online - No Channel", 956));
3636 gr_force_fit_string(title, 254, Multi_pxo_chat_coords[gr_screen.res][2] - 10);
3637 gr_get_string_size(&token_width,NULL,title);
3638 gr_set_color_fast(&Color_normal);
3639 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);
3641 // blit all active lines of text
3642 moveup = Multi_pxo_chat_start;
3644 y_start = Multi_pxo_chat_coords[gr_screen.res][1];
3645 while((moveup != NULL) && (moveup != Multi_pxo_chat_add) && (disp_count < (Multi_pxo_max_chat_display[gr_screen.res]))){
3646 switch(moveup->mode){
3647 // if this is text from the server, display it all "bright"
3648 case CHAT_MODE_SERVER:
3649 gr_set_color_fast(&Color_bright);
3650 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3653 // if this is motd, display it all "bright"
3654 case CHAT_MODE_MOTD:
3655 gr_set_color_fast(&Color_bright_white);
3656 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3659 // normal mode, just highlight the server
3660 case CHAT_MODE_PRIVATE:
3661 case CHAT_MODE_NORMAL:
3662 strcpy(piece,moveup->text);
3663 tok = strtok(piece," ");
3665 // get the width of just the first "piece"
3666 gr_get_string_size(&token_width, NULL, tok);
3669 gr_set_color_fast(&Color_bright);
3670 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, tok);
3672 // draw the rest of the string normally
3673 tok = strtok(NULL,"");
3675 gr_set_color_fast(&Color_normal);
3676 gr_string(Multi_pxo_chat_coords[gr_screen.res][0] + token_width + 6, y_start, tok);
3681 // carry mode, display with no highlight
3682 case CHAT_MODE_CARRY:
3683 gr_set_color_fast(&Color_normal);
3684 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3687 // "switching channels mode", display it bright
3688 case CHAT_MODE_CHANNEL_SWITCH:
3689 gr_set_color_fast(&Color_bright);
3690 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3695 moveup = moveup->next;
3700 if ((moveup != Multi_pxo_chat_add) && (moveup != NULL)) {
3701 Can_scroll_down = 1;
3703 Can_scroll_down = 0;
3707 // scroll to the very bottom of the chat area
3708 void multi_pxo_goto_bottom()
3713 if (Multi_pxo_chat == NULL) {
3717 // if we have less than the displayable amount of lines, do nothing
3718 if(Multi_pxo_chat_count <= Multi_pxo_max_chat_display[gr_screen.res]){
3719 Multi_pxo_chat_start = Multi_pxo_chat;
3721 // nothing to do for the slider
3722 Multi_pxo_chat_slider.set_numberItems(0);
3726 if (!Can_scroll_down)
3728 // otherwise move back the right # of items
3729 backup = Multi_pxo_chat_add;
3730 for(idx=0; idx<Multi_pxo_max_chat_display[gr_screen.res]; idx++){
3731 SDL_assert(backup->prev != NULL);
3732 backup = backup->prev;
3735 Multi_pxo_chat_start = backup;
3737 // fixup the start index
3738 multi_pxo_chat_adjust_start();
3742 // scroll the text up
3743 void multi_pxo_scroll_chat_up()
3745 // if we're already at the top of the list, don't do anything
3746 if ((Multi_pxo_chat_start == NULL) || (Multi_pxo_chat_start == Multi_pxo_chat)) {
3747 gamesnd_play_iface(SND_GENERAL_FAIL);
3751 // otherwise move up one
3752 Multi_pxo_chat_start = Multi_pxo_chat_start->prev;
3754 multi_pxo_chat_adjust_start();
3756 gamesnd_play_iface(SND_USER_SELECT);
3759 // returns 1 if we can scroll down, 0 otherwise
3760 int multi_pxo_can_scroll_down()
3765 // see if its okay to scroll down
3766 lookup = Multi_pxo_chat_start;
3767 if (lookup == NULL) {
3768 // gamesnd_play_iface(SND_GENERAL_FAIL);
3772 while (lookup != Multi_pxo_chat_add) {
3773 lookup = lookup->next;
3777 // check if we can move down, return accordingly
3778 if (count > Multi_pxo_max_chat_display[gr_screen.res]) {
3785 // scroll the text down
3786 void multi_pxo_scroll_chat_down()
3788 // if we can move down
3789 if (multi_pxo_can_scroll_down()) {
3790 Multi_pxo_chat_start = Multi_pxo_chat_start->next;
3791 multi_pxo_chat_adjust_start();
3792 gamesnd_play_iface(SND_USER_SELECT);
3794 gamesnd_play_iface(SND_GENERAL_FAIL);
3798 // process chat controls
3799 void multi_pxo_chat_process()
3801 char *remainder = NULL;
3802 const char *result = NULL;
3804 int msg_pixel_width;
3806 // if the chat line is getting too long, fire off the message, putting the last
3807 // word on the next input line.
3808 memset(msg, 0, 512);
3809 Multi_pxo_chat_input.get_text(msg);
3811 // determine if the width of the string in pixels is > than the inputbox width -- if so,
3812 // then send the message
3813 gr_get_string_size(&msg_pixel_width, NULL, msg);
3814 // if ( msg_pixel_width >= (Chatbox_inputbox_w - Player->short_callsign_width) ) {
3815 if ( msg_pixel_width >= (Multi_pxo_input_coords[gr_screen.res][2])) {
3816 remainder = strrchr(msg, ' ');
3822 // if we're connected to a channel, send the chat to the server
3824 result = SendChatString(msg,1);
3826 multi_pxo_chat_process_incoming(result);
3829 // display any remainder of text on the next line
3830 Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3832 Multi_pxo_chat_input.set_text("");
3834 } else if((Multi_pxo_chat_input.pressed() && (strlen(msg) > 0)) || (strlen(msg) >= MAX_CHAT_LINE_LEN)) {
3835 // tack on the null terminator in the boundary case
3836 int x = strlen(msg);
3837 if(x >= MAX_CHAT_LINE_LEN){
3838 msg[MAX_CHAT_LINE_LEN-1] = '\0';
3841 // ignore "/nick" commands
3842 if(multi_pxo_is_nick_command(msg)){
3843 Multi_pxo_chat_input.set_text("");
3847 // send the chat to the server
3848 // if we're connected to a channel, send the chat to the server
3850 result = SendChatString(msg,1);
3852 multi_pxo_chat_process_incoming(result);
3855 // display any remainder of text on the next line
3856 Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3858 Multi_pxo_chat_input.set_text("");
3863 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3866 // NOTE : DO NOT LOCALIZE THESE STRINGS!!!! THEY ARE CONSTANTS WHICH ARE CHECKED AGAINST
3867 // PXO CHAT SERVER DATA. THEY CANNOT CHANGE!!!
3868 #define PMSG_FROM "private message from "
3869 #define PMSG_TO "private message to "
3870 const char *multi_pxo_chat_is_private(const char *txt)
3873 if( strlen(txt) > strlen( PMSG_FROM ) ){
3874 // otherwise do a comparison
3875 if(!SDL_strncasecmp( txt, PMSG_FROM, strlen(PMSG_FROM) )){
3876 return &txt[strlen( PMSG_FROM )];
3881 if(strlen(txt) > strlen( PMSG_TO )){
3882 // otherwise do a comparison
3883 if(!SDL_strncasecmp(txt,PMSG_TO,strlen(PMSG_TO))){
3884 return &txt[strlen(PMSG_TO)];
3892 // if the text came from the server
3893 int multi_pxo_is_server_text(const char *txt)
3895 // if the message is prefaced by a ***
3896 if((strlen(txt) >= strlen(MULTI_PXO_SERVER_PREFIX)) && !strncmp(txt, MULTI_PXO_SERVER_PREFIX, strlen(MULTI_PXO_SERVER_PREFIX))){
3903 // if the text is message of the day text
3904 int multi_pxo_is_motd_text(const char *txt)
3906 // if we're not on a channel, and this is not a channel switching message assume its coming from a server
3907 if((strlen(txt) >= strlen(PXO_CHAT_MOTD_PREFIX)) && !strncmp(txt, PXO_CHAT_MOTD_PREFIX, strlen(PXO_CHAT_MOTD_PREFIX))){
3914 // if the text is the end of motd text
3915 int multi_pxo_is_end_of_motd_text(const char *txt)
3917 // if we're not on a channel, and this is not a channel switching message assume its coming from a server
3918 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))){
3925 // if the text is a "has left message" from the server
3926 int multi_pxo_chat_is_left_message(const char *txt)
3928 char last_portion[100];
3930 // if the text is not server text
3931 if(!multi_pxo_is_server_text(txt)){
3935 // check to see if the last portion is the correct wording
3936 memset(last_portion, 0, 100);
3937 if((strlen(txt) > strlen(MULTI_PXO_HAS_LEFT)) && !strcmp(&txt[strlen(txt) - strlen(MULTI_PXO_HAS_LEFT)], MULTI_PXO_HAS_LEFT)){
3941 // check the end of the line
3945 // recalculate the chat start index, and adjust the slider properly
3946 void multi_pxo_chat_adjust_start()
3950 // if we have no chat
3951 if (Multi_pxo_chat == NULL) {
3952 Multi_pxo_chat_start_index = -1;
3957 Multi_pxo_chat_start_index = 0;
3958 moveup = Multi_pxo_chat;
3959 while((moveup != Multi_pxo_chat_start) && (moveup != NULL)){
3960 Multi_pxo_chat_start_index++;
3961 moveup = moveup->next;
3964 // set the slider index
3965 Multi_pxo_chat_slider.force_currentItem(Multi_pxo_chat_start_index);
3968 // motd stuff ---------------------------------------------------------
3970 // initialize motd when going into this screen
3971 void multi_pxo_motd_init()
3973 // zero the motd string
3974 strcpy(Pxo_motd, "");
3976 // haven't gotten it yet
3979 // haven't read it yet either
3983 // set the motd text
3984 void multi_pxo_motd_add_text(const char *text)
3986 int cur_len = strlen(Pxo_motd);
3994 // make sure its motd text
3995 SDL_assert(multi_pxo_is_motd_text(text));
3996 if(!multi_pxo_is_motd_text(text)){
4000 // if its a 0 line motd
4001 if(strlen(text) <= strlen(PXO_CHAT_MOTD_PREFIX)){
4005 // add text to the motd
4006 new_len = strlen(text + strlen(PXO_CHAT_MOTD_PREFIX)) - 1;
4007 if((cur_len + new_len + 1) < MAX_PXO_MOTD_LEN){
4008 strcat(Pxo_motd, text + strlen(PXO_CHAT_MOTD_PREFIX) + 1);
4009 strcat(Pxo_motd, "\n");
4010 mprintf(("MOTD ADD : %s\n", Pxo_motd));
4015 void multi_pxo_set_end_of_motd()
4020 mprintf(("MOTD ALL : %s\n", Pxo_motd));
4024 // do we have an old MOTD file laying around? If so, read it in and see if its the same
4025 unsigned int old_chksum;
4026 unsigned long new_chksum;
4028 // checksum the current motd
4029 new_chksum = cf_add_chksum_long(0, Pxo_motd, strlen(Pxo_motd));
4031 // checksum the old motd if its lying around
4032 CFILE *in = cfopen("oldmotd.txt", "rb");
4034 // read the old checksum
4035 cfread(&old_chksum, sizeof(old_chksum), 1, in);
4038 // same checksum? no blink
4039 if(new_chksum == old_chksum){
4044 // write out the motd for next time
4045 if(strlen(Pxo_motd)){
4046 CFILE *out = cfopen("oldmotd.txt", "wb", CFILE_NORMAL, CF_TYPE_DATA);
4048 // write all the text
4049 cfwrite(&new_chksum, sizeof(new_chksum), 1, out);
4051 // close the outfile
4056 // set the blink stamp
4057 Pxo_motd_blink_stamp = -1;
4059 Pxo_motd_blink_on = 0;
4060 if(!Pxo_motd_blinked_already){
4061 Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4062 Pxo_motd_blink_on = 1;
4066 Pxo_motd_blinked_already = 1;
4069 // display the motd dialog
4070 void multi_pxo_motd_dialog()
4072 // mark the motd as read
4075 // simple popup, with a slider
4076 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, Pxo_motd);
4079 // call to maybe blink the motd button
4080 void multi_pxo_motd_maybe_blit()
4082 // if we got the end of the motd, and he hasn't read it yet
4083 if(Pxo_motd_end && !Pxo_motd_read && (Pxo_motd_blink_stamp != -1)){
4084 // if the timestamp elapsed, flip the blink flag
4085 if(timestamp_elapsed(Pxo_motd_blink_stamp)){
4086 Pxo_motd_blink_on = !Pxo_motd_blink_on;
4087 Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4091 if(Pxo_motd_blink_on){
4092 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_MOTD].button.draw_forced(2);
4098 // common dialog stuff ------------------------------------------------
4100 int Multi_pxo_searching = 0;
4102 // initialize the common dialog with the passed max input length
4103 void multi_pxo_com_init(int input_len)
4107 // create the interface window
4108 Multi_pxo_com_window.create(0, 0, gr_screen.max_w,gr_screen.max_h, 0);
4109 Multi_pxo_com_window.set_mask_bmap(Multi_pxo_com_mask_fname[gr_screen.res]);
4111 // create the interface buttons
4112 for(idx=0; idx<MULTI_PXO_COM_NUM_BUTTONS; idx++){
4113 // create the object
4114 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);
4116 // set the sound to play when highlighted
4117 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4119 // set the ani for the button
4120 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_com_buttons[gr_screen.res][idx].filename);
4123 Multi_pxo_com_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_com_buttons[gr_screen.res][idx].hotspot);
4127 for(idx=0; idx<MULTI_PXO_COM_NUM_TEXT; idx++){
4128 Multi_pxo_com_window.add_XSTR(&Multi_pxo_com_text[gr_screen.res][idx]);
4131 // create the input box
4132 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);
4133 Multi_pxo_com_input.set_focus();
4135 // clear all text lines
4136 memset(Multi_pxo_com_bottom_text, 0, 255);
4137 memset(Multi_pxo_com_middle_text, 0, 255);
4138 memset(Multi_pxo_com_top_text, 0, 255);
4141 // close down the common dialog
4142 void multi_pxo_com_close()
4144 // destroy the UI_WINDOW
4145 Multi_pxo_com_window.destroy();
4148 // blit all text lines, top, middle, bottoms
4149 void multi_pxo_com_blit_text()
4151 // blit top, middle and bottom text if possible
4152 if(strlen(Multi_pxo_com_top_text) > 0){
4153 gr_set_color_fast(&Color_bright);
4154 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);
4156 if(strlen(Multi_pxo_com_middle_text) > 0){
4157 gr_set_color_fast(&Color_bright);
4158 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);
4160 if(strlen(Multi_pxo_com_bottom_text) > 0){
4161 gr_set_color_fast(&Color_bright);
4162 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);
4166 // set the top text, shortening as necessary
4167 void multi_pxo_com_set_top_text(const char *txt)
4169 if((txt != NULL) && strlen(txt)){
4170 strcpy(Multi_pxo_com_top_text,txt);
4171 gr_force_fit_string(Multi_pxo_com_top_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4175 // set the middle text, shortening as necessary
4176 void multi_pxo_com_set_middle_text(const char *txt)
4178 if((txt != NULL) && strlen(txt)){
4179 strcpy(Multi_pxo_com_middle_text,txt);
4180 gr_force_fit_string(Multi_pxo_com_middle_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4184 // set the bottom text, shortening as necessary
4185 void multi_pxo_com_set_bottom_text(const char *txt)
4187 if((txt != NULL) && strlen(txt)){
4188 strcpy(Multi_pxo_com_bottom_text,txt);
4189 gr_force_fit_string(Multi_pxo_com_bottom_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4194 // private channel join stuff -----------------------------------------
4196 // initialize the popup
4197 void multi_pxo_priv_init()
4199 SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE);
4201 // initialize the common dialog with the passed max input length
4202 multi_pxo_com_init(MULTI_PXO_PRIV_MAX_TEXT_LEN);
4204 // initialize the return code
4205 Multi_pxo_priv_return_code = -1;
4207 // mark us as running
4208 Multi_pxo_mode = MULTI_PXO_MODE_PRIVATE;
4211 multi_pxo_com_set_middle_text(XSTR("Type the name of the channel to join/create",961));
4214 // close down the popup
4215 void multi_pxo_priv_close()
4217 // close down the common dialog
4218 multi_pxo_com_close();
4220 // mark us as not running any more
4221 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4224 // run the popup, 0 if still running, -1 if cancel, 1 if ok
4225 int multi_pxo_priv_popup()
4229 // if we're not already running, initialize stuff
4230 if(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE){
4232 multi_pxo_priv_init();
4234 // return "still running"
4238 k = Multi_pxo_com_window.process();
4240 // process keypresses
4242 // like hitting the cancel button
4244 Multi_pxo_priv_return_code = 0;
4248 // process button presses
4249 multi_pxo_priv_process_buttons();
4251 // process the inputbox
4252 multi_pxo_priv_process_input();
4254 // blit the background
4255 multi_pxo_blit_all();
4259 gr_set_bitmap(Multi_pxo_com_bitmap);
4260 gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4261 Multi_pxo_com_window.draw();
4263 // blit all text lines, top, middle, bottoms
4264 multi_pxo_com_blit_text();
4268 // check the return code
4269 switch(Multi_pxo_priv_return_code){
4270 // still in progress
4276 multi_pxo_priv_close();
4281 multi_pxo_priv_close();
4288 // process button presses
4289 void multi_pxo_priv_process_buttons()
4293 // check all buttons
4294 for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4295 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4296 multi_pxo_priv_button_pressed(idx);
4302 // handle a button press
4303 void multi_pxo_priv_button_pressed(int n)
4305 char priv_chan_name[128];
4308 case MULTI_PXO_COM_CANCEL:
4309 Multi_pxo_priv_return_code = 0;
4312 case MULTI_PXO_COM_OK:
4313 Multi_pxo_com_input.get_text(priv_chan_name);
4314 multi_pxo_strip_space(priv_chan_name,priv_chan_name);
4316 // if its a 0 length string, interpret as a cancel
4317 if(strlen(priv_chan_name) <= 0){
4318 Multi_pxo_priv_return_code = 0;
4322 Multi_pxo_priv_return_code = 1;
4327 // process the inputbox
4328 void multi_pxo_priv_process_input()
4330 char priv_chan_name[128];
4332 // see if the user has pressed enter
4333 if(Multi_pxo_com_input.pressed()){
4334 Multi_pxo_com_input.get_text(priv_chan_name);
4335 multi_pxo_strip_space(priv_chan_name,priv_chan_name);
4337 // if its a 0 length string, interpret as a cancel
4338 if(strlen(priv_chan_name) <= 0){
4339 Multi_pxo_priv_return_code = 0;
4343 // otherwise interpret as "accept"
4344 Multi_pxo_priv_return_code = 1;
4346 // add in the "+" which indicates a private room
4347 strcpy(Multi_pxo_priv_chan,"+");
4348 strcat(Multi_pxo_priv_chan,priv_chan_name);
4352 // find player stuff -----------------------------------------
4354 char name_lookup[255];
4356 // initialize the popup
4357 void multi_pxo_find_init()
4359 SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_FIND);
4361 // initialize the common dialog with the passed max input length
4362 multi_pxo_com_init(MAX_PLAYER_NAME_LEN);
4364 // return code, set to something other than -1 if we're supposed to return
4365 Multi_pxo_find_return_code = -1;
4367 // mark us as running
4368 Multi_pxo_mode = MULTI_PXO_MODE_FIND;
4370 // not searching yet
4371 Multi_pxo_searching = 0;
4374 multi_pxo_com_set_top_text(XSTR("Enter user to be found",962));
4377 strcpy(Multi_pxo_find_channel,"");
4380 strcpy(name_lookup,"");
4383 // close down the popup
4384 void multi_pxo_find_close()
4386 // close down the common dialog
4387 multi_pxo_com_close();
4389 // mark us as not running any more
4390 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4393 // run the popup, 0 if still running, -1 if cancel, 1 if ok
4394 int multi_pxo_find_popup()
4398 // if we're not already running, initialize stuff
4399 if(Multi_pxo_mode != MULTI_PXO_MODE_FIND){
4401 multi_pxo_find_init();
4403 // return "still running"
4407 k = Multi_pxo_com_window.process();
4409 // process keypresses
4411 // like hitting the cancel button
4413 Multi_pxo_find_return_code = 0;
4417 // process button presses
4418 multi_pxo_find_process_buttons();
4420 // process the inputbox
4421 multi_pxo_find_process_input();
4423 // process search mode if applicable
4424 multi_pxo_find_search_process();
4426 // blit the background
4427 multi_pxo_blit_all();
4431 gr_set_bitmap(Multi_pxo_com_bitmap);
4432 gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4433 Multi_pxo_com_window.draw();
4435 // blit any text lines
4436 multi_pxo_com_blit_text();
4440 // check the return code
4441 switch(Multi_pxo_find_return_code){
4442 // still in progress
4448 // close the popup down
4449 multi_pxo_find_close();
4454 // close the popup down
4455 multi_pxo_find_close();
4457 // if we have a channel, join it now if possible
4458 if(strlen(Multi_pxo_find_channel) > 0){
4459 pxo_channel *lookup;
4460 lookup = multi_pxo_find_channel(Multi_pxo_find_channel,Multi_pxo_channels);
4462 // if we couldn't find it, don't join
4464 multi_pxo_join_channel(lookup);
4473 // process button presses
4474 void multi_pxo_find_process_buttons()
4478 // check all buttons
4479 for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4480 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4481 multi_pxo_find_button_pressed(idx);
4487 // handle a button press
4488 void multi_pxo_find_button_pressed(int n)
4491 case MULTI_PXO_COM_CANCEL:
4492 Multi_pxo_find_return_code = 0;
4495 case MULTI_PXO_COM_OK:
4496 Multi_pxo_find_return_code = 1;
4501 // process the inputbox
4502 void multi_pxo_find_process_input()
4504 // see if the user has pressed enter
4505 if(Multi_pxo_com_input.pressed()){
4506 // if we're not already in search mode
4507 if(!Multi_pxo_searching){
4509 memset(Multi_pxo_com_middle_text,0,255);
4510 memset(Multi_pxo_com_bottom_text,0,255);
4512 Multi_pxo_com_input.get_text(name_lookup);
4513 multi_pxo_strip_space(name_lookup,name_lookup);
4515 // never search with a zero length string
4516 if(strlen(name_lookup) > 0){
4517 char search_text[512];
4519 // put us in search mode
4520 Multi_pxo_searching = 1;
4523 GetChannelByUser(name_lookup);
4526 memset(search_text,0,512);
4527 sprintf(search_text,XSTR("Searching for %s",963),name_lookup);
4528 multi_pxo_com_set_top_text(search_text);
4532 memset(Multi_pxo_com_top_text,0,255);
4538 // process search mode if applicable
4539 void multi_pxo_find_search_process()
4543 // if we're not searching for anything, return
4544 if(!Multi_pxo_searching){
4548 // otherwise check to see if we've found him
4549 channel = GetChannelByUser(NULL);
4551 // if we've got a result, let the user know
4553 // if he couldn't be found
4554 if(channel == (char *)-1){
4555 multi_pxo_com_set_middle_text(XSTR("User not found",964));
4556 strcpy(Multi_pxo_find_channel,"");
4558 if(channel[0] == '*'){
4559 multi_pxo_com_set_middle_text(XSTR("Player is logged in but is not on a channel",965));
4560 strcpy(Multi_pxo_find_channel,"");
4563 memset(p_text,0,512);
4565 // if this guy is on a public channel, display which one
4566 if(channel[0] == '#'){
4567 sprintf(p_text,XSTR("Found %s on :",966),name_lookup);
4569 // display the results
4570 multi_pxo_com_set_middle_text(p_text);
4571 multi_pxo_com_set_bottom_text(channel+1);
4573 // mark down the channel name so we know where to find him
4574 strcpy(Multi_pxo_find_channel,channel);
4575 // strip out trailing whitespace
4576 if(Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] == ' '){
4577 Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] = '\0';
4580 // if this is a private channel
4581 else if(channel[0] == '+'){
4582 sprintf(p_text,XSTR("Found %s on a private channel",967),name_lookup);
4583 multi_pxo_com_set_middle_text(p_text);
4585 strcpy(Multi_pxo_find_channel,"");
4590 // unset search mode
4591 Multi_pxo_searching = 0;
4593 // clear the inputbox
4594 Multi_pxo_com_input.set_text("");
4599 // player info stuff -----------------------------------------
4601 // popup conditional functions, returns 10 on successful get of stats
4602 int multi_pxo_pinfo_cond()
4605 char temp_string[255];
4608 // process common stuff
4609 multi_pxo_process_common();
4611 // run the networking functions for the PXO API
4612 multi_pxo_api_process();
4614 // process depending on what mode we're in
4615 switch(Multi_pxo_retrieve_mode){
4616 // getting his player tracker id
4618 // if the thing is non-null, do something
4619 ret_string = GetTrackerIdByUser(Multi_pxo_retrieve_name);
4620 if(ret_string != NULL){
4621 // user not-online/not found
4622 if(ret_string == (char *)-1){
4626 // user not a tracker pilot
4627 if(!SDL_strcasecmp(ret_string,"-1")){
4631 // otherwise parse into his id and callsign
4632 strcpy(temp_string,ret_string);
4633 tok = strtok(temp_string," ");
4637 strcpy(Multi_pxo_retrieve_id,tok);
4640 tok = strtok(NULL,"");
4642 strcpy(Multi_pxo_retrieve_name,tok);
4649 // failure of some kind or another
4654 Multi_pxo_retrieve_mode = 1;
4659 // initial call to get his stats
4661 // change the popup text
4662 popup_change_text(XSTR("Getting player stats",968));
4665 memset(&Multi_pxo_pinfo, 0, sizeof(vmt_freespace2_struct));
4666 strcpy(Multi_pxo_pinfo.pilot_name, Multi_pxo_retrieve_name);
4667 strncpy(Multi_pxo_pinfo.tracker_id, Multi_pxo_retrieve_id, TRACKER_ID_LEN);
4669 // make the initial call to the API
4670 GetFSPilotData((vmt_freespace2_struct*)0xffffffff,NULL,NULL,0);
4671 if(GetFSPilotData(&Multi_pxo_pinfo,Multi_pxo_retrieve_name,Multi_pxo_retrieve_id,0) != 0){
4674 // if the call went through, set the mode to 2
4676 Multi_pxo_retrieve_mode = 2;
4680 // busy retrieving his stats
4682 switch(GetFSPilotData(NULL,NULL,NULL,0)){
4683 // timeout, fail, cancel
4701 // return not done yet
4705 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
4706 int multi_pxo_pinfo_get(char *name)
4709 Multi_pxo_retrieve_mode = 0;
4710 strcpy(Multi_pxo_retrieve_name,name);
4711 switch(popup_till_condition(multi_pxo_pinfo_cond,XSTR("&Cancel", 779),XSTR("Retrieving player tracker id",969))){
4716 // failed to get his tracker id
4720 // failed to get his stats
4725 // we didn't get the stats
4729 // fire up the stats view popup
4730 void multi_pxo_pinfo_show()
4732 // initialize the popup
4733 multi_pxo_pinfo_init();
4737 game_set_frametime(GS_STATE_PXO);
4738 } while(!multi_pxo_pinfo_do());
4740 // close down the popup
4741 multi_pxo_pinfo_close();
4744 // build the stats labels values
4745 void multi_pxo_pinfo_build_vals()
4747 vmt_freespace2_struct *fs = &Multi_pxo_pinfo;
4750 memset(Multi_pxo_pinfo_vals[0],0,50);
4751 strcpy(Multi_pxo_pinfo_vals[0],fs->pilot_name);
4752 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]));
4755 memset(Multi_pxo_pinfo_vals[1],0,50);
4756 multi_sg_rank_build_name(Ranks[fs->rank].name,Multi_pxo_pinfo_vals[1]);
4757 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]));
4760 memset(Multi_pxo_pinfo_vals[2],0,50);
4761 sprintf(Multi_pxo_pinfo_vals[2],"%d",fs->kill_count);
4764 memset(Multi_pxo_pinfo_vals[3],0,50);
4765 sprintf(Multi_pxo_pinfo_vals[3],"%d",fs->assists);
4768 memset(Multi_pxo_pinfo_vals[4],0,50);
4769 sprintf(Multi_pxo_pinfo_vals[4],"%d",fs->kill_count - fs->kill_count_ok);
4772 memset(Multi_pxo_pinfo_vals[5],0,50);
4773 sprintf(Multi_pxo_pinfo_vals[5],"%d",(int)fs->missions_flown);
4776 memset(Multi_pxo_pinfo_vals[6],0,50);
4777 game_format_time(fl2f((float)fs->flight_time),Multi_pxo_pinfo_vals[6]);
4780 memset(Multi_pxo_pinfo_vals[7],0,50);
4781 if(fs->last_flown == 0){
4782 strcpy(Multi_pxo_pinfo_vals[7],XSTR("No missions flown",970));
4784 tm *tmr = gmtime((time_t*)&fs->last_flown);
4786 strftime(Multi_pxo_pinfo_vals[7],30,"%m/%d/%y %H:%M",tmr);
4788 strcpy(Multi_pxo_pinfo_vals[7], "");
4792 // primary shots fired
4793 memset(Multi_pxo_pinfo_vals[8],0,50);
4794 sprintf(Multi_pxo_pinfo_vals[8],"%d",(int)fs->p_shots_fired);
4796 // primary shots hit
4797 memset(Multi_pxo_pinfo_vals[9],0,50);
4798 sprintf(Multi_pxo_pinfo_vals[9],"%d",(int)fs->p_shots_hit);
4801 memset(Multi_pxo_pinfo_vals[10],0,50);
4802 if(fs->p_shots_fired > 0){
4803 sprintf(Multi_pxo_pinfo_vals[10],"%d%%",(int)((float)fs->p_shots_hit / (float)fs->p_shots_fired * 100.0f));
4805 strcpy(Multi_pxo_pinfo_vals[10],"0%");
4808 // secondary shots fired
4809 memset(Multi_pxo_pinfo_vals[11],0,50);
4810 sprintf(Multi_pxo_pinfo_vals[11],"%d",(int)fs->s_shots_fired);
4812 // secondary shots hit
4813 memset(Multi_pxo_pinfo_vals[12],0,50);
4814 sprintf(Multi_pxo_pinfo_vals[12],"%d",(int)fs->s_shots_hit);
4816 // secondary hit pct
4817 memset(Multi_pxo_pinfo_vals[13],0,50);
4818 if(fs->s_shots_fired > 0){
4819 sprintf(Multi_pxo_pinfo_vals[13],"%d%%",(int)((float)fs->s_shots_hit / (float)fs->s_shots_fired * 100.0f));
4821 strcpy(Multi_pxo_pinfo_vals[13],"0%");
4824 // primary friendly hits
4825 memset(Multi_pxo_pinfo_vals[14],0,50);
4826 sprintf(Multi_pxo_pinfo_vals[14],"%d",fs->p_bonehead_hits);
4828 // primary friendly hit %
4829 memset(Multi_pxo_pinfo_vals[15],0,50);
4830 if(fs->p_shots_hit > 0){
4831 sprintf(Multi_pxo_pinfo_vals[15],"%d%%",(int)((float)100.0f*((float)fs->p_bonehead_hits/(float)fs->p_shots_fired)));
4833 strcpy(Multi_pxo_pinfo_vals[15],"0%");
4836 // secondary friendly hits
4837 memset(Multi_pxo_pinfo_vals[16],0,50);
4838 sprintf(Multi_pxo_pinfo_vals[16],"%d",fs->s_bonehead_hits);
4840 // secondary friendly hit %
4841 memset(Multi_pxo_pinfo_vals[17],0,50);
4842 if(fs->s_shots_hit > 0){
4843 sprintf(Multi_pxo_pinfo_vals[17],"%d%%",(int)((float)100.0f*((float)fs->s_bonehead_hits/(float)fs->s_shots_fired)));
4845 strcpy(Multi_pxo_pinfo_vals[17],"0%");
4849 // initialize the popup
4850 void multi_pxo_pinfo_init()
4854 // create the interface window
4855 Multi_pxo_pinfo_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4856 Multi_pxo_pinfo_window.set_mask_bmap(Multi_pxo_pinfo_mask_fname[gr_screen.res]);
4858 Multi_pxo_pinfo_bitmap = bm_load(Multi_pxo_pinfo_fname[gr_screen.res]);
4859 SDL_assert(Multi_pxo_pinfo_bitmap != -1);
4861 // create the interface buttons
4862 for(idx=0; idx<MULTI_PXO_PINFO_NUM_BUTTONS; idx++){
4863 // create the object
4864 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);
4866 // set the sound to play when highlighted
4867 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4869 // set the ani for the button
4870 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_pinfo_buttons[gr_screen.res][idx].filename);
4873 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_pinfo_buttons[gr_screen.res][idx].hotspot);
4877 for(idx=0; idx<MULTI_PXO_PINFO_NUM_TEXT; idx++){
4878 Multi_pxo_pinfo_window.add_XSTR(&Multi_pxo_pinfo_text[gr_screen.res][idx]);
4881 // set up the stats labels
4882 Multi_pxo_pinfo_stats_labels[0] = strdup(XSTR("Name", 1532));
4883 Multi_pxo_pinfo_stats_labels[1] = strdup(XSTR("Rank", 1533));
4884 Multi_pxo_pinfo_stats_labels[2] = strdup(XSTR("Kills", 1534));
4885 Multi_pxo_pinfo_stats_labels[3] = strdup(XSTR("Assists", 1535));
4886 Multi_pxo_pinfo_stats_labels[4] = strdup(XSTR("Friendly kills", 1536));
4887 Multi_pxo_pinfo_stats_labels[5] = strdup(XSTR("Missions flown", 1537));
4888 Multi_pxo_pinfo_stats_labels[6] = strdup(XSTR("Flight time", 1538));
4889 Multi_pxo_pinfo_stats_labels[7] = strdup(XSTR("Last flown", 1539));
4890 Multi_pxo_pinfo_stats_labels[8] = strdup(XSTR("Primary shots fired", 1540));
4891 Multi_pxo_pinfo_stats_labels[9] = strdup(XSTR("Primary shots hit", 1541));
4892 Multi_pxo_pinfo_stats_labels[10] = strdup(XSTR("Primary hit %", 1542));
4893 Multi_pxo_pinfo_stats_labels[11] = strdup(XSTR("Secondary shots fired", 1543));
4894 Multi_pxo_pinfo_stats_labels[12] = strdup(XSTR("Secondary shots hit", 1544));
4895 Multi_pxo_pinfo_stats_labels[13] = strdup(XSTR("Secondary hit %", 1545));
4896 Multi_pxo_pinfo_stats_labels[14] = strdup(XSTR("Primary friendly hits", 1546));
4897 Multi_pxo_pinfo_stats_labels[15] = strdup(XSTR("Primary friendly hit %", 1547));
4898 Multi_pxo_pinfo_stats_labels[16] = strdup(XSTR("Secondary friendly hits", 1548));
4899 Multi_pxo_pinfo_stats_labels[17] = strdup(XSTR("Secondary friendly hit %", 1549));
4901 // build the stats labels values
4902 multi_pxo_pinfo_build_vals();
4906 int multi_pxo_pinfo_do()
4908 int k = Multi_pxo_pinfo_window.process();
4910 // process common stuff
4911 multi_pxo_process_common();
4913 // run the networking functions for the PXO API
4914 multi_pxo_api_process();
4916 // check to see if he pressed escp
4917 if(k == SDLK_ESCAPE){
4921 // if he pressed the ok button
4922 if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_OK].button.pressed()){
4926 // if he pressed the medals buttons, run the medals screen
4927 if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_MEDALS].button.pressed()){
4929 game_feature_not_in_demo_popup();
4931 multi_pxo_run_medals();
4937 // blit everything on the "normal" screen
4938 multi_pxo_blit_all();
4940 // blit our own stuff
4942 gr_set_bitmap(Multi_pxo_pinfo_bitmap);
4944 Multi_pxo_pinfo_window.draw();
4946 // blit the stats themselves
4947 multi_pxo_pinfo_blit();
4957 void multi_pxo_pinfo_close()
4961 // destroy the UI_WINDOW
4962 Multi_pxo_pinfo_window.destroy();
4964 // unload the bitmap
4965 if(Multi_pxo_pinfo_bitmap != -1){
4966 bm_unload(Multi_pxo_pinfo_bitmap);
4969 // free the stats labels strings
4970 for (i=0; i<MULTI_PXO_PINFO_NUM_LABELS; i++) {
4971 free(Multi_pxo_pinfo_stats_labels[i]);
4975 // blit all the stats on this screen
4976 void multi_pxo_pinfo_blit()
4981 // blit all the labels
4982 y_start = Multi_pxo_pinfo_coords[gr_screen.res][1];
4983 for(idx=0; idx<MULTI_PXO_PINFO_NUM_LABELS; idx++){
4985 gr_set_color_fast(&Color_bright);
4986 gr_string(Multi_pxo_pinfo_coords[gr_screen.res][0], y_start, Multi_pxo_pinfo_stats_labels[idx]);
4988 // blit the label's value
4989 gr_set_color_fast(&Color_normal);
4990 gr_string(Multi_pxo_pinfo_val_x[gr_screen.res], y_start, Multi_pxo_pinfo_vals[idx]);
4993 y_start += Multi_pxo_pinfo_stats_spacing[idx];
4997 // run the medals screen
4998 void multi_pxo_run_medals()
5002 // process common stuff
5003 multi_pxo_process_common();
5005 // run the networking functions for the PXO API
5006 multi_pxo_api_process();
5008 // initialize the freespace data and the player struct
5009 multi_stats_tracker_to_fs(&Multi_pxo_pinfo, &Multi_pxo_pinfo_player.stats);
5010 strcpy(Multi_pxo_pinfo_player.callsign, Multi_pxo_pinfo.pilot_name);
5012 // initialize the medals screen
5013 medal_main_init(&Multi_pxo_pinfo_player, MM_POPUP);
5015 // run the medals screen until it says that it should be closed
5017 // set frametime and run common functions
5018 game_set_frametime(-1);
5019 game_do_state_common(gameseq_get_state());
5021 // run the medals screen
5022 ret_code = medal_main_do();
5025 // close the medals screen down
5028 // reset the palette
5029 multi_pxo_load_palette();
5033 // notify stuff stuff -----------------------------------------
5035 // add a notification string
5036 void multi_pxo_notify_add(const char *txt)
5039 strcpy(Multi_pxo_notify_text, txt);
5041 // set the timestamp
5042 Multi_pxo_notify_stamp = timestamp(MULTI_PXO_NOTIFY_TIME);
5045 // blit and process the notification string
5046 void multi_pxo_notify_blit()
5050 // if the timestamp is -1, do nothing
5051 if(Multi_pxo_notify_stamp == -1){
5055 // if it has expired, do nothing
5056 if(timestamp_elapsed(Multi_pxo_notify_stamp)){
5057 Multi_pxo_notify_stamp = -1;
5060 // otherwise blit the text
5061 gr_set_color_fast(&Color_bright);
5062 gr_get_string_size(&w,NULL,Multi_pxo_notify_text);
5063 gr_string((gr_screen.max_w - w)/2,MULTI_PXO_NOTIFY_Y,Multi_pxo_notify_text);
5067 // initialize the PXO help screen
5068 void multi_pxo_help_init()
5072 // load the background bitmap
5073 Multi_pxo_help_bitmap = bm_load(Multi_pxo_help_fname[gr_screen.res]);
5074 if(Multi_pxo_help_bitmap < 0){
5075 // we failed to load the bitmap - this is very bad
5078 // create the interface window
5079 Multi_pxo_help_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
5080 Multi_pxo_help_window.set_mask_bmap(Multi_pxo_help_mask_fname[gr_screen.res]);
5082 // create the interface buttons
5083 for(idx=0; idx<MULTI_PXO_HELP_NUM_BUTTONS; idx++){
5084 // create the object
5085 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);
5087 // set the sound to play when highlighted
5088 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
5090 // set the ani for the button
5091 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_help_buttons[gr_screen.res][idx].filename);
5094 Multi_pxo_help_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_help_buttons[gr_screen.res][idx].hotspot);
5098 for(idx=0; idx<MULTI_PXO_HELP_NUM_TEXT; idx++){
5099 Multi_pxo_help_window.add_XSTR(&Multi_pxo_help_text[gr_screen.res][idx]);
5102 // if we haven't already loaded in the text, do so
5103 // if(!Multi_pxo_help_loaded){
5104 multi_pxo_help_load();
5107 // set the current page to 0
5108 Multi_pxo_help_cur = 0;
5111 // do frame for PXO help
5112 void multi_pxo_help_do()
5115 if(Multi_pxo_connected){
5116 multi_pxo_api_process();
5119 // process common stuff
5120 multi_pxo_process_common();
5122 int k = Multi_pxo_help_window.process();
5124 // process any keypresses
5127 gamesnd_play_iface(SND_USER_SELECT);
5128 gameseq_post_event(GS_EVENT_PXO);
5132 // process button presses
5133 multi_pxo_help_process_buttons();
5135 // draw the background, etc
5137 GR_MAYBE_CLEAR_RES(Multi_pxo_help_bitmap);
5138 if(Multi_pxo_help_bitmap != -1){
5139 gr_set_bitmap(Multi_pxo_help_bitmap);
5142 Multi_pxo_help_window.draw();
5144 // blit the current page
5145 multi_pxo_help_blit_page();
5151 // close the pxo screen
5152 void multi_pxo_help_close()
5156 // unload any bitmaps
5157 bm_unload(Multi_pxo_help_bitmap);
5159 // destroy the UI_WINDOW
5160 Multi_pxo_help_window.destroy();
5163 for(idx=0; idx<Multi_pxo_help_num_pages; idx++){
5164 for(idx2=0; idx2<Multi_pxo_help_pages[idx].num_lines; idx2++){
5166 if(Multi_pxo_help_pages[idx].text[idx2] != NULL){
5167 free(Multi_pxo_help_pages[idx].text[idx2]);
5168 Multi_pxo_help_pages[idx].text[idx2] = NULL;
5174 // load the help file up
5175 void multi_pxo_help_load()
5180 // if its already loaded, do nothing
5181 // if(Multi_pxo_help_loaded){
5185 // read in the text file
5187 in = cfopen(MULTI_PXO_HELP_FILE,"rt",CFILE_NORMAL,CF_TYPE_DATA);
5188 SDL_assert(in != NULL);
5193 Multi_pxo_help_num_pages = 0;
5195 // blast all the help pages clear
5196 memset(Multi_pxo_help_pages, 0, sizeof(help_page) * MULTI_PXO_MAX_PAGES);
5197 Multi_pxo_help_num_pages = 0;
5198 cp = &Multi_pxo_help_pages[0];
5202 cp->text[cp->num_lines] = (char*)malloc(Multi_pxo_chars_per_line[gr_screen.res]);
5203 if(cp->text[cp->num_lines] == NULL){
5207 // read in the next line
5208 cfgets(cp->text[cp->num_lines++], Multi_pxo_chars_per_line[gr_screen.res], in);
5210 // skip to the next page if necessary
5211 if(cp->num_lines == Multi_pxo_lines_pp[gr_screen.res]){
5212 Multi_pxo_help_num_pages++;
5213 SDL_assert(Multi_pxo_help_num_pages < MULTI_PXO_MAX_PAGES);
5214 if(Multi_pxo_help_num_pages >= MULTI_PXO_MAX_PAGES){
5215 Multi_pxo_help_num_pages--;
5218 cp = &Multi_pxo_help_pages[Multi_pxo_help_num_pages];
5225 // mark the help as having been loaded
5226 // Multi_pxo_help_loaded = 1;
5229 // blit the current page
5230 void multi_pxo_help_blit_page()
5235 help_page *cp = &Multi_pxo_help_pages[Multi_pxo_help_cur];
5238 y_start = Multi_pxo_help_coords[gr_screen.res][1];
5239 for(idx=0;idx<cp->num_lines;idx++){
5240 // if the first symbol is "@", highlight the line
5241 if(cp->text[idx][0] == '@'){
5242 gr_set_color_fast(&Color_bright);
5245 gr_set_color_fast(&Color_normal);
5250 gr_string(Multi_pxo_help_coords[gr_screen.res][0], y_start, cp->text[idx] + start_pos);
5252 // increment the y location
5257 // process button presses
5258 void multi_pxo_help_process_buttons()
5262 // process all buttons
5263 for(idx=0;idx<MULTI_PXO_HELP_NUM_BUTTONS;idx++){
5264 if(Multi_pxo_help_buttons[gr_screen.res][idx].button.pressed()){
5265 multi_pxo_help_button_pressed(idx);
5272 void multi_pxo_help_button_pressed(int n)
5275 case MULTI_PXO_HELP_PREV:
5276 // if we're already at page 0, do nothing
5277 if(Multi_pxo_help_cur == 0){
5278 gamesnd_play_iface(SND_GENERAL_FAIL);
5280 Multi_pxo_help_cur--;
5281 gamesnd_play_iface(SND_USER_SELECT);
5285 case MULTI_PXO_HELP_NEXT:
5286 // if we're already at max pages, do nothing
5287 if(Multi_pxo_help_cur == Multi_pxo_help_num_pages){
5288 gamesnd_play_iface(SND_GENERAL_FAIL);
5290 Multi_pxo_help_cur++;
5291 gamesnd_play_iface(SND_USER_SELECT);
5295 case MULTI_PXO_HELP_CONTINUE:
5296 gamesnd_play_iface(SND_USER_SELECT);
5297 gameseq_post_event(GS_EVENT_PXO);
5302 // http banner stuff ---------------------------------------------
5305 void multi_pxo_ban_init()
5307 // zero the active banner bitmap
5308 Multi_pxo_banner.ban_bitmap = -1;
5310 // are we doing banners at all?
5311 if(os_config_read_uint(NULL, "PXOBanners", 1)){
5312 // if we're already in idle mode, we're done downloading for this instance of freespace. pick a random image we already have
5313 if(Multi_pxo_ban_mode == PXO_BAN_MODE_IDLE){
5314 Multi_pxo_ban_mode = PXO_BAN_MODE_CHOOSE_RANDOM;
5318 // set ourselves to startup mode
5319 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
5320 Multi_pxo_ban_get = NULL;
5322 // set ourselves to idle mode
5323 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5324 Multi_pxo_ban_get = NULL;
5327 // zero the active banner bitmap
5328 Multi_pxo_banner.ban_bitmap = -1;
5329 strcpy(Multi_pxo_banner.ban_file, "");
5330 strcpy(Multi_pxo_banner.ban_file_url, "");
5331 strcpy(Multi_pxo_banner.ban_url, "");
5334 // process http download details
5335 void multi_pxo_ban_process()
5337 char url_string[512] = "";
5338 char local_file[512] = "";
5341 switch(Multi_pxo_ban_mode){
5342 // start downloading list
5343 case PXO_BAN_MODE_LIST_STARTUP:
5345 sprintf(url_string, "%s/%s", Multi_options_g.pxo_banner_url, PXO_BANNERS_CONFIG_FILE);
5348 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, PXO_BANNERS_CONFIG_FILE);
5350 // try creating the file get object
5351 Multi_pxo_ban_get = new InetGetFile(url_string, local_file);
5354 if(Multi_pxo_ban_get == NULL){
5355 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5358 // go to the downloading list mode
5359 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST;
5363 case PXO_BAN_MODE_LIST:
5365 if(Multi_pxo_ban_get->IsFileError()){
5366 delete Multi_pxo_ban_get;
5367 Multi_pxo_ban_get = NULL;
5368 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5372 // connecting, receiving
5373 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5378 if(Multi_pxo_ban_get->IsFileReceived()){
5379 delete Multi_pxo_ban_get;
5380 Multi_pxo_ban_get = NULL;
5381 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_STARTUP;
5385 // start downloading files
5386 case PXO_BAN_MODE_IMAGES_STARTUP:
5387 // first thing - parse the banners file and pick a file
5388 multi_pxo_ban_parse_banner_file(0);
5390 // if we have no active file, we're done
5391 if((strlen(Multi_pxo_banner.ban_file) <= 0) || (strlen(Multi_pxo_banner.ban_file_url) <= 0)){
5392 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5396 // if the file already exists, we're done
5397 if(cf_exist(Multi_pxo_banner.ban_file, CF_TYPE_MULTI_CACHE)){
5398 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5402 // otherwise try and download it
5403 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, Multi_pxo_banner.ban_file);
5404 // try creating the file get object
5405 Multi_pxo_ban_get = new InetGetFile(Multi_pxo_banner.ban_file_url, local_file);
5408 if(Multi_pxo_ban_get == NULL){
5409 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5412 // go to the downloading images mode
5413 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES;
5416 // downloading files
5417 case PXO_BAN_MODE_IMAGES:
5419 if(Multi_pxo_ban_get->IsFileError()){
5420 delete Multi_pxo_ban_get;
5421 Multi_pxo_ban_get = NULL;
5422 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5426 // connecting, receiving
5427 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5432 if(Multi_pxo_ban_get->IsFileReceived()){
5433 delete Multi_pxo_ban_get;
5434 Multi_pxo_ban_get = NULL;
5435 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5439 // done downloading - maybe load an image
5440 case PXO_BAN_MODE_IMAGES_DONE:
5441 // make sure we have a valid filename
5442 // SDL_assert(strlen(Multi_pxo_banner.ban_file) > 0);
5443 if(strlen(Multi_pxo_banner.ban_file) > 0){
5444 Multi_pxo_banner.ban_bitmap = bm_load(Multi_pxo_banner.ban_file);
5448 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5451 // idle (done with EVERYTHING)
5452 case PXO_BAN_MODE_IDLE:
5453 // if the banner button was clicked
5454 if(Multi_pxo_ban_button.pressed()){
5455 multi_pxo_ban_clicked();
5459 case PXO_BAN_MODE_CHOOSE_RANDOM:
5460 // first thing - parse the banners file and pick a file
5461 multi_pxo_ban_parse_banner_file(1);
5463 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5469 void multi_pxo_ban_close()
5471 // if we have a currently active transfer
5472 if(Multi_pxo_ban_get != NULL){
5473 Multi_pxo_ban_get->AbortGet();
5474 delete Multi_pxo_ban_get;
5475 Multi_pxo_ban_get = NULL;
5478 // if we have a loaded bitmap, unload it
5479 if(Multi_pxo_banner.ban_bitmap != -1){
5480 bm_unload(Multi_pxo_banner.ban_bitmap);
5481 Multi_pxo_banner.ban_bitmap = -1;
5485 // parse the banners file and maybe fill in Multi_pxo_dl_file
5486 void multi_pxo_ban_parse_banner_file(int choose_existing)
5488 char file_url[512] = "";
5489 char banners[10][512];
5493 int num_banners, idx;
5494 CFILE *in = cfopen(PXO_BANNERS_CONFIG_FILE, "rt", CFILE_NORMAL, CF_TYPE_MULTI_CACHE);
5496 Multi_pxo_banner.ban_bitmap = -1;
5497 strcpy(Multi_pxo_banner.ban_file, "");
5498 strcpy(Multi_pxo_banner.ban_file_url, "");
5499 strcpy(Multi_pxo_banner.ban_url, "");
5506 // clear all strings
5507 for(idx=0; idx<10; idx++){
5508 strcpy(banners[idx], "");
5509 strcpy(urls[idx], "");
5512 // get the global banner url
5513 if(cfgets(file_url, 254, in) == NULL){
5515 cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5518 drop_leading_white_space(file_url);
5519 drop_trailing_white_space(file_url);
5521 // otherwise read in
5523 while(num_banners < 10){
5524 // try and get the pcx
5525 if(cfgets(banners[num_banners], 254, in) == NULL){
5528 // try and get the url
5529 if(cfgets(urls[num_banners], 254, in) == NULL){
5533 // strip off trailing and leading whitespace
5534 drop_leading_white_space(banners[num_banners]);
5535 drop_trailing_white_space(banners[num_banners]);
5536 drop_leading_white_space(urls[num_banners]);
5537 drop_trailing_white_space(urls[num_banners]);
5547 if(num_banners <= 0){
5551 // if we're only selecting files which already exist (previously downloaded)
5552 if(choose_existing){
5554 for(idx=0; idx<10; idx++){
5558 // build a list of existing files
5560 for(idx=0; idx<num_banners; idx++){
5561 if(cf_exist(banners[idx], CF_TYPE_MULTI_CACHE)){
5568 if(exist_count <= 0){
5573 int select = (int)frand_range(0.0f, (float)exist_count);
5574 if(select >= exist_count){
5575 select = exist_count - 1;
5580 for(idx=0; idx<exist_count; idx++){
5590 if(idx < exist_count){
5592 strncpy(Multi_pxo_banner.ban_file, banners[idx], MAX_FILENAME_LEN);
5594 // get the full file url
5595 strncpy(Multi_pxo_banner.ban_file_url, file_url, MULTI_OPTIONS_STRING_LEN);
5596 strncat(Multi_pxo_banner.ban_file_url, banners[idx], MULTI_OPTIONS_STRING_LEN);
5598 // url of where to go to when clicked
5599 strncpy(Multi_pxo_banner.ban_url, urls[idx], MULTI_OPTIONS_STRING_LEN);
5602 // randomly pick a file for download
5604 idx = (int)frand_range(0.0f, (float)num_banners);
5606 if(idx >= num_banners){
5607 idx = num_banners - 1;
5614 strncpy(Multi_pxo_banner.ban_file, banners[idx], MAX_FILENAME_LEN);
5616 // get the full file url
5617 strncpy(Multi_pxo_banner.ban_file_url, file_url, MULTI_OPTIONS_STRING_LEN);
5618 strncat(Multi_pxo_banner.ban_file_url, banners[idx], MULTI_OPTIONS_STRING_LEN);
5620 // url of where to go to when clicked
5621 strncpy(Multi_pxo_banner.ban_url, urls[idx], MULTI_OPTIONS_STRING_LEN);
5624 // delete the banner config file
5625 // cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5628 // any bitmap or info or whatever
5629 void multi_pxo_ban_draw()
5631 // if we have a valid bitmap
5632 if(Multi_pxo_banner.ban_bitmap >= 0){
5633 // if the mouse is over the banner button, highlight with a rectangle
5634 if(Multi_pxo_ban_button.is_mouse_on()){
5635 gr_set_color_fast(&Color_bright_blue);
5636 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);
5639 // draw the bitmap itself
5640 gr_set_bitmap(Multi_pxo_banner.ban_bitmap);
5641 gr_bitmap(Pxo_ban_coords[gr_screen.res][0], Pxo_ban_coords[gr_screen.res][1]);
5645 // called when the URL button is clicked
5646 void multi_pxo_ban_clicked()
5648 // if we have a valid bitmap and URL, launch the URL
5649 if((Multi_pxo_banner.ban_bitmap >= 0) && (strlen(Multi_pxo_banner.ban_url) > 0)){
5650 multi_pxo_url(Multi_pxo_banner.ban_url);