2 * Copyright (C) Volition, Inc. 2005. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on the
11 * $Logfile: /Freespace2/code/Network/multi_pxo.cpp $
13 * $Date: 10/25/99 5:52p $
16 * $Log: /Freespace2/code/Network/multi_pxo.cpp $
18 * 42 10/25/99 5:52p Jefff
19 * fixed some non-localized text
21 * 41 10/14/99 2:51p Jefff
24 * 40 10/13/99 3:29p Jefff
25 * fixed unnumbered XSTRs
27 * 39 9/13/99 11:30a Dave
28 * Added checkboxes and functionality for disabling PXO banners as well as
29 * disabling d3d zbuffer biasing.
31 * 38 8/30/99 5:01p Dave
32 * Made d3d do less state changing in the nebula. Use new chat server for
35 * 37 8/20/99 2:09p Dave
38 * 36 8/17/99 3:31p Jefff
39 * fixed too-big chat area in 640
41 * 35 8/16/99 9:51a Jefff
42 * webcursor over html links
44 * 34 8/11/99 12:19p Jefff
45 * no longer force pxo chatbox to snap to bottom when new text is added
47 * 33 8/10/99 5:53p Jefff
48 * fixed 640 chatbox width
50 * 32 8/06/99 1:49p Jefff
51 * added hi-res pxo logo
53 * 31 8/06/99 12:29a Dave
56 * 30 8/05/99 4:17p Dave
57 * Tweaks to client interpolation.
59 * 29 8/05/99 4:11p Jefff
61 * 28 8/04/99 10:59a Dave
62 * Removed "squad" text.
64 * 27 8/03/99 5:55p Jefff
65 * column headings in channel window, some general scrubbing
67 * 26 7/26/99 11:14a Andsager
68 * Disable medals in demo multiplayer
70 * 25 7/19/99 2:13p Dave
71 * Added some new strings for Heiko.
73 * 24 6/29/99 7:39p Dave
74 * Lots of small bug fixes.
76 * 23 6/18/99 5:16p Dave
77 * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
78 * dialog to PXO screen.
80 * 22 6/16/99 4:55p Dave
81 * New pxo pilot info popup.
83 * 21 6/07/99 9:51p Dave
84 * Consolidated all multiplayer ports into one.
86 * 20 6/01/99 3:52p Dave
87 * View footage screen. Fixed xstrings to not display the & symbol. Popup,
88 * dead popup, pxo find player popup, pxo private room popup.
90 * 19 5/21/99 6:45p Dave
91 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
92 * start game screen, multi password, and multi pxo-help screen.
94 * 18 5/19/99 4:07p Dave
95 * Moved versioning code into a nice isolated common place. Fixed up
96 * updating code on the pxo screen. Fixed several stub problems.
98 * 17 5/17/99 9:25a Dave
99 * Updated PXO screen. Still needs popups.
101 * 16 4/30/99 12:18p Dave
102 * Several minor bug fixes.
104 * 15 4/24/99 8:03p Dave
105 * Finalized banner code on PXO screen.
107 * 14 4/21/99 11:29p Dave
108 * Improved the banner stuff. Will display bitmaps, launcher URLs and
109 * highlight the banner bitmap.
111 * 13 4/20/99 6:39p Dave
112 * Almost done with artillery targeting. Added support for downloading
113 * images on the PXO screen.
115 * 12 2/17/99 2:11p Dave
116 * First full run of squad war. All freespace and tracker side stuff
119 * 11 2/08/99 5:07p Dave
120 * FS2 chat server support. FS2 specific validated missions.
122 * 10 2/04/99 6:29p Dave
123 * First full working rev of FS2 PXO support. Fixed Glide lighting
126 * 9 2/03/99 6:06p Dave
127 * Groundwork for FS2 PXO usertracker support. Gametracker support next.
129 * 8 1/30/99 5:08p Dave
130 * More new hi-res stuff.Support for nice D3D textures.
132 * 7 12/18/98 1:13a Dave
133 * Rough 1024x768 support for Direct3D. Proper detection and usage through
136 * 6 11/30/98 1:07p Dave
137 * 16 bit conversion, first run.
139 * 5 11/19/98 4:19p Dave
140 * Put IPX sockets back in psnet. Consolidated all multiplayer config
143 * 4 11/05/98 5:55p Dave
144 * Big pass at reducing #includes
146 * 3 10/13/98 9:29a Dave
147 * Started neatening up freespace.h. Many variables renamed and
148 * reorganized. Added AlphaColors.[h,cpp]
150 * 2 10/07/98 10:53a Dave
153 * 1 10/07/98 10:50a Dave
155 * 43 9/18/98 2:22a Dave
156 * Fixed freespace-side PXO api to correctly handle length 10 id strings.
157 * Fixed team select screen to handle alpha/beta/gamma ships which are not
158 * marked as OF_PLAYER_SHIP
160 * 42 9/17/98 3:08p Dave
161 * PXO to non-pxo game warning popup. Player icon stuff in create and join
162 * game screens. Upped server count refresh time in PXO to 35 secs (from
165 * 41 9/09/98 5:53p Dave
166 * Put in new tracker packets in API. Change cfile to be able to checksum
167 * portions of a file.
169 * 40 7/13/98 10:30a Lawrance
170 * add index numbers for newly localized strings
172 * 39 7/13/98 10:12a Dave
173 * Remove improperly localized strings.
175 * 38 7/10/98 2:03p Dave
176 * Additional PXO changes.
178 * 37 7/09/98 6:01p Dave
179 * Firsts full version of PXO updater. Put in stub for displaying
182 * 36 7/09/98 4:51p Dave
183 * First revision of PXO autoupdate check system.
185 * 35 7/08/98 8:15p Dave
186 * Fixed bandwidth capping for new system, and fixed pxo join button bug.
188 * 34 7/08/98 5:28p Dave
189 * Display channel name when returning to previous PXO channel from
192 * 33 7/08/98 4:54p Dave
193 * Join last used channel when returning from the games list.
195 * 32 6/17/98 10:56a Dave
196 * Put in debug code for detecting potential tracker stats update
199 * 31 6/16/98 10:39a Allender
202 * 30 6/13/98 6:01p Hoffoss
203 * Externalized all new (or forgot to be added) strings to all the code.
205 * 29 6/13/98 4:42p Hoffoss
206 * Fixed code to work properly with the XSTR program.
208 * 28 6/13/98 3:19p Hoffoss
209 * NOX()ed out a bunch of strings that shouldn't be translated.
211 * 27 6/13/98 1:45p Sandeep
213 * 26 6/05/98 3:08p Dave
214 * Fixed broken version checking. Fixed pxo chat scroll-down bug.
216 * 25 6/04/98 3:52p Dave
217 * Fixed pxo chat bug. Used physics compression code to compress observer
218 * updates. Put in rate limiting for observer updates.
220 * 24 5/27/98 1:15p Dave
221 * Change pxo login failure popup text. Put in message display of player
222 * who is currently doing voice.
224 * 23 5/26/98 11:25a Dave
225 * Check for NULL when scroll chat area up.
227 * 22 5/24/98 8:15p Dave
228 * Tweaked pxo some more.
230 * 21 5/24/98 3:45a Dave
231 * Minor object update fixes. Justify channel information on PXO. Add a
232 * bunch of configuration stuff for the standalone.
234 * 20 5/24/98 12:14a Dave
237 * 19 5/23/98 3:31p Dave
238 * Tweaked pxo code. Fixed observer HUD stuff.
240 * 18 5/23/98 3:02a Dave
243 * 17 5/21/98 9:45p Dave
244 * Lengthened tracker polling times. Put in initial support for PXO
245 * servers with channel filters. Fixed several small UI bugs.
247 * 16 5/21/98 1:52a Dave
248 * Remove obsolete command line functions. Reduce shield explosion packets
249 * drastically. Tweak PXO screen even more. Fix file xfer system so that
250 * we can guarantee file uniqueness.
252 * 15 5/20/98 12:12p Dave
253 * Channel name fixes.
255 * 14 5/20/98 2:24a Dave
256 * Fixed server side voice muting. Tweaked multi debrief/endgame
257 * sequencing a bit. Much friendlier for stats tossing/accepting now.
259 * 13 5/19/98 8:35p Dave
260 * Revamp PXO channel listing system. Send campaign goals/events to
261 * clients for evaluation. Made lock button pressable on all screens.
263 * 12 5/19/98 1:35a Dave
264 * Tweaked pxo interface. Added rankings url to pxo.cfg. Make netplayer
265 * local options update dynamically in netgames.
267 * 11 5/18/98 9:15p Dave
268 * Put in network config file support.
270 * 10 5/17/98 1:43a Dave
271 * Eradicated chatbox problems. Remove speed match for observers. Put in
272 * help screens for PXO. Fix messaging and end mission privelges. Fixed
273 * team select screen bugs. Misc UI fixes.
275 * 9 5/15/98 9:52p Dave
276 * Added new stats for freespace. Put in artwork for viewing stats on PXO.
278 * 8 5/15/98 1:41p Dave
279 * Update api so everyone can test.
281 * 7 5/15/98 12:09a Dave
282 * New tracker api code. New game tracker code. Finished up first run of
283 * the PXO screen. Fixed a few game server list exceptions.
285 * 6 5/14/98 2:41p Johnson
286 * Fixed a nick switching bug.
288 * 5 5/14/98 12:40a Dave
289 * Still more additions to the PXO screen. Updated tracker code.
291 * 4 5/13/98 6:54p Dave
292 * More sophistication to PXO interface. Changed respawn checking so
293 * there's no window for desynchronization between the server and the
296 * 3 5/12/98 11:59p Dave
297 * Put in some more functionality for Parallax Online.
299 * 2 5/12/98 2:46a Dave
300 * Rudimentary communication between Parallax Online and freespace. Can
301 * get and store channel lists.
303 * 1 5/11/98 11:47p Dave
311 #include <netinet/in.h>
314 #include "multi_pxo.h"
315 #include "animplay.h"
321 #include "gamesequence.h"
323 #include "chat_api.h"
325 #include "freespace.h"
329 #include "multi_fstracker.h"
333 #include "multi_update.h"
334 #include "alphacolors.h"
336 #include "inetgetfile.h"
337 #include "cfilesystem.h"
338 #include "osregistry.h"
340 // ----------------------------------------------------------------------------------------------------
344 // button definitions
345 #define MULTI_PXO_NUM_BUTTONS 15
346 #define MULTI_PXO_PLIST_UP 0
347 #define MULTI_PXO_PLIST_DOWN 1
348 #define MULTI_PXO_RANKINGS 2
349 #define MULTI_PXO_PINFO 3
350 #define MULTI_PXO_FIND 4
351 #define MULTI_PXO_MOTD 5
352 #define MULTI_PXO_JOIN 6
353 #define MULTI_PXO_JOIN_PRIV 7
354 #define MULTI_PXO_CHAN_UP 8
355 #define MULTI_PXO_CHAN_DOWN 9
356 #define MULTI_PXO_TEXT_UP 10
357 #define MULTI_PXO_TEXT_DOWN 11
358 #define MULTI_PXO_EXIT 12
359 #define MULTI_PXO_HELP 13
360 #define MULTI_PXO_GAMES 14
363 ui_button_info Multi_pxo_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_NUM_BUTTONS] = {
365 ui_button_info( "PXB_00", 1, 104, -1, -1, 0 ), // scroll player list up
366 ui_button_info( "PXB_01", 1, 334, -1, -1, 1 ), // scroll player list down
367 ui_button_info( "PXB_02", 18, 385, -1, -1, 2 ), // rankings webpage
368 ui_button_info( "PXB_03", 71, 385, -1, -1, 3 ), // pilot info
369 ui_button_info( "PXB_04", 115, 385, -1, -1, 4 ), // find player
370 ui_button_info( "PXB_05", 1, 443, -1, -1, 5 ), // motd
371 ui_button_info( "PXB_06", 330, 96, -1, -1, 6 ), // join channel
372 ui_button_info( "PXB_07", 330, 131, -1, -1, 7 ), // join private channel
373 ui_button_info( "PXB_08", 618, 92, -1, -1, 8 ), // scroll channels up
374 ui_button_info( "PXB_09", 618, 128, -1, -1, 9 ), // scroll channels down
375 ui_button_info( "PXB_10", 615, 171, -1, -1, 10 ), // scroll text up
376 ui_button_info( "PXB_11", 615, 355, -1, -1, 11 ), // scroll text down
377 ui_button_info( "PXB_12", 482, 435, -1, -1, 12 ), // exit
378 ui_button_info( "PXB_13", 533, 432, -1, -1, 13 ), // help
379 ui_button_info( "PXB_14", 573, 432, -1, -1, 14 ), // games list
382 ui_button_info( "2_PXB_00", 2, 166, -1, -1, 0 ), // scroll player list up
383 ui_button_info( "2_PXB_01", 2, 534, -1, -1, 1 ), // scroll player list down
384 ui_button_info( "2_PXB_02", 29, 616, -1, -1, 2 ), // rankings webpage
385 ui_button_info( "2_PXB_03", 114, 616, -1, -1, 3 ), // pilot info
386 ui_button_info( "2_PXB_04", 184, 616, -1, -1, 4 ), // find player
387 ui_button_info( "2_PXB_05", 2, 709, -1, -1, 5 ), // motd
388 ui_button_info( "2_PXB_06", 528, 119, -1, -1, 6 ), // join channel
389 ui_button_info( "2_PXB_07", 528, 175, -1, -1, 7 ), // join private channel
390 ui_button_info( "2_PXB_08", 989, 112, -1, -1, 8 ), // scroll channels up
391 ui_button_info( "2_PXB_09", 989, 170, -1, -1, 9 ), // scroll channels down
392 ui_button_info( "2_PXB_10", 984, 240, -1, -1, 10 ), // scroll text up
393 ui_button_info( "2_PXB_11", 984, 568, -1, -1, 11 ), // scroll text down
394 ui_button_info( "2_PXB_12", 771, 696, -1, -1, 12 ), // exit
395 ui_button_info( "2_PXB_13", 853, 691, -1, -1, 13 ), // help
396 ui_button_info( "2_PXB_14", 917, 691, -1, -1, 14 ), // games list
400 // define MULTI_PXO_NUM_TEXT 18
401 #define MULTI_PXO_NUM_TEXT 16
402 UI_XSTR Multi_pxo_text[GR_NUM_RESOLUTIONS][MULTI_PXO_NUM_TEXT] = {
404 {"Web", 1313, 20, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_RANKINGS].button},
405 {"Ranking", 1314, 6, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_RANKINGS].button},
406 {"Pilot", 1310, 68, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_PINFO].button},
407 {"Info", 1311, 72, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_PINFO].button},
408 {"Find", 1315, 119, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_FIND].button},
409 {"Motd", 1316, 36, 456, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_MOTD].button},
410 {"Join", 1505, 291, 100, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN].button},
411 {"Channel", 1317, 266, 112, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN].button},
412 {"Join", 1506, 291, 134, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN_PRIV].button},
413 {"Private", 1318, 273, 146, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN_PRIV].button},
414 {"Exit", 1416, 493, 424, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_EXIT].button},
415 {"Help", 928, 535, 416, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_HELP].button},
416 {"Games", 1319, 579, 416, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_GAMES].button},
417 {"Players", 1269, 29, 102, UI_XSTR_COLOR_GREEN, -1, NULL},
418 // {"Squad", 1320, 110, 101, UI_XSTR_COLOR_GREEN, -1, NULL},
419 // {"Channels", 1321, 369, 76, UI_XSTR_COLOR_GREEN, -1, NULL},
420 {"Players", 1269, 507, 90, UI_XSTR_COLOR_GREEN, -1, NULL},
421 {"Games", 1319, 568, 90, UI_XSTR_COLOR_GREEN, -1, NULL}
424 {"Web", 1313, 32, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_RANKINGS].button},
425 {"Ranking", 1314, 9, 674, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_RANKINGS].button},
426 {"Pilot", 1310, 109, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_PINFO].button},
427 {"Info", 1311, 115, 674, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_PINFO].button},
428 {"Find", 1315, 190, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_FIND].button},
429 {"Motd", 1316, 58, 729, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_MOTD].button},
430 {"Join", 1505, 488, 129, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN].button},
431 {"Channel", 1317, 461, 139, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN].button},
432 {"Join", 1506, 487, 184, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN_PRIV].button},
433 {"Private", 1318, 467, 194, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN_PRIV].button},
434 {"Exit", 1416, 789, 678, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_EXIT].button},
435 {"Help", 928, 857, 667, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_HELP].button},
436 {"Games", 1319, 917, 667, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_GAMES].button},
437 {"Players", 1269, 47, 163, UI_XSTR_COLOR_GREEN, -1, NULL},
438 // {"Squad", 1320, 176, 163, UI_XSTR_COLOR_GREEN, -1, NULL},
439 // {"Channels", 1321, 591, 86, UI_XSTR_COLOR_GREEN, -1, NULL},
440 {"Players", 1269, 852, 109, UI_XSTR_COLOR_GREEN, -1, NULL},
441 {"Games", 1319, 926, 109, UI_XSTR_COLOR_GREEN, -1, NULL}
445 char Multi_pxo_bitmap_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
449 char Multi_pxo_mask_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
454 UI_WINDOW Multi_pxo_window;
455 int Multi_pxo_bitmap = -1;
456 int Multi_pxo_palette = -1;
460 #define MULTI_PXO_ANIM_FNAME "pxologo"
461 #define MULTI_PXO_ANIM_X 0
462 #define MULTI_PXO_ANIM_Y 4
463 anim *Multi_pxo_anim = NULL;
464 anim_instance *Multi_pxo_anim_instance = NULL;
466 // rankings last clicked time
467 #define MULTI_PXO_RANK_TIME (5.0f)
468 float Multi_pxo_ranking_last = -1.0f;
471 int Multi_pxo_must_connect = 0; // if we still need to connect
472 int Multi_pxo_connected = 0; // if we are connected
473 int Multi_pxo_must_validate = 0; // if we need to validate on the tracker
474 int Multi_pxo_must_autojoin = 1; // still need to autojoin a channel
475 int Multi_pxo_must_verify_version = 1; // only do it once per instance of freespace
478 #define MULTI_PXO_MODE_NORMAL 0 // normal mode
479 #define MULTI_PXO_MODE_PRIVATE 1 // private channel popup
480 #define MULTI_PXO_MODE_FIND 2 // find player popup
481 int Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
483 // our nick for this session
484 char Multi_pxo_nick[NAME_LENGTH+1];
486 // check for button presses
487 void multi_pxo_check_buttons();
489 // handle a button press
490 void multi_pxo_button_pressed(int n);
492 // condition function for popup_do_with_condition for connected to Parallax Online
493 // return 10 : on successful connect
494 int multi_pxo_connect_do();
496 // attempt to connect to Parallax Online, return success or fail
497 int multi_pxo_connect();
499 // run the networking functions for the PXO API
500 void multi_pxo_api_process();
502 // process a "nick" change event
503 void multi_pxo_process_nick_change(char *data);
505 // run normally (no popups)
506 void multi_pxo_do_normal();
508 // blit everything on the "normal" screen
509 void multi_pxo_blit_all();
511 // process common stuff
512 void multi_pxo_process_common();
514 // get selected player information
515 void multi_pxo_get_data(char *name);
517 // handle being kicked
518 void multi_pxo_handle_kick();
520 // handle being disconnected
521 void multi_pxo_handle_disconnect();
523 // return string2, which is the first substring of string 1 without a space
524 // it is safe to pass the same pointer for both parameters
525 void multi_pxo_strip_space(char *string1,char *string2, const int str2_len);
527 // fire up the given URL
528 void multi_pxo_url(char *url);
530 // load/set the palette
531 void multi_pxo_load_palette();
533 // unload the palette
534 void multi_pxo_unload_palette();
536 // if we're currently on a private channel
537 int multi_pxo_on_private_channel();
539 // convert string 1 into string 2, substituting underscores for spaces
540 void multi_pxo_underscore_nick(char *string1, char *string2, const int str2_len);
542 // if the command is a potential "nick" command
543 int multi_pxo_is_nick_command(char *msg);
546 // status bar stuff -----------------------------------------------
547 int Multi_pxo_status_coords[GR_NUM_RESOLUTIONS][4] = {
556 // the status text itself
557 char Multi_pxo_status_text[255];
559 // set the status text
560 void multi_pxo_set_status_text(const char *txt);
562 // blit the status text
563 void multi_pxo_blit_status_text();
566 // channel related stuff -------------------------------------------
567 #define MAX_CHANNEL_NAME_LEN 32
568 #define MAX_CHANNEL_DESCRIPT_LEN 120
570 // some convenient macros
571 #define SWITCHING_CHANNELS() (Multi_pxo_channel_switch.num_users != -1)
572 #define ON_CHANNEL() (Multi_pxo_channel_current.num_users != -1)
574 typedef struct pxo_channel {
575 pxo_channel *next,*prev; // next and previous items in the list
576 char name[MAX_CHANNEL_NAME_LEN+1]; // name
577 char desc[MAX_CHANNEL_DESCRIPT_LEN+1]; // description
578 short num_users; // # users, or -1 if not in use
579 short num_servers; // the # of servers registered on this channel
582 // last channel we were on before going to the game list screen
583 char Multi_pxo_channel_last[MAX_CHANNEL_NAME_LEN+1] = "";
584 int Multi_pxo_use_last_channel = 0;
586 // all channels which are prefixed with this are "lobby" channels
587 #define MULTI_PXO_AUTOJOIN_PREFIX "#lobby"
589 // join this channel to get put in an appropriate lobby channel
590 #define MULTI_PXO_AUTOJOIN_CHANNEL "#autoselect"
592 int Multi_pxo_chan_coords[GR_NUM_RESOLUTIONS][4] = {
601 // this is the offset from the RIGHT side of the channel box
602 #define CHAN_PLAYERS_COLUMN 0
603 #define CHAN_GAMES_COLUMN 1
604 static int Multi_pxo_chan_column_offsets[GR_NUM_RESOLUTIONS][2] = {
609 #define CHANNEL_REFRESH_TIME (75.0f)
610 float Multi_pxo_channel_last_refresh = -1.0f;
612 #define CHANNEL_SERVER_REFRESH_TIME (35.0f)
613 float Multi_pxo_channel_server_refresh = -1.0f;
615 int Multi_pxo_max_chan_display[GR_NUM_RESOLUTIONS] = {
620 UI_BUTTON Multi_pxo_channel_button;
622 // head of the list of available (displayed) channels
623 pxo_channel *Multi_pxo_channels = NULL;
624 int Multi_pxo_channel_count = 0;
626 // item we're going to start displaying at
627 pxo_channel *Multi_pxo_channel_start = NULL;
628 int Multi_pxo_channel_start_index = -1;
630 // items we've currently got selected
631 pxo_channel *Multi_pxo_channel_select = NULL;
633 // channel we're currently connected to, num_users == -1, if we're not connected
634 pxo_channel Multi_pxo_channel_current;
636 // channel we're currently trying to change to, num_users == -1, if we're not trying to change channels
637 pxo_channel Multi_pxo_channel_switch;
639 // get a list of channels on the server (clear any old list as well)
640 void multi_pxo_get_channels();
642 // clear the old channel list
643 void multi_pxo_clear_channels();
645 // parse the input string and make a list of new channels
646 void multi_pxo_make_channels(char *chan_str);
648 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
649 pxo_channel *multi_pxo_add_channel(char *name, pxo_channel **list);
651 // lookup a channel with the specified name
652 pxo_channel *multi_pxo_find_channel(char *name, pxo_channel *list);
654 // process the channel list (select, etc)
655 void multi_pxo_process_channels();
657 // display the channel list
658 void multi_pxo_blit_channels();
660 // scroll channel list up
661 void multi_pxo_scroll_channels_up();
663 // scroll channel list down
664 void multi_pxo_scroll_channels_down();
666 // attempt to join a channel
667 void multi_pxo_join_channel(pxo_channel *chan);
669 // handle any processing details if we're currently trying to join a channel
670 void multi_pxo_handle_channel_change();
672 // autojoin an appropriate channel
673 void multi_pxo_autojoin();
675 // does the string match the "autojoin" prefic
676 int multi_pxo_is_autojoin(char *name);
678 // send a request to refresh our channel server counts
679 void multi_pxo_channel_refresh_servers();
681 // refresh current channel server count
682 void multi_pxo_channel_refresh_current();
685 // player related stuff -------------------------------------------
686 #define MAX_PLAYER_NAME_LEN 32
688 typedef struct player_list {
689 player_list *next,*prev;
690 char name[MAX_PLAYER_NAME_LEN+1];
693 // channel list region
694 int Multi_pxo_player_coords[GR_NUM_RESOLUTIONS][4] = {
703 int Multi_pxo_max_player_display[GR_NUM_RESOLUTIONS] = {
707 UI_BUTTON Multi_pxo_player_button;
709 // UI_SLIDER2 Multi_pxo_player_slider;
712 int Multi_pxo_player_slider_coords[GR_NUM_RESOLUTIONS][4] = {
720 const char *Multi_pxo_player_slider_name[GR_NUM_RESOLUTIONS] = {
722 "2_slider" // GR_1024
725 // head of the list of players in this channel
726 player_list *Multi_pxo_players = NULL;
727 int Multi_pxo_player_count = 0;
729 // item we're going to start displaying at
730 player_list *Multi_pxo_player_start = NULL;
731 // int Multi_pxo_player_start_index = -1;
733 // items we've currently got selected
734 player_list *Multi_pxo_player_select = NULL;
736 // clear the old player list
737 void multi_pxo_clear_players();
739 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
740 player_list *multi_pxo_add_player(char *name);
742 // remove a player with the given name
743 void multi_pxo_del_player(char *name);
745 // try and find a player with the given name, return a pointer to his entry (or NULL)
746 player_list *multi_pxo_find_player(char *name);
748 // process the player list (select, etc)
749 void multi_pxo_process_players();
751 // display the player list
752 void multi_pxo_blit_players();
754 // scroll player list up
755 void multi_pxo_scroll_players_up();
757 // scroll player list down
758 void multi_pxo_scroll_players_down();
760 // get the absolute index of the displayed items which our currently selected one is
761 int multi_pxo_get_select_index();
767 // add a bunch of bogus players
769 for(int idx=0; idx<Dc_arg_int; idx++){
770 SDL_snprintf(name, SDL_arraysize(name), "player %d", idx);
771 multi_pxo_add_player(name);
775 // chat text stuff -----------------------------------------
776 #define MAX_CHAT_LINES 60
777 #define MAX_CHAT_LINE_LEN 256
779 int Multi_pxo_chat_title_y[GR_NUM_RESOLUTIONS] = {
784 int Multi_pxo_chat_coords[GR_NUM_RESOLUTIONS][4] = {
793 int Multi_pxo_input_coords[GR_NUM_RESOLUTIONS][4] = {
802 int Multi_pxo_max_chat_display[GR_NUM_RESOLUTIONS] = {
807 // all messages from the server are prefixed with this
808 #define MULTI_PXO_SERVER_PREFIX "*** "
810 // the "has left" message from the server
811 #define MULTI_PXO_HAS_LEFT "has left"
814 #define CHAT_MODE_NORMAL 0 // normal chat from someone
815 #define CHAT_MODE_SERVER 1 // is from the server, display appropriately
816 #define CHAT_MODE_CARRY 2 // is a carryover from a previous line
817 #define CHAT_MODE_PRIVATE 3 // is a private message
818 #define CHAT_MODE_CHANNEL_SWITCH 4 // "switching channels" message - draw in red
819 #define CHAT_MODE_MOTD 5 // message of the day from the chat server
821 typedef struct chat_line {
822 chat_line *next,*prev;
823 char text[MAX_CHAT_LINE_LEN+1];
827 // the chat linked list itself
828 chat_line *Multi_pxo_chat = NULL;
830 // the current add line
831 chat_line *Multi_pxo_chat_add = NULL;
833 // the current line to start displaying from
834 chat_line *Multi_pxo_chat_start = NULL;
835 int Multi_pxo_chat_start_index = -1;
837 // input box for text
838 UI_INPUTBOX Multi_pxo_chat_input;
841 UI_SLIDER2 Multi_pxo_chat_slider;
843 int Multi_pxo_chat_slider_coords[GR_NUM_RESOLUTIONS][4] = {
852 const char *Multi_pxo_chat_slider_name[GR_NUM_RESOLUTIONS] = {
857 // how many chat lines we have
858 int Multi_pxo_chat_count = 0;
860 // extra delay time when switching channels
861 #define MULTI_PXO_SWITCH_DELAY_TIME 2000
862 int Multi_pxo_switch_delay = -1;
864 // initialize and create the chat text linked list
865 void multi_pxo_chat_init();
867 // free up all chat list stuff
868 void multi_pxo_chat_free();
870 // clear all lines of chat text in the chat area
871 void multi_pxo_chat_clear();
873 // blit the chat text
874 void multi_pxo_chat_blit();
876 // add a line of text
877 void multi_pxo_chat_add_line(char *txt,int mode);
879 // process an incoming line of text
880 void multi_pxo_chat_process_incoming(const char *txt,int mode = CHAT_MODE_NORMAL);
882 // scroll to the very bottom of the chat area
883 void multi_pxo_goto_bottom();
885 // check whether we can scroll down or not
886 int multi_pxo_can_scroll_down();
888 static int Can_scroll_down = 0;
890 // scroll the text up
891 void multi_pxo_scroll_chat_up();
893 // scroll the text down
894 void multi_pxo_scroll_chat_down();
896 // process chat controls
897 void multi_pxo_chat_process();
899 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
900 const char *multi_pxo_chat_is_private(const char *txt);
902 // if the text came from the server
903 int multi_pxo_is_server_text(const char *txt);
905 // if the text is message of the day text
906 int multi_pxo_is_motd_text(const char *txt);
908 // if the text is the end of motd text
909 int multi_pxo_is_end_of_motd_text(const char *txt);
911 // if the text is a "has left message" from the server
912 int multi_pxo_chat_is_left_message(const char *txt);
914 // recalculate the chat start index, and adjust the slider properly
915 void multi_pxo_chat_adjust_start();
918 // motd stuff ---------------------------------------------------------
919 #define MAX_PXO_MOTD_LEN 1024
920 #define PXO_MOTD_BLINK_TIME 500
921 char Pxo_motd[1024] = "";
922 int Pxo_motd_end = 0;
923 int Pxo_motd_read = 0;
924 int Pxo_motd_blink_stamp = -1;
925 int Pxo_motd_blink_on = 0;
926 int Pxo_motd_blinked_already = 0;
928 // initialize motd when going into this screen
929 void multi_pxo_motd_init();
932 void multi_pxo_motd_add_text(const char *text);
935 void multi_pxo_set_end_of_motd();
937 // display the motd dialog
938 void multi_pxo_motd_dialog();
940 // call to maybe blink the motd button
941 void multi_pxo_motd_maybe_blit();
944 // common dialog stuff ------------------------------------------------
945 const char *Multi_pxo_com_fname[GR_NUM_RESOLUTIONS] = {
949 const char *Multi_pxo_com_mask_fname[GR_NUM_RESOLUTIONS] = {
955 int Multi_pxo_com_coords[GR_NUM_RESOLUTIONS][2] = {
965 int Multi_pxo_com_input_coords[GR_NUM_RESOLUTIONS][4] = {
974 #define MULTI_PXO_COM_NUM_BUTTONS 2
975 #define MULTI_PXO_COM_CANCEL 0
976 #define MULTI_PXO_COM_OK 1
978 ui_button_info Multi_pxo_com_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_BUTTONS] = {
980 ui_button_info("PXP_00", 573, 192, -1, -1, 0),
981 ui_button_info("PXP_01", 573, 226, -1, -1, 1)
984 ui_button_info("2_PXP_00", 917, 308, -1, -1, 0),
985 ui_button_info("2_PXP_01", 917, 361, -1, -1, 1)
989 #define MULTI_PXO_COM_NUM_TEXT 2
990 UI_XSTR Multi_pxo_com_text[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_TEXT] = {
992 { "&Cancel", 645, 510, 204, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_CANCEL].button },
993 { "&Ok", 669, 548, 233, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_OK].button }
996 { "&Cancel", 645, 847, 327, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_CANCEL].button },
997 { "&Ok", 669, 877, 372, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_OK].button }
1001 int Multi_pxo_com_bitmap = -1;
1002 UI_WINDOW Multi_pxo_com_window;
1003 UI_INPUTBOX Multi_pxo_com_input;
1005 // text on the "top" half of the dialog display area
1006 char Multi_pxo_com_top_text[255];
1008 // text on the "middle" portion of the dialog display area
1009 char Multi_pxo_com_middle_text[255];
1011 // text on the "bottom" half of the dialog display area
1012 char Multi_pxo_com_bottom_text[255];
1014 int Multi_pxo_com_top_text_coords[GR_NUM_RESOLUTIONS][2] = {
1022 int Multi_pxo_com_middle_text_y[GR_NUM_RESOLUTIONS] = {
1026 int Multi_pxo_com_bottom_text_y[GR_NUM_RESOLUTIONS] = {
1031 // initialize the common dialog with the passed max input length
1032 void multi_pxo_com_init();
1034 // close down the common dialog
1035 void multi_pxo_com_close();
1037 // blit all text lines, top, middle, bottoms
1038 void multi_pxo_com_blit_text();
1040 // set the top text, shortening as necessary
1041 void multi_pxo_com_set_top_text(const char *txt);
1043 // set the middle text, shortening as necessary
1044 void multi_pxo_com_set_middle_text(const char *txt);
1046 // set the bottom text, shortening as necessary
1047 void multi_pxo_com_set_bottom_text(const char *txt);
1050 // private channel join stuff -----------------------------------------
1051 #define MULTI_PXO_PRIV_MAX_TEXT_LEN 30
1053 // max private channel name length
1054 char Multi_pxo_priv_chan[MULTI_PXO_PRIV_MAX_TEXT_LEN+100];
1056 // return code, set to something other than -1 if we're supposed to return
1057 int Multi_pxo_priv_return_code = -1;
1059 // initialize the popup
1060 void multi_pxo_priv_init();
1062 // close down the popup
1063 void multi_pxo_priv_close();
1065 // run the popup, 0 if still running, -1 if cancel, 1 if ok
1066 int multi_pxo_priv_popup();
1068 // process button presses
1069 void multi_pxo_priv_process_buttons();
1071 // handle a button press
1072 void multi_pxo_priv_button_pressed(int n);
1074 // process the inputbox
1075 void multi_pxo_priv_process_input();
1078 // find player stuff -----------------------------------------
1080 char Multi_pxo_find_channel[MAX_CHANNEL_NAME_LEN+1];
1082 // return code, set to something other than -1 if we're supposed to return
1083 int Multi_pxo_find_return_code = -1;
1085 // initialize the popup
1086 void multi_pxo_find_init();
1088 // close down the popup
1089 void multi_pxo_find_close();
1091 // run the popup, 0 if still running, -1 if cancel, 1 if ok
1092 int multi_pxo_find_popup();
1094 // process button presses
1095 void multi_pxo_find_process_buttons();
1097 // handle a button press
1098 void multi_pxo_find_button_pressed(int n);
1100 // process the inputbox
1101 void multi_pxo_find_process_input();
1103 // process search mode if applicable
1104 void multi_pxo_find_search_process();
1107 // player info stuff -----------------------------------------
1108 const char *Multi_pxo_pinfo_fname[GR_NUM_RESOLUTIONS] = {
1112 const char *Multi_pxo_pinfo_mask_fname[GR_NUM_RESOLUTIONS] = {
1118 #define MULTI_PXO_PINFO_NUM_BUTTONS 2
1119 #define MULTI_PXO_PINFO_MEDALS 0
1120 #define MULTI_PXO_PINFO_OK 1
1122 ui_button_info Multi_pxo_pinfo_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_BUTTONS] = {
1124 ui_button_info("PI2_00", 328, 446, 319, 433, 0),
1125 ui_button_info("PI2_01", 376, 446, 382, 433, 1),
1128 ui_button_info("2_PI2_00", 525, 714, 510, 695, 0),
1129 ui_button_info("2_PI2_01", 601, 714, 611, 695, 1),
1134 #define MULTI_PXO_PINFO_NUM_TEXT 2
1135 UI_XSTR Multi_pxo_pinfo_text[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_TEXT] = {
1137 { "Medals", 1037, 319, 433, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_MEDALS].button },
1138 { "Ok", 345, 382, 433, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_OK].button },
1141 { "Medals", 1037, 510, 695, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_MEDALS].button },
1142 { "Ok", 345, 611, 695, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_OK].button },
1146 int Multi_pxo_pinfo_bitmap = -1;
1147 UI_WINDOW Multi_pxo_pinfo_window;
1149 vmt_stats_struct Multi_pxo_pinfo;
1150 player Multi_pxo_pinfo_player;
1152 int Multi_pxo_retrieve_mode = -1;
1154 char Multi_pxo_retrieve_name[MAX_PLAYER_NAME_LEN+1];
1155 char Multi_pxo_retrieve_id[128];
1157 // stats label stuff
1158 #define MULTI_PXO_PINFO_NUM_LABELS 18
1160 int Multi_pxo_pinfo_coords[GR_NUM_RESOLUTIONS][4] = {
1168 int Multi_pxo_pinfo_val_x[GR_NUM_RESOLUTIONS] = {
1173 char *Multi_pxo_pinfo_stats_labels[MULTI_PXO_PINFO_NUM_LABELS];
1184 "Primary shots fired",
1185 "Primary shots hit",
1187 "Secondary shots fired",
1188 "Secondary shots hit",
1190 "Primary friendly hits",
1191 "Primary friendly hit %",
1192 "Secondary friendly hits",
1193 "Secondary friendly hit %"
1198 char Multi_pxo_pinfo_vals[MULTI_PXO_PINFO_NUM_LABELS][50];
1200 int Multi_pxo_pinfo_stats_spacing[MULTI_PXO_PINFO_NUM_LABELS] = {
1201 10,20,10,10,20,10,10,20,10,10,20,10,10,20,10,20,10,0
1204 // popup conditional functions, returns 10 on successful get of stats
1205 int multi_pxo_pinfo_cond();
1207 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
1208 int multi_pxo_pinfo_get(char *name);
1210 // fire up the stats view popup
1211 void multi_pxo_pinfo_show();
1213 // build the stats labels values
1214 void multi_pxo_pinfo_build_vals();
1216 // initialize the popup
1217 void multi_pxo_pinfo_init();
1220 int multi_pxo_pinfo_do();
1223 void multi_pxo_pinfo_close();
1225 // blit all the stats on this screen
1226 void multi_pxo_pinfo_blit();
1228 // run the medals screen
1229 void multi_pxo_run_medals();
1231 // notify stuff stuff -----------------------------------------
1232 #define MULTI_PXO_NOTIFY_TIME 4000
1233 #define MULTI_PXO_NOTIFY_Y 435
1235 char Multi_pxo_notify_text[255];
1236 int Multi_pxo_notify_stamp = -1;
1238 // add a notification string
1239 void multi_pxo_notify_add(const char *txt);
1241 // blit and process the notification string
1242 void multi_pxo_notify_blit();
1245 // help screen stuff -----------------------------------------
1247 const char *Multi_pxo_help_fname[GR_NUM_RESOLUTIONS] = {
1251 const char *Multi_pxo_help_mask_fname[GR_NUM_RESOLUTIONS] = {
1256 #define MULTI_PXO_HELP_NUM_BUTTONS 3
1257 #define MULTI_PXO_HELP_PREV 0
1258 #define MULTI_PXO_HELP_NEXT 1
1259 #define MULTI_PXO_HELP_CONTINUE 2
1261 ui_button_info Multi_pxo_help_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_BUTTONS] = {
1263 ui_button_info("PXH_00", 15, 389, -1, -1, 0),
1264 ui_button_info("PXH_01", 60, 389, -1, -1, 1),
1265 ui_button_info("PXH_02", 574, 431, 571, 413, 2),
1268 ui_button_info("2_PXH_00", 24, 622, -1, -1, 0),
1269 ui_button_info("2_PXH_01", 96, 622, -1, -1, 1),
1270 ui_button_info("2_PXH_02", 919, 689, 928, 663, 2),
1274 #define MULTI_PXO_HELP_NUM_TEXT 1
1275 UI_XSTR Multi_pxo_help_text[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_TEXT] = {
1277 {"Continue", 1069, 571, 413, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[0][MULTI_PXO_HELP_CONTINUE].button },
1280 {"Continue", 1069, 928, 663, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[1][MULTI_PXO_HELP_CONTINUE].button },
1285 #define MULTI_PXO_HELP_FILE "pxohelp.txt"
1286 #define MULTI_PXO_MAX_LINES_PP 57
1287 #define MULTI_PXO_MAX_PAGES 3
1289 int Multi_pxo_help_coords[GR_NUM_RESOLUTIONS][2] = {
1298 int Multi_pxo_chars_per_line[GR_NUM_RESOLUTIONS] = {
1303 int Multi_pxo_lines_pp[GR_NUM_RESOLUTIONS] = {
1309 typedef struct help_page {
1310 char *text[MULTI_PXO_MAX_LINES_PP];
1314 help_page Multi_pxo_help_pages[MULTI_PXO_MAX_PAGES];
1315 // int Multi_pxo_help_loaded = 0;
1317 int Multi_pxo_help_num_pages = 0;
1319 int Multi_pxo_help_bitmap = -1;
1320 UI_WINDOW Multi_pxo_help_window;
1322 // current page we're on
1323 int Multi_pxo_help_cur = 0;
1325 // load the help file up
1326 void multi_pxo_help_load();
1328 // blit the current page
1329 void multi_pxo_help_blit_page();
1331 // process button presses
1332 void multi_pxo_help_process_buttons();
1335 void multi_pxo_help_button_pressed(int n);
1338 // http banner stuff ---------------------------------------------
1339 InetGetFile *Multi_pxo_ban_get = NULL;
1342 #define PXO_BANNERS_CONFIG_FILE "pxobanners.cfg"
1344 // coords to display banners at
1345 int Pxo_ban_coords[GR_NUM_RESOLUTIONS][4] = {
1355 #define PXO_BAN_MODE_LIST_STARTUP 0 // start downloading list
1356 #define PXO_BAN_MODE_LIST 1 // downloading list
1357 #define PXO_BAN_MODE_IMAGES_STARTUP 2 // start downloading images
1358 #define PXO_BAN_MODE_IMAGES 3 // downloading images
1359 #define PXO_BAN_MODE_IMAGES_DONE 4 // done downloading everything - now maybe load an image
1360 #define PXO_BAN_MODE_IDLE 5 // done with everything - doing nothing
1361 #define PXO_BAN_MODE_CHOOSE_RANDOM 6 // choose a bitmap we've already downloaded at random
1363 // interface button for detecting clicks
1364 UI_BUTTON Multi_pxo_ban_button;
1367 typedef struct pxo_banner {
1368 char ban_file[MAX_FILENAME_LEN+1]; // base filename of the banner
1369 char ban_file_url[MULTI_OPTIONS_STRING_LEN+1]; // full url of the file to get (convenient)
1370 char ban_url[MULTI_OPTIONS_STRING_LEN+1]; // url to go to when clicked
1371 int ban_bitmap; // banner bitmap
1374 // active pxo banner
1375 pxo_banner Multi_pxo_banner;
1378 int Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
1381 void multi_pxo_ban_init();
1383 // process http download details
1384 void multi_pxo_ban_process();
1387 void multi_pxo_ban_close();
1389 // parse the banners file and maybe fill in Multi_pxo_dl_file[]
1390 void multi_pxo_ban_parse_banner_file(int choose_existing);
1392 // any bitmap or info or whatever
1393 void multi_pxo_ban_draw();
1395 // called when the URL button is clicked
1396 void multi_pxo_ban_clicked();
1399 // ----------------------------------------------------------------------------------------------------
1403 // initialize the PXO screen
1404 void multi_pxo_init(int use_last_channel)
1408 // load the background bitmap
1409 Multi_pxo_bitmap = bm_load(Multi_pxo_bitmap_fname[gr_screen.res]);
1410 if(Multi_pxo_bitmap < 0){
1411 // we failed to load the bitmap - this is very bad
1415 // load up the private channel bitmap
1416 Multi_pxo_com_bitmap = bm_load(Multi_pxo_com_fname[gr_screen.res]);
1417 SDL_assert(Multi_pxo_com_bitmap != -1);
1419 // create the interface window
1420 Multi_pxo_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1421 Multi_pxo_window.set_mask_bmap(Multi_pxo_mask_fname[gr_screen.res]);
1423 // multiplayer screen common palettes
1424 multi_pxo_load_palette();
1426 // create the interface buttons
1427 for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
1428 // create the object
1429 Multi_pxo_buttons[gr_screen.res][idx].button.create(&Multi_pxo_window, "", Multi_pxo_buttons[gr_screen.res][idx].x, Multi_pxo_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
1431 // set the sound to play when highlighted
1432 Multi_pxo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1434 // set the ani for the button
1435 Multi_pxo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_buttons[gr_screen.res][idx].filename);
1438 Multi_pxo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_buttons[gr_screen.res][idx].hotspot);
1442 for(idx=0; idx<MULTI_PXO_NUM_TEXT; idx++){
1443 Multi_pxo_window.add_XSTR(&Multi_pxo_text[gr_screen.res][idx]);
1446 if(use_last_channel && strlen(Multi_pxo_channel_last)){
1447 Multi_pxo_use_last_channel = 1;
1449 SDL_zero(Multi_pxo_channel_last);
1450 Multi_pxo_use_last_channel = 0;
1453 // make all scrolling buttons repeatable
1454 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_UP].button.repeatable(1);
1455 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_DOWN].button.repeatable(1);
1456 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_UP].button.repeatable(1);
1457 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_DOWN].button.repeatable(1);
1458 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_UP].button.repeatable(1);
1459 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_DOWN].button.repeatable(1);
1461 // set the mouseover cursor if it loaded ok
1462 if (Web_cursor_bitmap > 0) {
1463 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_RANKINGS].button.set_custom_cursor_bmap(Web_cursor_bitmap);
1466 // create the channel list select button and hide it
1467 Multi_pxo_channel_button.create(&Multi_pxo_window, "", Multi_pxo_chan_coords[gr_screen.res][0], Multi_pxo_chan_coords[gr_screen.res][1], Multi_pxo_chan_coords[gr_screen.res][2], Multi_pxo_chan_coords[gr_screen.res][3], 0, 1);
1468 Multi_pxo_channel_button.hide();
1470 // create the player list select button and hide it
1471 Multi_pxo_player_button.create(&Multi_pxo_window, "", Multi_pxo_player_coords[gr_screen.res][0], Multi_pxo_player_coords[gr_screen.res][1], Multi_pxo_player_coords[gr_screen.res][2], Multi_pxo_player_coords[gr_screen.res][3], 0, 1);
1472 Multi_pxo_player_button.hide();
1474 // create the chat input box
1475 Multi_pxo_chat_input.create(&Multi_pxo_window, Multi_pxo_input_coords[gr_screen.res][0], Multi_pxo_input_coords[gr_screen.res][1], Multi_pxo_input_coords[gr_screen.res][2], MAX_CHAT_LINE_LEN + 1, "", UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_EAT_USED);
1476 Multi_pxo_chat_input.set_focus();
1478 // create the banner button and hide it
1479 Multi_pxo_ban_button.create(&Multi_pxo_window, "", Pxo_ban_coords[gr_screen.res][0], Pxo_ban_coords[gr_screen.res][1], Pxo_ban_coords[gr_screen.res][2], Pxo_ban_coords[gr_screen.res][3], 0, 1);
1480 Multi_pxo_ban_button.hide();
1482 // create the player list slider
1483 // Multi_pxo_player_slider.create(&Multi_pxo_window, Multi_pxo_player_slider_coords[gr_screen.res][0], Multi_pxo_player_slider_coords[gr_screen.res][1], Multi_pxo_player_slider_coords[gr_screen.res][2], Multi_pxo_player_slider_coords[gr_screen.res][3], 0, Multi_pxo_player_slider_name[gr_screen.res], multi_pxo_scroll_players_up, multi_pxo_scroll_players_down, NULL);
1485 // create the chat slider
1486 Multi_pxo_chat_slider.create(&Multi_pxo_window, Multi_pxo_chat_slider_coords[gr_screen.res][0], Multi_pxo_chat_slider_coords[gr_screen.res][1], Multi_pxo_chat_slider_coords[gr_screen.res][2], Multi_pxo_chat_slider_coords[gr_screen.res][3], 0, Multi_pxo_chat_slider_name[gr_screen.res], multi_pxo_scroll_chat_up, multi_pxo_scroll_chat_down, NULL);
1488 // set our connection status so that we do the right stuff next frame
1489 Multi_pxo_must_validate = 1;
1490 Multi_pxo_must_connect = 0;
1491 Multi_pxo_connected = 0;
1493 // channel we're currently connected to
1494 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
1495 Multi_pxo_channel_current.num_users = -1;
1497 // channel we're currently trying to change to, or NULL if nont
1498 memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
1499 Multi_pxo_channel_switch.num_users = -1;
1501 // last time clicked the url button (so we don't have repeats)
1502 Multi_pxo_ranking_last = -1.0f;
1504 // channel switching extra time delay stamp
1505 Multi_pxo_switch_delay = -1;
1507 // our nick for this session
1508 multi_pxo_underscore_nick(Player->callsign, Multi_pxo_nick, SDL_arraysize(Multi_pxo_nick));
1510 // clear the channel list
1511 multi_pxo_clear_channels();
1513 // clear the player list
1514 multi_pxo_clear_players();
1516 // initialize the chat system
1517 multi_pxo_chat_init();
1520 multi_pxo_ban_init();
1522 // load the animation up
1523 if (gr_screen.res == GR_1024) {
1524 char anim_filename[32] = "2_";
1525 SDL_strlcat(anim_filename, MULTI_PXO_ANIM_FNAME, SDL_arraysize(anim_filename));
1526 Multi_pxo_anim = anim_load(anim_filename);
1528 // if hi-res is not there, fallback to low
1529 if (Multi_pxo_anim == NULL) {
1530 Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME);
1533 Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME);
1536 // clear the status text
1537 multi_pxo_set_status_text("");
1539 // last refresh time
1540 Multi_pxo_channel_last_refresh = -1.0f;
1542 // server count last refresh time
1543 Multi_pxo_channel_server_refresh = -1.0f;
1546 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1549 multi_pxo_motd_init();
1551 // make sure we autojoin
1552 Multi_pxo_must_autojoin = 1;
1554 // clear all tracker channel related strings
1555 SDL_zero(Multi_fs_tracker_channel);
1556 SDL_zero(Multi_fs_tracker_filter);
1559 // do frame for the PXO screen
1562 pxo_channel priv_chan;
1565 if(Multi_pxo_connected) {
1566 multi_pxo_api_process();
1569 // process common stuff
1570 multi_pxo_process_common();
1572 switch(Multi_pxo_mode){
1573 // private channel join mode
1574 case MULTI_PXO_MODE_PRIVATE:
1575 switch(multi_pxo_priv_popup()){
1580 // user hit "cancel"
1582 // return to normal mode
1583 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1588 // setup some information
1589 memset(&priv_chan,0,sizeof(pxo_channel));
1590 priv_chan.num_users = 0;
1591 SDL_strlcpy(priv_chan.name, Multi_pxo_priv_chan, SDL_arraysize(priv_chan.name));
1593 // see if we know about this channel already
1594 multi_pxo_join_channel(&priv_chan);
1596 // return to normal mode
1597 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1603 case MULTI_PXO_MODE_FIND:
1604 switch(multi_pxo_find_popup()){
1609 // user hit "cancel"
1611 // return to normal mode
1612 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1617 // return to normal mode
1618 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1620 // if there is a valid channel name try and join it
1621 if(strlen(Multi_pxo_find_channel) && !SWITCHING_CHANNELS()){
1625 memset(&join,0,sizeof(pxo_channel));
1627 SDL_strlcpy(join.name, Multi_pxo_find_channel, SDL_arraysize(join.name));
1630 multi_pxo_join_channel(&join);
1636 case MULTI_PXO_MODE_NORMAL:
1637 multi_pxo_do_normal();
1642 // close the PXO screen
1643 void multi_pxo_close()
1645 // unload any bitmaps
1646 bm_unload(Multi_pxo_bitmap);
1647 bm_unload(Multi_pxo_com_bitmap);
1649 // record the last channel we were on, if any
1650 SDL_zero(Multi_fs_tracker_channel);
1651 SDL_zero(Multi_fs_tracker_filter);
1653 if( ON_CHANNEL() && strlen(Multi_pxo_channel_current.name) ){
1655 SDL_strlcpy(Multi_fs_tracker_channel, Multi_pxo_channel_current.name, SDL_arraysize(Multi_fs_tracker_channel));
1658 SDL_strlcpy(Multi_fs_tracker_filter, Multi_pxo_channel_current.name, SDL_arraysize(Multi_fs_tracker_filter));
1661 // disconnect from the server
1662 DisconnectFromChatServer();
1663 Multi_pxo_connected = 0;
1665 // unload the animation
1666 anim_release_all_instances(GS_STATE_PXO);
1667 Multi_pxo_anim_instance = NULL;
1668 if(Multi_pxo_anim != NULL){
1669 anim_free(Multi_pxo_anim);
1670 Multi_pxo_anim = NULL;
1673 // unload the palette for this screen
1674 multi_pxo_unload_palette();
1676 // destroy the UI_WINDOW
1677 Multi_pxo_window.destroy();
1679 // clear the channel list
1680 multi_pxo_clear_channels();
1682 // close the chat system
1683 multi_pxo_chat_free();
1686 multi_pxo_ban_close();
1689 // run normally (no popups)
1690 void multi_pxo_do_normal()
1693 int k = Multi_pxo_window.process();
1695 // if the animation isn't playing, start it up
1696 if((Multi_pxo_anim_instance == NULL) && (Multi_pxo_anim != NULL)){
1697 anim_play_struct aps;
1699 // fire up the animation
1700 anim_play_init(&aps, Multi_pxo_anim, MULTI_PXO_ANIM_X, MULTI_PXO_ANIM_Y);
1701 aps.screen_id = GS_STATE_PXO;
1702 aps.framerate_independent = 1;
1704 Multi_pxo_anim_instance = anim_play(&aps);
1707 // process any keypresses
1710 gamesnd_play_iface(SND_USER_SELECT);
1711 gameseq_post_event(GS_EVENT_MAIN_MENU);
1715 // check for button presses
1716 multi_pxo_check_buttons();
1718 // if we're not in a chatroom, disable and hide the chat input box
1720 Multi_pxo_chat_input.hide();
1721 Multi_pxo_chat_input.disable();
1723 Multi_pxo_chat_input.enable();
1724 Multi_pxo_chat_input.unhide();
1728 multi_pxo_blit_all();
1733 // verify version # now (only once per Freespace instance)
1734 if(Multi_pxo_must_verify_version){
1735 switch(multi_update_gobaby()){
1736 // everything is cool. Move along
1737 case MULTI_UPDATE_CONTINUE:
1738 Multi_pxo_must_verify_version = 0;
1741 // go back to the main menu
1742 case MULTI_UPDATE_MAIN_MENU:
1743 gameseq_post_event(GS_EVENT_MAIN_MENU);
1745 // unset these so we don't do anything else PXO related
1746 Multi_pxo_must_validate = 0;
1747 Multi_pxo_must_connect = 0;
1750 // freespace will be shutting down shortly
1751 case MULTI_UPDATE_SHUTTING_DOWN:
1756 // if we need to get tracker info for ourselves, do so
1757 if(Multi_pxo_must_validate){
1758 // initialize the master tracker API for Freespace
1759 multi_fs_tracker_init();
1761 // validate the current player with the master tracker (will create the pilot on the MT if necessary)
1762 validate_code = multi_fs_tracker_validate(0);
1763 if(validate_code != 1){
1764 // show an error popup if it failed (not cancelled by the user)
1765 if (validate_code == 0) {
1766 switch (popup(PF_USE_AFFIRMATIVE_ICON | PF_WEB_CURSOR_1 | PF_WEB_CURSOR_2, 3, POPUP_CANCEL,XSTR("&Create Acct",936), XSTR("&Verify Acct",937), XSTR("PXO Login not accepted. You may visit the Parallax Online website to create or verify your login. Or you may click Cancel to play without using the Parallax Online service. (You may switch back to Parallax Online from the Options Menu under the Multi tab.)",938))) {
1768 nprintf(("Network","PXO CANCEL\n"));
1770 // flip his "pxo" bit temporarily and push him to the join game screen
1771 Multi_options_g.pxo = 0;
1772 // Net_game_tcp_mode = NET_TCP;
1773 gameseq_post_event(GS_EVENT_MULTI_JOIN_GAME);
1777 nprintf(("Network","PXO CREATE\n"));
1778 // fire up the given URL
1779 multi_pxo_url(Multi_options_g.pxo_create_url);
1783 nprintf(("Network","PXO VERIFY\n"));
1784 // fire up the given URL
1785 multi_pxo_url(Multi_options_g.pxo_verify_url);
1790 // go back to the main hall
1791 gameseq_post_event(GS_EVENT_MAIN_MENU);
1793 Multi_pxo_must_connect = 0;
1794 Multi_pxo_must_validate = 0;
1796 // now we have to conenct to PXO
1798 Multi_pxo_must_connect = 1;
1799 Multi_pxo_must_validate = 0;
1803 // if we need to connect, do so now
1804 if(Multi_pxo_must_connect){
1805 // for now, just try once
1806 Multi_pxo_connected = multi_pxo_connect();
1808 // if we successfully connected, send a request for a list of channels on the server
1809 if(Multi_pxo_connected){
1810 multi_pxo_get_channels();
1813 multi_pxo_set_status_text(XSTR("Retrieving Public Channels",939));
1816 multi_pxo_set_status_text(XSTR("Failed to connect to Parallax Online",940));
1819 // no longer need to connect
1820 Multi_pxo_must_connect = 0;
1824 // blit everything on the "normal" screen
1825 void multi_pxo_blit_all()
1827 // draw the background, etc
1829 // GR_MAYBE_CLEAR_RES(Multi_pxo_bitmap);
1830 int bmap = Multi_pxo_bitmap;
1835 bm_get_info( bmap, &bmw, &bmh);
1836 if((bmw != gr_screen.max_w) || (bmh != gr_screen.max_h)){
1843 if(Multi_pxo_bitmap != -1){
1844 gr_set_bitmap(Multi_pxo_bitmap);
1847 Multi_pxo_window.draw();
1849 // display the channel list
1850 multi_pxo_blit_channels();
1852 // display the player list
1853 multi_pxo_blit_players();
1855 // blit the chat text
1856 multi_pxo_chat_blit();
1858 // blit the status text
1859 multi_pxo_blit_status_text();
1861 // blit and process the notification string
1862 multi_pxo_notify_blit();
1864 // any bitmap or info or whatever
1865 multi_pxo_ban_draw();
1867 // draw any motd stuff
1868 multi_pxo_motd_maybe_blit();
1870 // if we have a valid animation handle, play it
1871 if(Multi_pxo_anim_instance != NULL){
1872 anim_render_all(GS_STATE_PXO,flFrametime);
1876 // process common stuff
1877 void multi_pxo_process_common()
1879 // process the channel list (select, etc)
1880 multi_pxo_process_channels();
1882 // process the player list (select, etc)
1883 multi_pxo_process_players();
1885 // process chat controls
1886 multi_pxo_chat_process();
1888 // process http download details
1889 multi_pxo_ban_process();
1892 // get selected player information
1893 void multi_pxo_get_data(char *name)
1897 // handle being kicked
1898 void multi_pxo_handle_kick()
1900 // remove ourselves from the room
1901 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
1902 Multi_pxo_channel_current.num_users = -1;
1905 multi_pxo_chat_clear();
1907 // clear the old player list
1908 multi_pxo_clear_players();
1910 // add a notification string
1911 multi_pxo_notify_add(XSTR("You have been kicked",941));
1914 // handle being disconnected
1915 void multi_pxo_handle_disconnect()
1917 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been disconnected from the server",942));
1918 gameseq_post_event(GS_EVENT_MAIN_MENU);
1921 // return string2, which is the first substring of string 1 without a space
1922 // it is safe to pass the same pointer for both parameters
1923 void multi_pxo_strip_space(char *string1, char *string2, const int str2_len)
1928 // copy the original
1929 SDL_strlcpy(midway, string1, SDL_arraysize(midway));
1930 tok = strtok(midway," ");
1932 SDL_strlcpy(string2, tok, str2_len);
1934 SDL_strlcpy(string2, "", str2_len);
1938 // fire up the given URL
1939 void multi_pxo_url(char *url)
1941 if ( platform_open_url(url) ) {
1942 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,XSTR("Warning\nCould not locate/launch default Internet Browser",943));
1946 // load/set the palette
1947 void multi_pxo_load_palette()
1950 //#ifndef HARDWARE_ONLY
1951 // palette_use_bm_palette(Multi_pxo_palette);
1955 // unload the palette
1956 void multi_pxo_unload_palette()
1958 // unload the palette if it exists
1959 if(Multi_pxo_palette != -1){
1960 bm_release(Multi_pxo_palette);
1961 Multi_pxo_palette = -1;
1965 // if we're currently on a private channel
1966 int multi_pxo_on_private_channel()
1968 // if we're connected to a channel with the "+" symbol on front
1969 if(ON_CHANNEL() && (Multi_pxo_channel_current.name[0] == '+')){
1973 // otherwise return falos
1977 // convert string 1 into string 2, substituting underscores for spaces
1978 void multi_pxo_underscore_nick(char *string1, char *string2, const int str2_len)
1980 char nick_temp[512];
1983 // don't do anything if we have bogus string
1984 if((string1 == NULL) || (string2 == NULL)){
1988 // copy the nickname
1989 SDL_strlcpy(nick_temp, string1, SDL_arraysize(nick_temp));
1991 // get the first token
1992 tok = strtok(nick_temp, " ");
1994 SDL_strlcpy(string2, tok, str2_len);
1996 // get the next token
1997 tok = strtok(NULL," ");
2000 SDL_strlcat(string2, "_", str2_len);
2001 SDL_strlcat(string2, tok, str2_len);
2004 tok = strtok(NULL," ");
2007 SDL_strlcpy(string2, string1, str2_len);
2011 // if the command is a potential "nick" command
2012 int multi_pxo_is_nick_command(char *msg)
2017 // get the first token in the message
2018 SDL_strlcpy(tmp, msg, SDL_arraysize(tmp));
2019 tok = strtok(tmp," ");
2021 // can't be a nick message
2025 return !SDL_strcasecmp(tok,NOX("/nick"));
2028 // check for button presses
2029 void multi_pxo_check_buttons()
2033 // go through all buttons
2034 for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
2035 if(Multi_pxo_buttons[gr_screen.res][idx].button.pressed()){
2036 multi_pxo_button_pressed(idx);
2042 // handle a button press
2043 void multi_pxo_button_pressed(int n)
2046 case MULTI_PXO_EXIT:
2047 gamesnd_play_iface(SND_USER_SELECT);
2048 gameseq_post_event(GS_EVENT_MAIN_MENU);
2051 case MULTI_PXO_CHAN_UP:
2052 multi_pxo_scroll_channels_up();
2055 case MULTI_PXO_CHAN_DOWN:
2056 multi_pxo_scroll_channels_down();
2059 case MULTI_PXO_TEXT_UP:
2060 multi_pxo_scroll_chat_up();
2063 case MULTI_PXO_TEXT_DOWN:
2064 multi_pxo_scroll_chat_down();
2067 case MULTI_PXO_PLIST_UP:
2068 multi_pxo_scroll_players_up();
2069 multi_pxo_chat_adjust_start();
2072 case MULTI_PXO_PLIST_DOWN:
2073 multi_pxo_scroll_players_down();
2074 multi_pxo_chat_adjust_start();
2077 case MULTI_PXO_JOIN:
2078 // if there are no channels to join, let the user know
2079 if((Multi_pxo_channel_count == 0) || (Multi_pxo_channels == NULL)){
2080 gamesnd_play_iface(SND_GENERAL_FAIL);
2081 multi_pxo_notify_add(XSTR("No channels!",944));
2085 // if we're not already trying to join, allow this
2086 if(!SWITCHING_CHANNELS() && (Multi_pxo_channel_select != NULL)){
2087 gamesnd_play_iface(SND_USER_SELECT);
2088 multi_pxo_join_channel(Multi_pxo_channel_select);
2090 multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2091 gamesnd_play_iface(SND_GENERAL_FAIL);
2095 case MULTI_PXO_GAMES:
2096 // move to the join game screen as normally (temporary!)
2097 gameseq_post_event( GS_EVENT_MULTI_JOIN_GAME );
2100 case MULTI_PXO_JOIN_PRIV:
2101 // if we're not already trying to join, allow this
2102 if(!SWITCHING_CHANNELS()){
2103 gamesnd_play_iface(SND_USER_SELECT);
2105 // fire up the private join popup
2106 multi_pxo_priv_popup();
2108 multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2109 gamesnd_play_iface(SND_GENERAL_FAIL);
2113 case MULTI_PXO_FIND:
2114 gamesnd_play_iface(SND_USER_SELECT);
2116 // fire up the find join popup
2117 multi_pxo_find_popup();
2120 case MULTI_PXO_HELP:
2121 gamesnd_play_iface(SND_USER_SELECT);
2122 gameseq_post_event(GS_EVENT_PXO_HELP);
2125 case MULTI_PXO_PINFO:
2128 // if we have a guy selected, try and get his info
2129 if(Multi_pxo_player_select != NULL){
2130 // if we successfully got info for this guy
2131 if(multi_pxo_pinfo_get(Multi_pxo_player_select->name)){
2133 multi_pxo_pinfo_show();
2135 // if we didn't get stats for this guy.
2137 SDL_snprintf(stats, SDL_arraysize(stats), XSTR("Could not get stats for %s\n(May not be a registered pilot)", 946), Multi_pxo_player_select->name);
2138 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,stats);
2141 gamesnd_play_iface(SND_GENERAL_FAIL);
2145 case MULTI_PXO_RANKINGS:
2146 // make sure he doesn't click it too many times
2147 if((Multi_pxo_ranking_last < 0.0f) || ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_ranking_last) > MULTI_PXO_RANK_TIME) ){
2148 gamesnd_play_iface(SND_USER_SELECT);
2151 multi_pxo_url(Multi_options_g.pxo_rank_url);
2153 // mark the time down
2154 Multi_pxo_ranking_last = f2fl(timer_get_fixed_seconds());
2156 gamesnd_play_iface(SND_GENERAL_FAIL);
2160 case MULTI_PXO_MOTD:
2161 // maybe fire up the pxo motd dialog
2162 multi_pxo_motd_dialog();
2167 // condition function for popup_do_with_condition for connected to Parallax Online
2168 int mpxo_failed = 0;
2169 int multi_pxo_connect_do()
2172 char id_string[255] = "";
2173 char ip_string[255] = "";
2175 // if we already tried and failed, sit around until the user presses cancel
2177 // try and connect to the server
2180 // build the tracker id string
2181 SDL_snprintf(id_string, SDL_arraysize(id_string), "%s %s", Multi_tracker_id_string, Player->callsign);
2183 // build the ip string
2184 SDL_snprintf(ip_string, SDL_arraysize(ip_string), "%s:%d", Multi_options_g.pxo_ip, PXO_CHAT_PORT);
2186 // connect to the server
2187 ret_code = ConnectToChatServer(ip_string, Multi_pxo_nick, id_string);
2189 // give some time to the pxo api.
2190 multi_pxo_api_process();
2193 // already connected, return success
2197 // failed to connect, return fail
2200 popup_change_text(XSTR("Failed to connect to Parallax Online!", 947));
2203 // connected, return success
2216 // popup loop which does an autojoin of a public channel. Returns when the autojoin process is complete
2217 int multi_pxo_autojoin_do()
2219 pxo_channel last_channel;
2221 // if we need to autojoin, do so now
2222 if(Multi_pxo_must_autojoin){
2223 Multi_pxo_must_autojoin = 0;
2225 // if we're supposed to be using a (valid) "last" channel, do so
2226 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){
2228 memset(&last_channel, 0, sizeof(pxo_channel));
2229 last_channel.num_users = 0;
2230 SDL_strlcpy(last_channel.name, Multi_pxo_channel_last, SDL_arraysize(last_channel.name));
2233 multi_pxo_join_channel(&last_channel);
2235 nprintf(("Network","PXO : using last channel\n"));
2237 multi_pxo_autojoin();
2239 nprintf(("Network","PXO : using autojoin channel\n"));
2241 multi_pxo_get_channels();
2244 // give some time to the pxo api.
2245 multi_pxo_api_process();
2246 multi_pxo_process_common();
2248 // next value is not -1 when actually switching channels, so keep processing by returning 0.
2249 if ( SWITCHING_CHANNELS() ){
2253 // couldn't switch channel for some reason. bail out with -1
2254 if ( !ON_CHANNEL() ){
2262 // attempt to connect to Parallax Online, return success or fail
2263 int multi_pxo_connect()
2266 char join_fail_str[256];
2268 // intiialize chat api
2271 // set us to "must autojoin"
2272 Multi_pxo_must_autojoin = 1;
2274 // run the connect dialog/popup
2276 if(popup_till_condition(multi_pxo_connect_do, XSTR("&Cancel", 779), XSTR("Logging into Parallax Online",949)) == 10){
2279 // if we're going to use the "last" channel
2280 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){
2281 SDL_strlcpy(join_str, XSTR("Joining last channel (",982), SDL_arraysize(join_str));
2282 SDL_strlcat(join_str, Multi_pxo_channel_last + 1, SDL_arraysize(join_str));
2283 SDL_strlcat(join_str, ")", SDL_arraysize(join_str));
2285 SDL_strlcpy(join_fail_str, XSTR("Unable to join last channel", 983), SDL_arraysize(join_fail_str));
2287 SDL_strlcpy(join_str, XSTR("Autojoining public channel", 984), SDL_arraysize(join_str));
2288 SDL_strlcpy(join_fail_str, XSTR("Unable to autojoin public channel", 985), SDL_arraysize(join_fail_str));
2291 // once connected, we should do an autojoin before allowing the guy to continue.
2292 rval = popup_till_condition( multi_pxo_autojoin_do, XSTR("&Cancel", 779), join_str );
2297 popup( PF_USE_AFFIRMATIVE_ICON, 1, XSTR("OK", 1492), join_fail_str);
2300 // otherwise disconnect just to be safe
2301 DisconnectFromChatServer();
2302 gameseq_post_event(GS_EVENT_MAIN_MENU);
2304 // did not successfully connect
2308 // run the networking functions for the PXO API
2309 void multi_pxo_api_process()
2314 pxo_channel *lookup;
2316 // give some time to psnet
2317 PSNET_TOP_LAYER_PROCESS();
2319 // give some time to the game tracker API
2322 // give some time to the user tracker API
2325 // get any incoming text
2331 // process the chat line
2332 multi_pxo_chat_process_incoming(p);
2336 // get any incoming channel list stuff
2337 p = GetChannelList();
2340 // nprintf(("Network","%s\n",p));
2341 multi_pxo_make_channels(p);
2344 // process any chat commands
2345 cmd = GetChatCommand();
2348 switch(cmd->command)
2350 case CC_USER_JOINING:
2351 // add a user, if he doesn't already exist
2352 if(multi_pxo_find_player(cmd->data) == NULL){
2353 multi_pxo_add_player(cmd->data);
2356 // increase the player count
2358 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2360 lookup->num_users++;
2365 case CC_USER_LEAVING:
2367 multi_pxo_del_player(cmd->data);
2369 // add a text message
2370 SDL_snprintf(msg_str, SDL_arraysize(msg_str), XSTR("*** %s has left", 950), cmd->data);
2371 multi_pxo_chat_process_incoming(msg_str);
2373 // decrease the player count
2375 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2377 lookup->num_users--;
2382 case CC_DISCONNECTED:
2383 multi_pxo_handle_disconnect();
2387 multi_pxo_handle_kick();
2390 case CC_NICKCHANGED:
2391 // process a nick change
2392 multi_pxo_process_nick_change(cmd->data);
2395 case CC_YOURCHANNEL:
2396 // copy the current channel info, and unset the switching status
2397 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2398 Multi_pxo_channel_switch.num_users = -1;
2400 SetNewChatChannel(NULL);
2402 SDL_strlcpy(Multi_pxo_channel_current.name, cmd->data, SDL_arraysize(Multi_pxo_channel_current.name));
2404 // if we don't already have this guy on the list, add him
2405 pxo_channel *lookup;
2406 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2408 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2409 lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2412 // set the user count to be 0
2414 lookup->num_users = 0;
2417 // set our "last" channel to be this one
2418 SDL_strlcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name, SDL_arraysize(Multi_pxo_channel_last));
2420 // refresh current channel server count
2421 multi_pxo_channel_refresh_current();
2423 // clear the chat area
2424 // multi_pxo_chat_clear();
2431 cmd = GetChatCommand();
2434 // handle any processing details if we're currently trying to join a channel
2435 multi_pxo_handle_channel_change();
2438 // process a "nick" change event
2439 void multi_pxo_process_nick_change(char *data)
2442 player_list *lookup;
2444 // get the new string
2445 from = strtok(data," ");
2446 to = strtok(NULL,"");
2447 if((from != NULL) && (to != NULL)){
2448 lookup = multi_pxo_find_player(from);
2450 SDL_strlcpy(lookup->name, to, SDL_arraysize(lookup->name));
2452 // if this is also my nick, change it
2453 if(!SDL_strcasecmp(Multi_pxo_nick,from)){
2454 SDL_strlcpy(Multi_pxo_nick, to, SDL_arraysize(Multi_pxo_nick));
2460 // autojoin an appropriate channel
2461 void multi_pxo_autojoin()
2465 memset(&sw,0,sizeof(pxo_channel));
2467 SDL_strlcpy(sw.name, MULTI_PXO_AUTOJOIN_CHANNEL, SDL_arraysize(sw.name));
2469 // if we found a valid room, attempt to join it
2470 multi_pxo_join_channel(&sw);
2473 // does the string match the "autojoin" prefic
2474 int multi_pxo_is_autojoin(char *name)
2476 // check to see if the name is long enough
2477 if(strlen(name) < strlen(MULTI_PXO_AUTOJOIN_PREFIX)){
2481 // check to see if the first n chars match
2482 return !SDL_strncasecmp(name,MULTI_PXO_AUTOJOIN_PREFIX,strlen(MULTI_PXO_AUTOJOIN_PREFIX));
2485 // called from the game tracker API - server count update for a channel
2486 void multi_pxo_channel_count_update(char *name,int count)
2488 pxo_channel *lookup;
2490 // lookup the channel name on the normal list
2492 lookup = multi_pxo_find_channel(name,Multi_pxo_channels);
2494 lookup->num_servers = (ushort)count;
2496 nprintf(("Network","PXO : updated channel %s server count to %d\n",name,count));
2500 // status bar stuff -----------------------------------------------
2502 // set the status text
2503 void multi_pxo_set_status_text(const char *txt)
2506 SDL_strlcpy(Multi_pxo_status_text, txt, SDL_arraysize(Multi_pxo_status_text));
2508 // make sure it fits properly
2509 gr_force_fit_string(Multi_pxo_status_text, 254, Multi_pxo_status_coords[gr_screen.res][2]);
2512 // blit the status text
2513 void multi_pxo_blit_status_text()
2517 // center and draw the text
2518 if(strlen(Multi_pxo_status_text)) {
2519 gr_set_color_fast(&Color_bright);
2520 gr_get_string_size(&w, NULL, Multi_pxo_status_text);
2521 gr_string(Multi_pxo_status_coords[gr_screen.res][0] + ((Multi_pxo_status_coords[gr_screen.res][2] - w)/2), Multi_pxo_status_coords[gr_screen.res][1], Multi_pxo_status_text);
2526 // channel related stuff -------------------------------------------
2528 // get a list of channels on the server
2529 void multi_pxo_get_channels()
2531 SendChatString(NOX("/list"));
2534 // clear the old channel list
2535 void multi_pxo_clear_channels()
2537 pxo_channel *moveup,*backup;
2539 // only clear a non-null list
2540 if(Multi_pxo_channels != NULL){
2542 moveup = Multi_pxo_channels;
2547 moveup = moveup->next;
2549 // free the struct itself
2552 } while(moveup != Multi_pxo_channels);
2553 Multi_pxo_channels = NULL;
2556 // head of the list of available channels
2557 Multi_pxo_channels = NULL;
2558 Multi_pxo_channel_count = 0;
2560 // item we're going to start displaying at
2561 Multi_pxo_channel_start = NULL;
2562 Multi_pxo_channel_start_index = -1;
2564 // items we've currently got selected
2565 Multi_pxo_channel_select = NULL;
2569 // parse the input string and make a list of new channels
2570 void multi_pxo_make_channels(char *chan_str)
2572 char *name_tok,*user_tok,*desc_tok;
2574 pxo_channel *lookup;
2577 nprintf(("Network","Making some channels!\n"));
2579 // clear the channel list
2580 // multi_pxo_clear_channels();
2582 // set the last get time
2583 Multi_pxo_channel_last_refresh = f2fl(timer_get_fixed_seconds());
2585 name_tok = strtok(chan_str," ");
2586 if(name_tok == NULL){
2591 // parse the user count token
2592 user_tok = strtok(NULL," ");
2594 // parse the channel description token
2595 desc_tok = strtok(NULL,"$");
2597 // something invalid in the data, return here.....
2598 if((name_tok == NULL) || (user_tok == NULL) || (desc_tok == NULL)){
2602 // get the # of users
2603 num_users = (ubyte)atoi(user_tok);
2605 // if the # of users is > 0, or its not an autojoin, place it on the display list
2606 if((num_users > 0) || !multi_pxo_is_autojoin(name_tok)){
2607 // see if it exists already, and if so, just update the user count
2608 lookup = multi_pxo_find_channel(name_tok,Multi_pxo_channels);
2611 lookup->num_users = (short)num_users;
2615 res = multi_pxo_add_channel(name_tok,&Multi_pxo_channels);
2617 //Multi_pxo_channel_count++;
2618 res->num_users = (short)num_users;
2619 SDL_strlcpy(res->desc, desc_tok, SDL_arraysize(res->desc));
2624 // get the next name token
2625 name_tok = strtok(NULL," ");
2626 } while(name_tok != NULL);
2628 // if we need to autojoin, do so now
2629 //if(Multi_pxo_must_autojoin){
2630 // Multi_pxo_must_autojoin = 0;
2632 // multi_pxo_autojoin();
2636 multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
2638 // if we haven't refreshed server counts yet, do it now
2639 if(Multi_pxo_channel_server_refresh < 0.0f){
2640 multi_pxo_channel_refresh_servers();
2643 // if we don't already have this guy on the list, add him
2645 pxo_channel *lookup;
2646 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2648 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2649 multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2654 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2655 pxo_channel *multi_pxo_add_channel(char *name,pxo_channel **list)
2657 pxo_channel *new_channel;
2659 // try and allocate a new pxo_channel struct
2660 new_channel = (pxo_channel *)malloc(sizeof(pxo_channel));
2661 if ( new_channel == NULL ) {
2662 nprintf(("Network", "Cannot allocate space for new pxo_channel structure\n"));
2665 memset(new_channel,0,sizeof(pxo_channel));
2666 // try and allocate a string for the channel name
2667 SDL_strlcpy(new_channel->name, name, SDL_arraysize(new_channel->name));
2669 // insert it on the list
2670 if ( *list != NULL ) {
2671 new_channel->next = (*list)->next;
2672 new_channel->next->prev = new_channel;
2673 (*list)->next = new_channel;
2674 new_channel->prev = *list;
2676 *list = new_channel;
2677 (*list)->next = (*list)->prev = *list;
2680 Multi_pxo_channel_count++;
2684 // lookup a channel with the specified name
2685 pxo_channel *multi_pxo_find_channel(char *name,pxo_channel *list)
2687 pxo_channel *moveup;
2689 // look the sucker up
2695 if(!SDL_strcasecmp(name,moveup->name)){
2699 moveup = moveup->next;
2700 } while((moveup != list) && (moveup != NULL));
2705 // process the channel list (select, etc)
2706 void multi_pxo_process_channels()
2711 // if we don't have a start item, but the list is non-null
2712 if((Multi_pxo_channel_start == NULL) && (Multi_pxo_channels != NULL)){
2713 Multi_pxo_channel_start = Multi_pxo_channels;
2714 Multi_pxo_channel_start_index = 0;
2717 // if we don't have a selected item, but the list is non-null
2718 if((Multi_pxo_channel_select == NULL) && (Multi_pxo_channels != NULL)){
2719 Multi_pxo_channel_select = Multi_pxo_channels;
2722 multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2725 // if the "switch" delay timestamp is set, see if it has expired
2726 if((Multi_pxo_switch_delay != -1) && timestamp_elapsed(Multi_pxo_switch_delay)){
2727 Multi_pxo_switch_delay = -1;
2730 // see if we have a mouse click on the channel region
2731 if(Multi_pxo_channel_button.pressed()){
2732 Multi_pxo_channel_button.get_mouse_pos(NULL,&my);
2734 // index from the top
2735 item_index = my / 10;
2737 // select the item if possible
2738 if((item_index + Multi_pxo_channel_start_index) < Multi_pxo_channel_count){
2739 Multi_pxo_channel_select = Multi_pxo_channel_start;
2740 for(idx=0;idx<item_index;idx++){
2741 Multi_pxo_channel_select = Multi_pxo_channel_select->next;
2745 multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2749 // last refresh time
2750 if((Multi_pxo_channel_last_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_last_refresh) > CHANNEL_REFRESH_TIME) ){
2752 multi_pxo_set_status_text(XSTR("Refreshing Public Channel List",952));
2754 // get a list of channels on the server (clear any old list as well)
2755 multi_pxo_get_channels();
2758 Multi_pxo_channel_last_refresh = -1.0f;
2760 nprintf(("Network","Refreshing channels\n"));
2763 // if we haven't updated our server channel counts in a while, do so again
2764 // last refresh time
2765 if((Multi_pxo_channel_server_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_server_refresh) > CHANNEL_SERVER_REFRESH_TIME) ){
2766 // refresh server counts
2767 // multi_pxo_set_status_text("Refreshing Public Channel Server Counts");
2769 // do it _NOW_ I"M RIGHT HERE KILL ME WHAT ARE YOU WAITING FOR DO IT KILL ME DO IT NOW!
2770 multi_pxo_channel_refresh_servers();
2774 // send a request to refresh our channel server counts
2775 void multi_pxo_channel_refresh_servers()
2777 pxo_channel *lookup;
2778 filter_game_list_struct filter;
2780 // traverse the list of existing channels we know about and query the game tracker about them
2781 lookup = Multi_pxo_channels;
2786 if(strlen(lookup->name)){
2788 memset(&filter,0,sizeof(filter_game_list_struct));
2789 SDL_strlcpy(filter.channel, lookup->name, SDL_arraysize(filter.channel));
2792 RequestGameCountWithFilter(&filter);
2796 lookup = lookup->next;
2797 } while((lookup != NULL) && (lookup != Multi_pxo_channels));
2800 Multi_pxo_channel_server_refresh = f2fl(timer_get_fixed_seconds());
2803 // refresh current channel server count
2804 void multi_pxo_channel_refresh_current()
2806 // send a request for a server count on this channel
2807 if(strlen(Multi_pxo_channel_current.name)){
2809 filter_game_list_struct filter;
2810 memset(&filter,0,sizeof(filter_game_list_struct));
2811 SDL_strlcpy(filter.channel, Multi_pxo_channel_current.name, SDL_arraysize(filter.channel));
2814 RequestGameCountWithFilter(&filter);
2818 // display the channel list
2819 void multi_pxo_blit_channels()
2821 pxo_channel *moveup;
2822 char chan_name[255];
2823 char chan_users[15];
2824 char chan_servers[15];
2825 int user_w,server_w;
2826 int disp_count,y_start;
2828 // blit as many channels as we can
2830 y_start = Multi_pxo_chan_coords[gr_screen.res][1];
2831 moveup = Multi_pxo_channel_start;
2836 // if this is the currently selected item, highlight it
2837 if(moveup == Multi_pxo_channel_select){
2838 gr_set_color_fast(&Color_bright);
2840 // otherwise draw it normally
2842 gr_set_color_fast(&Color_normal);
2845 // get the # of users on the channel
2846 SDL_snprintf(chan_users, SDL_arraysize(chan_users), "%d", moveup->num_users);
2848 // get the width of the user count string
2849 gr_get_string_size(&user_w, NULL, chan_users);
2851 // get the # of servers on the channel
2852 SDL_snprintf(chan_servers, SDL_arraysize(chan_servers), "%d", moveup->num_servers);
2854 // get the width of the user count string
2855 gr_get_string_size(&server_w, NULL, chan_servers);
2857 // make sure the name fits
2858 SDL_assert(moveup->name);
2859 SDL_strlcpy(chan_name, moveup->name, SDL_arraysize(chan_name));
2860 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]);
2863 gr_string(Multi_pxo_chan_coords[gr_screen.res][0], y_start, chan_name + 1);
2864 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);
2865 gr_set_color_fast(&Color_bright);
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_GAMES_COLUMN], y_start, chan_servers);
2868 // increment the displayed count
2873 moveup = moveup->next;
2874 } while((moveup != Multi_pxo_channels) && (disp_count < Multi_pxo_max_chan_display[gr_screen.res]));
2877 // scroll channel list up
2878 void multi_pxo_scroll_channels_up()
2880 // if we're already at the head of the list, do nothing
2881 if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start == Multi_pxo_channels)){
2882 gamesnd_play_iface(SND_GENERAL_FAIL);
2886 // otherwise move up one
2887 Multi_pxo_channel_start = Multi_pxo_channel_start->prev;
2888 Multi_pxo_channel_start_index--;
2889 gamesnd_play_iface(SND_USER_SELECT);
2892 // scroll channel list down
2893 void multi_pxo_scroll_channels_down()
2895 // if we're already at the tail of the list, do nothing
2896 if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start->next == Multi_pxo_channels)){
2897 gamesnd_play_iface(SND_GENERAL_FAIL);
2901 // if we can't scroll further without going past the end of the viewable list, don't
2902 if((Multi_pxo_channel_start_index + Multi_pxo_max_chan_display[gr_screen.res]) >= Multi_pxo_channel_count){
2903 gamesnd_play_iface(SND_GENERAL_FAIL);
2907 // otherwise move down one
2908 Multi_pxo_channel_start = Multi_pxo_channel_start->next;
2909 Multi_pxo_channel_start_index++;
2910 gamesnd_play_iface(SND_USER_SELECT);
2913 // attempt to join a channel
2914 void multi_pxo_join_channel(pxo_channel *chan)
2916 char switch_msg[256];
2918 // if we're already on this channel, do nothing
2919 if(ON_CHANNEL() && !SDL_strcasecmp(chan->name,Multi_pxo_channel_current.name)){
2923 // if we're already trying to join a channel, do nothing
2924 if(SWITCHING_CHANNELS()){
2928 // try and join the channel
2929 switch(SetNewChatChannel(chan->name)){
2935 // decrement the count of our current channel
2936 pxo_channel *lookup;
2937 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2939 lookup->num_users--;
2942 // set our current channel as none
2943 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2944 Multi_pxo_channel_current.num_users = -1;
2946 multi_pxo_set_status_text(XSTR("Switching channels",953));
2949 memcpy(&Multi_pxo_channel_switch,chan,sizeof(pxo_channel));
2951 // clear the player list
2952 multi_pxo_clear_players();
2954 // display a line of text indicating that we're switching channels
2955 if(strlen(Multi_pxo_channel_switch.name) > 1){
2956 SDL_snprintf(switch_msg, SDL_arraysize(switch_msg), "[Switching to channel %s]", Multi_pxo_channel_switch.name + 1);
2958 SDL_snprintf(switch_msg, SDL_arraysize(switch_msg), "[Switching to channel %s]", Multi_pxo_channel_switch.name);
2960 multi_pxo_chat_process_incoming(switch_msg, CHAT_MODE_CHANNEL_SWITCH);
2968 // handle any processing details if we're currently trying to join a channel
2969 void multi_pxo_handle_channel_change()
2971 // if we're not switching channels, do nothing
2972 if(!SWITCHING_CHANNELS()){
2976 // if we are, check the status
2977 switch(SetNewChatChannel(NULL)){
2980 // unset our switching struct
2981 memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
2982 Multi_pxo_channel_switch.num_users = -1;
2985 multi_pxo_set_status_text(XSTR("No channel (error while switching)",954));
2992 // successfully changed
2994 // copy the current channel info, and unset the switching status
2995 memcpy(&Multi_pxo_channel_current,&Multi_pxo_channel_switch,sizeof(pxo_channel));
2996 Multi_pxo_channel_switch.num_users = -1;
2998 // set our "last" channel
2999 SDL_strlcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name, SDL_arraysize(Multi_pxo_channel_last));
3002 multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
3004 // if we don't already have this guy on the list, add him
3005 pxo_channel *lookup;
3006 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
3008 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
3009 lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
3012 // set the user count to be 1 (just me)
3014 lookup->num_users = 1;
3017 // clear the chat area
3018 // multi_pxo_chat_clear();
3020 // set the "switch" delay timestamp
3021 Multi_pxo_switch_delay = timestamp(MULTI_PXO_SWITCH_DELAY_TIME);
3023 // refresh current channel server count
3024 multi_pxo_channel_refresh_current();
3030 // player related stuff -------------------------------------------
3032 // clear the old player list
3033 void multi_pxo_clear_players()
3035 player_list *moveup,*backup;
3037 // if the list is null, don't free it up
3038 if(Multi_pxo_players != NULL){
3040 moveup = Multi_pxo_players;
3045 moveup = moveup->next;
3047 // free the struct itself
3050 } while(moveup != Multi_pxo_players);
3051 Multi_pxo_players = NULL;
3055 Multi_pxo_player_start = NULL;
3056 // Multi_pxo_player_start_index = -1;
3057 Multi_pxo_player_select = NULL;
3060 // Multi_pxo_player_slider.set_numberItems(0);
3062 // add a bunch of bogus players
3065 for(int idx=0;idx<30;idx++){
3066 sprintf(str,"player%d",idx);
3067 multi_pxo_add_player(str);
3072 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
3073 player_list *multi_pxo_add_player(char *name)
3075 player_list *new_player;
3077 // try and allocate a new player_list struct
3078 new_player = (player_list *)malloc(sizeof(player_list));
3079 if ( new_player == NULL ) {
3080 nprintf(("Network", "Cannot allocate space for new player_list structure\n"));
3083 // try and allocate a string for the channel name
3084 SDL_strlcpy(new_player->name, name, SDL_arraysize(new_player->name));
3086 // insert it on the list
3087 if ( Multi_pxo_players != NULL ) {
3088 new_player->next = Multi_pxo_players->next;
3089 new_player->next->prev = new_player;
3090 Multi_pxo_players->next = new_player;
3091 new_player->prev = Multi_pxo_players;
3093 Multi_pxo_players = new_player;
3094 Multi_pxo_players->next = Multi_pxo_players->prev = Multi_pxo_players;
3097 // increment the start position
3098 if(Multi_pxo_player_start != NULL){
3099 // Multi_pxo_player_start_index++;
3103 Multi_pxo_player_count++;
3105 // update the count on the slider
3106 // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3111 // remove a player with the given name
3112 void multi_pxo_del_player(char *name)
3114 player_list *lookup;
3116 // try and find this guy
3117 lookup = Multi_pxo_players;
3122 // if we found a match, delete it
3123 if(!SDL_strcasecmp(name,lookup->name)){
3124 // if this is the only item on the list, free stuff up
3125 if(lookup->next == lookup){
3126 SDL_assert(lookup == Multi_pxo_players);
3128 Multi_pxo_players = NULL;
3129 multi_pxo_clear_players();
3131 // otherwise, just delete it
3133 lookup->next->prev = lookup->prev;
3134 lookup->prev->next = lookup->next;
3136 // if this was our selected item, unselect it
3137 if((lookup == Multi_pxo_player_select) && (Multi_pxo_player_select != NULL)){
3138 Multi_pxo_player_select = Multi_pxo_player_select->next;
3141 // if this was our point to start viewing from, select another
3142 if(lookup == Multi_pxo_player_start){
3143 // if this is the head of the list, move up one
3144 if(Multi_pxo_players == lookup){
3145 Multi_pxo_player_start = Multi_pxo_player_start->next;
3146 // Multi_pxo_player_start_index = 0;
3148 // otherwise move back
3150 Multi_pxo_player_start = Multi_pxo_player_start->prev;
3151 // Multi_pxo_player_start_index++;
3155 // if this is the head of the list, move it up
3156 if(lookup == Multi_pxo_players){
3157 Multi_pxo_players = Multi_pxo_players->next;
3161 lookup->next = NULL;
3162 lookup->prev = NULL;
3167 Multi_pxo_player_count--;
3168 SDL_assert(Multi_pxo_player_count >= 0);
3170 // update the count on the slider
3171 // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3172 // Multi_pxo_player_slider.force_currentItem(Multi_pxo_player_start_index);
3179 lookup = lookup->next;
3180 } while((lookup != NULL) && (lookup != Multi_pxo_players));
3183 // try and find a player with the given name, return a pointer to his entry (or NULL)
3184 player_list *multi_pxo_find_player(char *name)
3186 player_list *lookup;
3188 // look through all players
3189 lookup = Multi_pxo_players;
3194 if(!SDL_strcasecmp(name,lookup->name)){
3198 lookup = lookup->next;
3199 } while((lookup != NULL) && (lookup != Multi_pxo_players));
3205 // process the player list (select, etc)
3206 void multi_pxo_process_players()
3209 player_list *lookup;
3211 // if we don't have a start item, but the list is non-null
3212 if((Multi_pxo_player_start == NULL) && (Multi_pxo_players != NULL)){
3213 Multi_pxo_player_start = Multi_pxo_players;
3214 // Multi_pxo_player_start_index = 0;
3216 // update the slider
3217 // Multi_pxo_player_slider.set_currentItem(Multi_pxo_player_start_index);
3220 // if we don't have a selected item, but the list is non-null
3221 if((Multi_pxo_player_select == NULL) && (Multi_pxo_players != NULL)){
3222 Multi_pxo_player_select = Multi_pxo_players;
3225 // see if we have a mouse click on the channel region
3226 if(Multi_pxo_player_button.pressed()){
3227 Multi_pxo_player_button.get_mouse_pos(NULL,&my);
3229 // index from the top
3230 item_index = my / 10;
3232 // select the item if possible
3233 lookup = Multi_pxo_player_start;
3238 if(item_index == 0){
3239 Multi_pxo_player_select = Multi_pxo_player_start;
3243 // move to the next item
3244 lookup = lookup->next;
3247 // if this item is our guy
3248 if((item_index == 0) && (lookup != Multi_pxo_players)){
3249 Multi_pxo_player_select = lookup;
3252 } while((lookup != Multi_pxo_players) && (item_index > 0));
3256 // display the player list
3257 void multi_pxo_blit_players()
3259 player_list *moveup;
3260 char player_name[255];
3261 int disp_count,y_start;
3263 // blit as many channels as we can
3265 y_start = Multi_pxo_player_coords[gr_screen.res][1];
3266 moveup = Multi_pxo_player_start;
3271 // if this is the currently selected item, highlight it
3272 if(moveup == Multi_pxo_player_select){
3273 gr_set_color_fast(&Color_bright);
3275 // otherwise draw it normally
3277 gr_set_color_fast(&Color_normal);
3280 // make sure the string fits
3281 SDL_strlcpy(player_name, moveup->name, SDL_arraysize(player_name));
3282 gr_force_fit_string(player_name, 254, Multi_pxo_player_coords[gr_screen.res][2]);
3285 gr_string(Multi_pxo_player_coords[gr_screen.res][0], y_start, player_name);
3287 // increment the displayed count
3292 moveup = moveup->next;
3293 } while((moveup != Multi_pxo_players) && (disp_count < Multi_pxo_max_player_display[gr_screen.res]));
3296 // scroll player list up
3297 void multi_pxo_scroll_players_up()
3299 // if we're already at the head of the list, do nothing
3300 if((Multi_pxo_player_start == NULL) || (Multi_pxo_player_start == Multi_pxo_players)){
3301 gamesnd_play_iface(SND_GENERAL_FAIL);
3305 // otherwise move up one
3306 Multi_pxo_player_start = Multi_pxo_player_start->prev;
3307 // Multi_pxo_player_start_index--;
3308 // SDL_assert(Multi_pxo_player_start_index >= 0);
3310 gamesnd_play_iface(SND_USER_SELECT);
3313 // scroll player list down
3314 void multi_pxo_scroll_players_down()
3316 player_list *lookup;
3319 // see if its okay to scroll down
3320 lookup = Multi_pxo_player_start;
3321 if(lookup == NULL ){
3322 gamesnd_play_iface(SND_GENERAL_FAIL);
3326 while(lookup->next != Multi_pxo_players){
3327 lookup = lookup->next;
3331 // if we can move down
3332 if(count >= Multi_pxo_max_player_display[gr_screen.res]){
3333 Multi_pxo_player_start = Multi_pxo_player_start->next;
3335 // Multi_pxo_player_start_index++;
3337 gamesnd_play_iface(SND_USER_SELECT);
3339 gamesnd_play_iface(SND_GENERAL_FAIL);
3344 // chat text stuff -----------------------------------------
3346 // initialize and create the chat text linked list
3347 void multi_pxo_chat_init()
3350 chat_line *new_line;
3353 Multi_pxo_chat = NULL;
3354 Multi_pxo_chat_add = NULL;
3355 Multi_pxo_chat_start = NULL;
3356 Multi_pxo_chat_start_index = -1;
3358 // create the lines in a non-circular doubly linked list
3359 for(idx=0;idx<MAX_CHAT_LINES;idx++){
3360 new_line = (chat_line*)malloc(sizeof(chat_line));
3362 // clear the line out
3363 SDL_assert(new_line != NULL);
3364 if(new_line == NULL){
3367 memset(new_line,0,sizeof(chat_line));
3368 new_line->prev = NULL;
3369 new_line->next = NULL;
3371 // insert it into the (empty) list
3372 if(Multi_pxo_chat == NULL){
3373 Multi_pxo_chat = new_line;
3375 // insert it onto the (non-empty) list
3377 Multi_pxo_chat->prev = new_line;
3378 new_line->next = Multi_pxo_chat;
3379 Multi_pxo_chat = new_line;
3383 // start adding chat lines at the beginning of the list
3384 Multi_pxo_chat_add = Multi_pxo_chat;
3387 // free up all chat list stuff
3388 void multi_pxo_chat_free()
3390 chat_line *moveup, *backup;
3392 // free all items up
3393 moveup = Multi_pxo_chat;
3394 while(moveup != NULL){
3396 moveup = moveup->next;
3402 Multi_pxo_chat = NULL;
3403 Multi_pxo_chat_add = NULL;
3404 Multi_pxo_chat_start = NULL;
3405 Multi_pxo_chat_start_index = -1;
3406 Multi_pxo_chat_count = 0;
3407 Multi_pxo_chat_slider.set_numberItems(0);
3410 // clear all lines of chat text in the chat area
3411 void multi_pxo_chat_clear()
3415 // clear the text in all the lines
3416 moveup = Multi_pxo_chat;
3417 while(moveup != NULL){
3418 SDL_zero(moveup->text);
3419 moveup = moveup->next;
3422 // how many chat lines we have
3423 Multi_pxo_chat_count = 0;
3425 // start adding chat lines at the beginning of the list
3426 Multi_pxo_chat_add = Multi_pxo_chat;
3429 // add a line of text
3430 void multi_pxo_chat_add_line(char *txt, int mode)
3435 SDL_assert(Multi_pxo_chat_add != NULL);
3436 SDL_strlcpy(Multi_pxo_chat_add->text, txt, SDL_arraysize(Multi_pxo_chat_add->text));
3437 Multi_pxo_chat_add->mode = mode;
3439 // if we're at the end of the list, move the front item down
3440 if(Multi_pxo_chat_add->next == NULL) {
3441 // store the new "head" of the list
3442 temp = Multi_pxo_chat->next;
3444 // move the current head to the end of the list
3445 Multi_pxo_chat_add->next = Multi_pxo_chat;
3447 Multi_pxo_chat->prev = Multi_pxo_chat_add;
3448 Multi_pxo_chat->next = NULL;
3450 // reset the head of the list
3451 Multi_pxo_chat = temp;
3453 // set the new add line
3454 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3455 SDL_zero(Multi_pxo_chat_add->text);
3456 Multi_pxo_chat_add->mode = CHAT_MODE_NORMAL;
3458 // if we're not at the end of the list, just move up by one
3460 // set the new add line
3461 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3464 // if we've reached max chat lines, don't increment
3465 if(Multi_pxo_chat_count < MAX_CHAT_LINES) {
3466 Multi_pxo_chat_count++;
3470 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
3472 // force the position, in case we arent at the bottom of the list
3475 // move to the bottom of the chat area
3478 multi_pxo_goto_bottom();
3481 // if we have more than the # of lines displayable
3482 if(Multi_pxo_chat_count >= MULTI_PXO_MAX_CHAT_DISPLAY){
3484 multi_pxo_goto_bottom();
3487 multi_pxo_goto_bottom();
3490 // process an incoming line of text
3491 void multi_pxo_chat_process_incoming(const char *txt,int mode)
3493 char msg_total[512],line[512];
3496 char *p_str[20]; // the initial line (unindented)
3497 const char *priv_ptr = NULL;
3499 // filter out "has left" channel messages, when switching channels
3500 if((SWITCHING_CHANNELS() || ((Multi_pxo_switch_delay != -1) && !timestamp_elapsed(Multi_pxo_switch_delay))) &&
3501 multi_pxo_chat_is_left_message(txt)){
3505 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3506 priv_ptr = multi_pxo_chat_is_private(txt);
3507 if(priv_ptr != NULL){
3508 SDL_strlcpy(msg_total, priv_ptr, SDL_arraysize(msg_total));
3510 SDL_strlcpy(msg_total, txt, SDL_arraysize(msg_total));
3513 // determine what mode to display this text in
3515 // if this is private chat
3516 if(priv_ptr != NULL){
3517 mode = CHAT_MODE_PRIVATE;
3521 // if this is a server message
3522 if(multi_pxo_is_server_text(txt)){
3523 mode = CHAT_MODE_SERVER;
3525 // if this is a MOTD
3526 else if(multi_pxo_is_motd_text(txt)){
3527 // mode = CHAT_MODE_MOTD;
3529 multi_pxo_motd_add_text(txt);
3532 // if this is the end of motd text
3533 else if(multi_pxo_is_end_of_motd_text(txt)){
3534 multi_pxo_set_end_of_motd();
3539 // split the text up into as many lines as necessary
3540 n_lines = split_str(msg_total, Multi_pxo_chat_coords[gr_screen.res][2] - 5, n_chars, p_str, 3);
3541 SDL_assert((n_lines != -1) && (n_lines <= 20));
3542 if((n_lines < 0) || (n_lines > 20)) {
3546 // if the string fits on one line
3548 multi_pxo_chat_add_line(msg_total,mode);
3550 // don't pad with extra spaces if its from the server
3552 if(mode != CHAT_MODE_SERVER){
3553 multi_pxo_chat_add_line("",CHAT_MODE_NORMAL);
3557 // if the string was split into multiple lines
3559 // add the first line
3560 memcpy(line,p_str[0],n_chars[0]);
3561 line[n_chars[0]] = '\0';
3562 multi_pxo_chat_add_line(line,mode);
3564 // copy the rest of the lines
3565 for(idx=1; idx<n_lines; idx++){
3566 memcpy(line,p_str[idx],n_chars[idx]);
3567 line[n_chars[idx]] = '\0';
3569 // unless the current mode is server or "switching channels", make all these CHAT_MODE_CARRY
3570 if((mode != CHAT_MODE_SERVER) && (mode != CHAT_MODE_CHANNEL_SWITCH)){
3571 mode = CHAT_MODE_CARRY;
3573 multi_pxo_chat_add_line(line, mode);
3578 // blit the chat text
3579 void multi_pxo_chat_blit()
3582 int disp_count,token_width;
3588 // blit the title line
3590 if(strlen(Multi_pxo_channel_current.name) > 1){
3591 SDL_snprintf(title, SDL_arraysize(title), XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name+1); // [[ <who> on <channel> ]]
3593 SDL_snprintf(title, SDL_arraysize(title), XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name); // [[ <who> on <channel> ]]
3596 SDL_strlcpy(title, XSTR("Parallax Online - No Channel", 956), SDL_arraysize(title));
3598 gr_force_fit_string(title, 254, Multi_pxo_chat_coords[gr_screen.res][2] - 10);
3599 gr_get_string_size(&token_width,NULL,title);
3600 gr_set_color_fast(&Color_normal);
3601 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);
3603 // blit all active lines of text
3604 moveup = Multi_pxo_chat_start;
3606 y_start = Multi_pxo_chat_coords[gr_screen.res][1];
3607 while((moveup != NULL) && (moveup != Multi_pxo_chat_add) && (disp_count < (Multi_pxo_max_chat_display[gr_screen.res]))){
3608 switch(moveup->mode){
3609 // if this is text from the server, display it all "bright"
3610 case CHAT_MODE_SERVER:
3611 gr_set_color_fast(&Color_bright);
3612 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3615 // if this is motd, display it all "bright"
3616 case CHAT_MODE_MOTD:
3617 gr_set_color_fast(&Color_bright_white);
3618 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3621 // normal mode, just highlight the server
3622 case CHAT_MODE_PRIVATE:
3623 case CHAT_MODE_NORMAL:
3624 SDL_strlcpy(piece, moveup->text, SDL_arraysize(piece));
3625 tok = strtok(piece," ");
3627 // get the width of just the first "piece"
3628 gr_get_string_size(&token_width, NULL, tok);
3631 gr_set_color_fast(&Color_bright);
3632 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, tok);
3634 // draw the rest of the string normally
3635 tok = strtok(NULL,"");
3637 gr_set_color_fast(&Color_normal);
3638 gr_string(Multi_pxo_chat_coords[gr_screen.res][0] + token_width + 6, y_start, tok);
3643 // carry mode, display with no highlight
3644 case CHAT_MODE_CARRY:
3645 gr_set_color_fast(&Color_normal);
3646 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3649 // "switching channels mode", display it bright
3650 case CHAT_MODE_CHANNEL_SWITCH:
3651 gr_set_color_fast(&Color_bright);
3652 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3657 moveup = moveup->next;
3662 if ((moveup != Multi_pxo_chat_add) && (moveup != NULL)) {
3663 Can_scroll_down = 1;
3665 Can_scroll_down = 0;
3669 // scroll to the very bottom of the chat area
3670 void multi_pxo_goto_bottom()
3675 if (Multi_pxo_chat == NULL) {
3679 // if we have less than the displayable amount of lines, do nothing
3680 if(Multi_pxo_chat_count <= Multi_pxo_max_chat_display[gr_screen.res]){
3681 Multi_pxo_chat_start = Multi_pxo_chat;
3683 // nothing to do for the slider
3684 Multi_pxo_chat_slider.set_numberItems(0);
3688 if (!Can_scroll_down)
3690 // otherwise move back the right # of items
3691 backup = Multi_pxo_chat_add;
3692 for(idx=0; idx<Multi_pxo_max_chat_display[gr_screen.res]; idx++){
3693 SDL_assert(backup->prev != NULL);
3694 backup = backup->prev;
3697 Multi_pxo_chat_start = backup;
3699 // fixup the start index
3700 multi_pxo_chat_adjust_start();
3704 // scroll the text up
3705 void multi_pxo_scroll_chat_up()
3707 // if we're already at the top of the list, don't do anything
3708 if ((Multi_pxo_chat_start == NULL) || (Multi_pxo_chat_start == Multi_pxo_chat)) {
3709 gamesnd_play_iface(SND_GENERAL_FAIL);
3713 // otherwise move up one
3714 Multi_pxo_chat_start = Multi_pxo_chat_start->prev;
3716 multi_pxo_chat_adjust_start();
3718 gamesnd_play_iface(SND_USER_SELECT);
3721 // returns 1 if we can scroll down, 0 otherwise
3722 int multi_pxo_can_scroll_down()
3727 // see if its okay to scroll down
3728 lookup = Multi_pxo_chat_start;
3729 if (lookup == NULL) {
3730 // gamesnd_play_iface(SND_GENERAL_FAIL);
3734 while (lookup != Multi_pxo_chat_add) {
3735 lookup = lookup->next;
3739 // check if we can move down, return accordingly
3740 if (count > Multi_pxo_max_chat_display[gr_screen.res]) {
3747 // scroll the text down
3748 void multi_pxo_scroll_chat_down()
3750 // if we can move down
3751 if (multi_pxo_can_scroll_down()) {
3752 Multi_pxo_chat_start = Multi_pxo_chat_start->next;
3753 multi_pxo_chat_adjust_start();
3754 gamesnd_play_iface(SND_USER_SELECT);
3756 gamesnd_play_iface(SND_GENERAL_FAIL);
3760 // process chat controls
3761 void multi_pxo_chat_process()
3763 char *remainder = NULL;
3764 const char *result = NULL;
3766 int msg_pixel_width;
3768 // if the chat line is getting too long, fire off the message, putting the last
3769 // word on the next input line.
3771 Multi_pxo_chat_input.get_text(msg);
3773 // determine if the width of the string in pixels is > than the inputbox width -- if so,
3774 // then send the message
3775 gr_get_string_size(&msg_pixel_width, NULL, msg);
3776 // if ( msg_pixel_width >= (Chatbox_inputbox_w - Player->short_callsign_width) ) {
3777 if ( msg_pixel_width >= (Multi_pxo_input_coords[gr_screen.res][2])) {
3778 remainder = strrchr(msg, ' ');
3784 // if we're connected to a channel, send the chat to the server
3786 result = SendChatString(msg,1);
3788 multi_pxo_chat_process_incoming(result);
3791 // display any remainder of text on the next line
3792 Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3794 Multi_pxo_chat_input.set_text("");
3796 } else if((Multi_pxo_chat_input.pressed() && (strlen(msg) > 0)) || (strlen(msg) >= MAX_CHAT_LINE_LEN)) {
3797 // tack on the null terminator in the boundary case
3798 int x = strlen(msg);
3799 if(x >= MAX_CHAT_LINE_LEN){
3800 msg[MAX_CHAT_LINE_LEN-1] = '\0';
3803 // ignore "/nick" commands
3804 if(multi_pxo_is_nick_command(msg)){
3805 Multi_pxo_chat_input.set_text("");
3809 // send the chat to the server
3810 // if we're connected to a channel, send the chat to the server
3812 result = SendChatString(msg,1);
3814 multi_pxo_chat_process_incoming(result);
3817 // display any remainder of text on the next line
3818 Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3820 Multi_pxo_chat_input.set_text("");
3825 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3828 // NOTE : DO NOT LOCALIZE THESE STRINGS!!!! THEY ARE CONSTANTS WHICH ARE CHECKED AGAINST
3829 // PXO CHAT SERVER DATA. THEY CANNOT CHANGE!!!
3830 #define PMSG_FROM "private message from "
3831 #define PMSG_TO "private message to "
3832 const char *multi_pxo_chat_is_private(const char *txt)
3835 if( strlen(txt) > strlen( PMSG_FROM ) ){
3836 // otherwise do a comparison
3837 if(!SDL_strncasecmp( txt, PMSG_FROM, strlen(PMSG_FROM) )){
3838 return &txt[strlen( PMSG_FROM )];
3843 if(strlen(txt) > strlen( PMSG_TO )){
3844 // otherwise do a comparison
3845 if(!SDL_strncasecmp(txt,PMSG_TO,strlen(PMSG_TO))){
3846 return &txt[strlen(PMSG_TO)];
3854 // if the text came from the server
3855 int multi_pxo_is_server_text(const char *txt)
3857 // if the message is prefaced by a ***
3858 if((strlen(txt) >= strlen(MULTI_PXO_SERVER_PREFIX)) && !strncmp(txt, MULTI_PXO_SERVER_PREFIX, strlen(MULTI_PXO_SERVER_PREFIX))){
3865 // if the text is message of the day text
3866 int multi_pxo_is_motd_text(const char *txt)
3868 // if we're not on a channel, and this is not a channel switching message assume its coming from a server
3869 if((strlen(txt) >= strlen(PXO_CHAT_MOTD_PREFIX)) && !strncmp(txt, PXO_CHAT_MOTD_PREFIX, strlen(PXO_CHAT_MOTD_PREFIX))){
3876 // if the text is the end of motd text
3877 int multi_pxo_is_end_of_motd_text(const char *txt)
3879 // if we're not on a channel, and this is not a channel switching message assume its coming from a server
3880 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))){
3887 // if the text is a "has left message" from the server
3888 int multi_pxo_chat_is_left_message(const char *txt)
3890 char last_portion[100];
3892 // if the text is not server text
3893 if(!multi_pxo_is_server_text(txt)){
3897 // check to see if the last portion is the correct wording
3898 SDL_zero(last_portion);
3899 if((strlen(txt) > strlen(MULTI_PXO_HAS_LEFT)) && !strcmp(&txt[strlen(txt) - strlen(MULTI_PXO_HAS_LEFT)], MULTI_PXO_HAS_LEFT)){
3903 // check the end of the line
3907 // recalculate the chat start index, and adjust the slider properly
3908 void multi_pxo_chat_adjust_start()
3912 // if we have no chat
3913 if (Multi_pxo_chat == NULL) {
3914 Multi_pxo_chat_start_index = -1;
3919 Multi_pxo_chat_start_index = 0;
3920 moveup = Multi_pxo_chat;
3921 while((moveup != Multi_pxo_chat_start) && (moveup != NULL)){
3922 Multi_pxo_chat_start_index++;
3923 moveup = moveup->next;
3926 // set the slider index
3927 Multi_pxo_chat_slider.force_currentItem(Multi_pxo_chat_start_index);
3930 // motd stuff ---------------------------------------------------------
3932 // initialize motd when going into this screen
3933 void multi_pxo_motd_init()
3935 // zero the motd string
3936 SDL_strlcpy(Pxo_motd, "", SDL_arraysize(Pxo_motd));
3938 // haven't gotten it yet
3941 // haven't read it yet either
3945 // set the motd text
3946 void multi_pxo_motd_add_text(const char *text)
3948 int cur_len = strlen(Pxo_motd);
3956 // make sure its motd text
3957 SDL_assert(multi_pxo_is_motd_text(text));
3958 if(!multi_pxo_is_motd_text(text)){
3962 // if its a 0 line motd
3963 if(strlen(text) <= strlen(PXO_CHAT_MOTD_PREFIX)){
3967 // add text to the motd
3968 new_len = strlen(text + strlen(PXO_CHAT_MOTD_PREFIX)) - 1;
3969 if((cur_len + new_len + 1) < MAX_PXO_MOTD_LEN){
3970 SDL_strlcat(Pxo_motd, text + strlen(PXO_CHAT_MOTD_PREFIX) + 1, SDL_arraysize(Pxo_motd));
3971 SDL_strlcat(Pxo_motd, "\n", SDL_arraysize(Pxo_motd));
3972 mprintf(("MOTD ADD : %s\n", Pxo_motd));
3977 void multi_pxo_set_end_of_motd()
3982 mprintf(("MOTD ALL : %s\n", Pxo_motd));
3986 // do we have an old MOTD file laying around? If so, read it in and see if its the same
3990 // checksum the current motd
3991 new_chksum = cf_add_chksum_long(0, Pxo_motd, strlen(Pxo_motd));
3993 // checksum the old motd if its lying around
3994 CFILE *in = cfopen("oldmotd.txt", "rb");
3996 // read the old checksum
3997 old_chksum = cfread_uint(in);
4000 // same checksum? no blink
4001 if(new_chksum == old_chksum){
4006 // write out the motd for next time
4007 if(strlen(Pxo_motd)){
4008 CFILE *out = cfopen("oldmotd.txt", "wb", CFILE_NORMAL, CF_TYPE_DATA);
4010 // write all the text
4011 cfwrite_uint(new_chksum, out);
4013 // close the outfile
4018 // set the blink stamp
4019 Pxo_motd_blink_stamp = -1;
4021 Pxo_motd_blink_on = 0;
4022 if(!Pxo_motd_blinked_already){
4023 Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4024 Pxo_motd_blink_on = 1;
4028 Pxo_motd_blinked_already = 1;
4031 // display the motd dialog
4032 void multi_pxo_motd_dialog()
4034 // mark the motd as read
4037 // simple popup, with a slider
4038 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, Pxo_motd);
4041 // call to maybe blink the motd button
4042 void multi_pxo_motd_maybe_blit()
4044 // if we got the end of the motd, and he hasn't read it yet
4045 if(Pxo_motd_end && !Pxo_motd_read && (Pxo_motd_blink_stamp != -1)){
4046 // if the timestamp elapsed, flip the blink flag
4047 if(timestamp_elapsed(Pxo_motd_blink_stamp)){
4048 Pxo_motd_blink_on = !Pxo_motd_blink_on;
4049 Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4053 if(Pxo_motd_blink_on){
4054 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_MOTD].button.draw_forced(2);
4060 // common dialog stuff ------------------------------------------------
4062 int Multi_pxo_searching = 0;
4064 // initialize the common dialog with the passed max input length
4065 void multi_pxo_com_init(int input_len)
4069 // create the interface window
4070 Multi_pxo_com_window.create(0, 0, gr_screen.max_w,gr_screen.max_h, 0);
4071 Multi_pxo_com_window.set_mask_bmap(Multi_pxo_com_mask_fname[gr_screen.res]);
4073 // create the interface buttons
4074 for(idx=0; idx<MULTI_PXO_COM_NUM_BUTTONS; idx++){
4075 // create the object
4076 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);
4078 // set the sound to play when highlighted
4079 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4081 // set the ani for the button
4082 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_com_buttons[gr_screen.res][idx].filename);
4085 Multi_pxo_com_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_com_buttons[gr_screen.res][idx].hotspot);
4089 for(idx=0; idx<MULTI_PXO_COM_NUM_TEXT; idx++){
4090 Multi_pxo_com_window.add_XSTR(&Multi_pxo_com_text[gr_screen.res][idx]);
4093 // create the input box
4094 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);
4095 Multi_pxo_com_input.set_focus();
4097 // clear all text lines
4098 SDL_zero(Multi_pxo_com_bottom_text);
4099 SDL_zero(Multi_pxo_com_middle_text);
4100 SDL_zero(Multi_pxo_com_top_text);
4103 // close down the common dialog
4104 void multi_pxo_com_close()
4106 // destroy the UI_WINDOW
4107 Multi_pxo_com_window.destroy();
4110 // blit all text lines, top, middle, bottoms
4111 void multi_pxo_com_blit_text()
4113 // blit top, middle and bottom text if possible
4114 if(strlen(Multi_pxo_com_top_text) > 0){
4115 gr_set_color_fast(&Color_bright);
4116 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);
4118 if(strlen(Multi_pxo_com_middle_text) > 0){
4119 gr_set_color_fast(&Color_bright);
4120 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);
4122 if(strlen(Multi_pxo_com_bottom_text) > 0){
4123 gr_set_color_fast(&Color_bright);
4124 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);
4128 // set the top text, shortening as necessary
4129 void multi_pxo_com_set_top_text(const char *txt)
4131 if((txt != NULL) && strlen(txt)){
4132 SDL_strlcpy(Multi_pxo_com_top_text, txt, SDL_arraysize(Multi_pxo_com_top_text));
4133 gr_force_fit_string(Multi_pxo_com_top_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4137 // set the middle text, shortening as necessary
4138 void multi_pxo_com_set_middle_text(const char *txt)
4140 if((txt != NULL) && strlen(txt)){
4141 SDL_strlcpy(Multi_pxo_com_middle_text, txt, SDL_arraysize(Multi_pxo_com_middle_text));
4142 gr_force_fit_string(Multi_pxo_com_middle_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4146 // set the bottom text, shortening as necessary
4147 void multi_pxo_com_set_bottom_text(const char *txt)
4149 if((txt != NULL) && strlen(txt)){
4150 SDL_strlcpy(Multi_pxo_com_bottom_text, txt, SDL_arraysize(Multi_pxo_com_bottom_text));
4151 gr_force_fit_string(Multi_pxo_com_bottom_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4156 // private channel join stuff -----------------------------------------
4158 // initialize the popup
4159 void multi_pxo_priv_init()
4161 SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE);
4163 // initialize the common dialog with the passed max input length
4164 multi_pxo_com_init(MULTI_PXO_PRIV_MAX_TEXT_LEN);
4166 // initialize the return code
4167 Multi_pxo_priv_return_code = -1;
4169 // mark us as running
4170 Multi_pxo_mode = MULTI_PXO_MODE_PRIVATE;
4173 multi_pxo_com_set_middle_text(XSTR("Type the name of the channel to join/create",961));
4176 // close down the popup
4177 void multi_pxo_priv_close()
4179 // close down the common dialog
4180 multi_pxo_com_close();
4182 // mark us as not running any more
4183 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4186 // run the popup, 0 if still running, -1 if cancel, 1 if ok
4187 int multi_pxo_priv_popup()
4191 // if we're not already running, initialize stuff
4192 if(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE){
4194 multi_pxo_priv_init();
4196 // return "still running"
4200 k = Multi_pxo_com_window.process();
4202 // process keypresses
4204 // like hitting the cancel button
4206 Multi_pxo_priv_return_code = 0;
4210 // process button presses
4211 multi_pxo_priv_process_buttons();
4213 // process the inputbox
4214 multi_pxo_priv_process_input();
4216 // blit the background
4217 multi_pxo_blit_all();
4221 gr_set_bitmap(Multi_pxo_com_bitmap);
4222 gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4223 Multi_pxo_com_window.draw();
4225 // blit all text lines, top, middle, bottoms
4226 multi_pxo_com_blit_text();
4230 // check the return code
4231 switch(Multi_pxo_priv_return_code){
4232 // still in progress
4238 multi_pxo_priv_close();
4243 multi_pxo_priv_close();
4250 // process button presses
4251 void multi_pxo_priv_process_buttons()
4255 // check all buttons
4256 for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4257 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4258 multi_pxo_priv_button_pressed(idx);
4264 // handle a button press
4265 void multi_pxo_priv_button_pressed(int n)
4267 char priv_chan_name[128];
4270 case MULTI_PXO_COM_CANCEL:
4271 Multi_pxo_priv_return_code = 0;
4274 case MULTI_PXO_COM_OK:
4275 Multi_pxo_com_input.get_text(priv_chan_name);
4276 multi_pxo_strip_space(priv_chan_name, priv_chan_name, SDL_arraysize(priv_chan_name));
4278 // if its a 0 length string, interpret as a cancel
4279 if(strlen(priv_chan_name) <= 0){
4280 Multi_pxo_priv_return_code = 0;
4284 Multi_pxo_priv_return_code = 1;
4289 // process the inputbox
4290 void multi_pxo_priv_process_input()
4292 char priv_chan_name[128];
4294 // see if the user has pressed enter
4295 if(Multi_pxo_com_input.pressed()){
4296 Multi_pxo_com_input.get_text(priv_chan_name);
4297 multi_pxo_strip_space(priv_chan_name, priv_chan_name, SDL_arraysize(priv_chan_name));
4299 // if its a 0 length string, interpret as a cancel
4300 if(strlen(priv_chan_name) <= 0){
4301 Multi_pxo_priv_return_code = 0;
4305 // otherwise interpret as "accept"
4306 Multi_pxo_priv_return_code = 1;
4308 // add in the "+" which indicates a private room
4309 SDL_strlcpy(Multi_pxo_priv_chan, "+", SDL_arraysize(Multi_pxo_priv_chan));
4310 SDL_strlcat(Multi_pxo_priv_chan, priv_chan_name, SDL_arraysize(Multi_pxo_priv_chan));
4314 // find player stuff -----------------------------------------
4316 char name_lookup[255];
4318 // initialize the popup
4319 void multi_pxo_find_init()
4321 SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_FIND);
4323 // initialize the common dialog with the passed max input length
4324 multi_pxo_com_init(MAX_PLAYER_NAME_LEN);
4326 // return code, set to something other than -1 if we're supposed to return
4327 Multi_pxo_find_return_code = -1;
4329 // mark us as running
4330 Multi_pxo_mode = MULTI_PXO_MODE_FIND;
4332 // not searching yet
4333 Multi_pxo_searching = 0;
4336 multi_pxo_com_set_top_text(XSTR("Enter user to be found",962));
4339 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4342 SDL_strlcpy(name_lookup, "", SDL_arraysize(name_lookup));
4345 // close down the popup
4346 void multi_pxo_find_close()
4348 // close down the common dialog
4349 multi_pxo_com_close();
4351 // mark us as not running any more
4352 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4355 // run the popup, 0 if still running, -1 if cancel, 1 if ok
4356 int multi_pxo_find_popup()
4360 // if we're not already running, initialize stuff
4361 if(Multi_pxo_mode != MULTI_PXO_MODE_FIND){
4363 multi_pxo_find_init();
4365 // return "still running"
4369 k = Multi_pxo_com_window.process();
4371 // process keypresses
4373 // like hitting the cancel button
4375 Multi_pxo_find_return_code = 0;
4379 // process button presses
4380 multi_pxo_find_process_buttons();
4382 // process the inputbox
4383 multi_pxo_find_process_input();
4385 // process search mode if applicable
4386 multi_pxo_find_search_process();
4388 // blit the background
4389 multi_pxo_blit_all();
4393 gr_set_bitmap(Multi_pxo_com_bitmap);
4394 gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4395 Multi_pxo_com_window.draw();
4397 // blit any text lines
4398 multi_pxo_com_blit_text();
4402 // check the return code
4403 switch(Multi_pxo_find_return_code){
4404 // still in progress
4410 // close the popup down
4411 multi_pxo_find_close();
4416 // close the popup down
4417 multi_pxo_find_close();
4419 // if we have a channel, join it now if possible
4420 if(strlen(Multi_pxo_find_channel) > 0){
4421 pxo_channel *lookup;
4422 lookup = multi_pxo_find_channel(Multi_pxo_find_channel,Multi_pxo_channels);
4424 // if we couldn't find it, don't join
4426 multi_pxo_join_channel(lookup);
4435 // process button presses
4436 void multi_pxo_find_process_buttons()
4440 // check all buttons
4441 for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4442 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4443 multi_pxo_find_button_pressed(idx);
4449 // handle a button press
4450 void multi_pxo_find_button_pressed(int n)
4453 case MULTI_PXO_COM_CANCEL:
4454 Multi_pxo_find_return_code = 0;
4457 case MULTI_PXO_COM_OK:
4458 Multi_pxo_find_return_code = 1;
4463 // process the inputbox
4464 void multi_pxo_find_process_input()
4466 // see if the user has pressed enter
4467 if(Multi_pxo_com_input.pressed()){
4468 // if we're not already in search mode
4469 if(!Multi_pxo_searching){
4471 SDL_zero(Multi_pxo_com_middle_text);
4472 SDL_zero(Multi_pxo_com_bottom_text);
4474 Multi_pxo_com_input.get_text(name_lookup);
4475 multi_pxo_strip_space(name_lookup, name_lookup, SDL_arraysize(name_lookup));
4477 // never search with a zero length string
4478 if(strlen(name_lookup) > 0){
4479 char search_text[512];
4481 // put us in search mode
4482 Multi_pxo_searching = 1;
4485 GetChannelByUser(name_lookup);
4488 SDL_snprintf(search_text, SDL_arraysize(search_text), XSTR("Searching for %s", 963), name_lookup);
4489 multi_pxo_com_set_top_text(search_text);
4493 SDL_zero(Multi_pxo_com_top_text);
4499 // process search mode if applicable
4500 void multi_pxo_find_search_process()
4504 // if we're not searching for anything, return
4505 if(!Multi_pxo_searching){
4509 // otherwise check to see if we've found him
4510 channel = GetChannelByUser(NULL);
4512 // if we've got a result, let the user know
4514 // if he couldn't be found
4515 if(channel == (char *)-1){
4516 multi_pxo_com_set_middle_text(XSTR("User not found",964));
4517 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4519 if(channel[0] == '*'){
4520 multi_pxo_com_set_middle_text(XSTR("Player is logged in but is not on a channel",965));
4521 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4525 // if this guy is on a public channel, display which one
4526 if(channel[0] == '#'){
4527 SDL_snprintf(p_text, SDL_arraysize(p_text), XSTR("Found %s on :", 966), name_lookup);
4529 // display the results
4530 multi_pxo_com_set_middle_text(p_text);
4531 multi_pxo_com_set_bottom_text(channel+1);
4533 // mark down the channel name so we know where to find him
4534 SDL_strlcpy(Multi_pxo_find_channel, channel, SDL_arraysize(Multi_pxo_find_channel));
4535 // strip out trailing whitespace
4536 if(Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] == ' '){
4537 Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] = '\0';
4540 // if this is a private channel
4541 else if(channel[0] == '+'){
4542 SDL_snprintf(p_text, SDL_arraysize(p_text), XSTR("Found %s on a private channel", 967), name_lookup);
4543 multi_pxo_com_set_middle_text(p_text);
4545 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4550 // unset search mode
4551 Multi_pxo_searching = 0;
4553 // clear the inputbox
4554 Multi_pxo_com_input.set_text("");
4559 // player info stuff -----------------------------------------
4561 // popup conditional functions, returns 10 on successful get of stats
4562 int multi_pxo_pinfo_cond()
4565 char temp_string[255];
4568 // process common stuff
4569 multi_pxo_process_common();
4571 // run the networking functions for the PXO API
4572 multi_pxo_api_process();
4574 // process depending on what mode we're in
4575 switch(Multi_pxo_retrieve_mode){
4576 // getting his player tracker id
4578 // if the thing is non-null, do something
4579 ret_string = GetTrackerIdByUser(Multi_pxo_retrieve_name);
4580 if(ret_string != NULL){
4581 // user not-online/not found
4582 if(ret_string == (char *)-1){
4586 // user not a tracker pilot
4587 if(!SDL_strcasecmp(ret_string,"-1")){
4591 // otherwise parse into his id and callsign
4592 SDL_strlcpy(temp_string, ret_string, SDL_arraysize(temp_string));
4593 tok = strtok(temp_string," ");
4597 SDL_strlcpy(Multi_pxo_retrieve_id, tok, SDL_arraysize(Multi_pxo_retrieve_id));
4600 tok = strtok(NULL,"");
4602 SDL_strlcpy(Multi_pxo_retrieve_name, tok, SDL_arraysize(Multi_pxo_retrieve_name));
4609 // failure of some kind or another
4614 Multi_pxo_retrieve_mode = 1;
4619 // initial call to get his stats
4621 // change the popup text
4622 popup_change_text(XSTR("Getting player stats",968));
4625 memset(&Multi_pxo_pinfo, 0, sizeof(Multi_pxo_pinfo));
4626 SDL_strlcpy(Multi_pxo_pinfo.pilot_name, Multi_pxo_retrieve_name, SDL_arraysize(Multi_pxo_pinfo.pilot_name));
4627 SDL_strlcpy(Multi_pxo_pinfo.tracker_id, Multi_pxo_retrieve_id, SDL_arraysize(Multi_pxo_pinfo.tracker_id));
4629 // make the initial call to the API
4630 GetFSPilotData((vmt_stats_struct*)0xffffffff,NULL,NULL,0);
4631 if(GetFSPilotData(&Multi_pxo_pinfo,Multi_pxo_retrieve_name,Multi_pxo_retrieve_id,0) != 0){
4634 // if the call went through, set the mode to 2
4636 Multi_pxo_retrieve_mode = 2;
4640 // busy retrieving his stats
4642 switch(GetFSPilotData(NULL,NULL,NULL,0)){
4643 // timeout, fail, cancel
4661 // return not done yet
4665 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
4666 int multi_pxo_pinfo_get(char *name)
4669 Multi_pxo_retrieve_mode = 0;
4670 SDL_strlcpy(Multi_pxo_retrieve_name, name, SDL_arraysize(Multi_pxo_retrieve_name));
4671 switch(popup_till_condition(multi_pxo_pinfo_cond,XSTR("&Cancel", 779),XSTR("Retrieving player tracker id",969))){
4676 // failed to get his tracker id
4680 // failed to get his stats
4685 // we didn't get the stats
4689 // fire up the stats view popup
4690 void multi_pxo_pinfo_show()
4692 // initialize the popup
4693 multi_pxo_pinfo_init();
4697 game_set_frametime(GS_STATE_PXO);
4698 } while(!multi_pxo_pinfo_do());
4700 // close down the popup
4701 multi_pxo_pinfo_close();
4704 // build the stats labels values
4705 void multi_pxo_pinfo_build_vals()
4707 vmt_stats_struct *fs = &Multi_pxo_pinfo;
4709 SDL_zero(Multi_pxo_pinfo_vals);
4712 SDL_strlcpy(Multi_pxo_pinfo_vals[0], fs->pilot_name, SDL_arraysize(Multi_pxo_pinfo_vals[0]));
4713 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]));
4716 multi_sg_rank_build_name(Ranks[fs->rank].name, Multi_pxo_pinfo_vals[1], SDL_arraysize(Multi_pxo_pinfo_vals[1]));
4717 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]));
4720 SDL_snprintf(Multi_pxo_pinfo_vals[2], SDL_arraysize(Multi_pxo_pinfo_vals[2]), "%d", fs->kill_count);
4723 SDL_snprintf(Multi_pxo_pinfo_vals[3], SDL_arraysize(Multi_pxo_pinfo_vals[3]), "%d", fs->assists);
4726 SDL_snprintf(Multi_pxo_pinfo_vals[4], SDL_arraysize(Multi_pxo_pinfo_vals[4]), "%d", fs->kill_count - fs->kill_count_ok);
4729 SDL_snprintf(Multi_pxo_pinfo_vals[5], SDL_arraysize(Multi_pxo_pinfo_vals[5]), "%d", (int)fs->missions_flown);
4732 game_format_time(fl2f((float)fs->flight_time), Multi_pxo_pinfo_vals[6], SDL_arraysize(Multi_pxo_pinfo_vals[6]));
4735 if(fs->last_flown == 0){
4736 SDL_strlcpy(Multi_pxo_pinfo_vals[7], XSTR("No missions flown", 970), SDL_arraysize(Multi_pxo_pinfo_vals[7]));
4738 tm *tmr = gmtime((time_t*)&fs->last_flown);
4740 strftime(Multi_pxo_pinfo_vals[7],30,"%m/%d/%y %H:%M",tmr);
4742 SDL_strlcpy(Multi_pxo_pinfo_vals[7], "", SDL_arraysize(Multi_pxo_pinfo_vals[7]));
4746 // primary shots fired
4747 SDL_snprintf(Multi_pxo_pinfo_vals[8], SDL_arraysize(Multi_pxo_pinfo_vals[8]), "%d", (int)fs->p_shots_fired);
4749 // primary shots hit
4750 SDL_snprintf(Multi_pxo_pinfo_vals[9], SDL_arraysize(Multi_pxo_pinfo_vals[9]), "%d", (int)fs->p_shots_hit);
4753 if(fs->p_shots_fired > 0){
4754 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));
4756 SDL_strlcpy(Multi_pxo_pinfo_vals[10], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[10]));
4759 // secondary shots fired
4760 SDL_snprintf(Multi_pxo_pinfo_vals[11], SDL_arraysize(Multi_pxo_pinfo_vals[11]), "%d", (int)fs->s_shots_fired);
4762 // secondary shots hit
4763 SDL_snprintf(Multi_pxo_pinfo_vals[12], SDL_arraysize(Multi_pxo_pinfo_vals[12]), "%d", (int)fs->s_shots_hit);
4765 // secondary hit pct
4766 if(fs->s_shots_fired > 0){
4767 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));
4769 SDL_strlcpy(Multi_pxo_pinfo_vals[13], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[13]));
4772 // primary friendly hits
4773 SDL_snprintf(Multi_pxo_pinfo_vals[14], SDL_arraysize(Multi_pxo_pinfo_vals[14]), "%d", fs->p_bonehead_hits);
4775 // primary friendly hit %
4776 if(fs->p_shots_fired > 0){
4777 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)));
4779 SDL_strlcpy(Multi_pxo_pinfo_vals[15], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[15]));
4782 // secondary friendly hits
4783 SDL_snprintf(Multi_pxo_pinfo_vals[16], SDL_arraysize(Multi_pxo_pinfo_vals[16]), "%d", fs->s_bonehead_hits);
4785 // secondary friendly hit %
4786 if(fs->s_shots_fired > 0){
4787 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)));
4789 SDL_strlcpy(Multi_pxo_pinfo_vals[17], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[17]));
4793 // initialize the popup
4794 void multi_pxo_pinfo_init()
4798 // create the interface window
4799 Multi_pxo_pinfo_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4800 Multi_pxo_pinfo_window.set_mask_bmap(Multi_pxo_pinfo_mask_fname[gr_screen.res]);
4802 Multi_pxo_pinfo_bitmap = bm_load(Multi_pxo_pinfo_fname[gr_screen.res]);
4803 SDL_assert(Multi_pxo_pinfo_bitmap != -1);
4805 // create the interface buttons
4806 for(idx=0; idx<MULTI_PXO_PINFO_NUM_BUTTONS; idx++){
4807 // create the object
4808 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);
4810 // set the sound to play when highlighted
4811 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4813 // set the ani for the button
4814 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_pinfo_buttons[gr_screen.res][idx].filename);
4817 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_pinfo_buttons[gr_screen.res][idx].hotspot);
4821 for(idx=0; idx<MULTI_PXO_PINFO_NUM_TEXT; idx++){
4822 Multi_pxo_pinfo_window.add_XSTR(&Multi_pxo_pinfo_text[gr_screen.res][idx]);
4825 // set up the stats labels
4826 Multi_pxo_pinfo_stats_labels[0] = strdup(XSTR("Name", 1532));
4827 Multi_pxo_pinfo_stats_labels[1] = strdup(XSTR("Rank", 1533));
4828 Multi_pxo_pinfo_stats_labels[2] = strdup(XSTR("Kills", 1534));
4829 Multi_pxo_pinfo_stats_labels[3] = strdup(XSTR("Assists", 1535));
4830 Multi_pxo_pinfo_stats_labels[4] = strdup(XSTR("Friendly kills", 1536));
4831 Multi_pxo_pinfo_stats_labels[5] = strdup(XSTR("Missions flown", 1537));
4832 Multi_pxo_pinfo_stats_labels[6] = strdup(XSTR("Flight time", 1538));
4833 Multi_pxo_pinfo_stats_labels[7] = strdup(XSTR("Last flown", 1539));
4834 Multi_pxo_pinfo_stats_labels[8] = strdup(XSTR("Primary shots fired", 1540));
4835 Multi_pxo_pinfo_stats_labels[9] = strdup(XSTR("Primary shots hit", 1541));
4836 Multi_pxo_pinfo_stats_labels[10] = strdup(XSTR("Primary hit %", 1542));
4837 Multi_pxo_pinfo_stats_labels[11] = strdup(XSTR("Secondary shots fired", 1543));
4838 Multi_pxo_pinfo_stats_labels[12] = strdup(XSTR("Secondary shots hit", 1544));
4839 Multi_pxo_pinfo_stats_labels[13] = strdup(XSTR("Secondary hit %", 1545));
4840 Multi_pxo_pinfo_stats_labels[14] = strdup(XSTR("Primary friendly hits", 1546));
4841 Multi_pxo_pinfo_stats_labels[15] = strdup(XSTR("Primary friendly hit %", 1547));
4842 Multi_pxo_pinfo_stats_labels[16] = strdup(XSTR("Secondary friendly hits", 1548));
4843 Multi_pxo_pinfo_stats_labels[17] = strdup(XSTR("Secondary friendly hit %", 1549));
4845 // build the stats labels values
4846 multi_pxo_pinfo_build_vals();
4850 int multi_pxo_pinfo_do()
4852 int k = Multi_pxo_pinfo_window.process();
4854 // process common stuff
4855 multi_pxo_process_common();
4857 // run the networking functions for the PXO API
4858 multi_pxo_api_process();
4860 // check to see if he pressed escp
4861 if(k == SDLK_ESCAPE){
4865 // if he pressed the ok button
4866 if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_OK].button.pressed()){
4870 // if he pressed the medals buttons, run the medals screen
4871 if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_MEDALS].button.pressed()){
4873 game_feature_not_in_demo_popup();
4875 multi_pxo_run_medals();
4881 // blit everything on the "normal" screen
4882 multi_pxo_blit_all();
4884 // blit our own stuff
4886 gr_set_bitmap(Multi_pxo_pinfo_bitmap);
4888 Multi_pxo_pinfo_window.draw();
4890 // blit the stats themselves
4891 multi_pxo_pinfo_blit();
4901 void multi_pxo_pinfo_close()
4905 // destroy the UI_WINDOW
4906 Multi_pxo_pinfo_window.destroy();
4908 // unload the bitmap
4909 if(Multi_pxo_pinfo_bitmap != -1){
4910 bm_unload(Multi_pxo_pinfo_bitmap);
4913 // free the stats labels strings
4914 for (i=0; i<MULTI_PXO_PINFO_NUM_LABELS; i++) {
4915 free(Multi_pxo_pinfo_stats_labels[i]);
4919 // blit all the stats on this screen
4920 void multi_pxo_pinfo_blit()
4925 // blit all the labels
4926 y_start = Multi_pxo_pinfo_coords[gr_screen.res][1];
4927 for(idx=0; idx<MULTI_PXO_PINFO_NUM_LABELS; idx++){
4929 gr_set_color_fast(&Color_bright);
4930 gr_string(Multi_pxo_pinfo_coords[gr_screen.res][0], y_start, Multi_pxo_pinfo_stats_labels[idx]);
4932 // blit the label's value
4933 gr_set_color_fast(&Color_normal);
4934 gr_string(Multi_pxo_pinfo_val_x[gr_screen.res], y_start, Multi_pxo_pinfo_vals[idx]);
4937 y_start += Multi_pxo_pinfo_stats_spacing[idx];
4941 // run the medals screen
4942 void multi_pxo_run_medals()
4946 // process common stuff
4947 multi_pxo_process_common();
4949 // run the networking functions for the PXO API
4950 multi_pxo_api_process();
4952 // initialize the freespace data and the player struct
4953 multi_stats_tracker_to_fs(&Multi_pxo_pinfo, &Multi_pxo_pinfo_player.stats);
4954 SDL_strlcpy(Multi_pxo_pinfo_player.callsign, Multi_pxo_pinfo.pilot_name, SDL_arraysize(Multi_pxo_pinfo_player.callsign));
4956 // initialize the medals screen
4957 medal_main_init(&Multi_pxo_pinfo_player, MM_POPUP);
4959 // run the medals screen until it says that it should be closed
4961 // set frametime and run common functions
4962 game_set_frametime(-1);
4963 game_do_state_common(gameseq_get_state());
4965 // run the medals screen
4966 ret_code = medal_main_do();
4969 // close the medals screen down
4972 // reset the palette
4973 multi_pxo_load_palette();
4977 // notify stuff stuff -----------------------------------------
4979 // add a notification string
4980 void multi_pxo_notify_add(const char *txt)
4983 SDL_strlcpy(Multi_pxo_notify_text, txt, SDL_arraysize(Multi_pxo_notify_text));
4985 // set the timestamp
4986 Multi_pxo_notify_stamp = timestamp(MULTI_PXO_NOTIFY_TIME);
4989 // blit and process the notification string
4990 void multi_pxo_notify_blit()
4994 // if the timestamp is -1, do nothing
4995 if(Multi_pxo_notify_stamp == -1){
4999 // if it has expired, do nothing
5000 if(timestamp_elapsed(Multi_pxo_notify_stamp)){
5001 Multi_pxo_notify_stamp = -1;
5004 // otherwise blit the text
5005 gr_set_color_fast(&Color_bright);
5006 gr_get_string_size(&w,NULL,Multi_pxo_notify_text);
5007 gr_string((gr_screen.max_w - w)/2,MULTI_PXO_NOTIFY_Y,Multi_pxo_notify_text);
5011 // initialize the PXO help screen
5012 void multi_pxo_help_init()
5016 // load the background bitmap
5017 Multi_pxo_help_bitmap = bm_load(Multi_pxo_help_fname[gr_screen.res]);
5018 if(Multi_pxo_help_bitmap < 0){
5019 // we failed to load the bitmap - this is very bad
5022 // create the interface window
5023 Multi_pxo_help_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
5024 Multi_pxo_help_window.set_mask_bmap(Multi_pxo_help_mask_fname[gr_screen.res]);
5026 // create the interface buttons
5027 for(idx=0; idx<MULTI_PXO_HELP_NUM_BUTTONS; idx++){
5028 // create the object
5029 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);
5031 // set the sound to play when highlighted
5032 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
5034 // set the ani for the button
5035 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_help_buttons[gr_screen.res][idx].filename);
5038 Multi_pxo_help_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_help_buttons[gr_screen.res][idx].hotspot);
5042 for(idx=0; idx<MULTI_PXO_HELP_NUM_TEXT; idx++){
5043 Multi_pxo_help_window.add_XSTR(&Multi_pxo_help_text[gr_screen.res][idx]);
5046 // if we haven't already loaded in the text, do so
5047 // if(!Multi_pxo_help_loaded){
5048 multi_pxo_help_load();
5051 // set the current page to 0
5052 Multi_pxo_help_cur = 0;
5055 // do frame for PXO help
5056 void multi_pxo_help_do()
5059 if(Multi_pxo_connected){
5060 multi_pxo_api_process();
5063 // process common stuff
5064 multi_pxo_process_common();
5066 int k = Multi_pxo_help_window.process();
5068 // process any keypresses
5071 gamesnd_play_iface(SND_USER_SELECT);
5072 gameseq_post_event(GS_EVENT_PXO);
5076 // process button presses
5077 multi_pxo_help_process_buttons();
5079 // draw the background, etc
5081 GR_MAYBE_CLEAR_RES(Multi_pxo_help_bitmap);
5082 if(Multi_pxo_help_bitmap != -1){
5083 gr_set_bitmap(Multi_pxo_help_bitmap);
5086 Multi_pxo_help_window.draw();
5088 // blit the current page
5089 multi_pxo_help_blit_page();
5095 // close the pxo screen
5096 void multi_pxo_help_close()
5100 // unload any bitmaps
5101 bm_unload(Multi_pxo_help_bitmap);
5103 // destroy the UI_WINDOW
5104 Multi_pxo_help_window.destroy();
5107 for(idx=0; idx<Multi_pxo_help_num_pages; idx++){
5108 for(idx2=0; idx2<Multi_pxo_help_pages[idx].num_lines; idx2++){
5110 if(Multi_pxo_help_pages[idx].text[idx2] != NULL){
5111 free(Multi_pxo_help_pages[idx].text[idx2]);
5112 Multi_pxo_help_pages[idx].text[idx2] = NULL;
5118 // load the help file up
5119 void multi_pxo_help_load()
5124 // if its already loaded, do nothing
5125 // if(Multi_pxo_help_loaded){
5129 // read in the text file
5131 in = cfopen(MULTI_PXO_HELP_FILE,"rt",CFILE_NORMAL,CF_TYPE_DATA);
5132 SDL_assert(in != NULL);
5137 Multi_pxo_help_num_pages = 0;
5139 // blast all the help pages clear
5140 memset(Multi_pxo_help_pages, 0, sizeof(help_page) * MULTI_PXO_MAX_PAGES);
5141 Multi_pxo_help_num_pages = 0;
5142 cp = &Multi_pxo_help_pages[0];
5146 cp->text[cp->num_lines] = (char*)malloc(Multi_pxo_chars_per_line[gr_screen.res]);
5147 if(cp->text[cp->num_lines] == NULL){
5151 // read in the next line
5152 cfgets(cp->text[cp->num_lines++], Multi_pxo_chars_per_line[gr_screen.res], in);
5154 // skip to the next page if necessary
5155 if(cp->num_lines == Multi_pxo_lines_pp[gr_screen.res]){
5156 Multi_pxo_help_num_pages++;
5157 SDL_assert(Multi_pxo_help_num_pages < MULTI_PXO_MAX_PAGES);
5158 if(Multi_pxo_help_num_pages >= MULTI_PXO_MAX_PAGES){
5159 Multi_pxo_help_num_pages--;
5162 cp = &Multi_pxo_help_pages[Multi_pxo_help_num_pages];
5169 // mark the help as having been loaded
5170 // Multi_pxo_help_loaded = 1;
5173 // blit the current page
5174 void multi_pxo_help_blit_page()
5179 help_page *cp = &Multi_pxo_help_pages[Multi_pxo_help_cur];
5182 y_start = Multi_pxo_help_coords[gr_screen.res][1];
5183 for(idx=0;idx<cp->num_lines;idx++){
5184 // if the first symbol is "@", highlight the line
5185 if(cp->text[idx][0] == '@'){
5186 gr_set_color_fast(&Color_bright);
5189 gr_set_color_fast(&Color_normal);
5194 gr_string(Multi_pxo_help_coords[gr_screen.res][0], y_start, cp->text[idx] + start_pos);
5196 // increment the y location
5201 // process button presses
5202 void multi_pxo_help_process_buttons()
5206 // process all buttons
5207 for(idx=0;idx<MULTI_PXO_HELP_NUM_BUTTONS;idx++){
5208 if(Multi_pxo_help_buttons[gr_screen.res][idx].button.pressed()){
5209 multi_pxo_help_button_pressed(idx);
5216 void multi_pxo_help_button_pressed(int n)
5219 case MULTI_PXO_HELP_PREV:
5220 // if we're already at page 0, do nothing
5221 if(Multi_pxo_help_cur == 0){
5222 gamesnd_play_iface(SND_GENERAL_FAIL);
5224 Multi_pxo_help_cur--;
5225 gamesnd_play_iface(SND_USER_SELECT);
5229 case MULTI_PXO_HELP_NEXT:
5230 // if we're already at max pages, do nothing
5231 if(Multi_pxo_help_cur == Multi_pxo_help_num_pages){
5232 gamesnd_play_iface(SND_GENERAL_FAIL);
5234 Multi_pxo_help_cur++;
5235 gamesnd_play_iface(SND_USER_SELECT);
5239 case MULTI_PXO_HELP_CONTINUE:
5240 gamesnd_play_iface(SND_USER_SELECT);
5241 gameseq_post_event(GS_EVENT_PXO);
5246 // http banner stuff ---------------------------------------------
5249 void multi_pxo_ban_init()
5251 // zero the active banner bitmap
5252 Multi_pxo_banner.ban_bitmap = -1;
5254 // are we doing banners at all?
5255 if(os_config_read_uint(NULL, "PXOBanners", 1)){
5256 // if we're already in idle mode, we're done downloading for this instance of freespace. pick a random image we already have
5257 if(Multi_pxo_ban_mode == PXO_BAN_MODE_IDLE){
5258 Multi_pxo_ban_mode = PXO_BAN_MODE_CHOOSE_RANDOM;
5262 // set ourselves to startup mode
5263 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
5264 Multi_pxo_ban_get = NULL;
5266 // set ourselves to idle mode
5267 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5268 Multi_pxo_ban_get = NULL;
5271 // zero the active banner bitmap
5272 SDL_zero(Multi_pxo_banner);
5273 Multi_pxo_banner.ban_bitmap = -1;
5276 // process http download details
5277 void multi_pxo_ban_process()
5279 char url_string[512] = "";
5280 char local_file[MAX_PATH_LEN] = "";
5283 switch(Multi_pxo_ban_mode){
5284 // start downloading list
5285 case PXO_BAN_MODE_LIST_STARTUP:
5287 SDL_snprintf(url_string, SDL_arraysize(url_string), "%s/%s", Multi_options_g.pxo_banner_url, PXO_BANNERS_CONFIG_FILE);
5290 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, PXO_BANNERS_CONFIG_FILE);
5292 // try creating the file get object
5293 Multi_pxo_ban_get = new InetGetFile(url_string, local_file);
5296 if(Multi_pxo_ban_get == NULL){
5297 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5300 // go to the downloading list mode
5301 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST;
5305 case PXO_BAN_MODE_LIST:
5307 if(Multi_pxo_ban_get->IsFileError()){
5308 delete Multi_pxo_ban_get;
5309 Multi_pxo_ban_get = NULL;
5310 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5314 // connecting, receiving
5315 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5320 if(Multi_pxo_ban_get->IsFileReceived()){
5321 delete Multi_pxo_ban_get;
5322 Multi_pxo_ban_get = NULL;
5323 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_STARTUP;
5327 // start downloading files
5328 case PXO_BAN_MODE_IMAGES_STARTUP:
5329 // first thing - parse the banners file and pick a file
5330 multi_pxo_ban_parse_banner_file(0);
5332 // if we have no active file, we're done
5333 if((strlen(Multi_pxo_banner.ban_file) <= 0) || (strlen(Multi_pxo_banner.ban_file_url) <= 0)){
5334 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5338 // if the file already exists, we're done
5339 if(cf_exist(Multi_pxo_banner.ban_file, CF_TYPE_MULTI_CACHE)){
5340 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5344 // otherwise try and download it
5345 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, Multi_pxo_banner.ban_file);
5346 // try creating the file get object
5347 Multi_pxo_ban_get = new InetGetFile(Multi_pxo_banner.ban_file_url, local_file);
5350 if(Multi_pxo_ban_get == NULL){
5351 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5354 // go to the downloading images mode
5355 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES;
5358 // downloading files
5359 case PXO_BAN_MODE_IMAGES:
5361 if(Multi_pxo_ban_get->IsFileError()){
5362 delete Multi_pxo_ban_get;
5363 Multi_pxo_ban_get = NULL;
5364 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5368 // connecting, receiving
5369 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5374 if(Multi_pxo_ban_get->IsFileReceived()){
5375 delete Multi_pxo_ban_get;
5376 Multi_pxo_ban_get = NULL;
5377 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5381 // done downloading - maybe load an image
5382 case PXO_BAN_MODE_IMAGES_DONE:
5383 // make sure we have a valid filename
5384 // SDL_assert(strlen(Multi_pxo_banner.ban_file) > 0);
5385 if(strlen(Multi_pxo_banner.ban_file) > 0){
5386 Multi_pxo_banner.ban_bitmap = bm_load(Multi_pxo_banner.ban_file);
5390 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5393 // idle (done with EVERYTHING)
5394 case PXO_BAN_MODE_IDLE:
5395 // if the banner button was clicked
5396 if(Multi_pxo_ban_button.pressed()){
5397 multi_pxo_ban_clicked();
5401 case PXO_BAN_MODE_CHOOSE_RANDOM:
5402 // first thing - parse the banners file and pick a file
5403 multi_pxo_ban_parse_banner_file(1);
5405 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5411 void multi_pxo_ban_close()
5413 // if we have a currently active transfer
5414 if(Multi_pxo_ban_get != NULL){
5415 Multi_pxo_ban_get->AbortGet();
5416 delete Multi_pxo_ban_get;
5417 Multi_pxo_ban_get = NULL;
5420 // if we have a loaded bitmap, unload it
5421 if(Multi_pxo_banner.ban_bitmap != -1){
5422 bm_unload(Multi_pxo_banner.ban_bitmap);
5423 Multi_pxo_banner.ban_bitmap = -1;
5427 // parse the banners file and maybe fill in Multi_pxo_dl_file
5428 void multi_pxo_ban_parse_banner_file(int choose_existing)
5430 char file_url[MAX_PATH_LEN] = "";
5431 char banners[10][MAX_PATH_LEN];
5432 char urls[10][MAX_PATH_LEN];
5435 int num_banners, idx;
5436 CFILE *in = cfopen(PXO_BANNERS_CONFIG_FILE, "rt", CFILE_NORMAL, CF_TYPE_MULTI_CACHE);
5438 SDL_zero(Multi_pxo_banner);
5439 Multi_pxo_banner.ban_bitmap = -1;
5446 // clear all strings
5450 // get the global banner url
5451 if(cfgets(file_url, SDL_arraysize(file_url), in) == NULL){
5453 cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5456 drop_leading_white_space(file_url);
5457 drop_trailing_white_space(file_url);
5459 // otherwise read in
5461 while(num_banners < 10){
5462 // try and get the pcx
5463 if(cfgets(banners[num_banners], SDL_arraysize(banners[0]), in) == NULL){
5466 // try and get the url
5467 if(cfgets(urls[num_banners], SDL_arraysize(urls[0]), in) == NULL){
5471 // strip off trailing and leading whitespace
5472 drop_leading_white_space(banners[num_banners]);
5473 drop_trailing_white_space(banners[num_banners]);
5474 drop_leading_white_space(urls[num_banners]);
5475 drop_trailing_white_space(urls[num_banners]);
5485 if(num_banners <= 0){
5489 // if we're only selecting files which already exist (previously downloaded)
5490 if(choose_existing){
5492 for(idx=0; idx<10; idx++){
5496 // build a list of existing files
5498 for(idx=0; idx<num_banners; idx++){
5499 if(cf_exist(banners[idx], CF_TYPE_MULTI_CACHE)){
5506 if(exist_count <= 0){
5511 int select = (int)frand_range(0.0f, (float)exist_count);
5512 if(select >= exist_count){
5513 select = exist_count - 1;
5518 for(idx=0; idx<exist_count; idx++){
5528 if(idx < exist_count){
5530 SDL_strlcpy(Multi_pxo_banner.ban_file, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file));
5532 // get the full file url
5533 SDL_strlcpy(Multi_pxo_banner.ban_file_url, file_url, SDL_arraysize(Multi_pxo_banner.ban_file_url));
5534 SDL_strlcat(Multi_pxo_banner.ban_file_url, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file_url));
5536 // url of where to go to when clicked
5537 SDL_strlcpy(Multi_pxo_banner.ban_url, urls[idx], SDL_arraysize(Multi_pxo_banner.ban_url));
5540 // randomly pick a file for download
5542 idx = (int)frand_range(0.0f, (float)num_banners);
5544 if(idx >= num_banners){
5545 idx = num_banners - 1;
5552 SDL_strlcpy(Multi_pxo_banner.ban_file, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file));
5554 // get the full file url
5555 SDL_strlcpy(Multi_pxo_banner.ban_file_url, file_url, SDL_arraysize(Multi_pxo_banner.ban_file_url));
5556 SDL_strlcat(Multi_pxo_banner.ban_file_url, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file_url));
5558 // url of where to go to when clicked
5559 SDL_strlcpy(Multi_pxo_banner.ban_url, urls[idx], SDL_arraysize(Multi_pxo_banner.ban_url));
5562 // delete the banner config file
5563 // cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5566 // any bitmap or info or whatever
5567 void multi_pxo_ban_draw()
5569 // if we have a valid bitmap
5570 if(Multi_pxo_banner.ban_bitmap >= 0){
5571 // if the mouse is over the banner button, highlight with a rectangle
5572 if(Multi_pxo_ban_button.is_mouse_on()){
5573 gr_set_color_fast(&Color_bright_blue);
5574 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);
5577 // draw the bitmap itself
5578 gr_set_bitmap(Multi_pxo_banner.ban_bitmap);
5579 gr_bitmap(Pxo_ban_coords[gr_screen.res][0], Pxo_ban_coords[gr_screen.res][1]);
5583 // called when the URL button is clicked
5584 void multi_pxo_ban_clicked()
5586 // if we have a valid bitmap and URL, launch the URL
5587 if((Multi_pxo_banner.ban_bitmap >= 0) && (strlen(Multi_pxo_banner.ban_url) > 0)){
5588 multi_pxo_url(Multi_pxo_banner.ban_url);