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
484 char Multi_pxo_nick[NAME_LENGTH+1];
486 // check for button presses
487 void multi_pxo_check_buttons();
489 // handle a button press
490 void multi_pxo_button_pressed(int n);
492 // condition function for popup_do_with_condition for connected to Parallax Online
493 // return 10 : on successful connect
494 int multi_pxo_connect_do();
496 // attempt to connect to Parallax Online, return success or fail
497 int multi_pxo_connect();
499 // run the networking functions for the PXO API
500 void multi_pxo_api_process();
502 // process a "nick" change event
503 void multi_pxo_process_nick_change(char *data);
505 // run normally (no popups)
506 void multi_pxo_do_normal();
508 // blit everything on the "normal" screen
509 void multi_pxo_blit_all();
511 // process common stuff
512 void multi_pxo_process_common();
514 // get selected player information
515 void multi_pxo_get_data(char *name);
517 // handle being kicked
518 void multi_pxo_handle_kick();
520 // handle being disconnected
521 void multi_pxo_handle_disconnect();
523 // return string2, which is the first substring of string 1 without a space
524 // it is safe to pass the same pointer for both parameters
525 void multi_pxo_strip_space(char *string1,char *string2, const int str2_len);
527 // fire up the given URL
528 void multi_pxo_url(char *url);
530 // load/set the palette
531 void multi_pxo_load_palette();
533 // unload the palette
534 void multi_pxo_unload_palette();
536 // if we're currently on a private channel
537 int multi_pxo_on_private_channel();
539 // convert string 1 into string 2, substituting underscores for spaces
540 void multi_pxo_underscore_nick(char *string1, char *string2, const int str2_len);
542 // if the command is a potential "nick" command
543 int multi_pxo_is_nick_command(char *msg);
546 // status bar stuff -----------------------------------------------
547 int Multi_pxo_status_coords[GR_NUM_RESOLUTIONS][4] = {
556 // the status text itself
557 char Multi_pxo_status_text[255];
559 // set the status text
560 void multi_pxo_set_status_text(const char *txt);
562 // blit the status text
563 void multi_pxo_blit_status_text();
566 // channel related stuff -------------------------------------------
567 #define MAX_CHANNEL_NAME_LEN 32
568 #define MAX_CHANNEL_DESCRIPT_LEN 120
570 // some convenient macros
571 #define SWITCHING_CHANNELS() (Multi_pxo_channel_switch.num_users != -1)
572 #define ON_CHANNEL() (Multi_pxo_channel_current.num_users != -1)
574 typedef struct pxo_channel {
575 pxo_channel *next,*prev; // next and previous items in the list
576 char name[MAX_CHANNEL_NAME_LEN+1]; // name
577 char desc[MAX_CHANNEL_DESCRIPT_LEN+1]; // description
578 short num_users; // # users, or -1 if not in use
579 short num_servers; // the # of servers registered on this channel
582 // last channel we were on before going to the game list screen
583 char Multi_pxo_channel_last[MAX_CHANNEL_NAME_LEN+1] = "";
584 int Multi_pxo_use_last_channel = 0;
586 // all channels which are prefixed with this are "lobby" channels
587 #define MULTI_PXO_AUTOJOIN_PREFIX "#lobby"
589 // join this channel to get put in an appropriate lobby channel
590 #define MULTI_PXO_AUTOJOIN_CHANNEL "#autoselect"
592 int Multi_pxo_chan_coords[GR_NUM_RESOLUTIONS][4] = {
601 // this is the offset from the RIGHT side of the channel box
602 #define CHAN_PLAYERS_COLUMN 0
603 #define CHAN_GAMES_COLUMN 1
604 static int Multi_pxo_chan_column_offsets[GR_NUM_RESOLUTIONS][2] = {
609 #define CHANNEL_REFRESH_TIME (75.0f)
610 float Multi_pxo_channel_last_refresh = -1.0f;
612 #define CHANNEL_SERVER_REFRESH_TIME (35.0f)
613 float Multi_pxo_channel_server_refresh = -1.0f;
615 int Multi_pxo_max_chan_display[GR_NUM_RESOLUTIONS] = {
620 UI_BUTTON Multi_pxo_channel_button;
622 // head of the list of available (displayed) channels
623 pxo_channel *Multi_pxo_channels = NULL;
624 int Multi_pxo_channel_count = 0;
626 // item we're going to start displaying at
627 pxo_channel *Multi_pxo_channel_start = NULL;
628 int Multi_pxo_channel_start_index = -1;
630 // items we've currently got selected
631 pxo_channel *Multi_pxo_channel_select = NULL;
633 // channel we're currently connected to, num_users == -1, if we're not connected
634 pxo_channel Multi_pxo_channel_current;
636 // channel we're currently trying to change to, num_users == -1, if we're not trying to change channels
637 pxo_channel Multi_pxo_channel_switch;
639 // get a list of channels on the server (clear any old list as well)
640 void multi_pxo_get_channels();
642 // clear the old channel list
643 void multi_pxo_clear_channels();
645 // parse the input string and make a list of new channels
646 void multi_pxo_make_channels(char *chan_str);
648 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
649 pxo_channel *multi_pxo_add_channel(char *name, pxo_channel **list);
651 // lookup a channel with the specified name
652 pxo_channel *multi_pxo_find_channel(char *name, pxo_channel *list);
654 // process the channel list (select, etc)
655 void multi_pxo_process_channels();
657 // display the channel list
658 void multi_pxo_blit_channels();
660 // scroll channel list up
661 void multi_pxo_scroll_channels_up();
663 // scroll channel list down
664 void multi_pxo_scroll_channels_down();
666 // attempt to join a channel
667 void multi_pxo_join_channel(pxo_channel *chan);
669 // handle any processing details if we're currently trying to join a channel
670 void multi_pxo_handle_channel_change();
672 // autojoin an appropriate channel
673 void multi_pxo_autojoin();
675 // does the string match the "autojoin" prefic
676 int multi_pxo_is_autojoin(char *name);
678 // send a request to refresh our channel server counts
679 void multi_pxo_channel_refresh_servers();
681 // refresh current channel server count
682 void multi_pxo_channel_refresh_current();
685 // player related stuff -------------------------------------------
686 #define MAX_PLAYER_NAME_LEN 32
688 typedef struct player_list {
689 player_list *next,*prev;
690 char name[MAX_PLAYER_NAME_LEN+1];
693 // channel list region
694 int Multi_pxo_player_coords[GR_NUM_RESOLUTIONS][4] = {
703 int Multi_pxo_max_player_display[GR_NUM_RESOLUTIONS] = {
707 UI_BUTTON Multi_pxo_player_button;
709 // UI_SLIDER2 Multi_pxo_player_slider;
712 int Multi_pxo_player_slider_coords[GR_NUM_RESOLUTIONS][4] = {
720 const char *Multi_pxo_player_slider_name[GR_NUM_RESOLUTIONS] = {
722 "2_slider" // GR_1024
725 // head of the list of players in this channel
726 player_list *Multi_pxo_players = NULL;
727 int Multi_pxo_player_count = 0;
729 // item we're going to start displaying at
730 player_list *Multi_pxo_player_start = NULL;
731 // int Multi_pxo_player_start_index = -1;
733 // items we've currently got selected
734 player_list *Multi_pxo_player_select = NULL;
736 // clear the old player list
737 void multi_pxo_clear_players();
739 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
740 player_list *multi_pxo_add_player(char *name);
742 // remove a player with the given name
743 void multi_pxo_del_player(char *name);
745 // try and find a player with the given name, return a pointer to his entry (or NULL)
746 player_list *multi_pxo_find_player(char *name);
748 // process the player list (select, etc)
749 void multi_pxo_process_players();
751 // display the player list
752 void multi_pxo_blit_players();
754 // scroll player list up
755 void multi_pxo_scroll_players_up();
757 // scroll player list down
758 void multi_pxo_scroll_players_down();
760 // get the absolute index of the displayed items which our currently selected one is
761 int multi_pxo_get_select_index();
767 // add a bunch of bogus players
769 for(int idx=0; idx<Dc_arg_int; idx++){
770 SDL_snprintf(name, SDL_arraysize(name), "player %d", idx);
771 multi_pxo_add_player(name);
775 // chat text stuff -----------------------------------------
776 #define MAX_CHAT_LINES 60
777 #define MAX_CHAT_LINE_LEN 256
779 int Multi_pxo_chat_title_y[GR_NUM_RESOLUTIONS] = {
784 int Multi_pxo_chat_coords[GR_NUM_RESOLUTIONS][4] = {
793 int Multi_pxo_input_coords[GR_NUM_RESOLUTIONS][4] = {
802 int Multi_pxo_max_chat_display[GR_NUM_RESOLUTIONS] = {
807 // all messages from the server are prefixed with this
808 #define MULTI_PXO_SERVER_PREFIX "*** "
810 // the "has left" message from the server
811 #define MULTI_PXO_HAS_LEFT "has left"
814 #define CHAT_MODE_NORMAL 0 // normal chat from someone
815 #define CHAT_MODE_SERVER 1 // is from the server, display appropriately
816 #define CHAT_MODE_CARRY 2 // is a carryover from a previous line
817 #define CHAT_MODE_PRIVATE 3 // is a private message
818 #define CHAT_MODE_CHANNEL_SWITCH 4 // "switching channels" message - draw in red
819 #define CHAT_MODE_MOTD 5 // message of the day from the chat server
821 typedef struct chat_line {
822 chat_line *next,*prev;
823 char text[MAX_CHAT_LINE_LEN+1];
827 // the chat linked list itself
828 chat_line *Multi_pxo_chat = NULL;
830 // the current add line
831 chat_line *Multi_pxo_chat_add = NULL;
833 // the current line to start displaying from
834 chat_line *Multi_pxo_chat_start = NULL;
835 int Multi_pxo_chat_start_index = -1;
837 // input box for text
838 UI_INPUTBOX Multi_pxo_chat_input;
841 UI_SLIDER2 Multi_pxo_chat_slider;
843 int Multi_pxo_chat_slider_coords[GR_NUM_RESOLUTIONS][4] = {
852 const char *Multi_pxo_chat_slider_name[GR_NUM_RESOLUTIONS] = {
857 // how many chat lines we have
858 int Multi_pxo_chat_count = 0;
860 // extra delay time when switching channels
861 #define MULTI_PXO_SWITCH_DELAY_TIME 2000
862 int Multi_pxo_switch_delay = -1;
864 // initialize and create the chat text linked list
865 void multi_pxo_chat_init();
867 // free up all chat list stuff
868 void multi_pxo_chat_free();
870 // clear all lines of chat text in the chat area
871 void multi_pxo_chat_clear();
873 // blit the chat text
874 void multi_pxo_chat_blit();
876 // add a line of text
877 void multi_pxo_chat_add_line(char *txt,int mode);
879 // process an incoming line of text
880 void multi_pxo_chat_process_incoming(const char *txt,int mode = CHAT_MODE_NORMAL);
882 // scroll to the very bottom of the chat area
883 void multi_pxo_goto_bottom();
885 // check whether we can scroll down or not
886 int multi_pxo_can_scroll_down();
888 static int Can_scroll_down = 0;
890 // scroll the text up
891 void multi_pxo_scroll_chat_up();
893 // scroll the text down
894 void multi_pxo_scroll_chat_down();
896 // process chat controls
897 void multi_pxo_chat_process();
899 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
900 const char *multi_pxo_chat_is_private(const char *txt);
902 // if the text came from the server
903 int multi_pxo_is_server_text(const char *txt);
905 // if the text is message of the day text
906 int multi_pxo_is_motd_text(const char *txt);
908 // if the text is the end of motd text
909 int multi_pxo_is_end_of_motd_text(const char *txt);
911 // if the text is a "has left message" from the server
912 int multi_pxo_chat_is_left_message(const char *txt);
914 // recalculate the chat start index, and adjust the slider properly
915 void multi_pxo_chat_adjust_start();
918 // motd stuff ---------------------------------------------------------
919 #define MAX_PXO_MOTD_LEN 1024
920 #define PXO_MOTD_BLINK_TIME 500
921 char Pxo_motd[1024] = "";
922 int Pxo_motd_end = 0;
923 int Pxo_motd_read = 0;
924 int Pxo_motd_blink_stamp = -1;
925 int Pxo_motd_blink_on = 0;
926 int Pxo_motd_blinked_already = 0;
928 // initialize motd when going into this screen
929 void multi_pxo_motd_init();
932 void multi_pxo_motd_add_text(const char *text);
935 void multi_pxo_set_end_of_motd();
937 // display the motd dialog
938 void multi_pxo_motd_dialog();
940 // call to maybe blink the motd button
941 void multi_pxo_motd_maybe_blit();
944 // common dialog stuff ------------------------------------------------
945 const char *Multi_pxo_com_fname[GR_NUM_RESOLUTIONS] = {
949 const char *Multi_pxo_com_mask_fname[GR_NUM_RESOLUTIONS] = {
955 int Multi_pxo_com_coords[GR_NUM_RESOLUTIONS][2] = {
965 int Multi_pxo_com_input_coords[GR_NUM_RESOLUTIONS][4] = {
974 #define MULTI_PXO_COM_NUM_BUTTONS 2
975 #define MULTI_PXO_COM_CANCEL 0
976 #define MULTI_PXO_COM_OK 1
978 ui_button_info Multi_pxo_com_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_BUTTONS] = {
980 ui_button_info("PXP_00", 573, 192, -1, -1, 0),
981 ui_button_info("PXP_01", 573, 226, -1, -1, 1)
984 ui_button_info("2_PXP_00", 917, 308, -1, -1, 0),
985 ui_button_info("2_PXP_01", 917, 361, -1, -1, 1)
989 #define MULTI_PXO_COM_NUM_TEXT 2
990 UI_XSTR Multi_pxo_com_text[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_TEXT] = {
992 { "&Cancel", 645, 510, 204, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_CANCEL].button },
993 { "&Ok", 669, 548, 233, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_OK].button }
996 { "&Cancel", 645, 847, 327, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_CANCEL].button },
997 { "&Ok", 669, 877, 372, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_OK].button }
1001 int Multi_pxo_com_bitmap = -1;
1002 UI_WINDOW Multi_pxo_com_window;
1003 UI_INPUTBOX Multi_pxo_com_input;
1005 // text on the "top" half of the dialog display area
1006 char Multi_pxo_com_top_text[255];
1008 // text on the "middle" portion of the dialog display area
1009 char Multi_pxo_com_middle_text[255];
1011 // text on the "bottom" half of the dialog display area
1012 char Multi_pxo_com_bottom_text[255];
1014 int Multi_pxo_com_top_text_coords[GR_NUM_RESOLUTIONS][2] = {
1022 int Multi_pxo_com_middle_text_y[GR_NUM_RESOLUTIONS] = {
1026 int Multi_pxo_com_bottom_text_y[GR_NUM_RESOLUTIONS] = {
1031 // initialize the common dialog with the passed max input length
1032 void multi_pxo_com_init();
1034 // close down the common dialog
1035 void multi_pxo_com_close();
1037 // blit all text lines, top, middle, bottoms
1038 void multi_pxo_com_blit_text();
1040 // set the top text, shortening as necessary
1041 void multi_pxo_com_set_top_text(const char *txt);
1043 // set the middle text, shortening as necessary
1044 void multi_pxo_com_set_middle_text(const char *txt);
1046 // set the bottom text, shortening as necessary
1047 void multi_pxo_com_set_bottom_text(const char *txt);
1050 // private channel join stuff -----------------------------------------
1051 #define MULTI_PXO_PRIV_MAX_TEXT_LEN 30
1053 // max private channel name length
1054 char Multi_pxo_priv_chan[MULTI_PXO_PRIV_MAX_TEXT_LEN+100];
1056 // return code, set to something other than -1 if we're supposed to return
1057 int Multi_pxo_priv_return_code = -1;
1059 // initialize the popup
1060 void multi_pxo_priv_init();
1062 // close down the popup
1063 void multi_pxo_priv_close();
1065 // run the popup, 0 if still running, -1 if cancel, 1 if ok
1066 int multi_pxo_priv_popup();
1068 // process button presses
1069 void multi_pxo_priv_process_buttons();
1071 // handle a button press
1072 void multi_pxo_priv_button_pressed(int n);
1074 // process the inputbox
1075 void multi_pxo_priv_process_input();
1078 // find player stuff -----------------------------------------
1080 char Multi_pxo_find_channel[MAX_CHANNEL_NAME_LEN+1];
1082 // return code, set to something other than -1 if we're supposed to return
1083 int Multi_pxo_find_return_code = -1;
1085 // initialize the popup
1086 void multi_pxo_find_init();
1088 // close down the popup
1089 void multi_pxo_find_close();
1091 // run the popup, 0 if still running, -1 if cancel, 1 if ok
1092 int multi_pxo_find_popup();
1094 // process button presses
1095 void multi_pxo_find_process_buttons();
1097 // handle a button press
1098 void multi_pxo_find_button_pressed(int n);
1100 // process the inputbox
1101 void multi_pxo_find_process_input();
1103 // process search mode if applicable
1104 void multi_pxo_find_search_process();
1107 // player info stuff -----------------------------------------
1108 const char *Multi_pxo_pinfo_fname[GR_NUM_RESOLUTIONS] = {
1112 const char *Multi_pxo_pinfo_mask_fname[GR_NUM_RESOLUTIONS] = {
1118 #define MULTI_PXO_PINFO_NUM_BUTTONS 2
1119 #define MULTI_PXO_PINFO_MEDALS 0
1120 #define MULTI_PXO_PINFO_OK 1
1122 ui_button_info Multi_pxo_pinfo_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_BUTTONS] = {
1124 ui_button_info("PI2_00", 328, 446, 319, 433, 0),
1125 ui_button_info("PI2_01", 376, 446, 382, 433, 1),
1128 ui_button_info("2_PI2_00", 525, 714, 510, 695, 0),
1129 ui_button_info("2_PI2_01", 601, 714, 611, 695, 1),
1134 #define MULTI_PXO_PINFO_NUM_TEXT 2
1135 UI_XSTR Multi_pxo_pinfo_text[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_TEXT] = {
1137 { "Medals", 1037, 319, 433, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_MEDALS].button },
1138 { "Ok", 345, 382, 433, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_OK].button },
1141 { "Medals", 1037, 510, 695, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_MEDALS].button },
1142 { "Ok", 345, 611, 695, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_OK].button },
1146 int Multi_pxo_pinfo_bitmap = -1;
1147 UI_WINDOW Multi_pxo_pinfo_window;
1149 vmt_freespace2_struct Multi_pxo_pinfo;
1150 player Multi_pxo_pinfo_player;
1152 int Multi_pxo_retrieve_mode = -1;
1154 char Multi_pxo_retrieve_name[MAX_PLAYER_NAME_LEN+1];
1155 char Multi_pxo_retrieve_id[128];
1157 // stats label stuff
1158 #define MULTI_PXO_PINFO_NUM_LABELS 18
1160 int Multi_pxo_pinfo_coords[GR_NUM_RESOLUTIONS][4] = {
1168 int Multi_pxo_pinfo_val_x[GR_NUM_RESOLUTIONS] = {
1173 char *Multi_pxo_pinfo_stats_labels[MULTI_PXO_PINFO_NUM_LABELS];
1184 "Primary shots fired",
1185 "Primary shots hit",
1187 "Secondary shots fired",
1188 "Secondary shots hit",
1190 "Primary friendly hits",
1191 "Primary friendly hit %",
1192 "Secondary friendly hits",
1193 "Secondary friendly hit %"
1198 char Multi_pxo_pinfo_vals[MULTI_PXO_PINFO_NUM_LABELS][50];
1200 int Multi_pxo_pinfo_stats_spacing[MULTI_PXO_PINFO_NUM_LABELS] = {
1201 10,20,10,10,20,10,10,20,10,10,20,10,10,20,10,20,10,0
1204 // popup conditional functions, returns 10 on successful get of stats
1205 int multi_pxo_pinfo_cond();
1207 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
1208 int multi_pxo_pinfo_get(char *name);
1210 // fire up the stats view popup
1211 void multi_pxo_pinfo_show();
1213 // build the stats labels values
1214 void multi_pxo_pinfo_build_vals();
1216 // initialize the popup
1217 void multi_pxo_pinfo_init();
1220 int multi_pxo_pinfo_do();
1223 void multi_pxo_pinfo_close();
1225 // blit all the stats on this screen
1226 void multi_pxo_pinfo_blit();
1228 // run the medals screen
1229 void multi_pxo_run_medals();
1231 // notify stuff stuff -----------------------------------------
1232 #define MULTI_PXO_NOTIFY_TIME 4000
1233 #define MULTI_PXO_NOTIFY_Y 435
1235 char Multi_pxo_notify_text[255];
1236 int Multi_pxo_notify_stamp = -1;
1238 // add a notification string
1239 void multi_pxo_notify_add(const char *txt);
1241 // blit and process the notification string
1242 void multi_pxo_notify_blit();
1245 // help screen stuff -----------------------------------------
1247 const char *Multi_pxo_help_fname[GR_NUM_RESOLUTIONS] = {
1251 const char *Multi_pxo_help_mask_fname[GR_NUM_RESOLUTIONS] = {
1256 #define MULTI_PXO_HELP_NUM_BUTTONS 3
1257 #define MULTI_PXO_HELP_PREV 0
1258 #define MULTI_PXO_HELP_NEXT 1
1259 #define MULTI_PXO_HELP_CONTINUE 2
1261 ui_button_info Multi_pxo_help_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_BUTTONS] = {
1263 ui_button_info("PXH_00", 15, 389, -1, -1, 0),
1264 ui_button_info("PXH_01", 60, 389, -1, -1, 1),
1265 ui_button_info("PXH_02", 574, 431, 571, 413, 2),
1268 ui_button_info("2_PXH_00", 24, 622, -1, -1, 0),
1269 ui_button_info("2_PXH_01", 96, 622, -1, -1, 1),
1270 ui_button_info("2_PXH_02", 919, 689, 928, 663, 2),
1274 #define MULTI_PXO_HELP_NUM_TEXT 1
1275 UI_XSTR Multi_pxo_help_text[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_TEXT] = {
1277 {"Continue", 1069, 571, 413, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[0][MULTI_PXO_HELP_CONTINUE].button },
1280 {"Continue", 1069, 928, 663, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[1][MULTI_PXO_HELP_CONTINUE].button },
1285 #define MULTI_PXO_HELP_FILE "pxohelp.txt"
1286 #define MULTI_PXO_MAX_LINES_PP 57
1287 #define MULTI_PXO_MAX_PAGES 3
1289 int Multi_pxo_help_coords[GR_NUM_RESOLUTIONS][2] = {
1298 int Multi_pxo_chars_per_line[GR_NUM_RESOLUTIONS] = {
1303 int Multi_pxo_lines_pp[GR_NUM_RESOLUTIONS] = {
1309 typedef struct help_page {
1310 char *text[MULTI_PXO_MAX_LINES_PP];
1314 help_page Multi_pxo_help_pages[MULTI_PXO_MAX_PAGES];
1315 // int Multi_pxo_help_loaded = 0;
1317 int Multi_pxo_help_num_pages = 0;
1319 int Multi_pxo_help_bitmap = -1;
1320 UI_WINDOW Multi_pxo_help_window;
1322 // current page we're on
1323 int Multi_pxo_help_cur = 0;
1325 // load the help file up
1326 void multi_pxo_help_load();
1328 // blit the current page
1329 void multi_pxo_help_blit_page();
1331 // process button presses
1332 void multi_pxo_help_process_buttons();
1335 void multi_pxo_help_button_pressed(int n);
1338 // http banner stuff ---------------------------------------------
1339 InetGetFile *Multi_pxo_ban_get = NULL;
1342 #define PXO_BANNERS_CONFIG_FILE "pxobanners.cfg"
1344 // coords to display banners at
1345 int Pxo_ban_coords[GR_NUM_RESOLUTIONS][4] = {
1355 #define PXO_BAN_MODE_LIST_STARTUP 0 // start downloading list
1356 #define PXO_BAN_MODE_LIST 1 // downloading list
1357 #define PXO_BAN_MODE_IMAGES_STARTUP 2 // start downloading images
1358 #define PXO_BAN_MODE_IMAGES 3 // downloading images
1359 #define PXO_BAN_MODE_IMAGES_DONE 4 // done downloading everything - now maybe load an image
1360 #define PXO_BAN_MODE_IDLE 5 // done with everything - doing nothing
1361 #define PXO_BAN_MODE_CHOOSE_RANDOM 6 // choose a bitmap we've already downloaded at random
1363 // interface button for detecting clicks
1364 UI_BUTTON Multi_pxo_ban_button;
1367 typedef struct pxo_banner {
1368 char ban_file[MAX_FILENAME_LEN+1]; // base filename of the banner
1369 char ban_file_url[MULTI_OPTIONS_STRING_LEN+1]; // full url of the file to get (convenient)
1370 char ban_url[MULTI_OPTIONS_STRING_LEN+1]; // url to go to when clicked
1371 int ban_bitmap; // banner bitmap
1374 // active pxo banner
1375 pxo_banner Multi_pxo_banner;
1378 int Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
1381 void multi_pxo_ban_init();
1383 // process http download details
1384 void multi_pxo_ban_process();
1387 void multi_pxo_ban_close();
1389 // parse the banners file and maybe fill in Multi_pxo_dl_file[]
1390 void multi_pxo_ban_parse_banner_file(int choose_existing);
1392 // any bitmap or info or whatever
1393 void multi_pxo_ban_draw();
1395 // called when the URL button is clicked
1396 void multi_pxo_ban_clicked();
1399 // ----------------------------------------------------------------------------------------------------
1403 // initialize the PXO screen
1404 void multi_pxo_init(int use_last_channel)
1408 // load the background bitmap
1409 Multi_pxo_bitmap = bm_load(Multi_pxo_bitmap_fname[gr_screen.res]);
1410 if(Multi_pxo_bitmap < 0){
1411 // we failed to load the bitmap - this is very bad
1415 // load up the private channel bitmap
1416 Multi_pxo_com_bitmap = bm_load(Multi_pxo_com_fname[gr_screen.res]);
1417 SDL_assert(Multi_pxo_com_bitmap != -1);
1419 // create the interface window
1420 Multi_pxo_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1421 Multi_pxo_window.set_mask_bmap(Multi_pxo_mask_fname[gr_screen.res]);
1423 // multiplayer screen common palettes
1424 multi_pxo_load_palette();
1426 // create the interface buttons
1427 for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
1428 // create the object
1429 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);
1431 // set the sound to play when highlighted
1432 Multi_pxo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1434 // set the ani for the button
1435 Multi_pxo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_buttons[gr_screen.res][idx].filename);
1438 Multi_pxo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_buttons[gr_screen.res][idx].hotspot);
1442 for(idx=0; idx<MULTI_PXO_NUM_TEXT; idx++){
1443 Multi_pxo_window.add_XSTR(&Multi_pxo_text[gr_screen.res][idx]);
1446 if(use_last_channel && strlen(Multi_pxo_channel_last)){
1447 Multi_pxo_use_last_channel = 1;
1449 SDL_zero(Multi_pxo_channel_last);
1450 Multi_pxo_use_last_channel = 0;
1453 // make all scrolling buttons repeatable
1454 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_UP].button.repeatable(1);
1455 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_DOWN].button.repeatable(1);
1456 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_UP].button.repeatable(1);
1457 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_DOWN].button.repeatable(1);
1458 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_UP].button.repeatable(1);
1459 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_DOWN].button.repeatable(1);
1461 // set the mouseover cursor if it loaded ok
1462 if (Web_cursor_bitmap > 0) {
1463 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_RANKINGS].button.set_custom_cursor_bmap(Web_cursor_bitmap);
1466 // create the channel list select button and hide it
1467 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);
1468 Multi_pxo_channel_button.hide();
1470 // create the player list select button and hide it
1471 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);
1472 Multi_pxo_player_button.hide();
1474 // create the chat input box
1475 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);
1476 Multi_pxo_chat_input.set_focus();
1478 // create the banner button and hide it
1479 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);
1480 Multi_pxo_ban_button.hide();
1482 // create the player list slider
1483 // 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);
1485 // create the chat slider
1486 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);
1488 // set our connection status so that we do the right stuff next frame
1489 Multi_pxo_must_validate = 1;
1490 Multi_pxo_must_connect = 0;
1491 Multi_pxo_connected = 0;
1493 // channel we're currently connected to
1494 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
1495 Multi_pxo_channel_current.num_users = -1;
1497 // channel we're currently trying to change to, or NULL if nont
1498 memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
1499 Multi_pxo_channel_switch.num_users = -1;
1501 // last time clicked the url button (so we don't have repeats)
1502 Multi_pxo_ranking_last = -1.0f;
1504 // channel switching extra time delay stamp
1505 Multi_pxo_switch_delay = -1;
1507 // our nick for this session
1508 multi_pxo_underscore_nick(Player->callsign, Multi_pxo_nick, SDL_arraysize(Multi_pxo_nick));
1510 // clear the channel list
1511 multi_pxo_clear_channels();
1513 // clear the player list
1514 multi_pxo_clear_players();
1516 // initialize the chat system
1517 multi_pxo_chat_init();
1520 multi_pxo_ban_init();
1522 // load the animation up
1523 if (gr_screen.res == GR_1024) {
1524 char anim_filename[32] = "2_";
1525 SDL_strlcat(anim_filename, MULTI_PXO_ANIM_FNAME, SDL_arraysize(anim_filename));
1526 Multi_pxo_anim = anim_load(anim_filename);
1528 // if hi-res is not there, fallback to low
1529 if (Multi_pxo_anim == NULL) {
1530 Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME);
1533 Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME);
1536 // clear the status text
1537 multi_pxo_set_status_text("");
1539 // last refresh time
1540 Multi_pxo_channel_last_refresh = -1.0f;
1542 // server count last refresh time
1543 Multi_pxo_channel_server_refresh = -1.0f;
1546 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1549 multi_pxo_motd_init();
1551 // make sure we autojoin
1552 Multi_pxo_must_autojoin = 1;
1554 // clear all tracker channel related strings
1555 SDL_zero(Multi_fs_tracker_channel);
1556 SDL_zero(Multi_fs_tracker_filter);
1559 // do frame for the PXO screen
1562 pxo_channel priv_chan;
1565 if(Multi_pxo_connected) {
1566 multi_pxo_api_process();
1569 // process common stuff
1570 multi_pxo_process_common();
1572 switch(Multi_pxo_mode){
1573 // private channel join mode
1574 case MULTI_PXO_MODE_PRIVATE:
1575 switch(multi_pxo_priv_popup()){
1580 // user hit "cancel"
1582 // return to normal mode
1583 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1588 // setup some information
1589 memset(&priv_chan,0,sizeof(pxo_channel));
1590 priv_chan.num_users = 0;
1591 SDL_strlcpy(priv_chan.name, Multi_pxo_priv_chan, SDL_arraysize(priv_chan.name));
1593 // see if we know about this channel already
1594 multi_pxo_join_channel(&priv_chan);
1596 // return to normal mode
1597 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1603 case MULTI_PXO_MODE_FIND:
1604 switch(multi_pxo_find_popup()){
1609 // user hit "cancel"
1611 // return to normal mode
1612 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1617 // return to normal mode
1618 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1620 // if there is a valid channel name try and join it
1621 if(strlen(Multi_pxo_find_channel) && !SWITCHING_CHANNELS()){
1625 memset(&join,0,sizeof(pxo_channel));
1627 SDL_strlcpy(join.name, Multi_pxo_find_channel, SDL_arraysize(join.name));
1630 multi_pxo_join_channel(&join);
1636 case MULTI_PXO_MODE_NORMAL:
1637 multi_pxo_do_normal();
1642 // close the PXO screen
1643 void multi_pxo_close()
1645 // unload any bitmaps
1646 bm_unload(Multi_pxo_bitmap);
1647 bm_unload(Multi_pxo_com_bitmap);
1649 // record the last channel we were on, if any
1650 SDL_zero(Multi_fs_tracker_channel);
1651 SDL_zero(Multi_fs_tracker_filter);
1653 if( ON_CHANNEL() && strlen(Multi_pxo_channel_current.name) ){
1655 SDL_strlcpy(Multi_fs_tracker_channel, Multi_pxo_channel_current.name, SDL_arraysize(Multi_fs_tracker_channel));
1658 SDL_strlcpy(Multi_fs_tracker_filter, Multi_pxo_channel_current.name, SDL_arraysize(Multi_fs_tracker_filter));
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, const int str2_len)
1928 // copy the original
1929 SDL_strlcpy(midway, string1, SDL_arraysize(midway));
1930 tok = strtok(midway," ");
1932 SDL_strlcpy(string2, tok, str2_len);
1934 SDL_strlcpy(string2, "", str2_len);
1938 // fire up the given URL
1939 void multi_pxo_url(char *url)
1941 if ( platform_open_url(url) ) {
1942 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,XSTR("Warning\nCould not locate/launch default Internet Browser",943));
1946 // load/set the palette
1947 void multi_pxo_load_palette()
1950 //#ifndef HARDWARE_ONLY
1951 // palette_use_bm_palette(Multi_pxo_palette);
1955 // unload the palette
1956 void multi_pxo_unload_palette()
1958 // unload the palette if it exists
1959 if(Multi_pxo_palette != -1){
1960 bm_release(Multi_pxo_palette);
1961 Multi_pxo_palette = -1;
1965 // if we're currently on a private channel
1966 int multi_pxo_on_private_channel()
1968 // if we're connected to a channel with the "+" symbol on front
1969 if(ON_CHANNEL() && (Multi_pxo_channel_current.name[0] == '+')){
1973 // otherwise return falos
1977 // convert string 1 into string 2, substituting underscores for spaces
1978 void multi_pxo_underscore_nick(char *string1, char *string2, const int str2_len)
1980 char nick_temp[512];
1983 // don't do anything if we have bogus string
1984 if((string1 == NULL) || (string2 == NULL)){
1988 // copy the nickname
1989 SDL_strlcpy(nick_temp, string1, SDL_arraysize(nick_temp));
1991 // get the first token
1992 tok = strtok(nick_temp, " ");
1994 SDL_strlcpy(string2, tok, str2_len);
1996 // get the next token
1997 tok = strtok(NULL," ");
2000 SDL_strlcat(string2, "_", str2_len);
2001 SDL_strlcat(string2, tok, str2_len);
2004 tok = strtok(NULL," ");
2007 SDL_strlcpy(string2, string1, str2_len);
2011 // if the command is a potential "nick" command
2012 int multi_pxo_is_nick_command(char *msg)
2017 // get the first token in the message
2018 SDL_strlcpy(tmp, msg, SDL_arraysize(tmp));
2019 tok = strtok(tmp," ");
2021 // can't be a nick message
2025 return !SDL_strcasecmp(tok,NOX("/nick"));
2028 // check for button presses
2029 void multi_pxo_check_buttons()
2033 // go through all buttons
2034 for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
2035 if(Multi_pxo_buttons[gr_screen.res][idx].button.pressed()){
2036 multi_pxo_button_pressed(idx);
2042 // handle a button press
2043 void multi_pxo_button_pressed(int n)
2046 case MULTI_PXO_EXIT:
2047 gamesnd_play_iface(SND_USER_SELECT);
2048 gameseq_post_event(GS_EVENT_MAIN_MENU);
2051 case MULTI_PXO_CHAN_UP:
2052 multi_pxo_scroll_channels_up();
2055 case MULTI_PXO_CHAN_DOWN:
2056 multi_pxo_scroll_channels_down();
2059 case MULTI_PXO_TEXT_UP:
2060 multi_pxo_scroll_chat_up();
2063 case MULTI_PXO_TEXT_DOWN:
2064 multi_pxo_scroll_chat_down();
2067 case MULTI_PXO_PLIST_UP:
2068 multi_pxo_scroll_players_up();
2069 multi_pxo_chat_adjust_start();
2072 case MULTI_PXO_PLIST_DOWN:
2073 multi_pxo_scroll_players_down();
2074 multi_pxo_chat_adjust_start();
2077 case MULTI_PXO_JOIN:
2078 // if there are no channels to join, let the user know
2079 if((Multi_pxo_channel_count == 0) || (Multi_pxo_channels == NULL)){
2080 gamesnd_play_iface(SND_GENERAL_FAIL);
2081 multi_pxo_notify_add(XSTR("No channels!",944));
2085 // if we're not already trying to join, allow this
2086 if(!SWITCHING_CHANNELS() && (Multi_pxo_channel_select != NULL)){
2087 gamesnd_play_iface(SND_USER_SELECT);
2088 multi_pxo_join_channel(Multi_pxo_channel_select);
2090 multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2091 gamesnd_play_iface(SND_GENERAL_FAIL);
2095 case MULTI_PXO_GAMES:
2096 // move to the join game screen as normally (temporary!)
2097 gameseq_post_event( GS_EVENT_MULTI_JOIN_GAME );
2100 case MULTI_PXO_JOIN_PRIV:
2101 // if we're not already trying to join, allow this
2102 if(!SWITCHING_CHANNELS()){
2103 gamesnd_play_iface(SND_USER_SELECT);
2105 // fire up the private join popup
2106 multi_pxo_priv_popup();
2108 multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2109 gamesnd_play_iface(SND_GENERAL_FAIL);
2113 case MULTI_PXO_FIND:
2114 gamesnd_play_iface(SND_USER_SELECT);
2116 // fire up the find join popup
2117 multi_pxo_find_popup();
2120 case MULTI_PXO_HELP:
2121 gamesnd_play_iface(SND_USER_SELECT);
2122 gameseq_post_event(GS_EVENT_PXO_HELP);
2125 case MULTI_PXO_PINFO:
2128 // if we have a guy selected, try and get his info
2129 if(Multi_pxo_player_select != NULL){
2130 // if we successfully got info for this guy
2131 if(multi_pxo_pinfo_get(Multi_pxo_player_select->name)){
2133 multi_pxo_pinfo_show();
2135 // if we didn't get stats for this guy.
2137 SDL_snprintf(stats, SDL_arraysize(stats), XSTR("Could not get stats for %s\n(May not be a registered pilot)", 946), Multi_pxo_player_select->name);
2138 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,stats);
2141 gamesnd_play_iface(SND_GENERAL_FAIL);
2145 case MULTI_PXO_RANKINGS:
2146 // make sure he doesn't click it too many times
2147 if((Multi_pxo_ranking_last < 0.0f) || ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_ranking_last) > MULTI_PXO_RANK_TIME) ){
2148 gamesnd_play_iface(SND_USER_SELECT);
2151 multi_pxo_url(Multi_options_g.pxo_rank_url);
2153 // mark the time down
2154 Multi_pxo_ranking_last = f2fl(timer_get_fixed_seconds());
2156 gamesnd_play_iface(SND_GENERAL_FAIL);
2160 case MULTI_PXO_MOTD:
2161 // maybe fire up the pxo motd dialog
2162 multi_pxo_motd_dialog();
2167 // condition function for popup_do_with_condition for connected to Parallax Online
2168 int mpxo_failed = 0;
2169 int multi_pxo_connect_do()
2172 char id_string[255] = "";
2173 char ip_string[255] = "";
2175 // if we already tried and failed, sit around until the user presses cancel
2177 // try and connect to the server
2180 // build the tracker id string
2181 SDL_snprintf(id_string, SDL_arraysize(id_string), "%s %s", Multi_tracker_id_string, Player->callsign);
2183 // build the ip string
2184 SDL_snprintf(ip_string, SDL_arraysize(ip_string), "%s:%d", Multi_options_g.pxo_ip, PXO_CHAT_PORT);
2186 // connect to the server
2187 ret_code = ConnectToChatServer(ip_string, Multi_pxo_nick, id_string);
2189 // give some time to the pxo api.
2190 multi_pxo_api_process();
2193 // already connected, return success
2197 // failed to connect, return fail
2200 popup_change_text(XSTR("Failed to connect to Parallax Online!", 947));
2203 // connected, return success
2216 // popup loop which does an autojoin of a public channel. Returns when the autojoin process is complete
2217 int multi_pxo_autojoin_do()
2219 pxo_channel last_channel;
2221 // if we need to autojoin, do so now
2222 if(Multi_pxo_must_autojoin){
2223 Multi_pxo_must_autojoin = 0;
2225 // if we're supposed to be using a (valid) "last" channel, do so
2226 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){
2228 memset(&last_channel, 0, sizeof(pxo_channel));
2229 last_channel.num_users = 0;
2230 SDL_strlcpy(last_channel.name, Multi_pxo_channel_last, SDL_arraysize(last_channel.name));
2233 multi_pxo_join_channel(&last_channel);
2235 nprintf(("Network","PXO : using last channel\n"));
2237 multi_pxo_autojoin();
2239 nprintf(("Network","PXO : using autojoin channel\n"));
2241 multi_pxo_get_channels();
2244 // give some time to the pxo api.
2245 multi_pxo_api_process();
2246 multi_pxo_process_common();
2248 // next value is not -1 when actually switching channels, so keep processing by returning 0.
2249 if ( SWITCHING_CHANNELS() ){
2253 // couldn't switch channel for some reason. bail out with -1
2254 if ( !ON_CHANNEL() ){
2262 // attempt to connect to Parallax Online, return success or fail
2263 int multi_pxo_connect()
2266 char join_fail_str[256];
2268 // intiialize chat api
2271 // set us to "must autojoin"
2272 Multi_pxo_must_autojoin = 1;
2274 // run the connect dialog/popup
2276 if(popup_till_condition(multi_pxo_connect_do, XSTR("&Cancel", 779), XSTR("Logging into Parallax Online",949)) == 10){
2279 // if we're going to use the "last" channel
2280 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){
2281 SDL_strlcpy(join_str, XSTR("Joining last channel (",982), SDL_arraysize(join_str));
2282 SDL_strlcat(join_str, Multi_pxo_channel_last + 1, SDL_arraysize(join_str));
2283 SDL_strlcat(join_str, ")", SDL_arraysize(join_str));
2285 SDL_strlcpy(join_fail_str, XSTR("Unable to join last channel", 983), SDL_arraysize(join_fail_str));
2287 SDL_strlcpy(join_str, XSTR("Autojoining public channel", 984), SDL_arraysize(join_str));
2288 SDL_strlcpy(join_fail_str, XSTR("Unable to autojoin public channel", 985), SDL_arraysize(join_fail_str));
2291 // once connected, we should do an autojoin before allowing the guy to continue.
2292 rval = popup_till_condition( multi_pxo_autojoin_do, XSTR("&Cancel", 779), join_str );
2297 popup( PF_USE_AFFIRMATIVE_ICON, 1, XSTR("OK", 1492), join_fail_str);
2300 // otherwise disconnect just to be safe
2301 DisconnectFromChatServer();
2302 gameseq_post_event(GS_EVENT_MAIN_MENU);
2304 // did not successfully connect
2308 // run the networking functions for the PXO API
2309 void multi_pxo_api_process()
2314 pxo_channel *lookup;
2316 // give some time to psnet
2317 PSNET_TOP_LAYER_PROCESS();
2319 // give some time to the game tracker API
2322 // give some time to the user tracker API
2325 // get any incoming text
2331 // process the chat line
2332 multi_pxo_chat_process_incoming(p);
2336 // get any incoming channel list stuff
2337 p = GetChannelList();
2340 // nprintf(("Network","%s\n",p));
2341 multi_pxo_make_channels(p);
2344 // process any chat commands
2345 cmd = GetChatCommand();
2348 switch(cmd->command)
2350 case CC_USER_JOINING:
2351 // add a user, if he doesn't already exist
2352 if(multi_pxo_find_player(cmd->data) == NULL){
2353 multi_pxo_add_player(cmd->data);
2356 // increase the player count
2358 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2360 lookup->num_users++;
2365 case CC_USER_LEAVING:
2367 multi_pxo_del_player(cmd->data);
2369 // add a text message
2370 SDL_snprintf(msg_str, SDL_arraysize(msg_str), XSTR("*** %s has left", 950), cmd->data);
2371 multi_pxo_chat_process_incoming(msg_str);
2373 // decrease the player count
2375 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2377 lookup->num_users--;
2382 case CC_DISCONNECTED:
2383 multi_pxo_handle_disconnect();
2387 multi_pxo_handle_kick();
2390 case CC_NICKCHANGED:
2391 // process a nick change
2392 multi_pxo_process_nick_change(cmd->data);
2395 case CC_YOURCHANNEL:
2396 // copy the current channel info, and unset the switching status
2397 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2398 Multi_pxo_channel_switch.num_users = -1;
2400 SetNewChatChannel(NULL);
2402 SDL_strlcpy(Multi_pxo_channel_current.name, cmd->data, SDL_arraysize(Multi_pxo_channel_current.name));
2404 // if we don't already have this guy on the list, add him
2405 pxo_channel *lookup;
2406 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2408 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2409 lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2412 // set the user count to be 0
2414 lookup->num_users = 0;
2417 // set our "last" channel to be this one
2418 SDL_strlcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name, SDL_arraysize(Multi_pxo_channel_last));
2420 // refresh current channel server count
2421 multi_pxo_channel_refresh_current();
2423 // clear the chat area
2424 // multi_pxo_chat_clear();
2431 cmd = GetChatCommand();
2434 // handle any processing details if we're currently trying to join a channel
2435 multi_pxo_handle_channel_change();
2438 // process a "nick" change event
2439 void multi_pxo_process_nick_change(char *data)
2442 player_list *lookup;
2444 // get the new string
2445 from = strtok(data," ");
2446 to = strtok(NULL,"");
2447 if((from != NULL) && (to != NULL)){
2448 lookup = multi_pxo_find_player(from);
2450 SDL_strlcpy(lookup->name, to, SDL_arraysize(lookup->name));
2452 // if this is also my nick, change it
2453 if(!SDL_strcasecmp(Multi_pxo_nick,from)){
2454 SDL_strlcpy(Multi_pxo_nick, to, SDL_arraysize(Multi_pxo_nick));
2460 // autojoin an appropriate channel
2461 void multi_pxo_autojoin()
2465 memset(&sw,0,sizeof(pxo_channel));
2467 SDL_strlcpy(sw.name, MULTI_PXO_AUTOJOIN_CHANNEL, SDL_arraysize(sw.name));
2469 // if we found a valid room, attempt to join it
2470 multi_pxo_join_channel(&sw);
2473 // does the string match the "autojoin" prefic
2474 int multi_pxo_is_autojoin(char *name)
2476 // check to see if the name is long enough
2477 if(strlen(name) < strlen(MULTI_PXO_AUTOJOIN_PREFIX)){
2481 // check to see if the first n chars match
2482 return !SDL_strncasecmp(name,MULTI_PXO_AUTOJOIN_PREFIX,strlen(MULTI_PXO_AUTOJOIN_PREFIX));
2485 // called from the game tracker API - server count update for a channel
2486 void multi_pxo_channel_count_update(char *name,int count)
2488 pxo_channel *lookup;
2490 // lookup the channel name on the normal list
2492 lookup = multi_pxo_find_channel(name,Multi_pxo_channels);
2494 lookup->num_servers = (ushort)count;
2496 nprintf(("Network","PXO : updated channel %s server count to %d\n",name,count));
2500 // status bar stuff -----------------------------------------------
2502 // set the status text
2503 void multi_pxo_set_status_text(const char *txt)
2506 SDL_strlcpy(Multi_pxo_status_text, txt, SDL_arraysize(Multi_pxo_status_text));
2508 // make sure it fits properly
2509 gr_force_fit_string(Multi_pxo_status_text, 254, Multi_pxo_status_coords[gr_screen.res][2]);
2512 // blit the status text
2513 void multi_pxo_blit_status_text()
2517 // center and draw the text
2518 if(strlen(Multi_pxo_status_text)) {
2519 gr_set_color_fast(&Color_bright);
2520 gr_get_string_size(&w, NULL, Multi_pxo_status_text);
2521 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);
2526 // channel related stuff -------------------------------------------
2528 // get a list of channels on the server
2529 void multi_pxo_get_channels()
2531 SendChatString(NOX("/list"));
2534 // clear the old channel list
2535 void multi_pxo_clear_channels()
2537 pxo_channel *moveup,*backup;
2539 // only clear a non-null list
2540 if(Multi_pxo_channels != NULL){
2542 moveup = Multi_pxo_channels;
2547 moveup = moveup->next;
2549 // free the struct itself
2552 } while(moveup != Multi_pxo_channels);
2553 Multi_pxo_channels = NULL;
2556 // head of the list of available channels
2557 Multi_pxo_channels = NULL;
2558 Multi_pxo_channel_count = 0;
2560 // item we're going to start displaying at
2561 Multi_pxo_channel_start = NULL;
2562 Multi_pxo_channel_start_index = -1;
2564 // items we've currently got selected
2565 Multi_pxo_channel_select = NULL;
2569 // parse the input string and make a list of new channels
2570 void multi_pxo_make_channels(char *chan_str)
2572 char *name_tok,*user_tok,*desc_tok;
2574 pxo_channel *lookup;
2577 nprintf(("Network","Making some channels!\n"));
2579 // clear the channel list
2580 // multi_pxo_clear_channels();
2582 // set the last get time
2583 Multi_pxo_channel_last_refresh = f2fl(timer_get_fixed_seconds());
2585 name_tok = strtok(chan_str," ");
2586 if(name_tok == NULL){
2591 // parse the user count token
2592 user_tok = strtok(NULL," ");
2594 // parse the channel description token
2595 desc_tok = strtok(NULL,"$");
2597 // something invalid in the data, return here.....
2598 if((name_tok == NULL) || (user_tok == NULL) || (desc_tok == NULL)){
2602 // get the # of users
2604 num_users = (ubyte)atoi(user_tok);
2606 // if the # of users is > 0, or its not an autojoin, place it on the display list
2607 if((num_users > 0) || !multi_pxo_is_autojoin(name_tok)){
2608 // see if it exists already, and if so, just update the user count
2609 lookup = multi_pxo_find_channel(name_tok,Multi_pxo_channels);
2612 lookup->num_users = (short)num_users;
2616 res = multi_pxo_add_channel(name_tok,&Multi_pxo_channels);
2618 //Multi_pxo_channel_count++;
2619 res->num_users = (short)num_users;
2620 SDL_strlcpy(res->desc, desc_tok, SDL_arraysize(res->desc));
2625 // get the next name token
2626 name_tok = strtok(NULL," ");
2627 } while(name_tok != NULL);
2629 // if we need to autojoin, do so now
2630 //if(Multi_pxo_must_autojoin){
2631 // Multi_pxo_must_autojoin = 0;
2633 // multi_pxo_autojoin();
2637 multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
2639 // if we haven't refreshed server counts yet, do it now
2640 if(Multi_pxo_channel_server_refresh < 0.0f){
2641 multi_pxo_channel_refresh_servers();
2644 // if we don't already have this guy on the list, add him
2646 pxo_channel *lookup;
2647 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2649 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2650 multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2655 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2656 pxo_channel *multi_pxo_add_channel(char *name,pxo_channel **list)
2658 pxo_channel *new_channel;
2660 // try and allocate a new pxo_channel struct
2661 new_channel = (pxo_channel *)malloc(sizeof(pxo_channel));
2662 if ( new_channel == NULL ) {
2663 nprintf(("Network", "Cannot allocate space for new pxo_channel structure\n"));
2666 memset(new_channel,0,sizeof(pxo_channel));
2667 // try and allocate a string for the channel name
2668 SDL_strlcpy(new_channel->name, name, SDL_arraysize(new_channel->name));
2670 // insert it on the list
2671 if ( *list != NULL ) {
2672 new_channel->next = (*list)->next;
2673 new_channel->next->prev = new_channel;
2674 (*list)->next = new_channel;
2675 new_channel->prev = *list;
2677 *list = new_channel;
2678 (*list)->next = (*list)->prev = *list;
2681 Multi_pxo_channel_count++;
2685 // lookup a channel with the specified name
2686 pxo_channel *multi_pxo_find_channel(char *name,pxo_channel *list)
2688 pxo_channel *moveup;
2690 // look the sucker up
2696 if(!SDL_strcasecmp(name,moveup->name)){
2700 moveup = moveup->next;
2701 } while((moveup != list) && (moveup != NULL));
2706 // process the channel list (select, etc)
2707 void multi_pxo_process_channels()
2712 // if we don't have a start item, but the list is non-null
2713 if((Multi_pxo_channel_start == NULL) && (Multi_pxo_channels != NULL)){
2714 Multi_pxo_channel_start = Multi_pxo_channels;
2715 Multi_pxo_channel_start_index = 0;
2718 // if we don't have a selected item, but the list is non-null
2719 if((Multi_pxo_channel_select == NULL) && (Multi_pxo_channels != NULL)){
2720 Multi_pxo_channel_select = Multi_pxo_channels;
2723 multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2726 // if the "switch" delay timestamp is set, see if it has expired
2727 if((Multi_pxo_switch_delay != -1) && timestamp_elapsed(Multi_pxo_switch_delay)){
2728 Multi_pxo_switch_delay = -1;
2731 // see if we have a mouse click on the channel region
2732 if(Multi_pxo_channel_button.pressed()){
2733 Multi_pxo_channel_button.get_mouse_pos(NULL,&my);
2735 // index from the top
2736 item_index = my / 10;
2738 // select the item if possible
2739 if((item_index + Multi_pxo_channel_start_index) < Multi_pxo_channel_count){
2740 Multi_pxo_channel_select = Multi_pxo_channel_start;
2741 for(idx=0;idx<item_index;idx++){
2742 Multi_pxo_channel_select = Multi_pxo_channel_select->next;
2746 multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2750 // last refresh time
2751 if((Multi_pxo_channel_last_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_last_refresh) > CHANNEL_REFRESH_TIME) ){
2753 multi_pxo_set_status_text(XSTR("Refreshing Public Channel List",952));
2755 // get a list of channels on the server (clear any old list as well)
2756 multi_pxo_get_channels();
2759 Multi_pxo_channel_last_refresh = -1.0f;
2761 nprintf(("Network","Refreshing channels\n"));
2764 // if we haven't updated our server channel counts in a while, do so again
2765 // last refresh time
2766 if((Multi_pxo_channel_server_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_server_refresh) > CHANNEL_SERVER_REFRESH_TIME) ){
2767 // refresh server counts
2768 // multi_pxo_set_status_text("Refreshing Public Channel Server Counts");
2770 // do it _NOW_ I"M RIGHT HERE KILL ME WHAT ARE YOU WAITING FOR DO IT KILL ME DO IT NOW!
2771 multi_pxo_channel_refresh_servers();
2775 // send a request to refresh our channel server counts
2776 void multi_pxo_channel_refresh_servers()
2778 pxo_channel *lookup;
2779 filter_game_list_struct filter;
2781 // traverse the list of existing channels we know about and query the game tracker about them
2782 lookup = Multi_pxo_channels;
2787 if(strlen(lookup->name)){
2789 memset(&filter,0,sizeof(filter_game_list_struct));
2790 SDL_strlcpy(filter.channel, lookup->name, SDL_arraysize(filter.channel));
2793 RequestGameCountWithFilter(&filter);
2797 lookup = lookup->next;
2798 } while((lookup != NULL) && (lookup != Multi_pxo_channels));
2801 Multi_pxo_channel_server_refresh = f2fl(timer_get_fixed_seconds());
2804 // refresh current channel server count
2805 void multi_pxo_channel_refresh_current()
2807 // send a request for a server count on this channel
2808 if(strlen(Multi_pxo_channel_current.name)){
2810 filter_game_list_struct filter;
2811 memset(&filter,0,sizeof(filter_game_list_struct));
2812 SDL_strlcpy(filter.channel, Multi_pxo_channel_current.name, SDL_arraysize(filter.channel));
2815 RequestGameCountWithFilter(&filter);
2819 // display the channel list
2820 void multi_pxo_blit_channels()
2822 pxo_channel *moveup;
2823 char chan_name[255];
2824 char chan_users[15];
2825 char chan_servers[15];
2826 int user_w,server_w;
2827 int disp_count,y_start;
2829 // blit as many channels as we can
2831 y_start = Multi_pxo_chan_coords[gr_screen.res][1];
2832 moveup = Multi_pxo_channel_start;
2837 // if this is the currently selected item, highlight it
2838 if(moveup == Multi_pxo_channel_select){
2839 gr_set_color_fast(&Color_bright);
2841 // otherwise draw it normally
2843 gr_set_color_fast(&Color_normal);
2846 // get the # of users on the channel
2847 SDL_snprintf(chan_users, SDL_arraysize(chan_users), "%d", moveup->num_users);
2849 // get the width of the user count string
2850 gr_get_string_size(&user_w, NULL, chan_users);
2852 // get the # of servers on the channel
2853 SDL_snprintf(chan_servers, SDL_arraysize(chan_servers), "%d", moveup->num_servers);
2855 // get the width of the user count string
2856 gr_get_string_size(&server_w, NULL, chan_servers);
2858 // make sure the name fits
2859 SDL_assert(moveup->name);
2860 SDL_strlcpy(chan_name, moveup->name, SDL_arraysize(chan_name));
2861 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]);
2864 gr_string(Multi_pxo_chan_coords[gr_screen.res][0], y_start, chan_name + 1);
2865 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);
2866 gr_set_color_fast(&Color_bright);
2867 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);
2869 // increment the displayed count
2874 moveup = moveup->next;
2875 } while((moveup != Multi_pxo_channels) && (disp_count < Multi_pxo_max_chan_display[gr_screen.res]));
2878 // scroll channel list up
2879 void multi_pxo_scroll_channels_up()
2881 // if we're already at the head of the list, do nothing
2882 if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start == Multi_pxo_channels)){
2883 gamesnd_play_iface(SND_GENERAL_FAIL);
2887 // otherwise move up one
2888 Multi_pxo_channel_start = Multi_pxo_channel_start->prev;
2889 Multi_pxo_channel_start_index--;
2890 gamesnd_play_iface(SND_USER_SELECT);
2893 // scroll channel list down
2894 void multi_pxo_scroll_channels_down()
2896 // if we're already at the tail of the list, do nothing
2897 if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start->next == Multi_pxo_channels)){
2898 gamesnd_play_iface(SND_GENERAL_FAIL);
2902 // if we can't scroll further without going past the end of the viewable list, don't
2903 if((Multi_pxo_channel_start_index + Multi_pxo_max_chan_display[gr_screen.res]) >= Multi_pxo_channel_count){
2904 gamesnd_play_iface(SND_GENERAL_FAIL);
2908 // otherwise move down one
2909 Multi_pxo_channel_start = Multi_pxo_channel_start->next;
2910 Multi_pxo_channel_start_index++;
2911 gamesnd_play_iface(SND_USER_SELECT);
2914 // attempt to join a channel
2915 void multi_pxo_join_channel(pxo_channel *chan)
2917 char switch_msg[256];
2919 // if we're already on this channel, do nothing
2920 if(ON_CHANNEL() && !SDL_strcasecmp(chan->name,Multi_pxo_channel_current.name)){
2924 // if we're already trying to join a channel, do nothing
2925 if(SWITCHING_CHANNELS()){
2929 // try and join the channel
2930 switch(SetNewChatChannel(chan->name)){
2936 // decrement the count of our current channel
2937 pxo_channel *lookup;
2938 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2940 lookup->num_users--;
2943 // set our current channel as none
2944 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2945 Multi_pxo_channel_current.num_users = -1;
2947 multi_pxo_set_status_text(XSTR("Switching channels",953));
2950 memcpy(&Multi_pxo_channel_switch,chan,sizeof(pxo_channel));
2952 // clear the player list
2953 multi_pxo_clear_players();
2955 // display a line of text indicating that we're switching channels
2956 if(strlen(Multi_pxo_channel_switch.name) > 1){
2957 SDL_snprintf(switch_msg, SDL_arraysize(switch_msg), "[Switching to channel %s]", Multi_pxo_channel_switch.name + 1);
2959 SDL_snprintf(switch_msg, SDL_arraysize(switch_msg), "[Switching to channel %s]", Multi_pxo_channel_switch.name);
2961 multi_pxo_chat_process_incoming(switch_msg, CHAT_MODE_CHANNEL_SWITCH);
2969 // handle any processing details if we're currently trying to join a channel
2970 void multi_pxo_handle_channel_change()
2972 // if we're not switching channels, do nothing
2973 if(!SWITCHING_CHANNELS()){
2977 // if we are, check the status
2978 switch(SetNewChatChannel(NULL)){
2981 // unset our switching struct
2982 memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
2983 Multi_pxo_channel_switch.num_users = -1;
2986 multi_pxo_set_status_text(XSTR("No channel (error while switching)",954));
2993 // successfully changed
2995 // copy the current channel info, and unset the switching status
2996 memcpy(&Multi_pxo_channel_current,&Multi_pxo_channel_switch,sizeof(pxo_channel));
2997 Multi_pxo_channel_switch.num_users = -1;
2999 // set our "last" channel
3000 SDL_strlcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name, SDL_arraysize(Multi_pxo_channel_last));
3003 multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
3005 // if we don't already have this guy on the list, add him
3006 pxo_channel *lookup;
3007 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
3009 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
3010 lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
3013 // set the user count to be 1 (just me)
3015 lookup->num_users = 1;
3018 // clear the chat area
3019 // multi_pxo_chat_clear();
3021 // set the "switch" delay timestamp
3022 Multi_pxo_switch_delay = timestamp(MULTI_PXO_SWITCH_DELAY_TIME);
3024 // refresh current channel server count
3025 multi_pxo_channel_refresh_current();
3031 // player related stuff -------------------------------------------
3033 // clear the old player list
3034 void multi_pxo_clear_players()
3036 player_list *moveup,*backup;
3038 // if the list is null, don't free it up
3039 if(Multi_pxo_players != NULL){
3041 moveup = Multi_pxo_players;
3046 moveup = moveup->next;
3048 // free the struct itself
3051 } while(moveup != Multi_pxo_players);
3052 Multi_pxo_players = NULL;
3056 Multi_pxo_player_start = NULL;
3057 // Multi_pxo_player_start_index = -1;
3058 Multi_pxo_player_select = NULL;
3061 // Multi_pxo_player_slider.set_numberItems(0);
3063 // add a bunch of bogus players
3066 for(int idx=0;idx<30;idx++){
3067 sprintf(str,"player%d",idx);
3068 multi_pxo_add_player(str);
3073 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
3074 player_list *multi_pxo_add_player(char *name)
3076 player_list *new_player;
3078 // try and allocate a new player_list struct
3079 new_player = (player_list *)malloc(sizeof(player_list));
3080 if ( new_player == NULL ) {
3081 nprintf(("Network", "Cannot allocate space for new player_list structure\n"));
3084 // try and allocate a string for the channel name
3085 SDL_strlcpy(new_player->name, name, SDL_arraysize(new_player->name));
3087 // insert it on the list
3088 if ( Multi_pxo_players != NULL ) {
3089 new_player->next = Multi_pxo_players->next;
3090 new_player->next->prev = new_player;
3091 Multi_pxo_players->next = new_player;
3092 new_player->prev = Multi_pxo_players;
3094 Multi_pxo_players = new_player;
3095 Multi_pxo_players->next = Multi_pxo_players->prev = Multi_pxo_players;
3098 // increment the start position
3099 if(Multi_pxo_player_start != NULL){
3100 // Multi_pxo_player_start_index++;
3104 Multi_pxo_player_count++;
3106 // update the count on the slider
3107 // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3112 // remove a player with the given name
3113 void multi_pxo_del_player(char *name)
3115 player_list *lookup;
3117 // try and find this guy
3118 lookup = Multi_pxo_players;
3123 // if we found a match, delete it
3124 if(!SDL_strcasecmp(name,lookup->name)){
3125 // if this is the only item on the list, free stuff up
3126 if(lookup->next == lookup){
3127 SDL_assert(lookup == Multi_pxo_players);
3129 Multi_pxo_players = NULL;
3130 multi_pxo_clear_players();
3132 // otherwise, just delete it
3134 lookup->next->prev = lookup->prev;
3135 lookup->prev->next = lookup->next;
3137 // if this was our selected item, unselect it
3138 if((lookup == Multi_pxo_player_select) && (Multi_pxo_player_select != NULL)){
3139 Multi_pxo_player_select = Multi_pxo_player_select->next;
3142 // if this was our point to start viewing from, select another
3143 if(lookup == Multi_pxo_player_start){
3144 // if this is the head of the list, move up one
3145 if(Multi_pxo_players == lookup){
3146 Multi_pxo_player_start = Multi_pxo_player_start->next;
3147 // Multi_pxo_player_start_index = 0;
3149 // otherwise move back
3151 Multi_pxo_player_start = Multi_pxo_player_start->prev;
3152 // Multi_pxo_player_start_index++;
3156 // if this is the head of the list, move it up
3157 if(lookup == Multi_pxo_players){
3158 Multi_pxo_players = Multi_pxo_players->next;
3162 lookup->next = NULL;
3163 lookup->prev = NULL;
3168 Multi_pxo_player_count--;
3169 SDL_assert(Multi_pxo_player_count >= 0);
3171 // update the count on the slider
3172 // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3173 // Multi_pxo_player_slider.force_currentItem(Multi_pxo_player_start_index);
3180 lookup = lookup->next;
3181 } while((lookup != NULL) && (lookup != Multi_pxo_players));
3184 // try and find a player with the given name, return a pointer to his entry (or NULL)
3185 player_list *multi_pxo_find_player(char *name)
3187 player_list *lookup;
3189 // look through all players
3190 lookup = Multi_pxo_players;
3195 if(!SDL_strcasecmp(name,lookup->name)){
3199 lookup = lookup->next;
3200 } while((lookup != NULL) && (lookup != Multi_pxo_players));
3206 // process the player list (select, etc)
3207 void multi_pxo_process_players()
3210 player_list *lookup;
3212 // if we don't have a start item, but the list is non-null
3213 if((Multi_pxo_player_start == NULL) && (Multi_pxo_players != NULL)){
3214 Multi_pxo_player_start = Multi_pxo_players;
3215 // Multi_pxo_player_start_index = 0;
3217 // update the slider
3218 // Multi_pxo_player_slider.set_currentItem(Multi_pxo_player_start_index);
3221 // if we don't have a selected item, but the list is non-null
3222 if((Multi_pxo_player_select == NULL) && (Multi_pxo_players != NULL)){
3223 Multi_pxo_player_select = Multi_pxo_players;
3226 // see if we have a mouse click on the channel region
3227 if(Multi_pxo_player_button.pressed()){
3228 Multi_pxo_player_button.get_mouse_pos(NULL,&my);
3230 // index from the top
3231 item_index = my / 10;
3233 // select the item if possible
3234 lookup = Multi_pxo_player_start;
3239 if(item_index == 0){
3240 Multi_pxo_player_select = Multi_pxo_player_start;
3244 // move to the next item
3245 lookup = lookup->next;
3248 // if this item is our guy
3249 if((item_index == 0) && (lookup != Multi_pxo_players)){
3250 Multi_pxo_player_select = lookup;
3253 } while((lookup != Multi_pxo_players) && (item_index > 0));
3257 // display the player list
3258 void multi_pxo_blit_players()
3260 player_list *moveup;
3261 char player_name[255];
3262 int disp_count,y_start;
3264 // blit as many channels as we can
3266 y_start = Multi_pxo_player_coords[gr_screen.res][1];
3267 moveup = Multi_pxo_player_start;
3272 // if this is the currently selected item, highlight it
3273 if(moveup == Multi_pxo_player_select){
3274 gr_set_color_fast(&Color_bright);
3276 // otherwise draw it normally
3278 gr_set_color_fast(&Color_normal);
3281 // make sure the string fits
3282 SDL_strlcpy(player_name, moveup->name, SDL_arraysize(player_name));
3283 gr_force_fit_string(player_name, 254, Multi_pxo_player_coords[gr_screen.res][2]);
3286 gr_string(Multi_pxo_player_coords[gr_screen.res][0], y_start, player_name);
3288 // increment the displayed count
3293 moveup = moveup->next;
3294 } while((moveup != Multi_pxo_players) && (disp_count < Multi_pxo_max_player_display[gr_screen.res]));
3297 // scroll player list up
3298 void multi_pxo_scroll_players_up()
3300 // if we're already at the head of the list, do nothing
3301 if((Multi_pxo_player_start == NULL) || (Multi_pxo_player_start == Multi_pxo_players)){
3302 gamesnd_play_iface(SND_GENERAL_FAIL);
3306 // otherwise move up one
3307 Multi_pxo_player_start = Multi_pxo_player_start->prev;
3308 // Multi_pxo_player_start_index--;
3309 // SDL_assert(Multi_pxo_player_start_index >= 0);
3311 gamesnd_play_iface(SND_USER_SELECT);
3314 // scroll player list down
3315 void multi_pxo_scroll_players_down()
3317 player_list *lookup;
3320 // see if its okay to scroll down
3321 lookup = Multi_pxo_player_start;
3322 if(lookup == NULL ){
3323 gamesnd_play_iface(SND_GENERAL_FAIL);
3327 while(lookup->next != Multi_pxo_players){
3328 lookup = lookup->next;
3332 // if we can move down
3333 if(count >= Multi_pxo_max_player_display[gr_screen.res]){
3334 Multi_pxo_player_start = Multi_pxo_player_start->next;
3336 // Multi_pxo_player_start_index++;
3338 gamesnd_play_iface(SND_USER_SELECT);
3340 gamesnd_play_iface(SND_GENERAL_FAIL);
3345 // chat text stuff -----------------------------------------
3347 // initialize and create the chat text linked list
3348 void multi_pxo_chat_init()
3351 chat_line *new_line;
3354 Multi_pxo_chat = NULL;
3355 Multi_pxo_chat_add = NULL;
3356 Multi_pxo_chat_start = NULL;
3357 Multi_pxo_chat_start_index = -1;
3359 // create the lines in a non-circular doubly linked list
3360 for(idx=0;idx<MAX_CHAT_LINES;idx++){
3361 new_line = (chat_line*)malloc(sizeof(chat_line));
3363 // clear the line out
3364 SDL_assert(new_line != NULL);
3365 if(new_line == NULL){
3368 memset(new_line,0,sizeof(chat_line));
3369 new_line->prev = NULL;
3370 new_line->next = NULL;
3372 // insert it into the (empty) list
3373 if(Multi_pxo_chat == NULL){
3374 Multi_pxo_chat = new_line;
3376 // insert it onto the (non-empty) list
3378 Multi_pxo_chat->prev = new_line;
3379 new_line->next = Multi_pxo_chat;
3380 Multi_pxo_chat = new_line;
3384 // start adding chat lines at the beginning of the list
3385 Multi_pxo_chat_add = Multi_pxo_chat;
3388 // free up all chat list stuff
3389 void multi_pxo_chat_free()
3391 chat_line *moveup, *backup;
3393 // free all items up
3394 moveup = Multi_pxo_chat;
3395 while(moveup != NULL){
3397 moveup = moveup->next;
3403 Multi_pxo_chat = NULL;
3404 Multi_pxo_chat_add = NULL;
3405 Multi_pxo_chat_start = NULL;
3406 Multi_pxo_chat_start_index = -1;
3407 Multi_pxo_chat_count = 0;
3408 Multi_pxo_chat_slider.set_numberItems(0);
3411 // clear all lines of chat text in the chat area
3412 void multi_pxo_chat_clear()
3416 // clear the text in all the lines
3417 moveup = Multi_pxo_chat;
3418 while(moveup != NULL){
3419 SDL_zero(moveup->text);
3420 moveup = moveup->next;
3423 // how many chat lines we have
3424 Multi_pxo_chat_count = 0;
3426 // start adding chat lines at the beginning of the list
3427 Multi_pxo_chat_add = Multi_pxo_chat;
3430 // add a line of text
3431 void multi_pxo_chat_add_line(char *txt, int mode)
3436 SDL_assert(Multi_pxo_chat_add != NULL);
3437 SDL_strlcpy(Multi_pxo_chat_add->text, txt, SDL_arraysize(Multi_pxo_chat_add->text));
3438 Multi_pxo_chat_add->mode = mode;
3440 // if we're at the end of the list, move the front item down
3441 if(Multi_pxo_chat_add->next == NULL) {
3442 // store the new "head" of the list
3443 temp = Multi_pxo_chat->next;
3445 // move the current head to the end of the list
3446 Multi_pxo_chat_add->next = Multi_pxo_chat;
3448 Multi_pxo_chat->prev = Multi_pxo_chat_add;
3449 Multi_pxo_chat->next = NULL;
3451 // reset the head of the list
3452 Multi_pxo_chat = temp;
3454 // set the new add line
3455 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3456 SDL_zero(Multi_pxo_chat_add->text);
3457 Multi_pxo_chat_add->mode = CHAT_MODE_NORMAL;
3459 // if we're not at the end of the list, just move up by one
3461 // set the new add line
3462 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3465 // if we've reached max chat lines, don't increment
3466 if(Multi_pxo_chat_count < MAX_CHAT_LINES) {
3467 Multi_pxo_chat_count++;
3471 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
3473 // force the position, in case we arent at the bottom of the list
3476 // move to the bottom of the chat area
3479 multi_pxo_goto_bottom();
3482 // if we have more than the # of lines displayable
3483 if(Multi_pxo_chat_count >= MULTI_PXO_MAX_CHAT_DISPLAY){
3485 multi_pxo_goto_bottom();
3488 multi_pxo_goto_bottom();
3491 // process an incoming line of text
3492 void multi_pxo_chat_process_incoming(const char *txt,int mode)
3494 char msg_total[512],line[512];
3497 char *p_str[20]; // the initial line (unindented)
3498 const char *priv_ptr = NULL;
3500 // filter out "has left" channel messages, when switching channels
3501 if((SWITCHING_CHANNELS() || ((Multi_pxo_switch_delay != -1) && !timestamp_elapsed(Multi_pxo_switch_delay))) &&
3502 multi_pxo_chat_is_left_message(txt)){
3506 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3507 priv_ptr = multi_pxo_chat_is_private(txt);
3508 if(priv_ptr != NULL){
3509 SDL_strlcpy(msg_total, priv_ptr, SDL_arraysize(msg_total));
3511 SDL_strlcpy(msg_total, txt, SDL_arraysize(msg_total));
3514 // determine what mode to display this text in
3516 // if this is private chat
3517 if(priv_ptr != NULL){
3518 mode = CHAT_MODE_PRIVATE;
3522 // if this is a server message
3523 if(multi_pxo_is_server_text(txt)){
3524 mode = CHAT_MODE_SERVER;
3526 // if this is a MOTD
3527 else if(multi_pxo_is_motd_text(txt)){
3528 // mode = CHAT_MODE_MOTD;
3530 multi_pxo_motd_add_text(txt);
3533 // if this is the end of motd text
3534 else if(multi_pxo_is_end_of_motd_text(txt)){
3535 multi_pxo_set_end_of_motd();
3540 // split the text up into as many lines as necessary
3541 n_lines = split_str(msg_total, Multi_pxo_chat_coords[gr_screen.res][2] - 5, n_chars, p_str, 3);
3542 SDL_assert((n_lines != -1) && (n_lines <= 20));
3543 if((n_lines < 0) || (n_lines > 20)) {
3547 // if the string fits on one line
3549 multi_pxo_chat_add_line(msg_total,mode);
3551 // don't pad with extra spaces if its from the server
3553 if(mode != CHAT_MODE_SERVER){
3554 multi_pxo_chat_add_line("",CHAT_MODE_NORMAL);
3558 // if the string was split into multiple lines
3560 // add the first line
3561 memcpy(line,p_str[0],n_chars[0]);
3562 line[n_chars[0]] = '\0';
3563 multi_pxo_chat_add_line(line,mode);
3565 // copy the rest of the lines
3566 for(idx=1; idx<n_lines; idx++){
3567 memcpy(line,p_str[idx],n_chars[idx]);
3568 line[n_chars[idx]] = '\0';
3570 // unless the current mode is server or "switching channels", make all these CHAT_MODE_CARRY
3571 if((mode != CHAT_MODE_SERVER) && (mode != CHAT_MODE_CHANNEL_SWITCH)){
3572 mode = CHAT_MODE_CARRY;
3574 multi_pxo_chat_add_line(line, mode);
3579 // blit the chat text
3580 void multi_pxo_chat_blit()
3583 int disp_count,token_width;
3589 // blit the title line
3591 if(strlen(Multi_pxo_channel_current.name) > 1){
3592 SDL_snprintf(title, SDL_arraysize(title), XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name+1); // [[ <who> on <channel> ]]
3594 SDL_snprintf(title, SDL_arraysize(title), XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name); // [[ <who> on <channel> ]]
3597 SDL_strlcpy(title, XSTR("Parallax Online - No Channel", 956), SDL_arraysize(title));
3599 gr_force_fit_string(title, 254, Multi_pxo_chat_coords[gr_screen.res][2] - 10);
3600 gr_get_string_size(&token_width,NULL,title);
3601 gr_set_color_fast(&Color_normal);
3602 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);
3604 // blit all active lines of text
3605 moveup = Multi_pxo_chat_start;
3607 y_start = Multi_pxo_chat_coords[gr_screen.res][1];
3608 while((moveup != NULL) && (moveup != Multi_pxo_chat_add) && (disp_count < (Multi_pxo_max_chat_display[gr_screen.res]))){
3609 switch(moveup->mode){
3610 // if this is text from the server, display it all "bright"
3611 case CHAT_MODE_SERVER:
3612 gr_set_color_fast(&Color_bright);
3613 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3616 // if this is motd, display it all "bright"
3617 case CHAT_MODE_MOTD:
3618 gr_set_color_fast(&Color_bright_white);
3619 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3622 // normal mode, just highlight the server
3623 case CHAT_MODE_PRIVATE:
3624 case CHAT_MODE_NORMAL:
3625 SDL_strlcpy(piece, moveup->text, SDL_arraysize(piece));
3626 tok = strtok(piece," ");
3628 // get the width of just the first "piece"
3629 gr_get_string_size(&token_width, NULL, tok);
3632 gr_set_color_fast(&Color_bright);
3633 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, tok);
3635 // draw the rest of the string normally
3636 tok = strtok(NULL,"");
3638 gr_set_color_fast(&Color_normal);
3639 gr_string(Multi_pxo_chat_coords[gr_screen.res][0] + token_width + 6, y_start, tok);
3644 // carry mode, display with no highlight
3645 case CHAT_MODE_CARRY:
3646 gr_set_color_fast(&Color_normal);
3647 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3650 // "switching channels mode", display it bright
3651 case CHAT_MODE_CHANNEL_SWITCH:
3652 gr_set_color_fast(&Color_bright);
3653 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3658 moveup = moveup->next;
3663 if ((moveup != Multi_pxo_chat_add) && (moveup != NULL)) {
3664 Can_scroll_down = 1;
3666 Can_scroll_down = 0;
3670 // scroll to the very bottom of the chat area
3671 void multi_pxo_goto_bottom()
3676 if (Multi_pxo_chat == NULL) {
3680 // if we have less than the displayable amount of lines, do nothing
3681 if(Multi_pxo_chat_count <= Multi_pxo_max_chat_display[gr_screen.res]){
3682 Multi_pxo_chat_start = Multi_pxo_chat;
3684 // nothing to do for the slider
3685 Multi_pxo_chat_slider.set_numberItems(0);
3689 if (!Can_scroll_down)
3691 // otherwise move back the right # of items
3692 backup = Multi_pxo_chat_add;
3693 for(idx=0; idx<Multi_pxo_max_chat_display[gr_screen.res]; idx++){
3694 SDL_assert(backup->prev != NULL);
3695 backup = backup->prev;
3698 Multi_pxo_chat_start = backup;
3700 // fixup the start index
3701 multi_pxo_chat_adjust_start();
3705 // scroll the text up
3706 void multi_pxo_scroll_chat_up()
3708 // if we're already at the top of the list, don't do anything
3709 if ((Multi_pxo_chat_start == NULL) || (Multi_pxo_chat_start == Multi_pxo_chat)) {
3710 gamesnd_play_iface(SND_GENERAL_FAIL);
3714 // otherwise move up one
3715 Multi_pxo_chat_start = Multi_pxo_chat_start->prev;
3717 multi_pxo_chat_adjust_start();
3719 gamesnd_play_iface(SND_USER_SELECT);
3722 // returns 1 if we can scroll down, 0 otherwise
3723 int multi_pxo_can_scroll_down()
3728 // see if its okay to scroll down
3729 lookup = Multi_pxo_chat_start;
3730 if (lookup == NULL) {
3731 // gamesnd_play_iface(SND_GENERAL_FAIL);
3735 while (lookup != Multi_pxo_chat_add) {
3736 lookup = lookup->next;
3740 // check if we can move down, return accordingly
3741 if (count > Multi_pxo_max_chat_display[gr_screen.res]) {
3748 // scroll the text down
3749 void multi_pxo_scroll_chat_down()
3751 // if we can move down
3752 if (multi_pxo_can_scroll_down()) {
3753 Multi_pxo_chat_start = Multi_pxo_chat_start->next;
3754 multi_pxo_chat_adjust_start();
3755 gamesnd_play_iface(SND_USER_SELECT);
3757 gamesnd_play_iface(SND_GENERAL_FAIL);
3761 // process chat controls
3762 void multi_pxo_chat_process()
3764 char *remainder = NULL;
3765 const char *result = NULL;
3767 int msg_pixel_width;
3769 // if the chat line is getting too long, fire off the message, putting the last
3770 // word on the next input line.
3772 Multi_pxo_chat_input.get_text(msg);
3774 // determine if the width of the string in pixels is > than the inputbox width -- if so,
3775 // then send the message
3776 gr_get_string_size(&msg_pixel_width, NULL, msg);
3777 // if ( msg_pixel_width >= (Chatbox_inputbox_w - Player->short_callsign_width) ) {
3778 if ( msg_pixel_width >= (Multi_pxo_input_coords[gr_screen.res][2])) {
3779 remainder = strrchr(msg, ' ');
3785 // if we're connected to a channel, send the chat to the server
3787 result = SendChatString(msg,1);
3789 multi_pxo_chat_process_incoming(result);
3792 // display any remainder of text on the next line
3793 Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3795 Multi_pxo_chat_input.set_text("");
3797 } else if((Multi_pxo_chat_input.pressed() && (strlen(msg) > 0)) || (strlen(msg) >= MAX_CHAT_LINE_LEN)) {
3798 // tack on the null terminator in the boundary case
3799 int x = strlen(msg);
3800 if(x >= MAX_CHAT_LINE_LEN){
3801 msg[MAX_CHAT_LINE_LEN-1] = '\0';
3804 // ignore "/nick" commands
3805 if(multi_pxo_is_nick_command(msg)){
3806 Multi_pxo_chat_input.set_text("");
3810 // send the chat to the server
3811 // if we're connected to a channel, send the chat to the server
3813 result = SendChatString(msg,1);
3815 multi_pxo_chat_process_incoming(result);
3818 // display any remainder of text on the next line
3819 Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3821 Multi_pxo_chat_input.set_text("");
3826 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3829 // NOTE : DO NOT LOCALIZE THESE STRINGS!!!! THEY ARE CONSTANTS WHICH ARE CHECKED AGAINST
3830 // PXO CHAT SERVER DATA. THEY CANNOT CHANGE!!!
3831 #define PMSG_FROM "private message from "
3832 #define PMSG_TO "private message to "
3833 const char *multi_pxo_chat_is_private(const char *txt)
3836 if( strlen(txt) > strlen( PMSG_FROM ) ){
3837 // otherwise do a comparison
3838 if(!SDL_strncasecmp( txt, PMSG_FROM, strlen(PMSG_FROM) )){
3839 return &txt[strlen( PMSG_FROM )];
3844 if(strlen(txt) > strlen( PMSG_TO )){
3845 // otherwise do a comparison
3846 if(!SDL_strncasecmp(txt,PMSG_TO,strlen(PMSG_TO))){
3847 return &txt[strlen(PMSG_TO)];
3855 // if the text came from the server
3856 int multi_pxo_is_server_text(const char *txt)
3858 // if the message is prefaced by a ***
3859 if((strlen(txt) >= strlen(MULTI_PXO_SERVER_PREFIX)) && !strncmp(txt, MULTI_PXO_SERVER_PREFIX, strlen(MULTI_PXO_SERVER_PREFIX))){
3866 // if the text is message of the day text
3867 int multi_pxo_is_motd_text(const char *txt)
3869 // if we're not on a channel, and this is not a channel switching message assume its coming from a server
3870 if((strlen(txt) >= strlen(PXO_CHAT_MOTD_PREFIX)) && !strncmp(txt, PXO_CHAT_MOTD_PREFIX, strlen(PXO_CHAT_MOTD_PREFIX))){
3877 // if the text is the end of motd text
3878 int multi_pxo_is_end_of_motd_text(const char *txt)
3880 // if we're not on a channel, and this is not a channel switching message assume its coming from a server
3881 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))){
3888 // if the text is a "has left message" from the server
3889 int multi_pxo_chat_is_left_message(const char *txt)
3891 char last_portion[100];
3893 // if the text is not server text
3894 if(!multi_pxo_is_server_text(txt)){
3898 // check to see if the last portion is the correct wording
3899 SDL_zero(last_portion);
3900 if((strlen(txt) > strlen(MULTI_PXO_HAS_LEFT)) && !strcmp(&txt[strlen(txt) - strlen(MULTI_PXO_HAS_LEFT)], MULTI_PXO_HAS_LEFT)){
3904 // check the end of the line
3908 // recalculate the chat start index, and adjust the slider properly
3909 void multi_pxo_chat_adjust_start()
3913 // if we have no chat
3914 if (Multi_pxo_chat == NULL) {
3915 Multi_pxo_chat_start_index = -1;
3920 Multi_pxo_chat_start_index = 0;
3921 moveup = Multi_pxo_chat;
3922 while((moveup != Multi_pxo_chat_start) && (moveup != NULL)){
3923 Multi_pxo_chat_start_index++;
3924 moveup = moveup->next;
3927 // set the slider index
3928 Multi_pxo_chat_slider.force_currentItem(Multi_pxo_chat_start_index);
3931 // motd stuff ---------------------------------------------------------
3933 // initialize motd when going into this screen
3934 void multi_pxo_motd_init()
3936 // zero the motd string
3937 SDL_strlcpy(Pxo_motd, "", SDL_arraysize(Pxo_motd));
3939 // haven't gotten it yet
3942 // haven't read it yet either
3946 // set the motd text
3947 void multi_pxo_motd_add_text(const char *text)
3949 int cur_len = strlen(Pxo_motd);
3957 // make sure its motd text
3958 SDL_assert(multi_pxo_is_motd_text(text));
3959 if(!multi_pxo_is_motd_text(text)){
3963 // if its a 0 line motd
3964 if(strlen(text) <= strlen(PXO_CHAT_MOTD_PREFIX)){
3968 // add text to the motd
3969 new_len = strlen(text + strlen(PXO_CHAT_MOTD_PREFIX)) - 1;
3970 if((cur_len + new_len + 1) < MAX_PXO_MOTD_LEN){
3971 SDL_strlcat(Pxo_motd, text + strlen(PXO_CHAT_MOTD_PREFIX) + 1, SDL_arraysize(Pxo_motd));
3972 SDL_strlcat(Pxo_motd, "\n", SDL_arraysize(Pxo_motd));
3973 mprintf(("MOTD ADD : %s\n", Pxo_motd));
3978 void multi_pxo_set_end_of_motd()
3983 mprintf(("MOTD ALL : %s\n", Pxo_motd));
3987 // do we have an old MOTD file laying around? If so, read it in and see if its the same
3991 // checksum the current motd
3992 new_chksum = cf_add_chksum_long(0, Pxo_motd, strlen(Pxo_motd));
3994 // checksum the old motd if its lying around
3995 CFILE *in = cfopen("oldmotd.txt", "rb");
3997 // read the old checksum
3998 old_chksum = cfread_uint(in);
4001 // same checksum? no blink
4002 if(new_chksum == old_chksum){
4007 // write out the motd for next time
4008 if(strlen(Pxo_motd)){
4009 CFILE *out = cfopen("oldmotd.txt", "wb", CFILE_NORMAL, CF_TYPE_DATA);
4011 // write all the text
4012 cfwrite_uint(new_chksum, out);
4014 // close the outfile
4019 // set the blink stamp
4020 Pxo_motd_blink_stamp = -1;
4022 Pxo_motd_blink_on = 0;
4023 if(!Pxo_motd_blinked_already){
4024 Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4025 Pxo_motd_blink_on = 1;
4029 Pxo_motd_blinked_already = 1;
4032 // display the motd dialog
4033 void multi_pxo_motd_dialog()
4035 // mark the motd as read
4038 // simple popup, with a slider
4039 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, Pxo_motd);
4042 // call to maybe blink the motd button
4043 void multi_pxo_motd_maybe_blit()
4045 // if we got the end of the motd, and he hasn't read it yet
4046 if(Pxo_motd_end && !Pxo_motd_read && (Pxo_motd_blink_stamp != -1)){
4047 // if the timestamp elapsed, flip the blink flag
4048 if(timestamp_elapsed(Pxo_motd_blink_stamp)){
4049 Pxo_motd_blink_on = !Pxo_motd_blink_on;
4050 Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4054 if(Pxo_motd_blink_on){
4055 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_MOTD].button.draw_forced(2);
4061 // common dialog stuff ------------------------------------------------
4063 int Multi_pxo_searching = 0;
4065 // initialize the common dialog with the passed max input length
4066 void multi_pxo_com_init(int input_len)
4070 // create the interface window
4071 Multi_pxo_com_window.create(0, 0, gr_screen.max_w,gr_screen.max_h, 0);
4072 Multi_pxo_com_window.set_mask_bmap(Multi_pxo_com_mask_fname[gr_screen.res]);
4074 // create the interface buttons
4075 for(idx=0; idx<MULTI_PXO_COM_NUM_BUTTONS; idx++){
4076 // create the object
4077 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);
4079 // set the sound to play when highlighted
4080 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4082 // set the ani for the button
4083 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_com_buttons[gr_screen.res][idx].filename);
4086 Multi_pxo_com_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_com_buttons[gr_screen.res][idx].hotspot);
4090 for(idx=0; idx<MULTI_PXO_COM_NUM_TEXT; idx++){
4091 Multi_pxo_com_window.add_XSTR(&Multi_pxo_com_text[gr_screen.res][idx]);
4094 // create the input box
4095 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);
4096 Multi_pxo_com_input.set_focus();
4098 // clear all text lines
4099 SDL_zero(Multi_pxo_com_bottom_text);
4100 SDL_zero(Multi_pxo_com_middle_text);
4101 SDL_zero(Multi_pxo_com_top_text);
4104 // close down the common dialog
4105 void multi_pxo_com_close()
4107 // destroy the UI_WINDOW
4108 Multi_pxo_com_window.destroy();
4111 // blit all text lines, top, middle, bottoms
4112 void multi_pxo_com_blit_text()
4114 // blit top, middle and bottom text if possible
4115 if(strlen(Multi_pxo_com_top_text) > 0){
4116 gr_set_color_fast(&Color_bright);
4117 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);
4119 if(strlen(Multi_pxo_com_middle_text) > 0){
4120 gr_set_color_fast(&Color_bright);
4121 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);
4123 if(strlen(Multi_pxo_com_bottom_text) > 0){
4124 gr_set_color_fast(&Color_bright);
4125 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);
4129 // set the top text, shortening as necessary
4130 void multi_pxo_com_set_top_text(const char *txt)
4132 if((txt != NULL) && strlen(txt)){
4133 SDL_strlcpy(Multi_pxo_com_top_text, txt, SDL_arraysize(Multi_pxo_com_top_text));
4134 gr_force_fit_string(Multi_pxo_com_top_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4138 // set the middle text, shortening as necessary
4139 void multi_pxo_com_set_middle_text(const char *txt)
4141 if((txt != NULL) && strlen(txt)){
4142 SDL_strlcpy(Multi_pxo_com_middle_text, txt, SDL_arraysize(Multi_pxo_com_middle_text));
4143 gr_force_fit_string(Multi_pxo_com_middle_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4147 // set the bottom text, shortening as necessary
4148 void multi_pxo_com_set_bottom_text(const char *txt)
4150 if((txt != NULL) && strlen(txt)){
4151 SDL_strlcpy(Multi_pxo_com_bottom_text, txt, SDL_arraysize(Multi_pxo_com_bottom_text));
4152 gr_force_fit_string(Multi_pxo_com_bottom_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4157 // private channel join stuff -----------------------------------------
4159 // initialize the popup
4160 void multi_pxo_priv_init()
4162 SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE);
4164 // initialize the common dialog with the passed max input length
4165 multi_pxo_com_init(MULTI_PXO_PRIV_MAX_TEXT_LEN);
4167 // initialize the return code
4168 Multi_pxo_priv_return_code = -1;
4170 // mark us as running
4171 Multi_pxo_mode = MULTI_PXO_MODE_PRIVATE;
4174 multi_pxo_com_set_middle_text(XSTR("Type the name of the channel to join/create",961));
4177 // close down the popup
4178 void multi_pxo_priv_close()
4180 // close down the common dialog
4181 multi_pxo_com_close();
4183 // mark us as not running any more
4184 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4187 // run the popup, 0 if still running, -1 if cancel, 1 if ok
4188 int multi_pxo_priv_popup()
4192 // if we're not already running, initialize stuff
4193 if(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE){
4195 multi_pxo_priv_init();
4197 // return "still running"
4201 k = Multi_pxo_com_window.process();
4203 // process keypresses
4205 // like hitting the cancel button
4207 Multi_pxo_priv_return_code = 0;
4211 // process button presses
4212 multi_pxo_priv_process_buttons();
4214 // process the inputbox
4215 multi_pxo_priv_process_input();
4217 // blit the background
4218 multi_pxo_blit_all();
4222 gr_set_bitmap(Multi_pxo_com_bitmap);
4223 gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4224 Multi_pxo_com_window.draw();
4226 // blit all text lines, top, middle, bottoms
4227 multi_pxo_com_blit_text();
4231 // check the return code
4232 switch(Multi_pxo_priv_return_code){
4233 // still in progress
4239 multi_pxo_priv_close();
4244 multi_pxo_priv_close();
4251 // process button presses
4252 void multi_pxo_priv_process_buttons()
4256 // check all buttons
4257 for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4258 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4259 multi_pxo_priv_button_pressed(idx);
4265 // handle a button press
4266 void multi_pxo_priv_button_pressed(int n)
4268 char priv_chan_name[128];
4271 case MULTI_PXO_COM_CANCEL:
4272 Multi_pxo_priv_return_code = 0;
4275 case MULTI_PXO_COM_OK:
4276 Multi_pxo_com_input.get_text(priv_chan_name);
4277 multi_pxo_strip_space(priv_chan_name, priv_chan_name, SDL_arraysize(priv_chan_name));
4279 // if its a 0 length string, interpret as a cancel
4280 if(strlen(priv_chan_name) <= 0){
4281 Multi_pxo_priv_return_code = 0;
4285 Multi_pxo_priv_return_code = 1;
4290 // process the inputbox
4291 void multi_pxo_priv_process_input()
4293 char priv_chan_name[128];
4295 // see if the user has pressed enter
4296 if(Multi_pxo_com_input.pressed()){
4297 Multi_pxo_com_input.get_text(priv_chan_name);
4298 multi_pxo_strip_space(priv_chan_name, priv_chan_name, SDL_arraysize(priv_chan_name));
4300 // if its a 0 length string, interpret as a cancel
4301 if(strlen(priv_chan_name) <= 0){
4302 Multi_pxo_priv_return_code = 0;
4306 // otherwise interpret as "accept"
4307 Multi_pxo_priv_return_code = 1;
4309 // add in the "+" which indicates a private room
4310 SDL_strlcpy(Multi_pxo_priv_chan, "+", SDL_arraysize(Multi_pxo_priv_chan));
4311 SDL_strlcat(Multi_pxo_priv_chan, priv_chan_name, SDL_arraysize(Multi_pxo_priv_chan));
4315 // find player stuff -----------------------------------------
4317 char name_lookup[255];
4319 // initialize the popup
4320 void multi_pxo_find_init()
4322 SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_FIND);
4324 // initialize the common dialog with the passed max input length
4325 multi_pxo_com_init(MAX_PLAYER_NAME_LEN);
4327 // return code, set to something other than -1 if we're supposed to return
4328 Multi_pxo_find_return_code = -1;
4330 // mark us as running
4331 Multi_pxo_mode = MULTI_PXO_MODE_FIND;
4333 // not searching yet
4334 Multi_pxo_searching = 0;
4337 multi_pxo_com_set_top_text(XSTR("Enter user to be found",962));
4340 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4343 SDL_strlcpy(name_lookup, "", SDL_arraysize(name_lookup));
4346 // close down the popup
4347 void multi_pxo_find_close()
4349 // close down the common dialog
4350 multi_pxo_com_close();
4352 // mark us as not running any more
4353 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4356 // run the popup, 0 if still running, -1 if cancel, 1 if ok
4357 int multi_pxo_find_popup()
4361 // if we're not already running, initialize stuff
4362 if(Multi_pxo_mode != MULTI_PXO_MODE_FIND){
4364 multi_pxo_find_init();
4366 // return "still running"
4370 k = Multi_pxo_com_window.process();
4372 // process keypresses
4374 // like hitting the cancel button
4376 Multi_pxo_find_return_code = 0;
4380 // process button presses
4381 multi_pxo_find_process_buttons();
4383 // process the inputbox
4384 multi_pxo_find_process_input();
4386 // process search mode if applicable
4387 multi_pxo_find_search_process();
4389 // blit the background
4390 multi_pxo_blit_all();
4394 gr_set_bitmap(Multi_pxo_com_bitmap);
4395 gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4396 Multi_pxo_com_window.draw();
4398 // blit any text lines
4399 multi_pxo_com_blit_text();
4403 // check the return code
4404 switch(Multi_pxo_find_return_code){
4405 // still in progress
4411 // close the popup down
4412 multi_pxo_find_close();
4417 // close the popup down
4418 multi_pxo_find_close();
4420 // if we have a channel, join it now if possible
4421 if(strlen(Multi_pxo_find_channel) > 0){
4422 pxo_channel *lookup;
4423 lookup = multi_pxo_find_channel(Multi_pxo_find_channel,Multi_pxo_channels);
4425 // if we couldn't find it, don't join
4427 multi_pxo_join_channel(lookup);
4436 // process button presses
4437 void multi_pxo_find_process_buttons()
4441 // check all buttons
4442 for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4443 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4444 multi_pxo_find_button_pressed(idx);
4450 // handle a button press
4451 void multi_pxo_find_button_pressed(int n)
4454 case MULTI_PXO_COM_CANCEL:
4455 Multi_pxo_find_return_code = 0;
4458 case MULTI_PXO_COM_OK:
4459 Multi_pxo_find_return_code = 1;
4464 // process the inputbox
4465 void multi_pxo_find_process_input()
4467 // see if the user has pressed enter
4468 if(Multi_pxo_com_input.pressed()){
4469 // if we're not already in search mode
4470 if(!Multi_pxo_searching){
4472 SDL_zero(Multi_pxo_com_middle_text);
4473 SDL_zero(Multi_pxo_com_bottom_text);
4475 Multi_pxo_com_input.get_text(name_lookup);
4476 multi_pxo_strip_space(name_lookup, name_lookup, SDL_arraysize(name_lookup));
4478 // never search with a zero length string
4479 if(strlen(name_lookup) > 0){
4480 char search_text[512];
4482 // put us in search mode
4483 Multi_pxo_searching = 1;
4486 GetChannelByUser(name_lookup);
4489 SDL_snprintf(search_text, SDL_arraysize(search_text), XSTR("Searching for %s", 963), name_lookup);
4490 multi_pxo_com_set_top_text(search_text);
4494 SDL_zero(Multi_pxo_com_top_text);
4500 // process search mode if applicable
4501 void multi_pxo_find_search_process()
4505 // if we're not searching for anything, return
4506 if(!Multi_pxo_searching){
4510 // otherwise check to see if we've found him
4511 channel = GetChannelByUser(NULL);
4513 // if we've got a result, let the user know
4515 // if he couldn't be found
4516 if(channel == (char *)-1){
4517 multi_pxo_com_set_middle_text(XSTR("User not found",964));
4518 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4520 if(channel[0] == '*'){
4521 multi_pxo_com_set_middle_text(XSTR("Player is logged in but is not on a channel",965));
4522 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4526 // if this guy is on a public channel, display which one
4527 if(channel[0] == '#'){
4528 SDL_snprintf(p_text, SDL_arraysize(p_text), XSTR("Found %s on :", 966), name_lookup);
4530 // display the results
4531 multi_pxo_com_set_middle_text(p_text);
4532 multi_pxo_com_set_bottom_text(channel+1);
4534 // mark down the channel name so we know where to find him
4535 SDL_strlcpy(Multi_pxo_find_channel, channel, SDL_arraysize(Multi_pxo_find_channel));
4536 // strip out trailing whitespace
4537 if(Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] == ' '){
4538 Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] = '\0';
4541 // if this is a private channel
4542 else if(channel[0] == '+'){
4543 SDL_snprintf(p_text, SDL_arraysize(p_text), XSTR("Found %s on a private channel", 967), name_lookup);
4544 multi_pxo_com_set_middle_text(p_text);
4546 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4551 // unset search mode
4552 Multi_pxo_searching = 0;
4554 // clear the inputbox
4555 Multi_pxo_com_input.set_text("");
4560 // player info stuff -----------------------------------------
4562 // popup conditional functions, returns 10 on successful get of stats
4563 int multi_pxo_pinfo_cond()
4566 char temp_string[255];
4569 // process common stuff
4570 multi_pxo_process_common();
4572 // run the networking functions for the PXO API
4573 multi_pxo_api_process();
4575 // process depending on what mode we're in
4576 switch(Multi_pxo_retrieve_mode){
4577 // getting his player tracker id
4579 // if the thing is non-null, do something
4580 ret_string = GetTrackerIdByUser(Multi_pxo_retrieve_name);
4581 if(ret_string != NULL){
4582 // user not-online/not found
4583 if(ret_string == (char *)-1){
4587 // user not a tracker pilot
4588 if(!SDL_strcasecmp(ret_string,"-1")){
4592 // otherwise parse into his id and callsign
4593 SDL_strlcpy(temp_string, ret_string, SDL_arraysize(temp_string));
4594 tok = strtok(temp_string," ");
4598 SDL_strlcpy(Multi_pxo_retrieve_id, tok, SDL_arraysize(Multi_pxo_retrieve_id));
4601 tok = strtok(NULL,"");
4603 SDL_strlcpy(Multi_pxo_retrieve_name, tok, SDL_arraysize(Multi_pxo_retrieve_name));
4610 // failure of some kind or another
4615 Multi_pxo_retrieve_mode = 1;
4620 // initial call to get his stats
4622 // change the popup text
4623 popup_change_text(XSTR("Getting player stats",968));
4626 memset(&Multi_pxo_pinfo, 0, sizeof(vmt_freespace2_struct));
4627 SDL_strlcpy(Multi_pxo_pinfo.pilot_name, Multi_pxo_retrieve_name, SDL_arraysize(Multi_pxo_pinfo.pilot_name));
4628 SDL_strlcpy(Multi_pxo_pinfo.tracker_id, Multi_pxo_retrieve_id, SDL_arraysize(Multi_pxo_pinfo.tracker_id));
4630 // make the initial call to the API
4631 GetFSPilotData((vmt_freespace2_struct*)0xffffffff,NULL,NULL,0);
4632 if(GetFSPilotData(&Multi_pxo_pinfo,Multi_pxo_retrieve_name,Multi_pxo_retrieve_id,0) != 0){
4635 // if the call went through, set the mode to 2
4637 Multi_pxo_retrieve_mode = 2;
4641 // busy retrieving his stats
4643 switch(GetFSPilotData(NULL,NULL,NULL,0)){
4644 // timeout, fail, cancel
4662 // return not done yet
4666 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
4667 int multi_pxo_pinfo_get(char *name)
4670 Multi_pxo_retrieve_mode = 0;
4671 SDL_strlcpy(Multi_pxo_retrieve_name, name, SDL_arraysize(Multi_pxo_retrieve_name));
4672 switch(popup_till_condition(multi_pxo_pinfo_cond,XSTR("&Cancel", 779),XSTR("Retrieving player tracker id",969))){
4677 // failed to get his tracker id
4681 // failed to get his stats
4686 // we didn't get the stats
4690 // fire up the stats view popup
4691 void multi_pxo_pinfo_show()
4693 // initialize the popup
4694 multi_pxo_pinfo_init();
4698 game_set_frametime(GS_STATE_PXO);
4699 } while(!multi_pxo_pinfo_do());
4701 // close down the popup
4702 multi_pxo_pinfo_close();
4705 // build the stats labels values
4706 void multi_pxo_pinfo_build_vals()
4708 vmt_freespace2_struct *fs = &Multi_pxo_pinfo;
4710 SDL_zero(Multi_pxo_pinfo_vals);
4713 SDL_strlcpy(Multi_pxo_pinfo_vals[0], fs->pilot_name, SDL_arraysize(Multi_pxo_pinfo_vals[0]));
4714 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]));
4717 multi_sg_rank_build_name(Ranks[fs->rank].name, Multi_pxo_pinfo_vals[1], SDL_arraysize(Multi_pxo_pinfo_vals[1]));
4718 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]));
4721 SDL_snprintf(Multi_pxo_pinfo_vals[2], SDL_arraysize(Multi_pxo_pinfo_vals[2]), "%d", fs->kill_count);
4724 SDL_snprintf(Multi_pxo_pinfo_vals[3], SDL_arraysize(Multi_pxo_pinfo_vals[3]), "%d", fs->assists);
4727 SDL_snprintf(Multi_pxo_pinfo_vals[4], SDL_arraysize(Multi_pxo_pinfo_vals[4]), "%d", fs->kill_count - fs->kill_count_ok);
4730 SDL_snprintf(Multi_pxo_pinfo_vals[5], SDL_arraysize(Multi_pxo_pinfo_vals[5]), "%d", (int)fs->missions_flown);
4733 game_format_time(fl2f((float)fs->flight_time), Multi_pxo_pinfo_vals[6], SDL_arraysize(Multi_pxo_pinfo_vals[6]));
4736 if(fs->last_flown == 0){
4737 SDL_strlcpy(Multi_pxo_pinfo_vals[7], XSTR("No missions flown", 970), SDL_arraysize(Multi_pxo_pinfo_vals[7]));
4739 tm *tmr = gmtime((time_t*)&fs->last_flown);
4741 strftime(Multi_pxo_pinfo_vals[7],30,"%m/%d/%y %H:%M",tmr);
4743 SDL_strlcpy(Multi_pxo_pinfo_vals[7], "", SDL_arraysize(Multi_pxo_pinfo_vals[7]));
4747 // primary shots fired
4748 SDL_snprintf(Multi_pxo_pinfo_vals[8], SDL_arraysize(Multi_pxo_pinfo_vals[8]), "%d", (int)fs->p_shots_fired);
4750 // primary shots hit
4751 SDL_snprintf(Multi_pxo_pinfo_vals[9], SDL_arraysize(Multi_pxo_pinfo_vals[9]), "%d", (int)fs->p_shots_hit);
4754 if(fs->p_shots_fired > 0){
4755 SDL_snprintf(Multi_pxo_pinfo_vals[10], SDL_arraysize(Multi_pxo_pinfo_vals[10]), "%d%%", (int)((float)fs->p_shots_hit / (float)fs->p_shots_fired * 100.0f));
4757 SDL_strlcpy(Multi_pxo_pinfo_vals[10], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[10]));
4760 // secondary shots fired
4761 SDL_snprintf(Multi_pxo_pinfo_vals[11], SDL_arraysize(Multi_pxo_pinfo_vals[11]), "%d", (int)fs->s_shots_fired);
4763 // secondary shots hit
4764 SDL_snprintf(Multi_pxo_pinfo_vals[12], SDL_arraysize(Multi_pxo_pinfo_vals[12]), "%d", (int)fs->s_shots_hit);
4766 // secondary hit pct
4767 if(fs->s_shots_fired > 0){
4768 SDL_snprintf(Multi_pxo_pinfo_vals[13], SDL_arraysize(Multi_pxo_pinfo_vals[13]), "%d%%", (int)((float)fs->s_shots_hit / (float)fs->s_shots_fired * 100.0f));
4770 SDL_strlcpy(Multi_pxo_pinfo_vals[13], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[13]));
4773 // primary friendly hits
4774 SDL_snprintf(Multi_pxo_pinfo_vals[14], SDL_arraysize(Multi_pxo_pinfo_vals[14]), "%d", fs->p_bonehead_hits);
4776 // primary friendly hit %
4777 if(fs->p_shots_fired > 0){
4778 SDL_snprintf(Multi_pxo_pinfo_vals[15], SDL_arraysize(Multi_pxo_pinfo_vals[15]), "%d%%", (int)((float)100.0f*((float)fs->p_bonehead_hits/(float)fs->p_shots_fired)));
4780 SDL_strlcpy(Multi_pxo_pinfo_vals[15], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[15]));
4783 // secondary friendly hits
4784 SDL_snprintf(Multi_pxo_pinfo_vals[16], SDL_arraysize(Multi_pxo_pinfo_vals[16]), "%d", fs->s_bonehead_hits);
4786 // secondary friendly hit %
4787 if(fs->s_shots_fired > 0){
4788 SDL_snprintf(Multi_pxo_pinfo_vals[17], SDL_arraysize(Multi_pxo_pinfo_vals[17]), "%d%%", (int)((float)100.0f*((float)fs->s_bonehead_hits/(float)fs->s_shots_fired)));
4790 SDL_strlcpy(Multi_pxo_pinfo_vals[17], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[17]));
4794 // initialize the popup
4795 void multi_pxo_pinfo_init()
4799 // create the interface window
4800 Multi_pxo_pinfo_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4801 Multi_pxo_pinfo_window.set_mask_bmap(Multi_pxo_pinfo_mask_fname[gr_screen.res]);
4803 Multi_pxo_pinfo_bitmap = bm_load(Multi_pxo_pinfo_fname[gr_screen.res]);
4804 SDL_assert(Multi_pxo_pinfo_bitmap != -1);
4806 // create the interface buttons
4807 for(idx=0; idx<MULTI_PXO_PINFO_NUM_BUTTONS; idx++){
4808 // create the object
4809 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);
4811 // set the sound to play when highlighted
4812 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4814 // set the ani for the button
4815 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_pinfo_buttons[gr_screen.res][idx].filename);
4818 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_pinfo_buttons[gr_screen.res][idx].hotspot);
4822 for(idx=0; idx<MULTI_PXO_PINFO_NUM_TEXT; idx++){
4823 Multi_pxo_pinfo_window.add_XSTR(&Multi_pxo_pinfo_text[gr_screen.res][idx]);
4826 // set up the stats labels
4827 Multi_pxo_pinfo_stats_labels[0] = strdup(XSTR("Name", 1532));
4828 Multi_pxo_pinfo_stats_labels[1] = strdup(XSTR("Rank", 1533));
4829 Multi_pxo_pinfo_stats_labels[2] = strdup(XSTR("Kills", 1534));
4830 Multi_pxo_pinfo_stats_labels[3] = strdup(XSTR("Assists", 1535));
4831 Multi_pxo_pinfo_stats_labels[4] = strdup(XSTR("Friendly kills", 1536));
4832 Multi_pxo_pinfo_stats_labels[5] = strdup(XSTR("Missions flown", 1537));
4833 Multi_pxo_pinfo_stats_labels[6] = strdup(XSTR("Flight time", 1538));
4834 Multi_pxo_pinfo_stats_labels[7] = strdup(XSTR("Last flown", 1539));
4835 Multi_pxo_pinfo_stats_labels[8] = strdup(XSTR("Primary shots fired", 1540));
4836 Multi_pxo_pinfo_stats_labels[9] = strdup(XSTR("Primary shots hit", 1541));
4837 Multi_pxo_pinfo_stats_labels[10] = strdup(XSTR("Primary hit %", 1542));
4838 Multi_pxo_pinfo_stats_labels[11] = strdup(XSTR("Secondary shots fired", 1543));
4839 Multi_pxo_pinfo_stats_labels[12] = strdup(XSTR("Secondary shots hit", 1544));
4840 Multi_pxo_pinfo_stats_labels[13] = strdup(XSTR("Secondary hit %", 1545));
4841 Multi_pxo_pinfo_stats_labels[14] = strdup(XSTR("Primary friendly hits", 1546));
4842 Multi_pxo_pinfo_stats_labels[15] = strdup(XSTR("Primary friendly hit %", 1547));
4843 Multi_pxo_pinfo_stats_labels[16] = strdup(XSTR("Secondary friendly hits", 1548));
4844 Multi_pxo_pinfo_stats_labels[17] = strdup(XSTR("Secondary friendly hit %", 1549));
4846 // build the stats labels values
4847 multi_pxo_pinfo_build_vals();
4851 int multi_pxo_pinfo_do()
4853 int k = Multi_pxo_pinfo_window.process();
4855 // process common stuff
4856 multi_pxo_process_common();
4858 // run the networking functions for the PXO API
4859 multi_pxo_api_process();
4861 // check to see if he pressed escp
4862 if(k == SDLK_ESCAPE){
4866 // if he pressed the ok button
4867 if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_OK].button.pressed()){
4871 // if he pressed the medals buttons, run the medals screen
4872 if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_MEDALS].button.pressed()){
4874 game_feature_not_in_demo_popup();
4876 multi_pxo_run_medals();
4882 // blit everything on the "normal" screen
4883 multi_pxo_blit_all();
4885 // blit our own stuff
4887 gr_set_bitmap(Multi_pxo_pinfo_bitmap);
4889 Multi_pxo_pinfo_window.draw();
4891 // blit the stats themselves
4892 multi_pxo_pinfo_blit();
4902 void multi_pxo_pinfo_close()
4906 // destroy the UI_WINDOW
4907 Multi_pxo_pinfo_window.destroy();
4909 // unload the bitmap
4910 if(Multi_pxo_pinfo_bitmap != -1){
4911 bm_unload(Multi_pxo_pinfo_bitmap);
4914 // free the stats labels strings
4915 for (i=0; i<MULTI_PXO_PINFO_NUM_LABELS; i++) {
4916 free(Multi_pxo_pinfo_stats_labels[i]);
4920 // blit all the stats on this screen
4921 void multi_pxo_pinfo_blit()
4926 // blit all the labels
4927 y_start = Multi_pxo_pinfo_coords[gr_screen.res][1];
4928 for(idx=0; idx<MULTI_PXO_PINFO_NUM_LABELS; idx++){
4930 gr_set_color_fast(&Color_bright);
4931 gr_string(Multi_pxo_pinfo_coords[gr_screen.res][0], y_start, Multi_pxo_pinfo_stats_labels[idx]);
4933 // blit the label's value
4934 gr_set_color_fast(&Color_normal);
4935 gr_string(Multi_pxo_pinfo_val_x[gr_screen.res], y_start, Multi_pxo_pinfo_vals[idx]);
4938 y_start += Multi_pxo_pinfo_stats_spacing[idx];
4942 // run the medals screen
4943 void multi_pxo_run_medals()
4947 // process common stuff
4948 multi_pxo_process_common();
4950 // run the networking functions for the PXO API
4951 multi_pxo_api_process();
4953 // initialize the freespace data and the player struct
4954 multi_stats_tracker_to_fs(&Multi_pxo_pinfo, &Multi_pxo_pinfo_player.stats);
4955 SDL_strlcpy(Multi_pxo_pinfo_player.callsign, Multi_pxo_pinfo.pilot_name, SDL_arraysize(Multi_pxo_pinfo_player.callsign));
4957 // initialize the medals screen
4958 medal_main_init(&Multi_pxo_pinfo_player, MM_POPUP);
4960 // run the medals screen until it says that it should be closed
4962 // set frametime and run common functions
4963 game_set_frametime(-1);
4964 game_do_state_common(gameseq_get_state());
4966 // run the medals screen
4967 ret_code = medal_main_do();
4970 // close the medals screen down
4973 // reset the palette
4974 multi_pxo_load_palette();
4978 // notify stuff stuff -----------------------------------------
4980 // add a notification string
4981 void multi_pxo_notify_add(const char *txt)
4984 SDL_strlcpy(Multi_pxo_notify_text, txt, SDL_arraysize(Multi_pxo_notify_text));
4986 // set the timestamp
4987 Multi_pxo_notify_stamp = timestamp(MULTI_PXO_NOTIFY_TIME);
4990 // blit and process the notification string
4991 void multi_pxo_notify_blit()
4995 // if the timestamp is -1, do nothing
4996 if(Multi_pxo_notify_stamp == -1){
5000 // if it has expired, do nothing
5001 if(timestamp_elapsed(Multi_pxo_notify_stamp)){
5002 Multi_pxo_notify_stamp = -1;
5005 // otherwise blit the text
5006 gr_set_color_fast(&Color_bright);
5007 gr_get_string_size(&w,NULL,Multi_pxo_notify_text);
5008 gr_string((gr_screen.max_w - w)/2,MULTI_PXO_NOTIFY_Y,Multi_pxo_notify_text);
5012 // initialize the PXO help screen
5013 void multi_pxo_help_init()
5017 // load the background bitmap
5018 Multi_pxo_help_bitmap = bm_load(Multi_pxo_help_fname[gr_screen.res]);
5019 if(Multi_pxo_help_bitmap < 0){
5020 // we failed to load the bitmap - this is very bad
5023 // create the interface window
5024 Multi_pxo_help_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
5025 Multi_pxo_help_window.set_mask_bmap(Multi_pxo_help_mask_fname[gr_screen.res]);
5027 // create the interface buttons
5028 for(idx=0; idx<MULTI_PXO_HELP_NUM_BUTTONS; idx++){
5029 // create the object
5030 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);
5032 // set the sound to play when highlighted
5033 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
5035 // set the ani for the button
5036 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_help_buttons[gr_screen.res][idx].filename);
5039 Multi_pxo_help_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_help_buttons[gr_screen.res][idx].hotspot);
5043 for(idx=0; idx<MULTI_PXO_HELP_NUM_TEXT; idx++){
5044 Multi_pxo_help_window.add_XSTR(&Multi_pxo_help_text[gr_screen.res][idx]);
5047 // if we haven't already loaded in the text, do so
5048 // if(!Multi_pxo_help_loaded){
5049 multi_pxo_help_load();
5052 // set the current page to 0
5053 Multi_pxo_help_cur = 0;
5056 // do frame for PXO help
5057 void multi_pxo_help_do()
5060 if(Multi_pxo_connected){
5061 multi_pxo_api_process();
5064 // process common stuff
5065 multi_pxo_process_common();
5067 int k = Multi_pxo_help_window.process();
5069 // process any keypresses
5072 gamesnd_play_iface(SND_USER_SELECT);
5073 gameseq_post_event(GS_EVENT_PXO);
5077 // process button presses
5078 multi_pxo_help_process_buttons();
5080 // draw the background, etc
5082 GR_MAYBE_CLEAR_RES(Multi_pxo_help_bitmap);
5083 if(Multi_pxo_help_bitmap != -1){
5084 gr_set_bitmap(Multi_pxo_help_bitmap);
5087 Multi_pxo_help_window.draw();
5089 // blit the current page
5090 multi_pxo_help_blit_page();
5096 // close the pxo screen
5097 void multi_pxo_help_close()
5101 // unload any bitmaps
5102 bm_unload(Multi_pxo_help_bitmap);
5104 // destroy the UI_WINDOW
5105 Multi_pxo_help_window.destroy();
5108 for(idx=0; idx<Multi_pxo_help_num_pages; idx++){
5109 for(idx2=0; idx2<Multi_pxo_help_pages[idx].num_lines; idx2++){
5111 if(Multi_pxo_help_pages[idx].text[idx2] != NULL){
5112 free(Multi_pxo_help_pages[idx].text[idx2]);
5113 Multi_pxo_help_pages[idx].text[idx2] = NULL;
5119 // load the help file up
5120 void multi_pxo_help_load()
5125 // if its already loaded, do nothing
5126 // if(Multi_pxo_help_loaded){
5130 // read in the text file
5132 in = cfopen(MULTI_PXO_HELP_FILE,"rt",CFILE_NORMAL,CF_TYPE_DATA);
5133 SDL_assert(in != NULL);
5138 Multi_pxo_help_num_pages = 0;
5140 // blast all the help pages clear
5141 memset(Multi_pxo_help_pages, 0, sizeof(help_page) * MULTI_PXO_MAX_PAGES);
5142 Multi_pxo_help_num_pages = 0;
5143 cp = &Multi_pxo_help_pages[0];
5147 cp->text[cp->num_lines] = (char*)malloc(Multi_pxo_chars_per_line[gr_screen.res]);
5148 if(cp->text[cp->num_lines] == NULL){
5152 // read in the next line
5153 cfgets(cp->text[cp->num_lines++], Multi_pxo_chars_per_line[gr_screen.res], in);
5155 // skip to the next page if necessary
5156 if(cp->num_lines == Multi_pxo_lines_pp[gr_screen.res]){
5157 Multi_pxo_help_num_pages++;
5158 SDL_assert(Multi_pxo_help_num_pages < MULTI_PXO_MAX_PAGES);
5159 if(Multi_pxo_help_num_pages >= MULTI_PXO_MAX_PAGES){
5160 Multi_pxo_help_num_pages--;
5163 cp = &Multi_pxo_help_pages[Multi_pxo_help_num_pages];
5170 // mark the help as having been loaded
5171 // Multi_pxo_help_loaded = 1;
5174 // blit the current page
5175 void multi_pxo_help_blit_page()
5180 help_page *cp = &Multi_pxo_help_pages[Multi_pxo_help_cur];
5183 y_start = Multi_pxo_help_coords[gr_screen.res][1];
5184 for(idx=0;idx<cp->num_lines;idx++){
5185 // if the first symbol is "@", highlight the line
5186 if(cp->text[idx][0] == '@'){
5187 gr_set_color_fast(&Color_bright);
5190 gr_set_color_fast(&Color_normal);
5195 gr_string(Multi_pxo_help_coords[gr_screen.res][0], y_start, cp->text[idx] + start_pos);
5197 // increment the y location
5202 // process button presses
5203 void multi_pxo_help_process_buttons()
5207 // process all buttons
5208 for(idx=0;idx<MULTI_PXO_HELP_NUM_BUTTONS;idx++){
5209 if(Multi_pxo_help_buttons[gr_screen.res][idx].button.pressed()){
5210 multi_pxo_help_button_pressed(idx);
5217 void multi_pxo_help_button_pressed(int n)
5220 case MULTI_PXO_HELP_PREV:
5221 // if we're already at page 0, do nothing
5222 if(Multi_pxo_help_cur == 0){
5223 gamesnd_play_iface(SND_GENERAL_FAIL);
5225 Multi_pxo_help_cur--;
5226 gamesnd_play_iface(SND_USER_SELECT);
5230 case MULTI_PXO_HELP_NEXT:
5231 // if we're already at max pages, do nothing
5232 if(Multi_pxo_help_cur == Multi_pxo_help_num_pages){
5233 gamesnd_play_iface(SND_GENERAL_FAIL);
5235 Multi_pxo_help_cur++;
5236 gamesnd_play_iface(SND_USER_SELECT);
5240 case MULTI_PXO_HELP_CONTINUE:
5241 gamesnd_play_iface(SND_USER_SELECT);
5242 gameseq_post_event(GS_EVENT_PXO);
5247 // http banner stuff ---------------------------------------------
5250 void multi_pxo_ban_init()
5252 // zero the active banner bitmap
5253 Multi_pxo_banner.ban_bitmap = -1;
5255 // are we doing banners at all?
5256 if(os_config_read_uint(NULL, "PXOBanners", 1)){
5257 // if we're already in idle mode, we're done downloading for this instance of freespace. pick a random image we already have
5258 if(Multi_pxo_ban_mode == PXO_BAN_MODE_IDLE){
5259 Multi_pxo_ban_mode = PXO_BAN_MODE_CHOOSE_RANDOM;
5263 // set ourselves to startup mode
5264 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
5265 Multi_pxo_ban_get = NULL;
5267 // set ourselves to idle mode
5268 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5269 Multi_pxo_ban_get = NULL;
5272 // zero the active banner bitmap
5273 SDL_zero(Multi_pxo_banner);
5274 Multi_pxo_banner.ban_bitmap = -1;
5277 // process http download details
5278 void multi_pxo_ban_process()
5280 char url_string[512] = "";
5281 char local_file[MAX_PATH_LEN] = "";
5284 switch(Multi_pxo_ban_mode){
5285 // start downloading list
5286 case PXO_BAN_MODE_LIST_STARTUP:
5288 SDL_snprintf(url_string, SDL_arraysize(url_string), "%s/%s", Multi_options_g.pxo_banner_url, PXO_BANNERS_CONFIG_FILE);
5291 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, PXO_BANNERS_CONFIG_FILE);
5293 // try creating the file get object
5294 Multi_pxo_ban_get = new InetGetFile(url_string, local_file);
5297 if(Multi_pxo_ban_get == NULL){
5298 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5301 // go to the downloading list mode
5302 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST;
5306 case PXO_BAN_MODE_LIST:
5308 if(Multi_pxo_ban_get->IsFileError()){
5309 delete Multi_pxo_ban_get;
5310 Multi_pxo_ban_get = NULL;
5311 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5315 // connecting, receiving
5316 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5321 if(Multi_pxo_ban_get->IsFileReceived()){
5322 delete Multi_pxo_ban_get;
5323 Multi_pxo_ban_get = NULL;
5324 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_STARTUP;
5328 // start downloading files
5329 case PXO_BAN_MODE_IMAGES_STARTUP:
5330 // first thing - parse the banners file and pick a file
5331 multi_pxo_ban_parse_banner_file(0);
5333 // if we have no active file, we're done
5334 if((strlen(Multi_pxo_banner.ban_file) <= 0) || (strlen(Multi_pxo_banner.ban_file_url) <= 0)){
5335 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5339 // if the file already exists, we're done
5340 if(cf_exist(Multi_pxo_banner.ban_file, CF_TYPE_MULTI_CACHE)){
5341 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5345 // otherwise try and download it
5346 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, Multi_pxo_banner.ban_file);
5347 // try creating the file get object
5348 Multi_pxo_ban_get = new InetGetFile(Multi_pxo_banner.ban_file_url, local_file);
5351 if(Multi_pxo_ban_get == NULL){
5352 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5355 // go to the downloading images mode
5356 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES;
5359 // downloading files
5360 case PXO_BAN_MODE_IMAGES:
5362 if(Multi_pxo_ban_get->IsFileError()){
5363 delete Multi_pxo_ban_get;
5364 Multi_pxo_ban_get = NULL;
5365 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5369 // connecting, receiving
5370 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5375 if(Multi_pxo_ban_get->IsFileReceived()){
5376 delete Multi_pxo_ban_get;
5377 Multi_pxo_ban_get = NULL;
5378 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5382 // done downloading - maybe load an image
5383 case PXO_BAN_MODE_IMAGES_DONE:
5384 // make sure we have a valid filename
5385 // SDL_assert(strlen(Multi_pxo_banner.ban_file) > 0);
5386 if(strlen(Multi_pxo_banner.ban_file) > 0){
5387 Multi_pxo_banner.ban_bitmap = bm_load(Multi_pxo_banner.ban_file);
5391 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5394 // idle (done with EVERYTHING)
5395 case PXO_BAN_MODE_IDLE:
5396 // if the banner button was clicked
5397 if(Multi_pxo_ban_button.pressed()){
5398 multi_pxo_ban_clicked();
5402 case PXO_BAN_MODE_CHOOSE_RANDOM:
5403 // first thing - parse the banners file and pick a file
5404 multi_pxo_ban_parse_banner_file(1);
5406 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5412 void multi_pxo_ban_close()
5414 // if we have a currently active transfer
5415 if(Multi_pxo_ban_get != NULL){
5416 Multi_pxo_ban_get->AbortGet();
5417 delete Multi_pxo_ban_get;
5418 Multi_pxo_ban_get = NULL;
5421 // if we have a loaded bitmap, unload it
5422 if(Multi_pxo_banner.ban_bitmap != -1){
5423 bm_unload(Multi_pxo_banner.ban_bitmap);
5424 Multi_pxo_banner.ban_bitmap = -1;
5428 // parse the banners file and maybe fill in Multi_pxo_dl_file
5429 void multi_pxo_ban_parse_banner_file(int choose_existing)
5431 char file_url[MAX_PATH_LEN] = "";
5432 char banners[10][MAX_PATH_LEN];
5433 char urls[10][MAX_PATH_LEN];
5436 int num_banners, idx;
5437 CFILE *in = cfopen(PXO_BANNERS_CONFIG_FILE, "rt", CFILE_NORMAL, CF_TYPE_MULTI_CACHE);
5439 SDL_zero(Multi_pxo_banner);
5440 Multi_pxo_banner.ban_bitmap = -1;
5447 // clear all strings
5451 // get the global banner url
5452 if(cfgets(file_url, SDL_arraysize(file_url), in) == NULL){
5454 cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5457 drop_leading_white_space(file_url);
5458 drop_trailing_white_space(file_url);
5460 // otherwise read in
5462 while(num_banners < 10){
5463 // try and get the pcx
5464 if(cfgets(banners[num_banners], SDL_arraysize(banners[0]), in) == NULL){
5467 // try and get the url
5468 if(cfgets(urls[num_banners], SDL_arraysize(urls[0]), in) == NULL){
5472 // strip off trailing and leading whitespace
5473 drop_leading_white_space(banners[num_banners]);
5474 drop_trailing_white_space(banners[num_banners]);
5475 drop_leading_white_space(urls[num_banners]);
5476 drop_trailing_white_space(urls[num_banners]);
5486 if(num_banners <= 0){
5490 // if we're only selecting files which already exist (previously downloaded)
5491 if(choose_existing){
5493 for(idx=0; idx<10; idx++){
5497 // build a list of existing files
5499 for(idx=0; idx<num_banners; idx++){
5500 if(cf_exist(banners[idx], CF_TYPE_MULTI_CACHE)){
5507 if(exist_count <= 0){
5512 int select = (int)frand_range(0.0f, (float)exist_count);
5513 if(select >= exist_count){
5514 select = exist_count - 1;
5519 for(idx=0; idx<exist_count; idx++){
5529 if(idx < exist_count){
5531 SDL_strlcpy(Multi_pxo_banner.ban_file, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file));
5533 // get the full file url
5534 SDL_strlcpy(Multi_pxo_banner.ban_file_url, file_url, SDL_arraysize(Multi_pxo_banner.ban_file_url));
5535 SDL_strlcat(Multi_pxo_banner.ban_file_url, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file_url));
5537 // url of where to go to when clicked
5538 SDL_strlcpy(Multi_pxo_banner.ban_url, urls[idx], SDL_arraysize(Multi_pxo_banner.ban_url));
5541 // randomly pick a file for download
5543 idx = (int)frand_range(0.0f, (float)num_banners);
5545 if(idx >= num_banners){
5546 idx = num_banners - 1;
5553 SDL_strlcpy(Multi_pxo_banner.ban_file, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file));
5555 // get the full file url
5556 SDL_strlcpy(Multi_pxo_banner.ban_file_url, file_url, SDL_arraysize(Multi_pxo_banner.ban_file_url));
5557 SDL_strlcat(Multi_pxo_banner.ban_file_url, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file_url));
5559 // url of where to go to when clicked
5560 SDL_strlcpy(Multi_pxo_banner.ban_url, urls[idx], SDL_arraysize(Multi_pxo_banner.ban_url));
5563 // delete the banner config file
5564 // cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5567 // any bitmap or info or whatever
5568 void multi_pxo_ban_draw()
5570 // if we have a valid bitmap
5571 if(Multi_pxo_banner.ban_bitmap >= 0){
5572 // if the mouse is over the banner button, highlight with a rectangle
5573 if(Multi_pxo_ban_button.is_mouse_on()){
5574 gr_set_color_fast(&Color_bright_blue);
5575 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);
5578 // draw the bitmap itself
5579 gr_set_bitmap(Multi_pxo_banner.ban_bitmap);
5580 gr_bitmap(Pxo_ban_coords[gr_screen.res][0], Pxo_ban_coords[gr_screen.res][1]);
5584 // called when the URL button is clicked
5585 void multi_pxo_ban_clicked()
5587 // if we have a valid bitmap and URL, launch the URL
5588 if((Multi_pxo_banner.ban_bitmap >= 0) && (strlen(Multi_pxo_banner.ban_url) > 0)){
5589 multi_pxo_url(Multi_pxo_banner.ban_url);