2 * Copyright (C) Volition, Inc. 2005. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on the
11 * $Logfile: /Freespace2/code/Network/multi_pxo.cpp $
13 * $Date: 10/25/99 5:52p $
16 * $Log: /Freespace2/code/Network/multi_pxo.cpp $
18 * 42 10/25/99 5:52p Jefff
19 * fixed some non-localized text
21 * 41 10/14/99 2:51p Jefff
24 * 40 10/13/99 3:29p Jefff
25 * fixed unnumbered XSTRs
27 * 39 9/13/99 11:30a Dave
28 * Added checkboxes and functionality for disabling PXO banners as well as
29 * disabling d3d zbuffer biasing.
31 * 38 8/30/99 5:01p Dave
32 * Made d3d do less state changing in the nebula. Use new chat server for
35 * 37 8/20/99 2:09p Dave
38 * 36 8/17/99 3:31p Jefff
39 * fixed too-big chat area in 640
41 * 35 8/16/99 9:51a Jefff
42 * webcursor over html links
44 * 34 8/11/99 12:19p Jefff
45 * no longer force pxo chatbox to snap to bottom when new text is added
47 * 33 8/10/99 5:53p Jefff
48 * fixed 640 chatbox width
50 * 32 8/06/99 1:49p Jefff
51 * added hi-res pxo logo
53 * 31 8/06/99 12:29a Dave
56 * 30 8/05/99 4:17p Dave
57 * Tweaks to client interpolation.
59 * 29 8/05/99 4:11p Jefff
61 * 28 8/04/99 10:59a Dave
62 * Removed "squad" text.
64 * 27 8/03/99 5:55p Jefff
65 * column headings in channel window, some general scrubbing
67 * 26 7/26/99 11:14a Andsager
68 * Disable medals in demo multiplayer
70 * 25 7/19/99 2:13p Dave
71 * Added some new strings for Heiko.
73 * 24 6/29/99 7:39p Dave
74 * Lots of small bug fixes.
76 * 23 6/18/99 5:16p Dave
77 * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
78 * dialog to PXO screen.
80 * 22 6/16/99 4:55p Dave
81 * New pxo pilot info popup.
83 * 21 6/07/99 9:51p Dave
84 * Consolidated all multiplayer ports into one.
86 * 20 6/01/99 3:52p Dave
87 * View footage screen. Fixed xstrings to not display the & symbol. Popup,
88 * dead popup, pxo find player popup, pxo private room popup.
90 * 19 5/21/99 6:45p Dave
91 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
92 * start game screen, multi password, and multi pxo-help screen.
94 * 18 5/19/99 4:07p Dave
95 * Moved versioning code into a nice isolated common place. Fixed up
96 * updating code on the pxo screen. Fixed several stub problems.
98 * 17 5/17/99 9:25a Dave
99 * Updated PXO screen. Still needs popups.
101 * 16 4/30/99 12:18p Dave
102 * Several minor bug fixes.
104 * 15 4/24/99 8:03p Dave
105 * Finalized banner code on PXO screen.
107 * 14 4/21/99 11:29p Dave
108 * Improved the banner stuff. Will display bitmaps, launcher URLs and
109 * highlight the banner bitmap.
111 * 13 4/20/99 6:39p Dave
112 * Almost done with artillery targeting. Added support for downloading
113 * images on the PXO screen.
115 * 12 2/17/99 2:11p Dave
116 * First full run of squad war. All freespace and tracker side stuff
119 * 11 2/08/99 5:07p Dave
120 * FS2 chat server support. FS2 specific validated missions.
122 * 10 2/04/99 6:29p Dave
123 * First full working rev of FS2 PXO support. Fixed Glide lighting
126 * 9 2/03/99 6:06p Dave
127 * Groundwork for FS2 PXO usertracker support. Gametracker support next.
129 * 8 1/30/99 5:08p Dave
130 * More new hi-res stuff.Support for nice D3D textures.
132 * 7 12/18/98 1:13a Dave
133 * Rough 1024x768 support for Direct3D. Proper detection and usage through
136 * 6 11/30/98 1:07p Dave
137 * 16 bit conversion, first run.
139 * 5 11/19/98 4:19p Dave
140 * Put IPX sockets back in psnet. Consolidated all multiplayer config
143 * 4 11/05/98 5:55p Dave
144 * Big pass at reducing #includes
146 * 3 10/13/98 9:29a Dave
147 * Started neatening up freespace.h. Many variables renamed and
148 * reorganized. Added AlphaColors.[h,cpp]
150 * 2 10/07/98 10:53a Dave
153 * 1 10/07/98 10:50a Dave
155 * 43 9/18/98 2:22a Dave
156 * Fixed freespace-side PXO api to correctly handle length 10 id strings.
157 * Fixed team select screen to handle alpha/beta/gamma ships which are not
158 * marked as OF_PLAYER_SHIP
160 * 42 9/17/98 3:08p Dave
161 * PXO to non-pxo game warning popup. Player icon stuff in create and join
162 * game screens. Upped server count refresh time in PXO to 35 secs (from
165 * 41 9/09/98 5:53p Dave
166 * Put in new tracker packets in API. Change cfile to be able to checksum
167 * portions of a file.
169 * 40 7/13/98 10:30a Lawrance
170 * add index numbers for newly localized strings
172 * 39 7/13/98 10:12a Dave
173 * Remove improperly localized strings.
175 * 38 7/10/98 2:03p Dave
176 * Additional PXO changes.
178 * 37 7/09/98 6:01p Dave
179 * Firsts full version of PXO updater. Put in stub for displaying
182 * 36 7/09/98 4:51p Dave
183 * First revision of PXO autoupdate check system.
185 * 35 7/08/98 8:15p Dave
186 * Fixed bandwidth capping for new system, and fixed pxo join button bug.
188 * 34 7/08/98 5:28p Dave
189 * Display channel name when returning to previous PXO channel from
192 * 33 7/08/98 4:54p Dave
193 * Join last used channel when returning from the games list.
195 * 32 6/17/98 10:56a Dave
196 * Put in debug code for detecting potential tracker stats update
199 * 31 6/16/98 10:39a Allender
202 * 30 6/13/98 6:01p Hoffoss
203 * Externalized all new (or forgot to be added) strings to all the code.
205 * 29 6/13/98 4:42p Hoffoss
206 * Fixed code to work properly with the XSTR program.
208 * 28 6/13/98 3:19p Hoffoss
209 * NOX()ed out a bunch of strings that shouldn't be translated.
211 * 27 6/13/98 1:45p Sandeep
213 * 26 6/05/98 3:08p Dave
214 * Fixed broken version checking. Fixed pxo chat scroll-down bug.
216 * 25 6/04/98 3:52p Dave
217 * Fixed pxo chat bug. Used physics compression code to compress observer
218 * updates. Put in rate limiting for observer updates.
220 * 24 5/27/98 1:15p Dave
221 * Change pxo login failure popup text. Put in message display of player
222 * who is currently doing voice.
224 * 23 5/26/98 11:25a Dave
225 * Check for NULL when scroll chat area up.
227 * 22 5/24/98 8:15p Dave
228 * Tweaked pxo some more.
230 * 21 5/24/98 3:45a Dave
231 * Minor object update fixes. Justify channel information on PXO. Add a
232 * bunch of configuration stuff for the standalone.
234 * 20 5/24/98 12:14a Dave
237 * 19 5/23/98 3:31p Dave
238 * Tweaked pxo code. Fixed observer HUD stuff.
240 * 18 5/23/98 3:02a Dave
243 * 17 5/21/98 9:45p Dave
244 * Lengthened tracker polling times. Put in initial support for PXO
245 * servers with channel filters. Fixed several small UI bugs.
247 * 16 5/21/98 1:52a Dave
248 * Remove obsolete command line functions. Reduce shield explosion packets
249 * drastically. Tweak PXO screen even more. Fix file xfer system so that
250 * we can guarantee file uniqueness.
252 * 15 5/20/98 12:12p Dave
253 * Channel name fixes.
255 * 14 5/20/98 2:24a Dave
256 * Fixed server side voice muting. Tweaked multi debrief/endgame
257 * sequencing a bit. Much friendlier for stats tossing/accepting now.
259 * 13 5/19/98 8:35p Dave
260 * Revamp PXO channel listing system. Send campaign goals/events to
261 * clients for evaluation. Made lock button pressable on all screens.
263 * 12 5/19/98 1:35a Dave
264 * Tweaked pxo interface. Added rankings url to pxo.cfg. Make netplayer
265 * local options update dynamically in netgames.
267 * 11 5/18/98 9:15p Dave
268 * Put in network config file support.
270 * 10 5/17/98 1:43a Dave
271 * Eradicated chatbox problems. Remove speed match for observers. Put in
272 * help screens for PXO. Fix messaging and end mission privelges. Fixed
273 * team select screen bugs. Misc UI fixes.
275 * 9 5/15/98 9:52p Dave
276 * Added new stats for freespace. Put in artwork for viewing stats on PXO.
278 * 8 5/15/98 1:41p Dave
279 * Update api so everyone can test.
281 * 7 5/15/98 12:09a Dave
282 * New tracker api code. New game tracker code. Finished up first run of
283 * the PXO screen. Fixed a few game server list exceptions.
285 * 6 5/14/98 2:41p Johnson
286 * Fixed a nick switching bug.
288 * 5 5/14/98 12:40a Dave
289 * Still more additions to the PXO screen. Updated tracker code.
291 * 4 5/13/98 6:54p Dave
292 * More sophistication to PXO interface. Changed respawn checking so
293 * there's no window for desynchronization between the server and the
296 * 3 5/12/98 11:59p Dave
297 * Put in some more functionality for Parallax Online.
299 * 2 5/12/98 2:46a Dave
300 * Rudimentary communication between Parallax Online and freespace. Can
301 * get and store channel lists.
303 * 1 5/11/98 11:47p Dave
311 #include <netinet/in.h>
314 #include "multi_pxo.h"
315 #include "animplay.h"
321 #include "gamesequence.h"
323 #include "chat_api.h"
325 #include "freespace.h"
329 #include "multi_fstracker.h"
333 #include "multi_update.h"
334 #include "alphacolors.h"
336 #include "inetgetfile.h"
337 #include "cfilesystem.h"
338 #include "osregistry.h"
340 // ----------------------------------------------------------------------------------------------------
344 // button definitions
345 #define MULTI_PXO_NUM_BUTTONS 15
346 #define MULTI_PXO_PLIST_UP 0
347 #define MULTI_PXO_PLIST_DOWN 1
348 #define MULTI_PXO_RANKINGS 2
349 #define MULTI_PXO_PINFO 3
350 #define MULTI_PXO_FIND 4
351 #define MULTI_PXO_MOTD 5
352 #define MULTI_PXO_JOIN 6
353 #define MULTI_PXO_JOIN_PRIV 7
354 #define MULTI_PXO_CHAN_UP 8
355 #define MULTI_PXO_CHAN_DOWN 9
356 #define MULTI_PXO_TEXT_UP 10
357 #define MULTI_PXO_TEXT_DOWN 11
358 #define MULTI_PXO_EXIT 12
359 #define MULTI_PXO_HELP 13
360 #define MULTI_PXO_GAMES 14
363 ui_button_info Multi_pxo_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_NUM_BUTTONS] = {
365 ui_button_info( "PXB_00", 1, 104, -1, -1, 0 ), // scroll player list up
366 ui_button_info( "PXB_01", 1, 334, -1, -1, 1 ), // scroll player list down
367 ui_button_info( "PXB_02", 18, 385, -1, -1, 2 ), // rankings webpage
368 ui_button_info( "PXB_03", 71, 385, -1, -1, 3 ), // pilot info
369 ui_button_info( "PXB_04", 115, 385, -1, -1, 4 ), // find player
370 ui_button_info( "PXB_05", 1, 443, -1, -1, 5 ), // motd
371 ui_button_info( "PXB_06", 330, 96, -1, -1, 6 ), // join channel
372 ui_button_info( "PXB_07", 330, 131, -1, -1, 7 ), // join private channel
373 ui_button_info( "PXB_08", 618, 92, -1, -1, 8 ), // scroll channels up
374 ui_button_info( "PXB_09", 618, 128, -1, -1, 9 ), // scroll channels down
375 ui_button_info( "PXB_10", 615, 171, -1, -1, 10 ), // scroll text up
376 ui_button_info( "PXB_11", 615, 355, -1, -1, 11 ), // scroll text down
377 ui_button_info( "PXB_12", 482, 435, -1, -1, 12 ), // exit
378 ui_button_info( "PXB_13", 533, 432, -1, -1, 13 ), // help
379 ui_button_info( "PXB_14", 573, 432, -1, -1, 14 ), // games list
382 ui_button_info( "2_PXB_00", 2, 166, -1, -1, 0 ), // scroll player list up
383 ui_button_info( "2_PXB_01", 2, 534, -1, -1, 1 ), // scroll player list down
384 ui_button_info( "2_PXB_02", 29, 616, -1, -1, 2 ), // rankings webpage
385 ui_button_info( "2_PXB_03", 114, 616, -1, -1, 3 ), // pilot info
386 ui_button_info( "2_PXB_04", 184, 616, -1, -1, 4 ), // find player
387 ui_button_info( "2_PXB_05", 2, 709, -1, -1, 5 ), // motd
388 ui_button_info( "2_PXB_06", 528, 119, -1, -1, 6 ), // join channel
389 ui_button_info( "2_PXB_07", 528, 175, -1, -1, 7 ), // join private channel
390 ui_button_info( "2_PXB_08", 989, 112, -1, -1, 8 ), // scroll channels up
391 ui_button_info( "2_PXB_09", 989, 170, -1, -1, 9 ), // scroll channels down
392 ui_button_info( "2_PXB_10", 984, 240, -1, -1, 10 ), // scroll text up
393 ui_button_info( "2_PXB_11", 984, 568, -1, -1, 11 ), // scroll text down
394 ui_button_info( "2_PXB_12", 771, 696, -1, -1, 12 ), // exit
395 ui_button_info( "2_PXB_13", 853, 691, -1, -1, 13 ), // help
396 ui_button_info( "2_PXB_14", 917, 691, -1, -1, 14 ), // games list
400 // define MULTI_PXO_NUM_TEXT 18
401 #define MULTI_PXO_NUM_TEXT 16
402 UI_XSTR Multi_pxo_text[GR_NUM_RESOLUTIONS][MULTI_PXO_NUM_TEXT] = {
404 {"Web", 1313, 20, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_RANKINGS].button},
405 {"Ranking", 1314, 6, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_RANKINGS].button},
406 {"Pilot", 1310, 68, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_PINFO].button},
407 {"Info", 1311, 72, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_PINFO].button},
408 {"Find", 1315, 119, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_FIND].button},
409 {"Motd", 1316, 36, 456, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_MOTD].button},
410 {"Join", 1505, 291, 100, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN].button},
411 {"Channel", 1317, 266, 112, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN].button},
412 {"Join", 1506, 291, 134, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN_PRIV].button},
413 {"Private", 1318, 273, 146, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN_PRIV].button},
414 {"Exit", 1416, 493, 424, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_EXIT].button},
415 {"Help", 928, 535, 416, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_HELP].button},
416 {"Games", 1319, 579, 416, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_GAMES].button},
417 {"Players", 1269, 29, 102, UI_XSTR_COLOR_GREEN, -1, NULL},
418 // {"Squad", 1320, 110, 101, UI_XSTR_COLOR_GREEN, -1, NULL},
419 // {"Channels", 1321, 369, 76, UI_XSTR_COLOR_GREEN, -1, NULL},
420 {"Players", 1269, 507, 90, UI_XSTR_COLOR_GREEN, -1, NULL},
421 {"Games", 1319, 568, 90, UI_XSTR_COLOR_GREEN, -1, NULL}
424 {"Web", 1313, 32, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_RANKINGS].button},
425 {"Ranking", 1314, 9, 674, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_RANKINGS].button},
426 {"Pilot", 1310, 109, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_PINFO].button},
427 {"Info", 1311, 115, 674, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_PINFO].button},
428 {"Find", 1315, 190, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_FIND].button},
429 {"Motd", 1316, 58, 729, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_MOTD].button},
430 {"Join", 1505, 488, 129, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN].button},
431 {"Channel", 1317, 461, 139, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN].button},
432 {"Join", 1506, 487, 184, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN_PRIV].button},
433 {"Private", 1318, 467, 194, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN_PRIV].button},
434 {"Exit", 1416, 789, 678, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_EXIT].button},
435 {"Help", 928, 857, 667, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_HELP].button},
436 {"Games", 1319, 917, 667, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_GAMES].button},
437 {"Players", 1269, 47, 163, UI_XSTR_COLOR_GREEN, -1, NULL},
438 // {"Squad", 1320, 176, 163, UI_XSTR_COLOR_GREEN, -1, NULL},
439 // {"Channels", 1321, 591, 86, UI_XSTR_COLOR_GREEN, -1, NULL},
440 {"Players", 1269, 852, 109, UI_XSTR_COLOR_GREEN, -1, NULL},
441 {"Games", 1319, 926, 109, UI_XSTR_COLOR_GREEN, -1, NULL}
445 char Multi_pxo_bitmap_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
449 char Multi_pxo_mask_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
454 UI_WINDOW Multi_pxo_window;
455 int Multi_pxo_bitmap = -1;
456 int Multi_pxo_palette = -1;
460 #define MULTI_PXO_ANIM_FNAME "pxologo"
461 #define MULTI_PXO_ANIM_X 0
462 #define MULTI_PXO_ANIM_Y 4
463 anim *Multi_pxo_anim = NULL;
464 anim_instance *Multi_pxo_anim_instance = NULL;
466 // rankings last clicked time
467 #define MULTI_PXO_RANK_TIME (5.0f)
468 float Multi_pxo_ranking_last = -1.0f;
471 int Multi_pxo_must_connect = 0; // if we still need to connect
472 int Multi_pxo_connected = 0; // if we are connected
473 int Multi_pxo_must_validate = 0; // if we need to validate on the tracker
474 int Multi_pxo_must_autojoin = 1; // still need to autojoin a channel
475 int Multi_pxo_must_verify_version = 1; // only do it once per instance of freespace
478 #define MULTI_PXO_MODE_NORMAL 0 // normal mode
479 #define MULTI_PXO_MODE_PRIVATE 1 // private channel popup
480 #define MULTI_PXO_MODE_FIND 2 // find player popup
481 int Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
483 // our nick for this session
485 char Multi_pxo_nick[NAME_LENGTH+1];
487 // check for button presses
488 void multi_pxo_check_buttons();
490 // handle a button press
491 void multi_pxo_button_pressed(int n);
493 // condition function for popup_do_with_condition for connected to Parallax Online
494 // return 10 : on successful connect
495 int multi_pxo_connect_do();
497 // attempt to connect to Parallax Online, return success or fail
498 int multi_pxo_connect();
500 // run the networking functions for the PXO API
501 void multi_pxo_api_process();
503 // process a "nick" change event
504 void multi_pxo_process_nick_change(char *data);
506 // run normally (no popups)
507 void multi_pxo_do_normal();
509 // blit everything on the "normal" screen
510 void multi_pxo_blit_all();
512 // process common stuff
513 void multi_pxo_process_common();
515 // get selected player information
516 void multi_pxo_get_data(char *name);
518 // handle being kicked
519 void multi_pxo_handle_kick();
521 // handle being disconnected
522 void multi_pxo_handle_disconnect();
524 // return string2, which is the first substring of string 1 without a space
525 // it is safe to pass the same pointer for both parameters
526 void multi_pxo_strip_space(char *string1,char *string2, const int str2_len);
528 // fire up the given URL
529 void multi_pxo_url(char *url);
531 // load/set the palette
532 void multi_pxo_load_palette();
534 // unload the palette
535 void multi_pxo_unload_palette();
537 // if we're currently on a private channel
538 int multi_pxo_on_private_channel();
540 // convert string 1 into string 2, substituting underscores for spaces
541 void multi_pxo_underscore_nick(char *string1, char *string2, const int str2_len);
543 // if the command is a potential "nick" command
544 int multi_pxo_is_nick_command(char *msg);
547 // status bar stuff -----------------------------------------------
548 int Multi_pxo_status_coords[GR_NUM_RESOLUTIONS][4] = {
557 // the status text itself
558 char Multi_pxo_status_text[255];
560 // set the status text
561 void multi_pxo_set_status_text(const char *txt);
563 // blit the status text
564 void multi_pxo_blit_status_text();
567 // channel related stuff -------------------------------------------
568 #define MAX_CHANNEL_NAME_LEN 32
569 #define MAX_CHANNEL_DESCRIPT_LEN 120
571 // some convenient macros
572 #define SWITCHING_CHANNELS() (Multi_pxo_channel_switch.num_users != -1)
573 #define ON_CHANNEL() (Multi_pxo_channel_current.num_users != -1)
575 typedef struct pxo_channel {
576 pxo_channel *next,*prev; // next and previous items in the list
577 char name[MAX_CHANNEL_NAME_LEN+1]; // name
578 char desc[MAX_CHANNEL_DESCRIPT_LEN+1]; // description
579 short num_users; // # users, or -1 if not in use
580 short num_servers; // the # of servers registered on this channel
583 // last channel we were on before going to the game list screen
584 char Multi_pxo_channel_last[MAX_CHANNEL_NAME_LEN+1] = "";
585 int Multi_pxo_use_last_channel = 0;
587 // all channels which are prefixed with this are "lobby" channels
588 #define MULTI_PXO_AUTOJOIN_PREFIX "#lobby"
590 // join this channel to get put in an appropriate lobby channel
591 #define MULTI_PXO_AUTOJOIN_CHANNEL "#autoselect"
593 int Multi_pxo_chan_coords[GR_NUM_RESOLUTIONS][4] = {
602 // this is the offset from the RIGHT side of the channel box
603 #define CHAN_PLAYERS_COLUMN 0
604 #define CHAN_GAMES_COLUMN 1
605 static int Multi_pxo_chan_column_offsets[GR_NUM_RESOLUTIONS][2] = {
610 #define CHANNEL_REFRESH_TIME (75.0f)
611 float Multi_pxo_channel_last_refresh = -1.0f;
613 #define CHANNEL_SERVER_REFRESH_TIME (35.0f)
614 float Multi_pxo_channel_server_refresh = -1.0f;
616 int Multi_pxo_max_chan_display[GR_NUM_RESOLUTIONS] = {
621 UI_BUTTON Multi_pxo_channel_button;
623 // head of the list of available (displayed) channels
624 pxo_channel *Multi_pxo_channels = NULL;
625 int Multi_pxo_channel_count = 0;
627 // item we're going to start displaying at
628 pxo_channel *Multi_pxo_channel_start = NULL;
629 int Multi_pxo_channel_start_index = -1;
631 // items we've currently got selected
632 pxo_channel *Multi_pxo_channel_select = NULL;
634 // channel we're currently connected to, num_users == -1, if we're not connected
635 pxo_channel Multi_pxo_channel_current;
637 // channel we're currently trying to change to, num_users == -1, if we're not trying to change channels
638 pxo_channel Multi_pxo_channel_switch;
640 // get a list of channels on the server (clear any old list as well)
641 void multi_pxo_get_channels();
643 // clear the old channel list
644 void multi_pxo_clear_channels();
646 // parse the input string and make a list of new channels
647 void multi_pxo_make_channels(char *chan_str);
649 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
650 pxo_channel *multi_pxo_add_channel(char *name, pxo_channel **list);
652 // lookup a channel with the specified name
653 pxo_channel *multi_pxo_find_channel(char *name, pxo_channel *list);
655 // process the channel list (select, etc)
656 void multi_pxo_process_channels();
658 // display the channel list
659 void multi_pxo_blit_channels();
661 // scroll channel list up
662 void multi_pxo_scroll_channels_up();
664 // scroll channel list down
665 void multi_pxo_scroll_channels_down();
667 // attempt to join a channel
668 void multi_pxo_join_channel(pxo_channel *chan);
670 // handle any processing details if we're currently trying to join a channel
671 void multi_pxo_handle_channel_change();
673 // autojoin an appropriate channel
674 void multi_pxo_autojoin();
676 // does the string match the "autojoin" prefic
677 int multi_pxo_is_autojoin(char *name);
679 // send a request to refresh our channel server counts
680 void multi_pxo_channel_refresh_servers();
682 // refresh current channel server count
683 void multi_pxo_channel_refresh_current();
686 // player related stuff -------------------------------------------
687 #define MAX_PLAYER_NAME_LEN 32
689 typedef struct player_list {
690 player_list *next,*prev;
691 char name[MAX_PLAYER_NAME_LEN+1];
694 // channel list region
695 int Multi_pxo_player_coords[GR_NUM_RESOLUTIONS][4] = {
704 int Multi_pxo_max_player_display[GR_NUM_RESOLUTIONS] = {
708 UI_BUTTON Multi_pxo_player_button;
710 // UI_SLIDER2 Multi_pxo_player_slider;
713 int Multi_pxo_player_slider_coords[GR_NUM_RESOLUTIONS][4] = {
721 const char *Multi_pxo_player_slider_name[GR_NUM_RESOLUTIONS] = {
723 "2_slider" // GR_1024
726 // head of the list of players in this channel
727 player_list *Multi_pxo_players = NULL;
728 int Multi_pxo_player_count = 0;
730 // item we're going to start displaying at
731 player_list *Multi_pxo_player_start = NULL;
732 // int Multi_pxo_player_start_index = -1;
734 // items we've currently got selected
735 player_list *Multi_pxo_player_select = NULL;
737 // clear the old player list
738 void multi_pxo_clear_players();
740 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
741 player_list *multi_pxo_add_player(char *name);
743 // remove a player with the given name
744 void multi_pxo_del_player(char *name);
746 // try and find a player with the given name, return a pointer to his entry (or NULL)
747 player_list *multi_pxo_find_player(char *name);
749 // process the player list (select, etc)
750 void multi_pxo_process_players();
752 // display the player list
753 void multi_pxo_blit_players();
755 // scroll player list up
756 void multi_pxo_scroll_players_up();
758 // scroll player list down
759 void multi_pxo_scroll_players_down();
761 // get the absolute index of the displayed items which our currently selected one is
762 int multi_pxo_get_select_index();
768 // add a bunch of bogus players
770 for(int idx=0; idx<Dc_arg_int; idx++){
771 SDL_snprintf(name, SDL_arraysize(name), "player %d", idx);
772 multi_pxo_add_player(name);
776 // chat text stuff -----------------------------------------
777 #define MAX_CHAT_LINES 60
778 #define MAX_CHAT_LINE_LEN 256
780 int Multi_pxo_chat_title_y[GR_NUM_RESOLUTIONS] = {
785 int Multi_pxo_chat_coords[GR_NUM_RESOLUTIONS][4] = {
794 int Multi_pxo_input_coords[GR_NUM_RESOLUTIONS][4] = {
803 int Multi_pxo_max_chat_display[GR_NUM_RESOLUTIONS] = {
808 // all messages from the server are prefixed with this
809 #define MULTI_PXO_SERVER_PREFIX "*** "
811 // the "has left" message from the server
812 #define MULTI_PXO_HAS_LEFT "has left"
815 #define CHAT_MODE_NORMAL 0 // normal chat from someone
816 #define CHAT_MODE_SERVER 1 // is from the server, display appropriately
817 #define CHAT_MODE_CARRY 2 // is a carryover from a previous line
818 #define CHAT_MODE_PRIVATE 3 // is a private message
819 #define CHAT_MODE_CHANNEL_SWITCH 4 // "switching channels" message - draw in red
820 #define CHAT_MODE_MOTD 5 // message of the day from the chat server
822 typedef struct chat_line {
823 chat_line *next,*prev;
824 char text[MAX_CHAT_LINE_LEN+1];
828 // the chat linked list itself
829 chat_line *Multi_pxo_chat = NULL;
831 // the current add line
832 chat_line *Multi_pxo_chat_add = NULL;
834 // the current line to start displaying from
835 chat_line *Multi_pxo_chat_start = NULL;
836 int Multi_pxo_chat_start_index = -1;
838 // input box for text
839 UI_INPUTBOX Multi_pxo_chat_input;
842 UI_SLIDER2 Multi_pxo_chat_slider;
844 int Multi_pxo_chat_slider_coords[GR_NUM_RESOLUTIONS][4] = {
853 const char *Multi_pxo_chat_slider_name[GR_NUM_RESOLUTIONS] = {
858 // how many chat lines we have
859 int Multi_pxo_chat_count = 0;
861 // extra delay time when switching channels
862 #define MULTI_PXO_SWITCH_DELAY_TIME 2000
863 int Multi_pxo_switch_delay = -1;
865 // initialize and create the chat text linked list
866 void multi_pxo_chat_init();
868 // free up all chat list stuff
869 void multi_pxo_chat_free();
871 // clear all lines of chat text in the chat area
872 void multi_pxo_chat_clear();
874 // blit the chat text
875 void multi_pxo_chat_blit();
877 // add a line of text
878 void multi_pxo_chat_add_line(char *txt,int mode);
880 // process an incoming line of text
881 void multi_pxo_chat_process_incoming(const char *txt,int mode = CHAT_MODE_NORMAL);
883 // scroll to the very bottom of the chat area
884 void multi_pxo_goto_bottom();
886 // check whether we can scroll down or not
887 int multi_pxo_can_scroll_down();
889 static int Can_scroll_down = 0;
891 // scroll the text up
892 void multi_pxo_scroll_chat_up();
894 // scroll the text down
895 void multi_pxo_scroll_chat_down();
897 // process chat controls
898 void multi_pxo_chat_process();
900 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
901 const char *multi_pxo_chat_is_private(const char *txt);
903 // if the text came from the server
904 int multi_pxo_is_server_text(const char *txt);
906 // if the text is message of the day text
907 int multi_pxo_is_motd_text(const char *txt);
909 // if the text is the end of motd text
910 int multi_pxo_is_end_of_motd_text(const char *txt);
912 // if the text is a "has left message" from the server
913 int multi_pxo_chat_is_left_message(const char *txt);
915 // recalculate the chat start index, and adjust the slider properly
916 void multi_pxo_chat_adjust_start();
919 // motd stuff ---------------------------------------------------------
920 #define MAX_PXO_MOTD_LEN 1024
921 #define PXO_MOTD_BLINK_TIME 500
922 char Pxo_motd[1024] = "";
923 int Pxo_motd_end = 0;
924 int Pxo_motd_read = 0;
925 int Pxo_motd_blink_stamp = -1;
926 int Pxo_motd_blink_on = 0;
927 int Pxo_motd_blinked_already = 0;
929 // initialize motd when going into this screen
930 void multi_pxo_motd_init();
933 void multi_pxo_motd_add_text(const char *text);
936 void multi_pxo_set_end_of_motd();
938 // display the motd dialog
939 void multi_pxo_motd_dialog();
941 // call to maybe blink the motd button
942 void multi_pxo_motd_maybe_blit();
945 // common dialog stuff ------------------------------------------------
946 const char *Multi_pxo_com_fname[GR_NUM_RESOLUTIONS] = {
950 const char *Multi_pxo_com_mask_fname[GR_NUM_RESOLUTIONS] = {
956 int Multi_pxo_com_coords[GR_NUM_RESOLUTIONS][2] = {
966 int Multi_pxo_com_input_coords[GR_NUM_RESOLUTIONS][4] = {
975 #define MULTI_PXO_COM_NUM_BUTTONS 2
976 #define MULTI_PXO_COM_CANCEL 0
977 #define MULTI_PXO_COM_OK 1
979 ui_button_info Multi_pxo_com_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_BUTTONS] = {
981 ui_button_info("PXP_00", 573, 192, -1, -1, 0),
982 ui_button_info("PXP_01", 573, 226, -1, -1, 1)
985 ui_button_info("2_PXP_00", 917, 308, -1, -1, 0),
986 ui_button_info("2_PXP_01", 917, 361, -1, -1, 1)
990 #define MULTI_PXO_COM_NUM_TEXT 2
991 UI_XSTR Multi_pxo_com_text[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_TEXT] = {
993 { "&Cancel", 645, 510, 204, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_CANCEL].button },
994 { "&Ok", 669, 548, 233, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_OK].button }
997 { "&Cancel", 645, 847, 327, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_CANCEL].button },
998 { "&Ok", 669, 877, 372, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_OK].button }
1002 int Multi_pxo_com_bitmap = -1;
1003 UI_WINDOW Multi_pxo_com_window;
1004 UI_INPUTBOX Multi_pxo_com_input;
1006 // text on the "top" half of the dialog display area
1007 char Multi_pxo_com_top_text[255];
1009 // text on the "middle" portion of the dialog display area
1010 char Multi_pxo_com_middle_text[255];
1012 // text on the "bottom" half of the dialog display area
1013 char Multi_pxo_com_bottom_text[255];
1015 int Multi_pxo_com_top_text_coords[GR_NUM_RESOLUTIONS][2] = {
1023 int Multi_pxo_com_middle_text_y[GR_NUM_RESOLUTIONS] = {
1027 int Multi_pxo_com_bottom_text_y[GR_NUM_RESOLUTIONS] = {
1032 // initialize the common dialog with the passed max input length
1033 void multi_pxo_com_init();
1035 // close down the common dialog
1036 void multi_pxo_com_close();
1038 // blit all text lines, top, middle, bottoms
1039 void multi_pxo_com_blit_text();
1041 // set the top text, shortening as necessary
1042 void multi_pxo_com_set_top_text(const char *txt);
1044 // set the middle text, shortening as necessary
1045 void multi_pxo_com_set_middle_text(const char *txt);
1047 // set the bottom text, shortening as necessary
1048 void multi_pxo_com_set_bottom_text(const char *txt);
1051 // private channel join stuff -----------------------------------------
1052 #define MULTI_PXO_PRIV_MAX_TEXT_LEN 30
1054 // max private channel name length
1055 char Multi_pxo_priv_chan[MULTI_PXO_PRIV_MAX_TEXT_LEN+100];
1057 // return code, set to something other than -1 if we're supposed to return
1058 int Multi_pxo_priv_return_code = -1;
1060 // initialize the popup
1061 void multi_pxo_priv_init();
1063 // close down the popup
1064 void multi_pxo_priv_close();
1066 // run the popup, 0 if still running, -1 if cancel, 1 if ok
1067 int multi_pxo_priv_popup();
1069 // process button presses
1070 void multi_pxo_priv_process_buttons();
1072 // handle a button press
1073 void multi_pxo_priv_button_pressed(int n);
1075 // process the inputbox
1076 void multi_pxo_priv_process_input();
1079 // find player stuff -----------------------------------------
1081 char Multi_pxo_find_channel[MAX_CHANNEL_NAME_LEN+1];
1083 // return code, set to something other than -1 if we're supposed to return
1084 int Multi_pxo_find_return_code = -1;
1086 // initialize the popup
1087 void multi_pxo_find_init();
1089 // close down the popup
1090 void multi_pxo_find_close();
1092 // run the popup, 0 if still running, -1 if cancel, 1 if ok
1093 int multi_pxo_find_popup();
1095 // process button presses
1096 void multi_pxo_find_process_buttons();
1098 // handle a button press
1099 void multi_pxo_find_button_pressed(int n);
1101 // process the inputbox
1102 void multi_pxo_find_process_input();
1104 // process search mode if applicable
1105 void multi_pxo_find_search_process();
1108 // player info stuff -----------------------------------------
1109 const char *Multi_pxo_pinfo_fname[GR_NUM_RESOLUTIONS] = {
1113 const char *Multi_pxo_pinfo_mask_fname[GR_NUM_RESOLUTIONS] = {
1119 #define MULTI_PXO_PINFO_NUM_BUTTONS 2
1120 #define MULTI_PXO_PINFO_MEDALS 0
1121 #define MULTI_PXO_PINFO_OK 1
1123 ui_button_info Multi_pxo_pinfo_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_BUTTONS] = {
1125 ui_button_info("PI2_00", 328, 446, 319, 433, 0),
1126 ui_button_info("PI2_01", 376, 446, 382, 433, 1),
1129 ui_button_info("2_PI2_00", 525, 714, 510, 695, 0),
1130 ui_button_info("2_PI2_01", 601, 714, 611, 695, 1),
1135 #define MULTI_PXO_PINFO_NUM_TEXT 2
1136 UI_XSTR Multi_pxo_pinfo_text[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_TEXT] = {
1138 { "Medals", 1037, 319, 433, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_MEDALS].button },
1139 { "Ok", 345, 382, 433, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_OK].button },
1142 { "Medals", 1037, 510, 695, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_MEDALS].button },
1143 { "Ok", 345, 611, 695, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_OK].button },
1147 int Multi_pxo_pinfo_bitmap = -1;
1148 UI_WINDOW Multi_pxo_pinfo_window;
1150 vmt_freespace2_struct Multi_pxo_pinfo;
1151 player Multi_pxo_pinfo_player;
1153 int Multi_pxo_retrieve_mode = -1;
1155 char Multi_pxo_retrieve_name[MAX_PLAYER_NAME_LEN+1];
1156 char Multi_pxo_retrieve_id[128];
1158 // stats label stuff
1159 #define MULTI_PXO_PINFO_NUM_LABELS 18
1161 int Multi_pxo_pinfo_coords[GR_NUM_RESOLUTIONS][4] = {
1169 int Multi_pxo_pinfo_val_x[GR_NUM_RESOLUTIONS] = {
1174 char *Multi_pxo_pinfo_stats_labels[MULTI_PXO_PINFO_NUM_LABELS];
1185 "Primary shots fired",
1186 "Primary shots hit",
1188 "Secondary shots fired",
1189 "Secondary shots hit",
1191 "Primary friendly hits",
1192 "Primary friendly hit %",
1193 "Secondary friendly hits",
1194 "Secondary friendly hit %"
1199 char Multi_pxo_pinfo_vals[MULTI_PXO_PINFO_NUM_LABELS][50];
1201 int Multi_pxo_pinfo_stats_spacing[MULTI_PXO_PINFO_NUM_LABELS] = {
1202 10,20,10,10,20,10,10,20,10,10,20,10,10,20,10,20,10,0
1205 // popup conditional functions, returns 10 on successful get of stats
1206 int multi_pxo_pinfo_cond();
1208 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
1209 int multi_pxo_pinfo_get(char *name);
1211 // fire up the stats view popup
1212 void multi_pxo_pinfo_show();
1214 // build the stats labels values
1215 void multi_pxo_pinfo_build_vals();
1217 // initialize the popup
1218 void multi_pxo_pinfo_init();
1221 int multi_pxo_pinfo_do();
1224 void multi_pxo_pinfo_close();
1226 // blit all the stats on this screen
1227 void multi_pxo_pinfo_blit();
1229 // run the medals screen
1230 void multi_pxo_run_medals();
1232 // notify stuff stuff -----------------------------------------
1233 #define MULTI_PXO_NOTIFY_TIME 4000
1234 #define MULTI_PXO_NOTIFY_Y 435
1236 char Multi_pxo_notify_text[255];
1237 int Multi_pxo_notify_stamp = -1;
1239 // add a notification string
1240 void multi_pxo_notify_add(const char *txt);
1242 // blit and process the notification string
1243 void multi_pxo_notify_blit();
1246 // help screen stuff -----------------------------------------
1248 const char *Multi_pxo_help_fname[GR_NUM_RESOLUTIONS] = {
1252 const char *Multi_pxo_help_mask_fname[GR_NUM_RESOLUTIONS] = {
1257 #define MULTI_PXO_HELP_NUM_BUTTONS 3
1258 #define MULTI_PXO_HELP_PREV 0
1259 #define MULTI_PXO_HELP_NEXT 1
1260 #define MULTI_PXO_HELP_CONTINUE 2
1262 ui_button_info Multi_pxo_help_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_BUTTONS] = {
1264 ui_button_info("PXH_00", 15, 389, -1, -1, 0),
1265 ui_button_info("PXH_01", 60, 389, -1, -1, 1),
1266 ui_button_info("PXH_02", 574, 431, 571, 413, 2),
1269 ui_button_info("2_PXH_00", 24, 622, -1, -1, 0),
1270 ui_button_info("2_PXH_01", 96, 622, -1, -1, 1),
1271 ui_button_info("2_PXH_02", 919, 689, 928, 663, 2),
1275 #define MULTI_PXO_HELP_NUM_TEXT 1
1276 UI_XSTR Multi_pxo_help_text[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_TEXT] = {
1278 {"Continue", 1069, 571, 413, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[0][MULTI_PXO_HELP_CONTINUE].button },
1281 {"Continue", 1069, 928, 663, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[1][MULTI_PXO_HELP_CONTINUE].button },
1286 #define MULTI_PXO_HELP_FILE "pxohelp.txt"
1287 #define MULTI_PXO_MAX_LINES_PP 57
1288 #define MULTI_PXO_MAX_PAGES 3
1290 int Multi_pxo_help_coords[GR_NUM_RESOLUTIONS][2] = {
1299 int Multi_pxo_chars_per_line[GR_NUM_RESOLUTIONS] = {
1304 int Multi_pxo_lines_pp[GR_NUM_RESOLUTIONS] = {
1310 typedef struct help_page {
1311 char *text[MULTI_PXO_MAX_LINES_PP];
1315 help_page Multi_pxo_help_pages[MULTI_PXO_MAX_PAGES];
1316 // int Multi_pxo_help_loaded = 0;
1318 int Multi_pxo_help_num_pages = 0;
1320 int Multi_pxo_help_bitmap = -1;
1321 UI_WINDOW Multi_pxo_help_window;
1323 // current page we're on
1324 int Multi_pxo_help_cur = 0;
1326 // load the help file up
1327 void multi_pxo_help_load();
1329 // blit the current page
1330 void multi_pxo_help_blit_page();
1332 // process button presses
1333 void multi_pxo_help_process_buttons();
1336 void multi_pxo_help_button_pressed(int n);
1339 // http banner stuff ---------------------------------------------
1340 InetGetFile *Multi_pxo_ban_get = NULL;
1343 #define PXO_BANNERS_CONFIG_FILE "pxobanners.cfg"
1345 // coords to display banners at
1346 int Pxo_ban_coords[GR_NUM_RESOLUTIONS][4] = {
1356 #define PXO_BAN_MODE_LIST_STARTUP 0 // start downloading list
1357 #define PXO_BAN_MODE_LIST 1 // downloading list
1358 #define PXO_BAN_MODE_IMAGES_STARTUP 2 // start downloading images
1359 #define PXO_BAN_MODE_IMAGES 3 // downloading images
1360 #define PXO_BAN_MODE_IMAGES_DONE 4 // done downloading everything - now maybe load an image
1361 #define PXO_BAN_MODE_IDLE 5 // done with everything - doing nothing
1362 #define PXO_BAN_MODE_CHOOSE_RANDOM 6 // choose a bitmap we've already downloaded at random
1364 // interface button for detecting clicks
1365 UI_BUTTON Multi_pxo_ban_button;
1368 typedef struct pxo_banner {
1369 char ban_file[MAX_FILENAME_LEN+1]; // base filename of the banner
1370 char ban_file_url[MULTI_OPTIONS_STRING_LEN+1]; // full url of the file to get (convenient)
1371 char ban_url[MULTI_OPTIONS_STRING_LEN+1]; // url to go to when clicked
1372 int ban_bitmap; // banner bitmap
1375 // active pxo banner
1376 pxo_banner Multi_pxo_banner;
1379 int Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
1382 void multi_pxo_ban_init();
1384 // process http download details
1385 void multi_pxo_ban_process();
1388 void multi_pxo_ban_close();
1390 // parse the banners file and maybe fill in Multi_pxo_dl_file[]
1391 void multi_pxo_ban_parse_banner_file(int choose_existing);
1393 // any bitmap or info or whatever
1394 void multi_pxo_ban_draw();
1396 // called when the URL button is clicked
1397 void multi_pxo_ban_clicked();
1400 // ----------------------------------------------------------------------------------------------------
1404 // initialize the PXO screen
1405 void multi_pxo_init(int use_last_channel)
1409 // load the background bitmap
1410 Multi_pxo_bitmap = bm_load(Multi_pxo_bitmap_fname[gr_screen.res]);
1411 if(Multi_pxo_bitmap < 0){
1412 // we failed to load the bitmap - this is very bad
1416 // load up the private channel bitmap
1417 Multi_pxo_com_bitmap = bm_load(Multi_pxo_com_fname[gr_screen.res]);
1418 SDL_assert(Multi_pxo_com_bitmap != -1);
1420 // create the interface window
1421 Multi_pxo_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1422 Multi_pxo_window.set_mask_bmap(Multi_pxo_mask_fname[gr_screen.res]);
1424 // multiplayer screen common palettes
1425 multi_pxo_load_palette();
1427 // create the interface buttons
1428 for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
1429 // create the object
1430 Multi_pxo_buttons[gr_screen.res][idx].button.create(&Multi_pxo_window, "", Multi_pxo_buttons[gr_screen.res][idx].x, Multi_pxo_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
1432 // set the sound to play when highlighted
1433 Multi_pxo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1435 // set the ani for the button
1436 Multi_pxo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_buttons[gr_screen.res][idx].filename);
1439 Multi_pxo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_buttons[gr_screen.res][idx].hotspot);
1443 for(idx=0; idx<MULTI_PXO_NUM_TEXT; idx++){
1444 Multi_pxo_window.add_XSTR(&Multi_pxo_text[gr_screen.res][idx]);
1447 if(use_last_channel && strlen(Multi_pxo_channel_last)){
1448 Multi_pxo_use_last_channel = 1;
1450 SDL_zero(Multi_pxo_channel_last);
1451 Multi_pxo_use_last_channel = 0;
1454 // make all scrolling buttons repeatable
1455 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_UP].button.repeatable(1);
1456 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_DOWN].button.repeatable(1);
1457 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_UP].button.repeatable(1);
1458 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_DOWN].button.repeatable(1);
1459 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_UP].button.repeatable(1);
1460 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_DOWN].button.repeatable(1);
1462 // set the mouseover cursor if it loaded ok
1463 if (Web_cursor_bitmap > 0) {
1464 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_RANKINGS].button.set_custom_cursor_bmap(Web_cursor_bitmap);
1467 // create the channel list select button and hide it
1468 Multi_pxo_channel_button.create(&Multi_pxo_window, "", Multi_pxo_chan_coords[gr_screen.res][0], Multi_pxo_chan_coords[gr_screen.res][1], Multi_pxo_chan_coords[gr_screen.res][2], Multi_pxo_chan_coords[gr_screen.res][3], 0, 1);
1469 Multi_pxo_channel_button.hide();
1471 // create the player list select button and hide it
1472 Multi_pxo_player_button.create(&Multi_pxo_window, "", Multi_pxo_player_coords[gr_screen.res][0], Multi_pxo_player_coords[gr_screen.res][1], Multi_pxo_player_coords[gr_screen.res][2], Multi_pxo_player_coords[gr_screen.res][3], 0, 1);
1473 Multi_pxo_player_button.hide();
1475 // create the chat input box
1476 Multi_pxo_chat_input.create(&Multi_pxo_window, Multi_pxo_input_coords[gr_screen.res][0], Multi_pxo_input_coords[gr_screen.res][1], Multi_pxo_input_coords[gr_screen.res][2], MAX_CHAT_LINE_LEN + 1, "", UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_EAT_USED);
1477 Multi_pxo_chat_input.set_focus();
1479 // create the banner button and hide it
1480 Multi_pxo_ban_button.create(&Multi_pxo_window, "", Pxo_ban_coords[gr_screen.res][0], Pxo_ban_coords[gr_screen.res][1], Pxo_ban_coords[gr_screen.res][2], Pxo_ban_coords[gr_screen.res][3], 0, 1);
1481 Multi_pxo_ban_button.hide();
1483 // create the player list slider
1484 // Multi_pxo_player_slider.create(&Multi_pxo_window, Multi_pxo_player_slider_coords[gr_screen.res][0], Multi_pxo_player_slider_coords[gr_screen.res][1], Multi_pxo_player_slider_coords[gr_screen.res][2], Multi_pxo_player_slider_coords[gr_screen.res][3], 0, Multi_pxo_player_slider_name[gr_screen.res], multi_pxo_scroll_players_up, multi_pxo_scroll_players_down, NULL);
1486 // create the chat slider
1487 Multi_pxo_chat_slider.create(&Multi_pxo_window, Multi_pxo_chat_slider_coords[gr_screen.res][0], Multi_pxo_chat_slider_coords[gr_screen.res][1], Multi_pxo_chat_slider_coords[gr_screen.res][2], Multi_pxo_chat_slider_coords[gr_screen.res][3], 0, Multi_pxo_chat_slider_name[gr_screen.res], multi_pxo_scroll_chat_up, multi_pxo_scroll_chat_down, NULL);
1489 // set our connection status so that we do the right stuff next frame
1490 Multi_pxo_must_validate = 1;
1491 Multi_pxo_must_connect = 0;
1492 Multi_pxo_connected = 0;
1494 // channel we're currently connected to
1495 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
1496 Multi_pxo_channel_current.num_users = -1;
1498 // channel we're currently trying to change to, or NULL if nont
1499 memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
1500 Multi_pxo_channel_switch.num_users = -1;
1502 // last time clicked the url button (so we don't have repeats)
1503 Multi_pxo_ranking_last = -1.0f;
1505 // channel switching extra time delay stamp
1506 Multi_pxo_switch_delay = -1;
1508 // our nick for this session
1509 multi_pxo_underscore_nick(Player->callsign, Multi_pxo_nick, SDL_arraysize(Multi_pxo_nick));
1511 // clear the channel list
1512 multi_pxo_clear_channels();
1514 // clear the player list
1515 multi_pxo_clear_players();
1517 // initialize the chat system
1518 multi_pxo_chat_init();
1521 multi_pxo_ban_init();
1523 // load the animation up
1524 if (gr_screen.res == GR_1024) {
1525 char anim_filename[32] = "2_";
1526 SDL_strlcat(anim_filename, MULTI_PXO_ANIM_FNAME, SDL_arraysize(anim_filename));
1527 Multi_pxo_anim = anim_load(anim_filename);
1529 // if hi-res is not there, fallback to low
1530 if (Multi_pxo_anim == NULL) {
1531 Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME);
1534 Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME);
1537 // clear the status text
1538 multi_pxo_set_status_text("");
1540 // last refresh time
1541 Multi_pxo_channel_last_refresh = -1.0f;
1543 // server count last refresh time
1544 Multi_pxo_channel_server_refresh = -1.0f;
1547 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1550 multi_pxo_motd_init();
1552 // make sure we autojoin
1553 Multi_pxo_must_autojoin = 1;
1555 // clear all tracker channel related strings
1556 SDL_zero(Multi_fs_tracker_channel);
1557 SDL_zero(Multi_fs_tracker_filter);
1560 // do frame for the PXO screen
1563 pxo_channel priv_chan;
1566 if(Multi_pxo_connected) {
1567 multi_pxo_api_process();
1570 // process common stuff
1571 multi_pxo_process_common();
1573 switch(Multi_pxo_mode){
1574 // private channel join mode
1575 case MULTI_PXO_MODE_PRIVATE:
1576 switch(multi_pxo_priv_popup()){
1581 // user hit "cancel"
1583 // return to normal mode
1584 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1589 // setup some information
1590 memset(&priv_chan,0,sizeof(pxo_channel));
1591 priv_chan.num_users = 0;
1592 SDL_strlcpy(priv_chan.name, Multi_pxo_priv_chan, SDL_arraysize(priv_chan.name));
1594 // see if we know about this channel already
1595 multi_pxo_join_channel(&priv_chan);
1597 // return to normal mode
1598 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1604 case MULTI_PXO_MODE_FIND:
1605 switch(multi_pxo_find_popup()){
1610 // user hit "cancel"
1612 // return to normal mode
1613 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1618 // return to normal mode
1619 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1621 // if there is a valid channel name try and join it
1622 if(strlen(Multi_pxo_find_channel) && !SWITCHING_CHANNELS()){
1626 memset(&join,0,sizeof(pxo_channel));
1628 SDL_strlcpy(join.name, Multi_pxo_find_channel, SDL_arraysize(join.name));
1631 multi_pxo_join_channel(&join);
1637 case MULTI_PXO_MODE_NORMAL:
1638 multi_pxo_do_normal();
1643 // close the PXO screen
1644 void multi_pxo_close()
1646 // unload any bitmaps
1647 bm_unload(Multi_pxo_bitmap);
1648 bm_unload(Multi_pxo_com_bitmap);
1650 // record the last channel we were on, if any
1651 SDL_zero(Multi_fs_tracker_channel);
1652 SDL_zero(Multi_fs_tracker_filter);
1654 if( ON_CHANNEL() && strlen(Multi_pxo_channel_current.name) ){
1656 SDL_strlcpy(Multi_fs_tracker_channel, Multi_pxo_channel_current.name, SDL_arraysize(Multi_fs_tracker_channel));
1659 SDL_strlcpy(Multi_fs_tracker_filter, Multi_pxo_channel_current.name, SDL_arraysize(Multi_fs_tracker_filter));
1662 // disconnect from the server
1663 DisconnectFromChatServer();
1664 Multi_pxo_connected = 0;
1666 // unload the animation
1667 anim_release_all_instances(GS_STATE_PXO);
1668 Multi_pxo_anim_instance = NULL;
1669 if(Multi_pxo_anim != NULL){
1670 anim_free(Multi_pxo_anim);
1671 Multi_pxo_anim = NULL;
1674 // unload the palette for this screen
1675 multi_pxo_unload_palette();
1677 // destroy the UI_WINDOW
1678 Multi_pxo_window.destroy();
1680 // clear the channel list
1681 multi_pxo_clear_channels();
1683 // close the chat system
1684 multi_pxo_chat_free();
1687 multi_pxo_ban_close();
1690 // run normally (no popups)
1691 void multi_pxo_do_normal()
1694 int k = Multi_pxo_window.process();
1696 // if the animation isn't playing, start it up
1697 if((Multi_pxo_anim_instance == NULL) && (Multi_pxo_anim != NULL)){
1698 anim_play_struct aps;
1700 // fire up the animation
1701 anim_play_init(&aps, Multi_pxo_anim, MULTI_PXO_ANIM_X, MULTI_PXO_ANIM_Y);
1702 aps.screen_id = GS_STATE_PXO;
1703 aps.framerate_independent = 1;
1705 Multi_pxo_anim_instance = anim_play(&aps);
1708 // process any keypresses
1711 gamesnd_play_iface(SND_USER_SELECT);
1712 gameseq_post_event(GS_EVENT_MAIN_MENU);
1716 // check for button presses
1717 multi_pxo_check_buttons();
1719 // if we're not in a chatroom, disable and hide the chat input box
1721 Multi_pxo_chat_input.hide();
1722 Multi_pxo_chat_input.disable();
1724 Multi_pxo_chat_input.enable();
1725 Multi_pxo_chat_input.unhide();
1729 multi_pxo_blit_all();
1734 // verify version # now (only once per Freespace instance)
1735 if(Multi_pxo_must_verify_version){
1736 switch(multi_update_gobaby()){
1737 // everything is cool. Move along
1738 case MULTI_UPDATE_CONTINUE:
1739 Multi_pxo_must_verify_version = 0;
1742 // go back to the main menu
1743 case MULTI_UPDATE_MAIN_MENU:
1744 gameseq_post_event(GS_EVENT_MAIN_MENU);
1746 // unset these so we don't do anything else PXO related
1747 Multi_pxo_must_validate = 0;
1748 Multi_pxo_must_connect = 0;
1751 // freespace will be shutting down shortly
1752 case MULTI_UPDATE_SHUTTING_DOWN:
1757 // if we need to get tracker info for ourselves, do so
1758 if(Multi_pxo_must_validate){
1759 // initialize the master tracker API for Freespace
1760 multi_fs_tracker_init();
1762 // validate the current player with the master tracker (will create the pilot on the MT if necessary)
1763 validate_code = multi_fs_tracker_validate(0);
1764 if(validate_code != 1){
1765 // show an error popup if it failed (not cancelled by the user)
1766 if (validate_code == 0) {
1767 switch (popup(PF_USE_AFFIRMATIVE_ICON | PF_WEB_CURSOR_1 | PF_WEB_CURSOR_2, 3, POPUP_CANCEL,XSTR("&Create Acct",936), XSTR("&Verify Acct",937), XSTR("PXO Login not accepted. You may visit the Parallax Online website to create or verify your login. Or you may click Cancel to play without using the Parallax Online service. (You may switch back to Parallax Online from the Options Menu under the Multi tab.)",938))) {
1769 nprintf(("Network","PXO CANCEL\n"));
1771 // flip his "pxo" bit temporarily and push him to the join game screen
1772 Multi_options_g.pxo = 0;
1773 // Net_game_tcp_mode = NET_TCP;
1774 gameseq_post_event(GS_EVENT_MULTI_JOIN_GAME);
1778 nprintf(("Network","PXO CREATE\n"));
1779 // fire up the given URL
1780 multi_pxo_url(Multi_options_g.pxo_create_url);
1784 nprintf(("Network","PXO VERIFY\n"));
1785 // fire up the given URL
1786 multi_pxo_url(Multi_options_g.pxo_verify_url);
1791 // go back to the main hall
1792 gameseq_post_event(GS_EVENT_MAIN_MENU);
1794 Multi_pxo_must_connect = 0;
1795 Multi_pxo_must_validate = 0;
1797 // now we have to conenct to PXO
1799 Multi_pxo_must_connect = 1;
1800 Multi_pxo_must_validate = 0;
1804 // if we need to connect, do so now
1805 if(Multi_pxo_must_connect){
1806 // for now, just try once
1807 Multi_pxo_connected = multi_pxo_connect();
1809 // if we successfully connected, send a request for a list of channels on the server
1810 if(Multi_pxo_connected){
1811 multi_pxo_get_channels();
1814 multi_pxo_set_status_text(XSTR("Retrieving Public Channels",939));
1817 multi_pxo_set_status_text(XSTR("Failed to connect to Parallax Online",940));
1820 // no longer need to connect
1821 Multi_pxo_must_connect = 0;
1825 // blit everything on the "normal" screen
1826 void multi_pxo_blit_all()
1828 // draw the background, etc
1830 // GR_MAYBE_CLEAR_RES(Multi_pxo_bitmap);
1831 int bmap = Multi_pxo_bitmap;
1836 bm_get_info( bmap, &bmw, &bmh);
1837 if((bmw != gr_screen.max_w) || (bmh != gr_screen.max_h)){
1844 if(Multi_pxo_bitmap != -1){
1845 gr_set_bitmap(Multi_pxo_bitmap);
1848 Multi_pxo_window.draw();
1850 // display the channel list
1851 multi_pxo_blit_channels();
1853 // display the player list
1854 multi_pxo_blit_players();
1856 // blit the chat text
1857 multi_pxo_chat_blit();
1859 // blit the status text
1860 multi_pxo_blit_status_text();
1862 // blit and process the notification string
1863 multi_pxo_notify_blit();
1865 // any bitmap or info or whatever
1866 multi_pxo_ban_draw();
1868 // draw any motd stuff
1869 multi_pxo_motd_maybe_blit();
1871 // if we have a valid animation handle, play it
1872 if(Multi_pxo_anim_instance != NULL){
1873 anim_render_all(GS_STATE_PXO,flFrametime);
1877 // process common stuff
1878 void multi_pxo_process_common()
1880 // process the channel list (select, etc)
1881 multi_pxo_process_channels();
1883 // process the player list (select, etc)
1884 multi_pxo_process_players();
1886 // process chat controls
1887 multi_pxo_chat_process();
1889 // process http download details
1890 multi_pxo_ban_process();
1893 // get selected player information
1894 void multi_pxo_get_data(char *name)
1898 // handle being kicked
1899 void multi_pxo_handle_kick()
1901 // remove ourselves from the room
1902 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
1903 Multi_pxo_channel_current.num_users = -1;
1906 multi_pxo_chat_clear();
1908 // clear the old player list
1909 multi_pxo_clear_players();
1911 // add a notification string
1912 multi_pxo_notify_add(XSTR("You have been kicked",941));
1915 // handle being disconnected
1916 void multi_pxo_handle_disconnect()
1918 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been disconnected from the server",942));
1919 gameseq_post_event(GS_EVENT_MAIN_MENU);
1922 // return string2, which is the first substring of string 1 without a space
1923 // it is safe to pass the same pointer for both parameters
1924 void multi_pxo_strip_space(char *string1, char *string2, const int str2_len)
1929 // copy the original
1930 SDL_strlcpy(midway, string1, SDL_arraysize(midway));
1931 tok = strtok(midway," ");
1933 SDL_strlcpy(string2, tok, str2_len);
1935 SDL_strlcpy(string2, "", str2_len);
1939 // fire up the given URL
1940 void multi_pxo_url(char *url)
1942 if ( platform_open_url(url) ) {
1943 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,XSTR("Warning\nCould not locate/launch default Internet Browser",943));
1947 // load/set the palette
1948 void multi_pxo_load_palette()
1951 #ifndef HARDWARE_ONLY
1952 palette_use_bm_palette(Multi_pxo_palette);
1956 // unload the palette
1957 void multi_pxo_unload_palette()
1959 // unload the palette if it exists
1960 if(Multi_pxo_palette != -1){
1961 bm_release(Multi_pxo_palette);
1962 Multi_pxo_palette = -1;
1966 // if we're currently on a private channel
1967 int multi_pxo_on_private_channel()
1969 // if we're connected to a channel with the "+" symbol on front
1970 if(ON_CHANNEL() && (Multi_pxo_channel_current.name[0] == '+')){
1974 // otherwise return falos
1978 // convert string 1 into string 2, substituting underscores for spaces
1979 void multi_pxo_underscore_nick(char *string1, char *string2, const int str2_len)
1981 char nick_temp[512];
1984 // don't do anything if we have bogus string
1985 if((string1 == NULL) || (string2 == NULL)){
1989 // copy the nickname
1990 SDL_strlcpy(nick_temp, string1, SDL_arraysize(nick_temp));
1992 // get the first token
1993 tok = strtok(nick_temp, " ");
1995 SDL_strlcpy(string2, tok, str2_len);
1997 // get the next token
1998 tok = strtok(NULL," ");
2001 SDL_strlcat(string2, "_", str2_len);
2002 SDL_strlcat(string2, tok, str2_len);
2005 tok = strtok(NULL," ");
2008 SDL_strlcpy(string2, string1, str2_len);
2012 // if the command is a potential "nick" command
2013 int multi_pxo_is_nick_command(char *msg)
2018 // get the first token in the message
2019 SDL_strlcpy(tmp, msg, SDL_arraysize(tmp));
2020 tok = strtok(tmp," ");
2022 // can't be a nick message
2026 return !SDL_strcasecmp(tok,NOX("/nick"));
2029 // check for button presses
2030 void multi_pxo_check_buttons()
2034 // go through all buttons
2035 for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
2036 if(Multi_pxo_buttons[gr_screen.res][idx].button.pressed()){
2037 multi_pxo_button_pressed(idx);
2043 // handle a button press
2044 void multi_pxo_button_pressed(int n)
2047 case MULTI_PXO_EXIT:
2048 gamesnd_play_iface(SND_USER_SELECT);
2049 gameseq_post_event(GS_EVENT_MAIN_MENU);
2052 case MULTI_PXO_CHAN_UP:
2053 multi_pxo_scroll_channels_up();
2056 case MULTI_PXO_CHAN_DOWN:
2057 multi_pxo_scroll_channels_down();
2060 case MULTI_PXO_TEXT_UP:
2061 multi_pxo_scroll_chat_up();
2064 case MULTI_PXO_TEXT_DOWN:
2065 multi_pxo_scroll_chat_down();
2068 case MULTI_PXO_PLIST_UP:
2069 multi_pxo_scroll_players_up();
2070 multi_pxo_chat_adjust_start();
2073 case MULTI_PXO_PLIST_DOWN:
2074 multi_pxo_scroll_players_down();
2075 multi_pxo_chat_adjust_start();
2078 case MULTI_PXO_JOIN:
2079 // if there are no channels to join, let the user know
2080 if((Multi_pxo_channel_count == 0) || (Multi_pxo_channels == NULL)){
2081 gamesnd_play_iface(SND_GENERAL_FAIL);
2082 multi_pxo_notify_add(XSTR("No channels!",944));
2086 // if we're not already trying to join, allow this
2087 if(!SWITCHING_CHANNELS() && (Multi_pxo_channel_select != NULL)){
2088 gamesnd_play_iface(SND_USER_SELECT);
2089 multi_pxo_join_channel(Multi_pxo_channel_select);
2091 multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2092 gamesnd_play_iface(SND_GENERAL_FAIL);
2096 case MULTI_PXO_GAMES:
2097 // move to the join game screen as normally (temporary!)
2098 gameseq_post_event( GS_EVENT_MULTI_JOIN_GAME );
2101 case MULTI_PXO_JOIN_PRIV:
2102 // if we're not already trying to join, allow this
2103 if(!SWITCHING_CHANNELS()){
2104 gamesnd_play_iface(SND_USER_SELECT);
2106 // fire up the private join popup
2107 multi_pxo_priv_popup();
2109 multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2110 gamesnd_play_iface(SND_GENERAL_FAIL);
2114 case MULTI_PXO_FIND:
2115 gamesnd_play_iface(SND_USER_SELECT);
2117 // fire up the find join popup
2118 multi_pxo_find_popup();
2121 case MULTI_PXO_HELP:
2122 gamesnd_play_iface(SND_USER_SELECT);
2123 gameseq_post_event(GS_EVENT_PXO_HELP);
2126 case MULTI_PXO_PINFO:
2129 // if we have a guy selected, try and get his info
2130 if(Multi_pxo_player_select != NULL){
2131 // if we successfully got info for this guy
2132 if(multi_pxo_pinfo_get(Multi_pxo_player_select->name)){
2134 multi_pxo_pinfo_show();
2136 // if we didn't get stats for this guy.
2138 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);
2139 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,stats);
2142 gamesnd_play_iface(SND_GENERAL_FAIL);
2146 case MULTI_PXO_RANKINGS:
2147 // make sure he doesn't click it too many times
2148 if((Multi_pxo_ranking_last < 0.0f) || ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_ranking_last) > MULTI_PXO_RANK_TIME) ){
2149 gamesnd_play_iface(SND_USER_SELECT);
2152 multi_pxo_url(Multi_options_g.pxo_rank_url);
2154 // mark the time down
2155 Multi_pxo_ranking_last = f2fl(timer_get_fixed_seconds());
2157 gamesnd_play_iface(SND_GENERAL_FAIL);
2161 case MULTI_PXO_MOTD:
2162 // maybe fire up the pxo motd dialog
2163 multi_pxo_motd_dialog();
2168 // condition function for popup_do_with_condition for connected to Parallax Online
2169 int mpxo_failed = 0;
2170 int multi_pxo_connect_do()
2173 char id_string[255] = "";
2174 char ip_string[255] = "";
2176 // if we already tried and failed, sit around until the user presses cancel
2178 // try and connect to the server
2181 // build the tracker id string
2182 SDL_snprintf(id_string, SDL_arraysize(id_string), "%s %s", Multi_tracker_id_string, Player->callsign);
2184 // build the ip string
2185 SDL_snprintf(ip_string, SDL_arraysize(ip_string), "%s:%d", Multi_options_g.pxo_ip, PXO_CHAT_PORT);
2187 // connect to the server
2188 ret_code = ConnectToChatServer(ip_string, Multi_pxo_nick, id_string);
2190 // give some time to the pxo api.
2191 multi_pxo_api_process();
2194 // already connected, return success
2198 // failed to connect, return fail
2201 popup_change_text(XSTR("Failed to connect to Parallax Online!", 947));
2204 // connected, return success
2217 // popup loop which does an autojoin of a public channel. Returns when the autojoin process is complete
2218 int multi_pxo_autojoin_do()
2220 pxo_channel last_channel;
2222 // if we need to autojoin, do so now
2223 if(Multi_pxo_must_autojoin){
2224 Multi_pxo_must_autojoin = 0;
2226 // if we're supposed to be using a (valid) "last" channel, do so
2227 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){
2229 memset(&last_channel, 0, sizeof(pxo_channel));
2230 last_channel.num_users = 0;
2231 SDL_strlcpy(last_channel.name, Multi_pxo_channel_last, SDL_arraysize(last_channel.name));
2234 multi_pxo_join_channel(&last_channel);
2236 nprintf(("Network","PXO : using last channel\n"));
2238 multi_pxo_autojoin();
2240 nprintf(("Network","PXO : using autojoin channel\n"));
2242 multi_pxo_get_channels();
2245 // give some time to the pxo api.
2246 multi_pxo_api_process();
2247 multi_pxo_process_common();
2249 // next value is not -1 when actually switching channels, so keep processing by returning 0.
2250 if ( SWITCHING_CHANNELS() ){
2254 // couldn't switch channel for some reason. bail out with -1
2255 if ( !ON_CHANNEL() ){
2263 // attempt to connect to Parallax Online, return success or fail
2264 int multi_pxo_connect()
2267 char join_fail_str[256];
2269 // intiialize chat api
2272 // set us to "must autojoin"
2273 Multi_pxo_must_autojoin = 1;
2275 // run the connect dialog/popup
2277 if(popup_till_condition(multi_pxo_connect_do, XSTR("&Cancel", 779), XSTR("Logging into Parallax Online",949)) == 10){
2280 // if we're going to use the "last" channel
2281 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){
2282 SDL_strlcpy(join_str, XSTR("Joining last channel (",982), SDL_arraysize(join_str));
2283 SDL_strlcat(join_str, Multi_pxo_channel_last + 1, SDL_arraysize(join_str));
2284 SDL_strlcat(join_str, ")", SDL_arraysize(join_str));
2286 SDL_strlcpy(join_fail_str, XSTR("Unable to join last channel", 983), SDL_arraysize(join_fail_str));
2288 SDL_strlcpy(join_str, XSTR("Autojoining public channel", 984), SDL_arraysize(join_str));
2289 SDL_strlcpy(join_fail_str, XSTR("Unable to autojoin public channel", 985), SDL_arraysize(join_fail_str));
2292 // once connected, we should do an autojoin before allowing the guy to continue.
2293 rval = popup_till_condition( multi_pxo_autojoin_do, XSTR("&Cancel", 779), join_str );
2298 popup( PF_USE_AFFIRMATIVE_ICON, 1, XSTR("OK", 1492), join_fail_str);
2301 // otherwise disconnect just to be safe
2302 DisconnectFromChatServer();
2303 gameseq_post_event(GS_EVENT_MAIN_MENU);
2305 // did not successfully connect
2309 // run the networking functions for the PXO API
2310 void multi_pxo_api_process()
2315 pxo_channel *lookup;
2317 // give some time to psnet
2318 PSNET_TOP_LAYER_PROCESS();
2320 // give some time to the game tracker API
2323 // give some time to the user tracker API
2326 // get any incoming text
2332 // process the chat line
2333 multi_pxo_chat_process_incoming(p);
2337 // get any incoming channel list stuff
2338 p = GetChannelList();
2341 // nprintf(("Network","%s\n",p));
2342 multi_pxo_make_channels(p);
2345 // process any chat commands
2346 cmd = GetChatCommand();
2349 switch(cmd->command)
2351 case CC_USER_JOINING:
2352 // add a user, if he doesn't already exist
2353 if(multi_pxo_find_player(cmd->data) == NULL){
2354 multi_pxo_add_player(cmd->data);
2357 // increase the player count
2359 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2361 lookup->num_users++;
2366 case CC_USER_LEAVING:
2368 multi_pxo_del_player(cmd->data);
2370 // add a text message
2371 SDL_snprintf(msg_str, SDL_arraysize(msg_str), XSTR("*** %s has left", 950), cmd->data);
2372 multi_pxo_chat_process_incoming(msg_str);
2374 // decrease the player count
2376 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2378 lookup->num_users--;
2383 case CC_DISCONNECTED:
2384 multi_pxo_handle_disconnect();
2388 multi_pxo_handle_kick();
2391 case CC_NICKCHANGED:
2392 // process a nick change
2393 multi_pxo_process_nick_change(cmd->data);
2396 case CC_YOURCHANNEL:
2397 // copy the current channel info, and unset the switching status
2398 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2399 Multi_pxo_channel_switch.num_users = -1;
2401 SetNewChatChannel(NULL);
2403 SDL_strlcpy(Multi_pxo_channel_current.name, cmd->data, SDL_arraysize(Multi_pxo_channel_current.name));
2405 // if we don't already have this guy on the list, add him
2406 pxo_channel *lookup;
2407 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2409 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2410 lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2413 // set the user count to be 0
2415 lookup->num_users = 0;
2418 // set our "last" channel to be this one
2419 SDL_strlcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name, SDL_arraysize(Multi_pxo_channel_last));
2421 // refresh current channel server count
2422 multi_pxo_channel_refresh_current();
2424 // clear the chat area
2425 // multi_pxo_chat_clear();
2432 cmd = GetChatCommand();
2435 // handle any processing details if we're currently trying to join a channel
2436 multi_pxo_handle_channel_change();
2439 // process a "nick" change event
2440 void multi_pxo_process_nick_change(char *data)
2443 player_list *lookup;
2445 // get the new string
2446 from = strtok(data," ");
2447 to = strtok(NULL,"");
2448 if((from != NULL) && (to != NULL)){
2449 lookup = multi_pxo_find_player(from);
2451 SDL_strlcpy(lookup->name, to, SDL_arraysize(lookup->name));
2453 // if this is also my nick, change it
2454 if(!SDL_strcasecmp(Multi_pxo_nick,from)){
2455 SDL_strlcpy(Multi_pxo_nick, to, SDL_arraysize(Multi_pxo_nick));
2461 // autojoin an appropriate channel
2462 void multi_pxo_autojoin()
2466 memset(&sw,0,sizeof(pxo_channel));
2468 SDL_strlcpy(sw.name, MULTI_PXO_AUTOJOIN_CHANNEL, SDL_arraysize(sw.name));
2470 // if we found a valid room, attempt to join it
2471 multi_pxo_join_channel(&sw);
2474 // does the string match the "autojoin" prefic
2475 int multi_pxo_is_autojoin(char *name)
2477 // check to see if the name is long enough
2478 if(strlen(name) < strlen(MULTI_PXO_AUTOJOIN_PREFIX)){
2482 // check to see if the first n chars match
2483 return !SDL_strncasecmp(name,MULTI_PXO_AUTOJOIN_PREFIX,strlen(MULTI_PXO_AUTOJOIN_PREFIX));
2486 // called from the game tracker API - server count update for a channel
2487 void multi_pxo_channel_count_update(char *name,int count)
2489 pxo_channel *lookup;
2491 // lookup the channel name on the normal list
2493 lookup = multi_pxo_find_channel(name,Multi_pxo_channels);
2495 lookup->num_servers = (ushort)count;
2497 nprintf(("Network","PXO : updated channel %s server count to %d\n",name,count));
2501 // status bar stuff -----------------------------------------------
2503 // set the status text
2504 void multi_pxo_set_status_text(const char *txt)
2507 SDL_strlcpy(Multi_pxo_status_text, txt, SDL_arraysize(Multi_pxo_status_text));
2509 // make sure it fits properly
2510 gr_force_fit_string(Multi_pxo_status_text, 254, Multi_pxo_status_coords[gr_screen.res][2]);
2513 // blit the status text
2514 void multi_pxo_blit_status_text()
2518 // center and draw the text
2519 if(strlen(Multi_pxo_status_text)) {
2520 gr_set_color_fast(&Color_bright);
2521 gr_get_string_size(&w, NULL, Multi_pxo_status_text);
2522 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);
2527 // channel related stuff -------------------------------------------
2529 // get a list of channels on the server
2530 void multi_pxo_get_channels()
2532 SendChatString(NOX("/list"));
2535 // clear the old channel list
2536 void multi_pxo_clear_channels()
2538 pxo_channel *moveup,*backup;
2540 // only clear a non-null list
2541 if(Multi_pxo_channels != NULL){
2543 moveup = Multi_pxo_channels;
2548 moveup = moveup->next;
2550 // free the struct itself
2553 } while(moveup != Multi_pxo_channels);
2554 Multi_pxo_channels = NULL;
2557 // head of the list of available channels
2558 Multi_pxo_channels = NULL;
2559 Multi_pxo_channel_count = 0;
2561 // item we're going to start displaying at
2562 Multi_pxo_channel_start = NULL;
2563 Multi_pxo_channel_start_index = -1;
2565 // items we've currently got selected
2566 Multi_pxo_channel_select = NULL;
2570 // parse the input string and make a list of new channels
2571 void multi_pxo_make_channels(char *chan_str)
2573 char *name_tok,*user_tok,*desc_tok;
2575 pxo_channel *lookup;
2578 nprintf(("Network","Making some channels!\n"));
2580 // clear the channel list
2581 // multi_pxo_clear_channels();
2583 // set the last get time
2584 Multi_pxo_channel_last_refresh = f2fl(timer_get_fixed_seconds());
2586 name_tok = strtok(chan_str," ");
2587 if(name_tok == NULL){
2592 // parse the user count token
2593 user_tok = strtok(NULL," ");
2595 // parse the channel description token
2596 desc_tok = strtok(NULL,"$");
2598 // something invalid in the data, return here.....
2599 if((name_tok == NULL) || (user_tok == NULL) || (desc_tok == NULL)){
2603 // get the # of users
2605 num_users = (ubyte)atoi(user_tok);
2607 // if the # of users is > 0, or its not an autojoin, place it on the display list
2608 if((num_users > 0) || !multi_pxo_is_autojoin(name_tok)){
2609 // see if it exists already, and if so, just update the user count
2610 lookup = multi_pxo_find_channel(name_tok,Multi_pxo_channels);
2613 lookup->num_users = (short)num_users;
2617 res = multi_pxo_add_channel(name_tok,&Multi_pxo_channels);
2619 //Multi_pxo_channel_count++;
2620 res->num_users = (short)num_users;
2621 SDL_strlcpy(res->desc, desc_tok, SDL_arraysize(res->desc));
2626 // get the next name token
2627 name_tok = strtok(NULL," ");
2628 } while(name_tok != NULL);
2630 // if we need to autojoin, do so now
2631 //if(Multi_pxo_must_autojoin){
2632 // Multi_pxo_must_autojoin = 0;
2634 // multi_pxo_autojoin();
2638 multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
2640 // if we haven't refreshed server counts yet, do it now
2641 if(Multi_pxo_channel_server_refresh < 0.0f){
2642 multi_pxo_channel_refresh_servers();
2645 // if we don't already have this guy on the list, add him
2647 pxo_channel *lookup;
2648 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2650 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2651 multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2656 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2657 pxo_channel *multi_pxo_add_channel(char *name,pxo_channel **list)
2659 pxo_channel *new_channel;
2661 // try and allocate a new pxo_channel struct
2662 new_channel = (pxo_channel *)malloc(sizeof(pxo_channel));
2663 if ( new_channel == NULL ) {
2664 nprintf(("Network", "Cannot allocate space for new pxo_channel structure\n"));
2667 memset(new_channel,0,sizeof(pxo_channel));
2668 // try and allocate a string for the channel name
2669 SDL_strlcpy(new_channel->name, name, SDL_arraysize(new_channel->name));
2671 // insert it on the list
2672 if ( *list != NULL ) {
2673 new_channel->next = (*list)->next;
2674 new_channel->next->prev = new_channel;
2675 (*list)->next = new_channel;
2676 new_channel->prev = *list;
2678 *list = new_channel;
2679 (*list)->next = (*list)->prev = *list;
2682 Multi_pxo_channel_count++;
2686 // lookup a channel with the specified name
2687 pxo_channel *multi_pxo_find_channel(char *name,pxo_channel *list)
2689 pxo_channel *moveup;
2691 // look the sucker up
2697 if(!SDL_strcasecmp(name,moveup->name)){
2701 moveup = moveup->next;
2702 } while((moveup != list) && (moveup != NULL));
2707 // process the channel list (select, etc)
2708 void multi_pxo_process_channels()
2713 // if we don't have a start item, but the list is non-null
2714 if((Multi_pxo_channel_start == NULL) && (Multi_pxo_channels != NULL)){
2715 Multi_pxo_channel_start = Multi_pxo_channels;
2716 Multi_pxo_channel_start_index = 0;
2719 // if we don't have a selected item, but the list is non-null
2720 if((Multi_pxo_channel_select == NULL) && (Multi_pxo_channels != NULL)){
2721 Multi_pxo_channel_select = Multi_pxo_channels;
2724 multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2727 // if the "switch" delay timestamp is set, see if it has expired
2728 if((Multi_pxo_switch_delay != -1) && timestamp_elapsed(Multi_pxo_switch_delay)){
2729 Multi_pxo_switch_delay = -1;
2732 // see if we have a mouse click on the channel region
2733 if(Multi_pxo_channel_button.pressed()){
2734 Multi_pxo_channel_button.get_mouse_pos(NULL,&my);
2736 // index from the top
2737 item_index = my / 10;
2739 // select the item if possible
2740 if((item_index + Multi_pxo_channel_start_index) < Multi_pxo_channel_count){
2741 Multi_pxo_channel_select = Multi_pxo_channel_start;
2742 for(idx=0;idx<item_index;idx++){
2743 Multi_pxo_channel_select = Multi_pxo_channel_select->next;
2747 multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2751 // last refresh time
2752 if((Multi_pxo_channel_last_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_last_refresh) > CHANNEL_REFRESH_TIME) ){
2754 multi_pxo_set_status_text(XSTR("Refreshing Public Channel List",952));
2756 // get a list of channels on the server (clear any old list as well)
2757 multi_pxo_get_channels();
2760 Multi_pxo_channel_last_refresh = -1.0f;
2762 nprintf(("Network","Refreshing channels\n"));
2765 // if we haven't updated our server channel counts in a while, do so again
2766 // last refresh time
2767 if((Multi_pxo_channel_server_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_server_refresh) > CHANNEL_SERVER_REFRESH_TIME) ){
2768 // refresh server counts
2769 // multi_pxo_set_status_text("Refreshing Public Channel Server Counts");
2771 // do it _NOW_ I"M RIGHT HERE KILL ME WHAT ARE YOU WAITING FOR DO IT KILL ME DO IT NOW!
2772 multi_pxo_channel_refresh_servers();
2776 // send a request to refresh our channel server counts
2777 void multi_pxo_channel_refresh_servers()
2779 pxo_channel *lookup;
2780 filter_game_list_struct filter;
2782 // traverse the list of existing channels we know about and query the game tracker about them
2783 lookup = Multi_pxo_channels;
2788 if(strlen(lookup->name)){
2790 memset(&filter,0,sizeof(filter_game_list_struct));
2791 SDL_strlcpy(filter.channel, lookup->name, SDL_arraysize(filter.channel));
2794 RequestGameCountWithFilter(&filter);
2798 lookup = lookup->next;
2799 } while((lookup != NULL) && (lookup != Multi_pxo_channels));
2802 Multi_pxo_channel_server_refresh = f2fl(timer_get_fixed_seconds());
2805 // refresh current channel server count
2806 void multi_pxo_channel_refresh_current()
2808 // send a request for a server count on this channel
2809 if(strlen(Multi_pxo_channel_current.name)){
2811 filter_game_list_struct filter;
2812 memset(&filter,0,sizeof(filter_game_list_struct));
2813 SDL_strlcpy(filter.channel, Multi_pxo_channel_current.name, SDL_arraysize(filter.channel));
2816 RequestGameCountWithFilter(&filter);
2820 // display the channel list
2821 void multi_pxo_blit_channels()
2823 pxo_channel *moveup;
2824 char chan_name[255];
2825 char chan_users[15];
2826 char chan_servers[15];
2827 int user_w,server_w;
2828 int disp_count,y_start;
2830 // blit as many channels as we can
2832 y_start = Multi_pxo_chan_coords[gr_screen.res][1];
2833 moveup = Multi_pxo_channel_start;
2838 // if this is the currently selected item, highlight it
2839 if(moveup == Multi_pxo_channel_select){
2840 gr_set_color_fast(&Color_bright);
2842 // otherwise draw it normally
2844 gr_set_color_fast(&Color_normal);
2847 // get the # of users on the channel
2848 SDL_snprintf(chan_users, SDL_arraysize(chan_users), "%d", moveup->num_users);
2850 // get the width of the user count string
2851 gr_get_string_size(&user_w, NULL, chan_users);
2853 // get the # of servers on the channel
2854 SDL_snprintf(chan_servers, SDL_arraysize(chan_servers), "%d", moveup->num_servers);
2856 // get the width of the user count string
2857 gr_get_string_size(&server_w, NULL, chan_servers);
2859 // make sure the name fits
2860 SDL_assert(moveup->name);
2861 SDL_strlcpy(chan_name, moveup->name, SDL_arraysize(chan_name));
2862 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]);
2865 gr_string(Multi_pxo_chan_coords[gr_screen.res][0], y_start, chan_name + 1);
2866 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);
2867 gr_set_color_fast(&Color_bright);
2868 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);
2870 // increment the displayed count
2875 moveup = moveup->next;
2876 } while((moveup != Multi_pxo_channels) && (disp_count < Multi_pxo_max_chan_display[gr_screen.res]));
2879 // scroll channel list up
2880 void multi_pxo_scroll_channels_up()
2882 // if we're already at the head of the list, do nothing
2883 if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start == Multi_pxo_channels)){
2884 gamesnd_play_iface(SND_GENERAL_FAIL);
2888 // otherwise move up one
2889 Multi_pxo_channel_start = Multi_pxo_channel_start->prev;
2890 Multi_pxo_channel_start_index--;
2891 gamesnd_play_iface(SND_USER_SELECT);
2894 // scroll channel list down
2895 void multi_pxo_scroll_channels_down()
2897 // if we're already at the tail of the list, do nothing
2898 if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start->next == Multi_pxo_channels)){
2899 gamesnd_play_iface(SND_GENERAL_FAIL);
2903 // if we can't scroll further without going past the end of the viewable list, don't
2904 if((Multi_pxo_channel_start_index + Multi_pxo_max_chan_display[gr_screen.res]) >= Multi_pxo_channel_count){
2905 gamesnd_play_iface(SND_GENERAL_FAIL);
2909 // otherwise move down one
2910 Multi_pxo_channel_start = Multi_pxo_channel_start->next;
2911 Multi_pxo_channel_start_index++;
2912 gamesnd_play_iface(SND_USER_SELECT);
2915 // attempt to join a channel
2916 void multi_pxo_join_channel(pxo_channel *chan)
2918 char switch_msg[256];
2920 // if we're already on this channel, do nothing
2921 if(ON_CHANNEL() && !SDL_strcasecmp(chan->name,Multi_pxo_channel_current.name)){
2925 // if we're already trying to join a channel, do nothing
2926 if(SWITCHING_CHANNELS()){
2930 // try and join the channel
2931 switch(SetNewChatChannel(chan->name)){
2937 // decrement the count of our current channel
2938 pxo_channel *lookup;
2939 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2941 lookup->num_users--;
2944 // set our current channel as none
2945 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2946 Multi_pxo_channel_current.num_users = -1;
2948 multi_pxo_set_status_text(XSTR("Switching channels",953));
2951 memcpy(&Multi_pxo_channel_switch,chan,sizeof(pxo_channel));
2953 // clear the player list
2954 multi_pxo_clear_players();
2956 // display a line of text indicating that we're switching channels
2957 if(strlen(Multi_pxo_channel_switch.name) > 1){
2958 SDL_snprintf(switch_msg, SDL_arraysize(switch_msg), "[Switching to channel %s]", Multi_pxo_channel_switch.name + 1);
2960 SDL_snprintf(switch_msg, SDL_arraysize(switch_msg), "[Switching to channel %s]", Multi_pxo_channel_switch.name);
2962 multi_pxo_chat_process_incoming(switch_msg, CHAT_MODE_CHANNEL_SWITCH);
2970 // handle any processing details if we're currently trying to join a channel
2971 void multi_pxo_handle_channel_change()
2973 // if we're not switching channels, do nothing
2974 if(!SWITCHING_CHANNELS()){
2978 // if we are, check the status
2979 switch(SetNewChatChannel(NULL)){
2982 // unset our switching struct
2983 memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
2984 Multi_pxo_channel_switch.num_users = -1;
2987 multi_pxo_set_status_text(XSTR("No channel (error while switching)",954));
2994 // successfully changed
2996 // copy the current channel info, and unset the switching status
2997 memcpy(&Multi_pxo_channel_current,&Multi_pxo_channel_switch,sizeof(pxo_channel));
2998 Multi_pxo_channel_switch.num_users = -1;
3000 // set our "last" channel
3001 SDL_strlcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name, SDL_arraysize(Multi_pxo_channel_last));
3004 multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
3006 // if we don't already have this guy on the list, add him
3007 pxo_channel *lookup;
3008 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
3010 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
3011 lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
3014 // set the user count to be 1 (just me)
3016 lookup->num_users = 1;
3019 // clear the chat area
3020 // multi_pxo_chat_clear();
3022 // set the "switch" delay timestamp
3023 Multi_pxo_switch_delay = timestamp(MULTI_PXO_SWITCH_DELAY_TIME);
3025 // refresh current channel server count
3026 multi_pxo_channel_refresh_current();
3032 // player related stuff -------------------------------------------
3034 // clear the old player list
3035 void multi_pxo_clear_players()
3037 player_list *moveup,*backup;
3039 // if the list is null, don't free it up
3040 if(Multi_pxo_players != NULL){
3042 moveup = Multi_pxo_players;
3047 moveup = moveup->next;
3049 // free the struct itself
3052 } while(moveup != Multi_pxo_players);
3053 Multi_pxo_players = NULL;
3057 Multi_pxo_player_start = NULL;
3058 // Multi_pxo_player_start_index = -1;
3059 Multi_pxo_player_select = NULL;
3062 // Multi_pxo_player_slider.set_numberItems(0);
3064 // add a bunch of bogus players
3067 for(int idx=0;idx<30;idx++){
3068 sprintf(str,"player%d",idx);
3069 multi_pxo_add_player(str);
3074 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
3075 player_list *multi_pxo_add_player(char *name)
3077 player_list *new_player;
3079 // try and allocate a new player_list struct
3080 new_player = (player_list *)malloc(sizeof(player_list));
3081 if ( new_player == NULL ) {
3082 nprintf(("Network", "Cannot allocate space for new player_list structure\n"));
3085 // try and allocate a string for the channel name
3086 SDL_strlcpy(new_player->name, name, SDL_arraysize(new_player->name));
3088 // insert it on the list
3089 if ( Multi_pxo_players != NULL ) {
3090 new_player->next = Multi_pxo_players->next;
3091 new_player->next->prev = new_player;
3092 Multi_pxo_players->next = new_player;
3093 new_player->prev = Multi_pxo_players;
3095 Multi_pxo_players = new_player;
3096 Multi_pxo_players->next = Multi_pxo_players->prev = Multi_pxo_players;
3099 // increment the start position
3100 if(Multi_pxo_player_start != NULL){
3101 // Multi_pxo_player_start_index++;
3105 Multi_pxo_player_count++;
3107 // update the count on the slider
3108 // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3113 // remove a player with the given name
3114 void multi_pxo_del_player(char *name)
3116 player_list *lookup;
3118 // try and find this guy
3119 lookup = Multi_pxo_players;
3124 // if we found a match, delete it
3125 if(!SDL_strcasecmp(name,lookup->name)){
3126 // if this is the only item on the list, free stuff up
3127 if(lookup->next == lookup){
3128 SDL_assert(lookup == Multi_pxo_players);
3130 Multi_pxo_players = NULL;
3131 multi_pxo_clear_players();
3133 // otherwise, just delete it
3135 lookup->next->prev = lookup->prev;
3136 lookup->prev->next = lookup->next;
3138 // if this was our selected item, unselect it
3139 if((lookup == Multi_pxo_player_select) && (Multi_pxo_player_select != NULL)){
3140 Multi_pxo_player_select = Multi_pxo_player_select->next;
3143 // if this was our point to start viewing from, select another
3144 if(lookup == Multi_pxo_player_start){
3145 // if this is the head of the list, move up one
3146 if(Multi_pxo_players == lookup){
3147 Multi_pxo_player_start = Multi_pxo_player_start->next;
3148 // Multi_pxo_player_start_index = 0;
3150 // otherwise move back
3152 Multi_pxo_player_start = Multi_pxo_player_start->prev;
3153 // Multi_pxo_player_start_index++;
3157 // if this is the head of the list, move it up
3158 if(lookup == Multi_pxo_players){
3159 Multi_pxo_players = Multi_pxo_players->next;
3163 lookup->next = NULL;
3164 lookup->prev = NULL;
3169 Multi_pxo_player_count--;
3170 SDL_assert(Multi_pxo_player_count >= 0);
3172 // update the count on the slider
3173 // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3174 // Multi_pxo_player_slider.force_currentItem(Multi_pxo_player_start_index);
3181 lookup = lookup->next;
3182 } while((lookup != NULL) && (lookup != Multi_pxo_players));
3185 // try and find a player with the given name, return a pointer to his entry (or NULL)
3186 player_list *multi_pxo_find_player(char *name)
3188 player_list *lookup;
3190 // look through all players
3191 lookup = Multi_pxo_players;
3196 if(!SDL_strcasecmp(name,lookup->name)){
3200 lookup = lookup->next;
3201 } while((lookup != NULL) && (lookup != Multi_pxo_players));
3207 // process the player list (select, etc)
3208 void multi_pxo_process_players()
3211 player_list *lookup;
3213 // if we don't have a start item, but the list is non-null
3214 if((Multi_pxo_player_start == NULL) && (Multi_pxo_players != NULL)){
3215 Multi_pxo_player_start = Multi_pxo_players;
3216 // Multi_pxo_player_start_index = 0;
3218 // update the slider
3219 // Multi_pxo_player_slider.set_currentItem(Multi_pxo_player_start_index);
3222 // if we don't have a selected item, but the list is non-null
3223 if((Multi_pxo_player_select == NULL) && (Multi_pxo_players != NULL)){
3224 Multi_pxo_player_select = Multi_pxo_players;
3227 // see if we have a mouse click on the channel region
3228 if(Multi_pxo_player_button.pressed()){
3229 Multi_pxo_player_button.get_mouse_pos(NULL,&my);
3231 // index from the top
3232 item_index = my / 10;
3234 // select the item if possible
3235 lookup = Multi_pxo_player_start;
3240 if(item_index == 0){
3241 Multi_pxo_player_select = Multi_pxo_player_start;
3245 // move to the next item
3246 lookup = lookup->next;
3249 // if this item is our guy
3250 if((item_index == 0) && (lookup != Multi_pxo_players)){
3251 Multi_pxo_player_select = lookup;
3254 } while((lookup != Multi_pxo_players) && (item_index > 0));
3258 // display the player list
3259 void multi_pxo_blit_players()
3261 player_list *moveup;
3262 char player_name[255];
3263 int disp_count,y_start;
3265 // blit as many channels as we can
3267 y_start = Multi_pxo_player_coords[gr_screen.res][1];
3268 moveup = Multi_pxo_player_start;
3273 // if this is the currently selected item, highlight it
3274 if(moveup == Multi_pxo_player_select){
3275 gr_set_color_fast(&Color_bright);
3277 // otherwise draw it normally
3279 gr_set_color_fast(&Color_normal);
3282 // make sure the string fits
3283 SDL_strlcpy(player_name, moveup->name, SDL_arraysize(player_name));
3284 gr_force_fit_string(player_name, 254, Multi_pxo_player_coords[gr_screen.res][2]);
3287 gr_string(Multi_pxo_player_coords[gr_screen.res][0], y_start, player_name);
3289 // increment the displayed count
3294 moveup = moveup->next;
3295 } while((moveup != Multi_pxo_players) && (disp_count < Multi_pxo_max_player_display[gr_screen.res]));
3298 // scroll player list up
3299 void multi_pxo_scroll_players_up()
3301 // if we're already at the head of the list, do nothing
3302 if((Multi_pxo_player_start == NULL) || (Multi_pxo_player_start == Multi_pxo_players)){
3303 gamesnd_play_iface(SND_GENERAL_FAIL);
3307 // otherwise move up one
3308 Multi_pxo_player_start = Multi_pxo_player_start->prev;
3309 // Multi_pxo_player_start_index--;
3310 // SDL_assert(Multi_pxo_player_start_index >= 0);
3312 gamesnd_play_iface(SND_USER_SELECT);
3315 // scroll player list down
3316 void multi_pxo_scroll_players_down()
3318 player_list *lookup;
3321 // see if its okay to scroll down
3322 lookup = Multi_pxo_player_start;
3323 if(lookup == NULL ){
3324 gamesnd_play_iface(SND_GENERAL_FAIL);
3328 while(lookup->next != Multi_pxo_players){
3329 lookup = lookup->next;
3333 // if we can move down
3334 if(count >= Multi_pxo_max_player_display[gr_screen.res]){
3335 Multi_pxo_player_start = Multi_pxo_player_start->next;
3337 // Multi_pxo_player_start_index++;
3339 gamesnd_play_iface(SND_USER_SELECT);
3341 gamesnd_play_iface(SND_GENERAL_FAIL);
3346 // chat text stuff -----------------------------------------
3348 // initialize and create the chat text linked list
3349 void multi_pxo_chat_init()
3352 chat_line *new_line;
3355 Multi_pxo_chat = NULL;
3356 Multi_pxo_chat_add = NULL;
3357 Multi_pxo_chat_start = NULL;
3358 Multi_pxo_chat_start_index = -1;
3360 // create the lines in a non-circular doubly linked list
3361 for(idx=0;idx<MAX_CHAT_LINES;idx++){
3362 new_line = (chat_line*)malloc(sizeof(chat_line));
3364 // clear the line out
3365 SDL_assert(new_line != NULL);
3366 if(new_line == NULL){
3369 memset(new_line,0,sizeof(chat_line));
3370 new_line->prev = NULL;
3371 new_line->next = NULL;
3373 // insert it into the (empty) list
3374 if(Multi_pxo_chat == NULL){
3375 Multi_pxo_chat = new_line;
3377 // insert it onto the (non-empty) list
3379 Multi_pxo_chat->prev = new_line;
3380 new_line->next = Multi_pxo_chat;
3381 Multi_pxo_chat = new_line;
3385 // start adding chat lines at the beginning of the list
3386 Multi_pxo_chat_add = Multi_pxo_chat;
3389 // free up all chat list stuff
3390 void multi_pxo_chat_free()
3392 chat_line *moveup, *backup;
3394 // free all items up
3395 moveup = Multi_pxo_chat;
3396 while(moveup != NULL){
3398 moveup = moveup->next;
3404 Multi_pxo_chat = NULL;
3405 Multi_pxo_chat_add = NULL;
3406 Multi_pxo_chat_start = NULL;
3407 Multi_pxo_chat_start_index = -1;
3408 Multi_pxo_chat_count = 0;
3409 Multi_pxo_chat_slider.set_numberItems(0);
3412 // clear all lines of chat text in the chat area
3413 void multi_pxo_chat_clear()
3417 // clear the text in all the lines
3418 moveup = Multi_pxo_chat;
3419 while(moveup != NULL){
3420 SDL_zero(moveup->text);
3421 moveup = moveup->next;
3424 // how many chat lines we have
3425 Multi_pxo_chat_count = 0;
3427 // start adding chat lines at the beginning of the list
3428 Multi_pxo_chat_add = Multi_pxo_chat;
3431 // add a line of text
3432 void multi_pxo_chat_add_line(char *txt, int mode)
3437 SDL_assert(Multi_pxo_chat_add != NULL);
3438 SDL_strlcpy(Multi_pxo_chat_add->text, txt, SDL_arraysize(Multi_pxo_chat_add->text));
3439 Multi_pxo_chat_add->mode = mode;
3441 // if we're at the end of the list, move the front item down
3442 if(Multi_pxo_chat_add->next == NULL) {
3443 // store the new "head" of the list
3444 temp = Multi_pxo_chat->next;
3446 // move the current head to the end of the list
3447 Multi_pxo_chat_add->next = Multi_pxo_chat;
3449 Multi_pxo_chat->prev = Multi_pxo_chat_add;
3450 Multi_pxo_chat->next = NULL;
3452 // reset the head of the list
3453 Multi_pxo_chat = temp;
3455 // set the new add line
3456 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3457 SDL_zero(Multi_pxo_chat_add->text);
3458 Multi_pxo_chat_add->mode = CHAT_MODE_NORMAL;
3460 // if we're not at the end of the list, just move up by one
3462 // set the new add line
3463 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3466 // if we've reached max chat lines, don't increment
3467 if(Multi_pxo_chat_count < MAX_CHAT_LINES) {
3468 Multi_pxo_chat_count++;
3472 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
3474 // force the position, in case we arent at the bottom of the list
3477 // move to the bottom of the chat area
3480 multi_pxo_goto_bottom();
3483 // if we have more than the # of lines displayable
3484 if(Multi_pxo_chat_count >= MULTI_PXO_MAX_CHAT_DISPLAY){
3486 multi_pxo_goto_bottom();
3489 multi_pxo_goto_bottom();
3492 // process an incoming line of text
3493 void multi_pxo_chat_process_incoming(const char *txt,int mode)
3495 char msg_total[512],line[512];
3498 char *p_str[20]; // the initial line (unindented)
3499 const char *priv_ptr = NULL;
3501 // filter out "has left" channel messages, when switching channels
3502 if((SWITCHING_CHANNELS() || ((Multi_pxo_switch_delay != -1) && !timestamp_elapsed(Multi_pxo_switch_delay))) &&
3503 multi_pxo_chat_is_left_message(txt)){
3507 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3508 priv_ptr = multi_pxo_chat_is_private(txt);
3509 if(priv_ptr != NULL){
3510 SDL_strlcpy(msg_total, priv_ptr, SDL_arraysize(msg_total));
3512 SDL_strlcpy(msg_total, txt, SDL_arraysize(msg_total));
3515 // determine what mode to display this text in
3517 // if this is private chat
3518 if(priv_ptr != NULL){
3519 mode = CHAT_MODE_PRIVATE;
3523 // if this is a server message
3524 if(multi_pxo_is_server_text(txt)){
3525 mode = CHAT_MODE_SERVER;
3527 // if this is a MOTD
3528 else if(multi_pxo_is_motd_text(txt)){
3529 // mode = CHAT_MODE_MOTD;
3531 multi_pxo_motd_add_text(txt);
3534 // if this is the end of motd text
3535 else if(multi_pxo_is_end_of_motd_text(txt)){
3536 multi_pxo_set_end_of_motd();
3541 // split the text up into as many lines as necessary
3542 n_lines = split_str(msg_total, Multi_pxo_chat_coords[gr_screen.res][2] - 5, n_chars, p_str, 3);
3543 SDL_assert((n_lines != -1) && (n_lines <= 20));
3544 if((n_lines < 0) || (n_lines > 20)) {
3548 // if the string fits on one line
3550 multi_pxo_chat_add_line(msg_total,mode);
3552 // don't pad with extra spaces if its from the server
3554 if(mode != CHAT_MODE_SERVER){
3555 multi_pxo_chat_add_line("",CHAT_MODE_NORMAL);
3559 // if the string was split into multiple lines
3561 // add the first line
3562 memcpy(line,p_str[0],n_chars[0]);
3563 line[n_chars[0]] = '\0';
3564 multi_pxo_chat_add_line(line,mode);
3566 // copy the rest of the lines
3567 for(idx=1; idx<n_lines; idx++){
3568 memcpy(line,p_str[idx],n_chars[idx]);
3569 line[n_chars[idx]] = '\0';
3571 // unless the current mode is server or "switching channels", make all these CHAT_MODE_CARRY
3572 if((mode != CHAT_MODE_SERVER) && (mode != CHAT_MODE_CHANNEL_SWITCH)){
3573 mode = CHAT_MODE_CARRY;
3575 multi_pxo_chat_add_line(line, mode);
3580 // blit the chat text
3581 void multi_pxo_chat_blit()
3584 int disp_count,token_width;
3590 // blit the title line
3592 if(strlen(Multi_pxo_channel_current.name) > 1){
3593 SDL_snprintf(title, SDL_arraysize(title), XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name+1); // [[ <who> on <channel> ]]
3595 SDL_snprintf(title, SDL_arraysize(title), XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name); // [[ <who> on <channel> ]]
3598 SDL_strlcpy(title, XSTR("Parallax Online - No Channel", 956), SDL_arraysize(title));
3600 gr_force_fit_string(title, 254, Multi_pxo_chat_coords[gr_screen.res][2] - 10);
3601 gr_get_string_size(&token_width,NULL,title);
3602 gr_set_color_fast(&Color_normal);
3603 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);
3605 // blit all active lines of text
3606 moveup = Multi_pxo_chat_start;
3608 y_start = Multi_pxo_chat_coords[gr_screen.res][1];
3609 while((moveup != NULL) && (moveup != Multi_pxo_chat_add) && (disp_count < (Multi_pxo_max_chat_display[gr_screen.res]))){
3610 switch(moveup->mode){
3611 // if this is text from the server, display it all "bright"
3612 case CHAT_MODE_SERVER:
3613 gr_set_color_fast(&Color_bright);
3614 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3617 // if this is motd, display it all "bright"
3618 case CHAT_MODE_MOTD:
3619 gr_set_color_fast(&Color_bright_white);
3620 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3623 // normal mode, just highlight the server
3624 case CHAT_MODE_PRIVATE:
3625 case CHAT_MODE_NORMAL:
3626 SDL_strlcpy(piece, moveup->text, SDL_arraysize(piece));
3627 tok = strtok(piece," ");
3629 // get the width of just the first "piece"
3630 gr_get_string_size(&token_width, NULL, tok);
3633 gr_set_color_fast(&Color_bright);
3634 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, tok);
3636 // draw the rest of the string normally
3637 tok = strtok(NULL,"");
3639 gr_set_color_fast(&Color_normal);
3640 gr_string(Multi_pxo_chat_coords[gr_screen.res][0] + token_width + 6, y_start, tok);
3645 // carry mode, display with no highlight
3646 case CHAT_MODE_CARRY:
3647 gr_set_color_fast(&Color_normal);
3648 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3651 // "switching channels mode", display it bright
3652 case CHAT_MODE_CHANNEL_SWITCH:
3653 gr_set_color_fast(&Color_bright);
3654 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3659 moveup = moveup->next;
3664 if ((moveup != Multi_pxo_chat_add) && (moveup != NULL)) {
3665 Can_scroll_down = 1;
3667 Can_scroll_down = 0;
3671 // scroll to the very bottom of the chat area
3672 void multi_pxo_goto_bottom()
3677 if (Multi_pxo_chat == NULL) {
3681 // if we have less than the displayable amount of lines, do nothing
3682 if(Multi_pxo_chat_count <= Multi_pxo_max_chat_display[gr_screen.res]){
3683 Multi_pxo_chat_start = Multi_pxo_chat;
3685 // nothing to do for the slider
3686 Multi_pxo_chat_slider.set_numberItems(0);
3690 if (!Can_scroll_down)
3692 // otherwise move back the right # of items
3693 backup = Multi_pxo_chat_add;
3694 for(idx=0; idx<Multi_pxo_max_chat_display[gr_screen.res]; idx++){
3695 SDL_assert(backup->prev != NULL);
3696 backup = backup->prev;
3699 Multi_pxo_chat_start = backup;
3701 // fixup the start index
3702 multi_pxo_chat_adjust_start();
3706 // scroll the text up
3707 void multi_pxo_scroll_chat_up()
3709 // if we're already at the top of the list, don't do anything
3710 if ((Multi_pxo_chat_start == NULL) || (Multi_pxo_chat_start == Multi_pxo_chat)) {
3711 gamesnd_play_iface(SND_GENERAL_FAIL);
3715 // otherwise move up one
3716 Multi_pxo_chat_start = Multi_pxo_chat_start->prev;
3718 multi_pxo_chat_adjust_start();
3720 gamesnd_play_iface(SND_USER_SELECT);
3723 // returns 1 if we can scroll down, 0 otherwise
3724 int multi_pxo_can_scroll_down()
3729 // see if its okay to scroll down
3730 lookup = Multi_pxo_chat_start;
3731 if (lookup == NULL) {
3732 // gamesnd_play_iface(SND_GENERAL_FAIL);
3736 while (lookup != Multi_pxo_chat_add) {
3737 lookup = lookup->next;
3741 // check if we can move down, return accordingly
3742 if (count > Multi_pxo_max_chat_display[gr_screen.res]) {
3749 // scroll the text down
3750 void multi_pxo_scroll_chat_down()
3752 // if we can move down
3753 if (multi_pxo_can_scroll_down()) {
3754 Multi_pxo_chat_start = Multi_pxo_chat_start->next;
3755 multi_pxo_chat_adjust_start();
3756 gamesnd_play_iface(SND_USER_SELECT);
3758 gamesnd_play_iface(SND_GENERAL_FAIL);
3762 // process chat controls
3763 void multi_pxo_chat_process()
3765 char *remainder = NULL;
3766 const char *result = NULL;
3768 int msg_pixel_width;
3770 // if the chat line is getting too long, fire off the message, putting the last
3771 // word on the next input line.
3773 Multi_pxo_chat_input.get_text(msg);
3775 // determine if the width of the string in pixels is > than the inputbox width -- if so,
3776 // then send the message
3777 gr_get_string_size(&msg_pixel_width, NULL, msg);
3778 // if ( msg_pixel_width >= (Chatbox_inputbox_w - Player->short_callsign_width) ) {
3779 if ( msg_pixel_width >= (Multi_pxo_input_coords[gr_screen.res][2])) {
3780 remainder = strrchr(msg, ' ');
3786 // if we're connected to a channel, send the chat to the server
3788 result = SendChatString(msg,1);
3790 multi_pxo_chat_process_incoming(result);
3793 // display any remainder of text on the next line
3794 Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3796 Multi_pxo_chat_input.set_text("");
3798 } else if((Multi_pxo_chat_input.pressed() && (strlen(msg) > 0)) || (strlen(msg) >= MAX_CHAT_LINE_LEN)) {
3799 // tack on the null terminator in the boundary case
3800 int x = strlen(msg);
3801 if(x >= MAX_CHAT_LINE_LEN){
3802 msg[MAX_CHAT_LINE_LEN-1] = '\0';
3805 // ignore "/nick" commands
3806 if(multi_pxo_is_nick_command(msg)){
3807 Multi_pxo_chat_input.set_text("");
3811 // send the chat to the server
3812 // if we're connected to a channel, send the chat to the server
3814 result = SendChatString(msg,1);
3816 multi_pxo_chat_process_incoming(result);
3819 // display any remainder of text on the next line
3820 Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3822 Multi_pxo_chat_input.set_text("");
3827 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3830 // NOTE : DO NOT LOCALIZE THESE STRINGS!!!! THEY ARE CONSTANTS WHICH ARE CHECKED AGAINST
3831 // PXO CHAT SERVER DATA. THEY CANNOT CHANGE!!!
3832 #define PMSG_FROM "private message from "
3833 #define PMSG_TO "private message to "
3834 const char *multi_pxo_chat_is_private(const char *txt)
3837 if( strlen(txt) > strlen( PMSG_FROM ) ){
3838 // otherwise do a comparison
3839 if(!SDL_strncasecmp( txt, PMSG_FROM, strlen(PMSG_FROM) )){
3840 return &txt[strlen( PMSG_FROM )];
3845 if(strlen(txt) > strlen( PMSG_TO )){
3846 // otherwise do a comparison
3847 if(!SDL_strncasecmp(txt,PMSG_TO,strlen(PMSG_TO))){
3848 return &txt[strlen(PMSG_TO)];
3856 // if the text came from the server
3857 int multi_pxo_is_server_text(const char *txt)
3859 // if the message is prefaced by a ***
3860 if((strlen(txt) >= strlen(MULTI_PXO_SERVER_PREFIX)) && !strncmp(txt, MULTI_PXO_SERVER_PREFIX, strlen(MULTI_PXO_SERVER_PREFIX))){
3867 // if the text is message of the day text
3868 int multi_pxo_is_motd_text(const char *txt)
3870 // if we're not on a channel, and this is not a channel switching message assume its coming from a server
3871 if((strlen(txt) >= strlen(PXO_CHAT_MOTD_PREFIX)) && !strncmp(txt, PXO_CHAT_MOTD_PREFIX, strlen(PXO_CHAT_MOTD_PREFIX))){
3878 // if the text is the end of motd text
3879 int multi_pxo_is_end_of_motd_text(const char *txt)
3881 // if we're not on a channel, and this is not a channel switching message assume its coming from a server
3882 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))){
3889 // if the text is a "has left message" from the server
3890 int multi_pxo_chat_is_left_message(const char *txt)
3892 char last_portion[100];
3894 // if the text is not server text
3895 if(!multi_pxo_is_server_text(txt)){
3899 // check to see if the last portion is the correct wording
3900 SDL_zero(last_portion);
3901 if((strlen(txt) > strlen(MULTI_PXO_HAS_LEFT)) && !strcmp(&txt[strlen(txt) - strlen(MULTI_PXO_HAS_LEFT)], MULTI_PXO_HAS_LEFT)){
3905 // check the end of the line
3909 // recalculate the chat start index, and adjust the slider properly
3910 void multi_pxo_chat_adjust_start()
3914 // if we have no chat
3915 if (Multi_pxo_chat == NULL) {
3916 Multi_pxo_chat_start_index = -1;
3921 Multi_pxo_chat_start_index = 0;
3922 moveup = Multi_pxo_chat;
3923 while((moveup != Multi_pxo_chat_start) && (moveup != NULL)){
3924 Multi_pxo_chat_start_index++;
3925 moveup = moveup->next;
3928 // set the slider index
3929 Multi_pxo_chat_slider.force_currentItem(Multi_pxo_chat_start_index);
3932 // motd stuff ---------------------------------------------------------
3934 // initialize motd when going into this screen
3935 void multi_pxo_motd_init()
3937 // zero the motd string
3938 SDL_strlcpy(Pxo_motd, "", SDL_arraysize(Pxo_motd));
3940 // haven't gotten it yet
3943 // haven't read it yet either
3947 // set the motd text
3948 void multi_pxo_motd_add_text(const char *text)
3950 int cur_len = strlen(Pxo_motd);
3958 // make sure its motd text
3959 SDL_assert(multi_pxo_is_motd_text(text));
3960 if(!multi_pxo_is_motd_text(text)){
3964 // if its a 0 line motd
3965 if(strlen(text) <= strlen(PXO_CHAT_MOTD_PREFIX)){
3969 // add text to the motd
3970 new_len = strlen(text + strlen(PXO_CHAT_MOTD_PREFIX)) - 1;
3971 if((cur_len + new_len + 1) < MAX_PXO_MOTD_LEN){
3972 SDL_strlcat(Pxo_motd, text + strlen(PXO_CHAT_MOTD_PREFIX) + 1, SDL_arraysize(Pxo_motd));
3973 SDL_strlcat(Pxo_motd, "\n", SDL_arraysize(Pxo_motd));
3974 mprintf(("MOTD ADD : %s\n", Pxo_motd));
3979 void multi_pxo_set_end_of_motd()
3984 mprintf(("MOTD ALL : %s\n", Pxo_motd));
3988 // do we have an old MOTD file laying around? If so, read it in and see if its the same
3992 // checksum the current motd
3993 new_chksum = cf_add_chksum_long(0, Pxo_motd, strlen(Pxo_motd));
3995 // checksum the old motd if its lying around
3996 CFILE *in = cfopen("oldmotd.txt", "rb");
3998 // read the old checksum
3999 old_chksum = cfread_uint(in);
4002 // same checksum? no blink
4003 if(new_chksum == old_chksum){
4008 // write out the motd for next time
4009 if(strlen(Pxo_motd)){
4010 CFILE *out = cfopen("oldmotd.txt", "wb", CFILE_NORMAL, CF_TYPE_DATA);
4012 // write all the text
4013 cfwrite_uint(new_chksum, out);
4015 // close the outfile
4020 // set the blink stamp
4021 Pxo_motd_blink_stamp = -1;
4023 Pxo_motd_blink_on = 0;
4024 if(!Pxo_motd_blinked_already){
4025 Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4026 Pxo_motd_blink_on = 1;
4030 Pxo_motd_blinked_already = 1;
4033 // display the motd dialog
4034 void multi_pxo_motd_dialog()
4036 // mark the motd as read
4039 // simple popup, with a slider
4040 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, Pxo_motd);
4043 // call to maybe blink the motd button
4044 void multi_pxo_motd_maybe_blit()
4046 // if we got the end of the motd, and he hasn't read it yet
4047 if(Pxo_motd_end && !Pxo_motd_read && (Pxo_motd_blink_stamp != -1)){
4048 // if the timestamp elapsed, flip the blink flag
4049 if(timestamp_elapsed(Pxo_motd_blink_stamp)){
4050 Pxo_motd_blink_on = !Pxo_motd_blink_on;
4051 Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4055 if(Pxo_motd_blink_on){
4056 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_MOTD].button.draw_forced(2);
4062 // common dialog stuff ------------------------------------------------
4064 int Multi_pxo_searching = 0;
4066 // initialize the common dialog with the passed max input length
4067 void multi_pxo_com_init(int input_len)
4071 // create the interface window
4072 Multi_pxo_com_window.create(0, 0, gr_screen.max_w,gr_screen.max_h, 0);
4073 Multi_pxo_com_window.set_mask_bmap(Multi_pxo_com_mask_fname[gr_screen.res]);
4075 // create the interface buttons
4076 for(idx=0; idx<MULTI_PXO_COM_NUM_BUTTONS; idx++){
4077 // create the object
4078 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);
4080 // set the sound to play when highlighted
4081 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4083 // set the ani for the button
4084 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_com_buttons[gr_screen.res][idx].filename);
4087 Multi_pxo_com_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_com_buttons[gr_screen.res][idx].hotspot);
4091 for(idx=0; idx<MULTI_PXO_COM_NUM_TEXT; idx++){
4092 Multi_pxo_com_window.add_XSTR(&Multi_pxo_com_text[gr_screen.res][idx]);
4095 // create the input box
4096 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);
4097 Multi_pxo_com_input.set_focus();
4099 // clear all text lines
4100 SDL_zero(Multi_pxo_com_bottom_text);
4101 SDL_zero(Multi_pxo_com_middle_text);
4102 SDL_zero(Multi_pxo_com_top_text);
4105 // close down the common dialog
4106 void multi_pxo_com_close()
4108 // destroy the UI_WINDOW
4109 Multi_pxo_com_window.destroy();
4112 // blit all text lines, top, middle, bottoms
4113 void multi_pxo_com_blit_text()
4115 // blit top, middle and bottom text if possible
4116 if(strlen(Multi_pxo_com_top_text) > 0){
4117 gr_set_color_fast(&Color_bright);
4118 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);
4120 if(strlen(Multi_pxo_com_middle_text) > 0){
4121 gr_set_color_fast(&Color_bright);
4122 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);
4124 if(strlen(Multi_pxo_com_bottom_text) > 0){
4125 gr_set_color_fast(&Color_bright);
4126 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);
4130 // set the top text, shortening as necessary
4131 void multi_pxo_com_set_top_text(const char *txt)
4133 if((txt != NULL) && strlen(txt)){
4134 SDL_strlcpy(Multi_pxo_com_top_text, txt, SDL_arraysize(Multi_pxo_com_top_text));
4135 gr_force_fit_string(Multi_pxo_com_top_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4139 // set the middle text, shortening as necessary
4140 void multi_pxo_com_set_middle_text(const char *txt)
4142 if((txt != NULL) && strlen(txt)){
4143 SDL_strlcpy(Multi_pxo_com_middle_text, txt, SDL_arraysize(Multi_pxo_com_middle_text));
4144 gr_force_fit_string(Multi_pxo_com_middle_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4148 // set the bottom text, shortening as necessary
4149 void multi_pxo_com_set_bottom_text(const char *txt)
4151 if((txt != NULL) && strlen(txt)){
4152 SDL_strlcpy(Multi_pxo_com_bottom_text, txt, SDL_arraysize(Multi_pxo_com_bottom_text));
4153 gr_force_fit_string(Multi_pxo_com_bottom_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4158 // private channel join stuff -----------------------------------------
4160 // initialize the popup
4161 void multi_pxo_priv_init()
4163 SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE);
4165 // initialize the common dialog with the passed max input length
4166 multi_pxo_com_init(MULTI_PXO_PRIV_MAX_TEXT_LEN);
4168 // initialize the return code
4169 Multi_pxo_priv_return_code = -1;
4171 // mark us as running
4172 Multi_pxo_mode = MULTI_PXO_MODE_PRIVATE;
4175 multi_pxo_com_set_middle_text(XSTR("Type the name of the channel to join/create",961));
4178 // close down the popup
4179 void multi_pxo_priv_close()
4181 // close down the common dialog
4182 multi_pxo_com_close();
4184 // mark us as not running any more
4185 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4188 // run the popup, 0 if still running, -1 if cancel, 1 if ok
4189 int multi_pxo_priv_popup()
4193 // if we're not already running, initialize stuff
4194 if(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE){
4196 multi_pxo_priv_init();
4198 // return "still running"
4202 k = Multi_pxo_com_window.process();
4204 // process keypresses
4206 // like hitting the cancel button
4208 Multi_pxo_priv_return_code = 0;
4212 // process button presses
4213 multi_pxo_priv_process_buttons();
4215 // process the inputbox
4216 multi_pxo_priv_process_input();
4218 // blit the background
4219 multi_pxo_blit_all();
4223 gr_set_bitmap(Multi_pxo_com_bitmap);
4224 gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4225 Multi_pxo_com_window.draw();
4227 // blit all text lines, top, middle, bottoms
4228 multi_pxo_com_blit_text();
4232 // check the return code
4233 switch(Multi_pxo_priv_return_code){
4234 // still in progress
4240 multi_pxo_priv_close();
4245 multi_pxo_priv_close();
4252 // process button presses
4253 void multi_pxo_priv_process_buttons()
4257 // check all buttons
4258 for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4259 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4260 multi_pxo_priv_button_pressed(idx);
4266 // handle a button press
4267 void multi_pxo_priv_button_pressed(int n)
4269 char priv_chan_name[128];
4272 case MULTI_PXO_COM_CANCEL:
4273 Multi_pxo_priv_return_code = 0;
4276 case MULTI_PXO_COM_OK:
4277 Multi_pxo_com_input.get_text(priv_chan_name);
4278 multi_pxo_strip_space(priv_chan_name, priv_chan_name, SDL_arraysize(priv_chan_name));
4280 // if its a 0 length string, interpret as a cancel
4281 if(strlen(priv_chan_name) <= 0){
4282 Multi_pxo_priv_return_code = 0;
4286 Multi_pxo_priv_return_code = 1;
4291 // process the inputbox
4292 void multi_pxo_priv_process_input()
4294 char priv_chan_name[128];
4296 // see if the user has pressed enter
4297 if(Multi_pxo_com_input.pressed()){
4298 Multi_pxo_com_input.get_text(priv_chan_name);
4299 multi_pxo_strip_space(priv_chan_name, priv_chan_name, SDL_arraysize(priv_chan_name));
4301 // if its a 0 length string, interpret as a cancel
4302 if(strlen(priv_chan_name) <= 0){
4303 Multi_pxo_priv_return_code = 0;
4307 // otherwise interpret as "accept"
4308 Multi_pxo_priv_return_code = 1;
4310 // add in the "+" which indicates a private room
4311 SDL_strlcpy(Multi_pxo_priv_chan, "+", SDL_arraysize(Multi_pxo_priv_chan));
4312 SDL_strlcat(Multi_pxo_priv_chan, priv_chan_name, SDL_arraysize(Multi_pxo_priv_chan));
4316 // find player stuff -----------------------------------------
4318 char name_lookup[255];
4320 // initialize the popup
4321 void multi_pxo_find_init()
4323 SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_FIND);
4325 // initialize the common dialog with the passed max input length
4326 multi_pxo_com_init(MAX_PLAYER_NAME_LEN);
4328 // return code, set to something other than -1 if we're supposed to return
4329 Multi_pxo_find_return_code = -1;
4331 // mark us as running
4332 Multi_pxo_mode = MULTI_PXO_MODE_FIND;
4334 // not searching yet
4335 Multi_pxo_searching = 0;
4338 multi_pxo_com_set_top_text(XSTR("Enter user to be found",962));
4341 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4344 SDL_strlcpy(name_lookup, "", SDL_arraysize(name_lookup));
4347 // close down the popup
4348 void multi_pxo_find_close()
4350 // close down the common dialog
4351 multi_pxo_com_close();
4353 // mark us as not running any more
4354 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4357 // run the popup, 0 if still running, -1 if cancel, 1 if ok
4358 int multi_pxo_find_popup()
4362 // if we're not already running, initialize stuff
4363 if(Multi_pxo_mode != MULTI_PXO_MODE_FIND){
4365 multi_pxo_find_init();
4367 // return "still running"
4371 k = Multi_pxo_com_window.process();
4373 // process keypresses
4375 // like hitting the cancel button
4377 Multi_pxo_find_return_code = 0;
4381 // process button presses
4382 multi_pxo_find_process_buttons();
4384 // process the inputbox
4385 multi_pxo_find_process_input();
4387 // process search mode if applicable
4388 multi_pxo_find_search_process();
4390 // blit the background
4391 multi_pxo_blit_all();
4395 gr_set_bitmap(Multi_pxo_com_bitmap);
4396 gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4397 Multi_pxo_com_window.draw();
4399 // blit any text lines
4400 multi_pxo_com_blit_text();
4404 // check the return code
4405 switch(Multi_pxo_find_return_code){
4406 // still in progress
4412 // close the popup down
4413 multi_pxo_find_close();
4418 // close the popup down
4419 multi_pxo_find_close();
4421 // if we have a channel, join it now if possible
4422 if(strlen(Multi_pxo_find_channel) > 0){
4423 pxo_channel *lookup;
4424 lookup = multi_pxo_find_channel(Multi_pxo_find_channel,Multi_pxo_channels);
4426 // if we couldn't find it, don't join
4428 multi_pxo_join_channel(lookup);
4437 // process button presses
4438 void multi_pxo_find_process_buttons()
4442 // check all buttons
4443 for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4444 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4445 multi_pxo_find_button_pressed(idx);
4451 // handle a button press
4452 void multi_pxo_find_button_pressed(int n)
4455 case MULTI_PXO_COM_CANCEL:
4456 Multi_pxo_find_return_code = 0;
4459 case MULTI_PXO_COM_OK:
4460 Multi_pxo_find_return_code = 1;
4465 // process the inputbox
4466 void multi_pxo_find_process_input()
4468 // see if the user has pressed enter
4469 if(Multi_pxo_com_input.pressed()){
4470 // if we're not already in search mode
4471 if(!Multi_pxo_searching){
4473 SDL_zero(Multi_pxo_com_middle_text);
4474 SDL_zero(Multi_pxo_com_bottom_text);
4476 Multi_pxo_com_input.get_text(name_lookup);
4477 multi_pxo_strip_space(name_lookup, name_lookup, SDL_arraysize(name_lookup));
4479 // never search with a zero length string
4480 if(strlen(name_lookup) > 0){
4481 char search_text[512];
4483 // put us in search mode
4484 Multi_pxo_searching = 1;
4487 GetChannelByUser(name_lookup);
4490 SDL_snprintf(search_text, SDL_arraysize(search_text), XSTR("Searching for %s", 963), name_lookup);
4491 multi_pxo_com_set_top_text(search_text);
4495 SDL_zero(Multi_pxo_com_top_text);
4501 // process search mode if applicable
4502 void multi_pxo_find_search_process()
4506 // if we're not searching for anything, return
4507 if(!Multi_pxo_searching){
4511 // otherwise check to see if we've found him
4512 channel = GetChannelByUser(NULL);
4514 // if we've got a result, let the user know
4516 // if he couldn't be found
4517 if(channel == (char *)-1){
4518 multi_pxo_com_set_middle_text(XSTR("User not found",964));
4519 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4521 if(channel[0] == '*'){
4522 multi_pxo_com_set_middle_text(XSTR("Player is logged in but is not on a channel",965));
4523 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4527 // if this guy is on a public channel, display which one
4528 if(channel[0] == '#'){
4529 SDL_snprintf(p_text, SDL_arraysize(p_text), XSTR("Found %s on :", 966), name_lookup);
4531 // display the results
4532 multi_pxo_com_set_middle_text(p_text);
4533 multi_pxo_com_set_bottom_text(channel+1);
4535 // mark down the channel name so we know where to find him
4536 SDL_strlcpy(Multi_pxo_find_channel, channel, SDL_arraysize(Multi_pxo_find_channel));
4537 // strip out trailing whitespace
4538 if(Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] == ' '){
4539 Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] = '\0';
4542 // if this is a private channel
4543 else if(channel[0] == '+'){
4544 SDL_snprintf(p_text, SDL_arraysize(p_text), XSTR("Found %s on a private channel", 967), name_lookup);
4545 multi_pxo_com_set_middle_text(p_text);
4547 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4552 // unset search mode
4553 Multi_pxo_searching = 0;
4555 // clear the inputbox
4556 Multi_pxo_com_input.set_text("");
4561 // player info stuff -----------------------------------------
4563 // popup conditional functions, returns 10 on successful get of stats
4564 int multi_pxo_pinfo_cond()
4567 char temp_string[255];
4570 // process common stuff
4571 multi_pxo_process_common();
4573 // run the networking functions for the PXO API
4574 multi_pxo_api_process();
4576 // process depending on what mode we're in
4577 switch(Multi_pxo_retrieve_mode){
4578 // getting his player tracker id
4580 // if the thing is non-null, do something
4581 ret_string = GetTrackerIdByUser(Multi_pxo_retrieve_name);
4582 if(ret_string != NULL){
4583 // user not-online/not found
4584 if(ret_string == (char *)-1){
4588 // user not a tracker pilot
4589 if(!SDL_strcasecmp(ret_string,"-1")){
4593 // otherwise parse into his id and callsign
4594 SDL_strlcpy(temp_string, ret_string, SDL_arraysize(temp_string));
4595 tok = strtok(temp_string," ");
4599 SDL_strlcpy(Multi_pxo_retrieve_id, tok, SDL_arraysize(Multi_pxo_retrieve_id));
4602 tok = strtok(NULL,"");
4604 SDL_strlcpy(Multi_pxo_retrieve_name, tok, SDL_arraysize(Multi_pxo_retrieve_name));
4611 // failure of some kind or another
4616 Multi_pxo_retrieve_mode = 1;
4621 // initial call to get his stats
4623 // change the popup text
4624 popup_change_text(XSTR("Getting player stats",968));
4627 memset(&Multi_pxo_pinfo, 0, sizeof(vmt_freespace2_struct));
4628 SDL_strlcpy(Multi_pxo_pinfo.pilot_name, Multi_pxo_retrieve_name, SDL_arraysize(Multi_pxo_pinfo.pilot_name));
4629 SDL_strlcpy(Multi_pxo_pinfo.tracker_id, Multi_pxo_retrieve_id, SDL_arraysize(Multi_pxo_pinfo.tracker_id));
4631 // make the initial call to the API
4632 GetFSPilotData((vmt_freespace2_struct*)0xffffffff,NULL,NULL,0);
4633 if(GetFSPilotData(&Multi_pxo_pinfo,Multi_pxo_retrieve_name,Multi_pxo_retrieve_id,0) != 0){
4636 // if the call went through, set the mode to 2
4638 Multi_pxo_retrieve_mode = 2;
4642 // busy retrieving his stats
4644 switch(GetFSPilotData(NULL,NULL,NULL,0)){
4645 // timeout, fail, cancel
4663 // return not done yet
4667 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
4668 int multi_pxo_pinfo_get(char *name)
4671 Multi_pxo_retrieve_mode = 0;
4672 SDL_strlcpy(Multi_pxo_retrieve_name, name, SDL_arraysize(Multi_pxo_retrieve_name));
4673 switch(popup_till_condition(multi_pxo_pinfo_cond,XSTR("&Cancel", 779),XSTR("Retrieving player tracker id",969))){
4678 // failed to get his tracker id
4682 // failed to get his stats
4687 // we didn't get the stats
4691 // fire up the stats view popup
4692 void multi_pxo_pinfo_show()
4694 // initialize the popup
4695 multi_pxo_pinfo_init();
4699 game_set_frametime(GS_STATE_PXO);
4700 } while(!multi_pxo_pinfo_do());
4702 // close down the popup
4703 multi_pxo_pinfo_close();
4706 // build the stats labels values
4707 void multi_pxo_pinfo_build_vals()
4709 vmt_freespace2_struct *fs = &Multi_pxo_pinfo;
4711 SDL_zero(Multi_pxo_pinfo_vals);
4714 SDL_strlcpy(Multi_pxo_pinfo_vals[0], fs->pilot_name, SDL_arraysize(Multi_pxo_pinfo_vals[0]));
4715 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]));
4718 multi_sg_rank_build_name(Ranks[fs->rank].name, Multi_pxo_pinfo_vals[1], SDL_arraysize(Multi_pxo_pinfo_vals[1]));
4719 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]));
4722 SDL_snprintf(Multi_pxo_pinfo_vals[2], SDL_arraysize(Multi_pxo_pinfo_vals[2]), "%d", fs->kill_count);
4725 SDL_snprintf(Multi_pxo_pinfo_vals[3], SDL_arraysize(Multi_pxo_pinfo_vals[3]), "%d", fs->assists);
4728 SDL_snprintf(Multi_pxo_pinfo_vals[4], SDL_arraysize(Multi_pxo_pinfo_vals[4]), "%d", fs->kill_count - fs->kill_count_ok);
4731 SDL_snprintf(Multi_pxo_pinfo_vals[5], SDL_arraysize(Multi_pxo_pinfo_vals[5]), "%d", (int)fs->missions_flown);
4734 game_format_time(fl2f((float)fs->flight_time), Multi_pxo_pinfo_vals[6], SDL_arraysize(Multi_pxo_pinfo_vals[6]));
4737 if(fs->last_flown == 0){
4738 SDL_strlcpy(Multi_pxo_pinfo_vals[7], XSTR("No missions flown", 970), SDL_arraysize(Multi_pxo_pinfo_vals[7]));
4740 tm *tmr = gmtime((time_t*)&fs->last_flown);
4742 strftime(Multi_pxo_pinfo_vals[7],30,"%m/%d/%y %H:%M",tmr);
4744 SDL_strlcpy(Multi_pxo_pinfo_vals[7], "", SDL_arraysize(Multi_pxo_pinfo_vals[7]));
4748 // primary shots fired
4749 SDL_snprintf(Multi_pxo_pinfo_vals[8], SDL_arraysize(Multi_pxo_pinfo_vals[8]), "%d", (int)fs->p_shots_fired);
4751 // primary shots hit
4752 SDL_snprintf(Multi_pxo_pinfo_vals[9], SDL_arraysize(Multi_pxo_pinfo_vals[9]), "%d", (int)fs->p_shots_hit);
4755 if(fs->p_shots_fired > 0){
4756 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));
4758 SDL_strlcpy(Multi_pxo_pinfo_vals[10], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[10]));
4761 // secondary shots fired
4762 SDL_snprintf(Multi_pxo_pinfo_vals[11], SDL_arraysize(Multi_pxo_pinfo_vals[11]), "%d", (int)fs->s_shots_fired);
4764 // secondary shots hit
4765 SDL_snprintf(Multi_pxo_pinfo_vals[12], SDL_arraysize(Multi_pxo_pinfo_vals[12]), "%d", (int)fs->s_shots_hit);
4767 // secondary hit pct
4768 if(fs->s_shots_fired > 0){
4769 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));
4771 SDL_strlcpy(Multi_pxo_pinfo_vals[13], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[13]));
4774 // primary friendly hits
4775 SDL_snprintf(Multi_pxo_pinfo_vals[14], SDL_arraysize(Multi_pxo_pinfo_vals[14]), "%d", fs->p_bonehead_hits);
4777 // primary friendly hit %
4778 if(fs->p_shots_hit > 0){
4779 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)));
4781 SDL_strlcpy(Multi_pxo_pinfo_vals[15], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[15]));
4784 // secondary friendly hits
4785 SDL_snprintf(Multi_pxo_pinfo_vals[16], SDL_arraysize(Multi_pxo_pinfo_vals[16]), "%d", fs->s_bonehead_hits);
4787 // secondary friendly hit %
4788 if(fs->s_shots_hit > 0){
4789 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)));
4791 SDL_strlcpy(Multi_pxo_pinfo_vals[17], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[17]));
4795 // initialize the popup
4796 void multi_pxo_pinfo_init()
4800 // create the interface window
4801 Multi_pxo_pinfo_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4802 Multi_pxo_pinfo_window.set_mask_bmap(Multi_pxo_pinfo_mask_fname[gr_screen.res]);
4804 Multi_pxo_pinfo_bitmap = bm_load(Multi_pxo_pinfo_fname[gr_screen.res]);
4805 SDL_assert(Multi_pxo_pinfo_bitmap != -1);
4807 // create the interface buttons
4808 for(idx=0; idx<MULTI_PXO_PINFO_NUM_BUTTONS; idx++){
4809 // create the object
4810 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);
4812 // set the sound to play when highlighted
4813 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4815 // set the ani for the button
4816 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_pinfo_buttons[gr_screen.res][idx].filename);
4819 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_pinfo_buttons[gr_screen.res][idx].hotspot);
4823 for(idx=0; idx<MULTI_PXO_PINFO_NUM_TEXT; idx++){
4824 Multi_pxo_pinfo_window.add_XSTR(&Multi_pxo_pinfo_text[gr_screen.res][idx]);
4827 // set up the stats labels
4828 Multi_pxo_pinfo_stats_labels[0] = strdup(XSTR("Name", 1532));
4829 Multi_pxo_pinfo_stats_labels[1] = strdup(XSTR("Rank", 1533));
4830 Multi_pxo_pinfo_stats_labels[2] = strdup(XSTR("Kills", 1534));
4831 Multi_pxo_pinfo_stats_labels[3] = strdup(XSTR("Assists", 1535));
4832 Multi_pxo_pinfo_stats_labels[4] = strdup(XSTR("Friendly kills", 1536));
4833 Multi_pxo_pinfo_stats_labels[5] = strdup(XSTR("Missions flown", 1537));
4834 Multi_pxo_pinfo_stats_labels[6] = strdup(XSTR("Flight time", 1538));
4835 Multi_pxo_pinfo_stats_labels[7] = strdup(XSTR("Last flown", 1539));
4836 Multi_pxo_pinfo_stats_labels[8] = strdup(XSTR("Primary shots fired", 1540));
4837 Multi_pxo_pinfo_stats_labels[9] = strdup(XSTR("Primary shots hit", 1541));
4838 Multi_pxo_pinfo_stats_labels[10] = strdup(XSTR("Primary hit %", 1542));
4839 Multi_pxo_pinfo_stats_labels[11] = strdup(XSTR("Secondary shots fired", 1543));
4840 Multi_pxo_pinfo_stats_labels[12] = strdup(XSTR("Secondary shots hit", 1544));
4841 Multi_pxo_pinfo_stats_labels[13] = strdup(XSTR("Secondary hit %", 1545));
4842 Multi_pxo_pinfo_stats_labels[14] = strdup(XSTR("Primary friendly hits", 1546));
4843 Multi_pxo_pinfo_stats_labels[15] = strdup(XSTR("Primary friendly hit %", 1547));
4844 Multi_pxo_pinfo_stats_labels[16] = strdup(XSTR("Secondary friendly hits", 1548));
4845 Multi_pxo_pinfo_stats_labels[17] = strdup(XSTR("Secondary friendly hit %", 1549));
4847 // build the stats labels values
4848 multi_pxo_pinfo_build_vals();
4852 int multi_pxo_pinfo_do()
4854 int k = Multi_pxo_pinfo_window.process();
4856 // process common stuff
4857 multi_pxo_process_common();
4859 // run the networking functions for the PXO API
4860 multi_pxo_api_process();
4862 // check to see if he pressed escp
4863 if(k == SDLK_ESCAPE){
4867 // if he pressed the ok button
4868 if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_OK].button.pressed()){
4872 // if he pressed the medals buttons, run the medals screen
4873 if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_MEDALS].button.pressed()){
4875 game_feature_not_in_demo_popup();
4877 multi_pxo_run_medals();
4883 // blit everything on the "normal" screen
4884 multi_pxo_blit_all();
4886 // blit our own stuff
4888 gr_set_bitmap(Multi_pxo_pinfo_bitmap);
4890 Multi_pxo_pinfo_window.draw();
4892 // blit the stats themselves
4893 multi_pxo_pinfo_blit();
4903 void multi_pxo_pinfo_close()
4907 // destroy the UI_WINDOW
4908 Multi_pxo_pinfo_window.destroy();
4910 // unload the bitmap
4911 if(Multi_pxo_pinfo_bitmap != -1){
4912 bm_unload(Multi_pxo_pinfo_bitmap);
4915 // free the stats labels strings
4916 for (i=0; i<MULTI_PXO_PINFO_NUM_LABELS; i++) {
4917 free(Multi_pxo_pinfo_stats_labels[i]);
4921 // blit all the stats on this screen
4922 void multi_pxo_pinfo_blit()
4927 // blit all the labels
4928 y_start = Multi_pxo_pinfo_coords[gr_screen.res][1];
4929 for(idx=0; idx<MULTI_PXO_PINFO_NUM_LABELS; idx++){
4931 gr_set_color_fast(&Color_bright);
4932 gr_string(Multi_pxo_pinfo_coords[gr_screen.res][0], y_start, Multi_pxo_pinfo_stats_labels[idx]);
4934 // blit the label's value
4935 gr_set_color_fast(&Color_normal);
4936 gr_string(Multi_pxo_pinfo_val_x[gr_screen.res], y_start, Multi_pxo_pinfo_vals[idx]);
4939 y_start += Multi_pxo_pinfo_stats_spacing[idx];
4943 // run the medals screen
4944 void multi_pxo_run_medals()
4948 // process common stuff
4949 multi_pxo_process_common();
4951 // run the networking functions for the PXO API
4952 multi_pxo_api_process();
4954 // initialize the freespace data and the player struct
4955 multi_stats_tracker_to_fs(&Multi_pxo_pinfo, &Multi_pxo_pinfo_player.stats);
4956 SDL_strlcpy(Multi_pxo_pinfo_player.callsign, Multi_pxo_pinfo.pilot_name, SDL_arraysize(Multi_pxo_pinfo_player.callsign));
4958 // initialize the medals screen
4959 medal_main_init(&Multi_pxo_pinfo_player, MM_POPUP);
4961 // run the medals screen until it says that it should be closed
4963 // set frametime and run common functions
4964 game_set_frametime(-1);
4965 game_do_state_common(gameseq_get_state());
4967 // run the medals screen
4968 ret_code = medal_main_do();
4971 // close the medals screen down
4974 // reset the palette
4975 multi_pxo_load_palette();
4979 // notify stuff stuff -----------------------------------------
4981 // add a notification string
4982 void multi_pxo_notify_add(const char *txt)
4985 SDL_strlcpy(Multi_pxo_notify_text, txt, SDL_arraysize(Multi_pxo_notify_text));
4987 // set the timestamp
4988 Multi_pxo_notify_stamp = timestamp(MULTI_PXO_NOTIFY_TIME);
4991 // blit and process the notification string
4992 void multi_pxo_notify_blit()
4996 // if the timestamp is -1, do nothing
4997 if(Multi_pxo_notify_stamp == -1){
5001 // if it has expired, do nothing
5002 if(timestamp_elapsed(Multi_pxo_notify_stamp)){
5003 Multi_pxo_notify_stamp = -1;
5006 // otherwise blit the text
5007 gr_set_color_fast(&Color_bright);
5008 gr_get_string_size(&w,NULL,Multi_pxo_notify_text);
5009 gr_string((gr_screen.max_w - w)/2,MULTI_PXO_NOTIFY_Y,Multi_pxo_notify_text);
5013 // initialize the PXO help screen
5014 void multi_pxo_help_init()
5018 // load the background bitmap
5019 Multi_pxo_help_bitmap = bm_load(Multi_pxo_help_fname[gr_screen.res]);
5020 if(Multi_pxo_help_bitmap < 0){
5021 // we failed to load the bitmap - this is very bad
5024 // create the interface window
5025 Multi_pxo_help_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
5026 Multi_pxo_help_window.set_mask_bmap(Multi_pxo_help_mask_fname[gr_screen.res]);
5028 // create the interface buttons
5029 for(idx=0; idx<MULTI_PXO_HELP_NUM_BUTTONS; idx++){
5030 // create the object
5031 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);
5033 // set the sound to play when highlighted
5034 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
5036 // set the ani for the button
5037 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_help_buttons[gr_screen.res][idx].filename);
5040 Multi_pxo_help_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_help_buttons[gr_screen.res][idx].hotspot);
5044 for(idx=0; idx<MULTI_PXO_HELP_NUM_TEXT; idx++){
5045 Multi_pxo_help_window.add_XSTR(&Multi_pxo_help_text[gr_screen.res][idx]);
5048 // if we haven't already loaded in the text, do so
5049 // if(!Multi_pxo_help_loaded){
5050 multi_pxo_help_load();
5053 // set the current page to 0
5054 Multi_pxo_help_cur = 0;
5057 // do frame for PXO help
5058 void multi_pxo_help_do()
5061 if(Multi_pxo_connected){
5062 multi_pxo_api_process();
5065 // process common stuff
5066 multi_pxo_process_common();
5068 int k = Multi_pxo_help_window.process();
5070 // process any keypresses
5073 gamesnd_play_iface(SND_USER_SELECT);
5074 gameseq_post_event(GS_EVENT_PXO);
5078 // process button presses
5079 multi_pxo_help_process_buttons();
5081 // draw the background, etc
5083 GR_MAYBE_CLEAR_RES(Multi_pxo_help_bitmap);
5084 if(Multi_pxo_help_bitmap != -1){
5085 gr_set_bitmap(Multi_pxo_help_bitmap);
5088 Multi_pxo_help_window.draw();
5090 // blit the current page
5091 multi_pxo_help_blit_page();
5097 // close the pxo screen
5098 void multi_pxo_help_close()
5102 // unload any bitmaps
5103 bm_unload(Multi_pxo_help_bitmap);
5105 // destroy the UI_WINDOW
5106 Multi_pxo_help_window.destroy();
5109 for(idx=0; idx<Multi_pxo_help_num_pages; idx++){
5110 for(idx2=0; idx2<Multi_pxo_help_pages[idx].num_lines; idx2++){
5112 if(Multi_pxo_help_pages[idx].text[idx2] != NULL){
5113 free(Multi_pxo_help_pages[idx].text[idx2]);
5114 Multi_pxo_help_pages[idx].text[idx2] = NULL;
5120 // load the help file up
5121 void multi_pxo_help_load()
5126 // if its already loaded, do nothing
5127 // if(Multi_pxo_help_loaded){
5131 // read in the text file
5133 in = cfopen(MULTI_PXO_HELP_FILE,"rt",CFILE_NORMAL,CF_TYPE_DATA);
5134 SDL_assert(in != NULL);
5139 Multi_pxo_help_num_pages = 0;
5141 // blast all the help pages clear
5142 memset(Multi_pxo_help_pages, 0, sizeof(help_page) * MULTI_PXO_MAX_PAGES);
5143 Multi_pxo_help_num_pages = 0;
5144 cp = &Multi_pxo_help_pages[0];
5148 cp->text[cp->num_lines] = (char*)malloc(Multi_pxo_chars_per_line[gr_screen.res]);
5149 if(cp->text[cp->num_lines] == NULL){
5153 // read in the next line
5154 cfgets(cp->text[cp->num_lines++], Multi_pxo_chars_per_line[gr_screen.res], in);
5156 // skip to the next page if necessary
5157 if(cp->num_lines == Multi_pxo_lines_pp[gr_screen.res]){
5158 Multi_pxo_help_num_pages++;
5159 SDL_assert(Multi_pxo_help_num_pages < MULTI_PXO_MAX_PAGES);
5160 if(Multi_pxo_help_num_pages >= MULTI_PXO_MAX_PAGES){
5161 Multi_pxo_help_num_pages--;
5164 cp = &Multi_pxo_help_pages[Multi_pxo_help_num_pages];
5171 // mark the help as having been loaded
5172 // Multi_pxo_help_loaded = 1;
5175 // blit the current page
5176 void multi_pxo_help_blit_page()
5181 help_page *cp = &Multi_pxo_help_pages[Multi_pxo_help_cur];
5184 y_start = Multi_pxo_help_coords[gr_screen.res][1];
5185 for(idx=0;idx<cp->num_lines;idx++){
5186 // if the first symbol is "@", highlight the line
5187 if(cp->text[idx][0] == '@'){
5188 gr_set_color_fast(&Color_bright);
5191 gr_set_color_fast(&Color_normal);
5196 gr_string(Multi_pxo_help_coords[gr_screen.res][0], y_start, cp->text[idx] + start_pos);
5198 // increment the y location
5203 // process button presses
5204 void multi_pxo_help_process_buttons()
5208 // process all buttons
5209 for(idx=0;idx<MULTI_PXO_HELP_NUM_BUTTONS;idx++){
5210 if(Multi_pxo_help_buttons[gr_screen.res][idx].button.pressed()){
5211 multi_pxo_help_button_pressed(idx);
5218 void multi_pxo_help_button_pressed(int n)
5221 case MULTI_PXO_HELP_PREV:
5222 // if we're already at page 0, do nothing
5223 if(Multi_pxo_help_cur == 0){
5224 gamesnd_play_iface(SND_GENERAL_FAIL);
5226 Multi_pxo_help_cur--;
5227 gamesnd_play_iface(SND_USER_SELECT);
5231 case MULTI_PXO_HELP_NEXT:
5232 // if we're already at max pages, do nothing
5233 if(Multi_pxo_help_cur == Multi_pxo_help_num_pages){
5234 gamesnd_play_iface(SND_GENERAL_FAIL);
5236 Multi_pxo_help_cur++;
5237 gamesnd_play_iface(SND_USER_SELECT);
5241 case MULTI_PXO_HELP_CONTINUE:
5242 gamesnd_play_iface(SND_USER_SELECT);
5243 gameseq_post_event(GS_EVENT_PXO);
5248 // http banner stuff ---------------------------------------------
5251 void multi_pxo_ban_init()
5253 // zero the active banner bitmap
5254 Multi_pxo_banner.ban_bitmap = -1;
5256 // are we doing banners at all?
5257 if(os_config_read_uint(NULL, "PXOBanners", 1)){
5258 // if we're already in idle mode, we're done downloading for this instance of freespace. pick a random image we already have
5259 if(Multi_pxo_ban_mode == PXO_BAN_MODE_IDLE){
5260 Multi_pxo_ban_mode = PXO_BAN_MODE_CHOOSE_RANDOM;
5264 // set ourselves to startup mode
5265 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
5266 Multi_pxo_ban_get = NULL;
5268 // set ourselves to idle mode
5269 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5270 Multi_pxo_ban_get = NULL;
5273 // zero the active banner bitmap
5274 SDL_zero(Multi_pxo_banner);
5275 Multi_pxo_banner.ban_bitmap = -1;
5278 // process http download details
5279 void multi_pxo_ban_process()
5281 char url_string[512] = "";
5282 char local_file[MAX_PATH_LEN] = "";
5285 switch(Multi_pxo_ban_mode){
5286 // start downloading list
5287 case PXO_BAN_MODE_LIST_STARTUP:
5289 SDL_snprintf(url_string, SDL_arraysize(url_string), "%s/%s", Multi_options_g.pxo_banner_url, PXO_BANNERS_CONFIG_FILE);
5292 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, PXO_BANNERS_CONFIG_FILE);
5294 // try creating the file get object
5295 Multi_pxo_ban_get = new InetGetFile(url_string, local_file);
5298 if(Multi_pxo_ban_get == NULL){
5299 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5302 // go to the downloading list mode
5303 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST;
5307 case PXO_BAN_MODE_LIST:
5309 if(Multi_pxo_ban_get->IsFileError()){
5310 delete Multi_pxo_ban_get;
5311 Multi_pxo_ban_get = NULL;
5312 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5316 // connecting, receiving
5317 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5322 if(Multi_pxo_ban_get->IsFileReceived()){
5323 delete Multi_pxo_ban_get;
5324 Multi_pxo_ban_get = NULL;
5325 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_STARTUP;
5329 // start downloading files
5330 case PXO_BAN_MODE_IMAGES_STARTUP:
5331 // first thing - parse the banners file and pick a file
5332 multi_pxo_ban_parse_banner_file(0);
5334 // if we have no active file, we're done
5335 if((strlen(Multi_pxo_banner.ban_file) <= 0) || (strlen(Multi_pxo_banner.ban_file_url) <= 0)){
5336 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5340 // if the file already exists, we're done
5341 if(cf_exist(Multi_pxo_banner.ban_file, CF_TYPE_MULTI_CACHE)){
5342 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5346 // otherwise try and download it
5347 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, Multi_pxo_banner.ban_file);
5348 // try creating the file get object
5349 Multi_pxo_ban_get = new InetGetFile(Multi_pxo_banner.ban_file_url, local_file);
5352 if(Multi_pxo_ban_get == NULL){
5353 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5356 // go to the downloading images mode
5357 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES;
5360 // downloading files
5361 case PXO_BAN_MODE_IMAGES:
5363 if(Multi_pxo_ban_get->IsFileError()){
5364 delete Multi_pxo_ban_get;
5365 Multi_pxo_ban_get = NULL;
5366 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5370 // connecting, receiving
5371 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5376 if(Multi_pxo_ban_get->IsFileReceived()){
5377 delete Multi_pxo_ban_get;
5378 Multi_pxo_ban_get = NULL;
5379 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5383 // done downloading - maybe load an image
5384 case PXO_BAN_MODE_IMAGES_DONE:
5385 // make sure we have a valid filename
5386 // SDL_assert(strlen(Multi_pxo_banner.ban_file) > 0);
5387 if(strlen(Multi_pxo_banner.ban_file) > 0){
5388 Multi_pxo_banner.ban_bitmap = bm_load(Multi_pxo_banner.ban_file);
5392 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5395 // idle (done with EVERYTHING)
5396 case PXO_BAN_MODE_IDLE:
5397 // if the banner button was clicked
5398 if(Multi_pxo_ban_button.pressed()){
5399 multi_pxo_ban_clicked();
5403 case PXO_BAN_MODE_CHOOSE_RANDOM:
5404 // first thing - parse the banners file and pick a file
5405 multi_pxo_ban_parse_banner_file(1);
5407 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5413 void multi_pxo_ban_close()
5415 // if we have a currently active transfer
5416 if(Multi_pxo_ban_get != NULL){
5417 Multi_pxo_ban_get->AbortGet();
5418 delete Multi_pxo_ban_get;
5419 Multi_pxo_ban_get = NULL;
5422 // if we have a loaded bitmap, unload it
5423 if(Multi_pxo_banner.ban_bitmap != -1){
5424 bm_unload(Multi_pxo_banner.ban_bitmap);
5425 Multi_pxo_banner.ban_bitmap = -1;
5429 // parse the banners file and maybe fill in Multi_pxo_dl_file
5430 void multi_pxo_ban_parse_banner_file(int choose_existing)
5432 char file_url[MAX_PATH_LEN] = "";
5433 char banners[10][MAX_PATH_LEN];
5434 char urls[10][MAX_PATH_LEN];
5437 int num_banners, idx;
5438 CFILE *in = cfopen(PXO_BANNERS_CONFIG_FILE, "rt", CFILE_NORMAL, CF_TYPE_MULTI_CACHE);
5440 SDL_zero(Multi_pxo_banner);
5441 Multi_pxo_banner.ban_bitmap = -1;
5448 // clear all strings
5452 // get the global banner url
5453 if(cfgets(file_url, SDL_arraysize(file_url), in) == NULL){
5455 cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5458 drop_leading_white_space(file_url);
5459 drop_trailing_white_space(file_url);
5461 // otherwise read in
5463 while(num_banners < 10){
5464 // try and get the pcx
5465 if(cfgets(banners[num_banners], SDL_arraysize(banners[0]), in) == NULL){
5468 // try and get the url
5469 if(cfgets(urls[num_banners], SDL_arraysize(urls[0]), in) == NULL){
5473 // strip off trailing and leading whitespace
5474 drop_leading_white_space(banners[num_banners]);
5475 drop_trailing_white_space(banners[num_banners]);
5476 drop_leading_white_space(urls[num_banners]);
5477 drop_trailing_white_space(urls[num_banners]);
5487 if(num_banners <= 0){
5491 // if we're only selecting files which already exist (previously downloaded)
5492 if(choose_existing){
5494 for(idx=0; idx<10; idx++){
5498 // build a list of existing files
5500 for(idx=0; idx<num_banners; idx++){
5501 if(cf_exist(banners[idx], CF_TYPE_MULTI_CACHE)){
5508 if(exist_count <= 0){
5513 int select = (int)frand_range(0.0f, (float)exist_count);
5514 if(select >= exist_count){
5515 select = exist_count - 1;
5520 for(idx=0; idx<exist_count; idx++){
5530 if(idx < exist_count){
5532 SDL_strlcpy(Multi_pxo_banner.ban_file, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file));
5534 // get the full file url
5535 SDL_strlcpy(Multi_pxo_banner.ban_file_url, file_url, SDL_arraysize(Multi_pxo_banner.ban_file_url));
5536 SDL_strlcat(Multi_pxo_banner.ban_file_url, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file_url));
5538 // url of where to go to when clicked
5539 SDL_strlcpy(Multi_pxo_banner.ban_url, urls[idx], SDL_arraysize(Multi_pxo_banner.ban_url));
5542 // randomly pick a file for download
5544 idx = (int)frand_range(0.0f, (float)num_banners);
5546 if(idx >= num_banners){
5547 idx = num_banners - 1;
5554 SDL_strlcpy(Multi_pxo_banner.ban_file, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file));
5556 // get the full file url
5557 SDL_strlcpy(Multi_pxo_banner.ban_file_url, file_url, SDL_arraysize(Multi_pxo_banner.ban_file_url));
5558 SDL_strlcat(Multi_pxo_banner.ban_file_url, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file_url));
5560 // url of where to go to when clicked
5561 SDL_strlcpy(Multi_pxo_banner.ban_url, urls[idx], SDL_arraysize(Multi_pxo_banner.ban_url));
5564 // delete the banner config file
5565 // cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5568 // any bitmap or info or whatever
5569 void multi_pxo_ban_draw()
5571 // if we have a valid bitmap
5572 if(Multi_pxo_banner.ban_bitmap >= 0){
5573 // if the mouse is over the banner button, highlight with a rectangle
5574 if(Multi_pxo_ban_button.is_mouse_on()){
5575 gr_set_color_fast(&Color_bright_blue);
5576 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);
5579 // draw the bitmap itself
5580 gr_set_bitmap(Multi_pxo_banner.ban_bitmap);
5581 gr_bitmap(Pxo_ban_coords[gr_screen.res][0], Pxo_ban_coords[gr_screen.res][1]);
5585 // called when the URL button is clicked
5586 void multi_pxo_ban_clicked()
5588 // if we have a valid bitmap and URL, launch the URL
5589 if((Multi_pxo_banner.ban_bitmap >= 0) && (strlen(Multi_pxo_banner.ban_url) > 0)){
5590 multi_pxo_url(Multi_pxo_banner.ban_url);