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
346 #define MULTI_PXO_NUM_BUTTONS 15
347 #define MULTI_PXO_PLIST_UP 0
348 #define MULTI_PXO_PLIST_DOWN 1
349 #define MULTI_PXO_RANKINGS 2
350 #define MULTI_PXO_PINFO 3
351 #define MULTI_PXO_FIND 4
352 #define MULTI_PXO_MOTD 5
353 #define MULTI_PXO_JOIN 6
354 #define MULTI_PXO_JOIN_PRIV 7
355 #define MULTI_PXO_CHAN_UP 8
356 #define MULTI_PXO_CHAN_DOWN 9
357 #define MULTI_PXO_TEXT_UP 10
358 #define MULTI_PXO_TEXT_DOWN 11
359 #define MULTI_PXO_EXIT 12
360 #define MULTI_PXO_HELP 13
361 #define MULTI_PXO_GAMES 14
363 #define MULTI_PXO_NUM_BUTTONS 14
364 #define MULTI_PXO_PLIST_UP 0
365 #define MULTI_PXO_PLIST_DOWN 1
366 #define MULTI_PXO_CHAN_UP 2
367 #define MULTI_PXO_CHAN_DOWN 3
368 #define MULTI_PXO_JOIN 4
369 #define MULTI_PXO_JOIN_PRIV 5
370 #define MULTI_PXO_TEXT_UP 6
371 #define MULTI_PXO_TEXT_DOWN 7
372 #define MULTI_PXO_EXIT 8
373 #define MULTI_PXO_RANKINGS 9
374 #define MULTI_PXO_PINFO 10
375 #define MULTI_PXO_FIND 11
376 #define MULTI_PXO_HELP 12
377 #define MULTI_PXO_GAMES 13
381 ui_button_info Multi_pxo_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_NUM_BUTTONS] = {
384 ui_button_info( "PXB_00", 1, 104, -1, -1, 0 ), // scroll player list up
385 ui_button_info( "PXB_01", 1, 334, -1, -1, 1 ), // scroll player list down
386 ui_button_info( "PXB_02", 18, 385, -1, -1, 2 ), // rankings webpage
387 ui_button_info( "PXB_03", 71, 385, -1, -1, 3 ), // pilot info
388 ui_button_info( "PXB_04", 115, 385, -1, -1, 4 ), // find player
389 ui_button_info( "PXB_05", 1, 443, -1, -1, 5 ), // motd
390 ui_button_info( "PXB_06", 330, 96, -1, -1, 6 ), // join channel
391 ui_button_info( "PXB_07", 330, 131, -1, -1, 7 ), // join private channel
392 ui_button_info( "PXB_08", 618, 92, -1, -1, 8 ), // scroll channels up
393 ui_button_info( "PXB_09", 618, 128, -1, -1, 9 ), // scroll channels down
394 ui_button_info( "PXB_10", 615, 171, -1, -1, 10 ), // scroll text up
395 ui_button_info( "PXB_11", 615, 355, -1, -1, 11 ), // scroll text down
396 ui_button_info( "PXB_12", 482, 435, -1, -1, 12 ), // exit
397 ui_button_info( "PXB_13", 533, 432, -1, -1, 13 ), // help
398 ui_button_info( "PXB_14", 573, 432, -1, -1, 14 ), // games list
400 ui_button_info( "PXB_00", 0, 139, -1, -1, 0 ), // scroll player list up
401 ui_button_info( "PXB_01", 0, 179, -1, -1, 1 ), // scroll player list down
402 ui_button_info( "PXB_02", 242, 105, -1, -1, 2 ), // scroll channels up
403 ui_button_info( "PXB_03", 242, 145, -1, -1, 3 ), // scroll channels down
404 ui_button_info( "PXB_04", 508, 121, -1, -1, 4 ), // join channel
405 ui_button_info( "PXB_05", 508, 157, -1, -1, 5 ), // join private channel
406 ui_button_info( "PXB_06", 611, 221, -1, -1, 6 ), // scroll text up
407 ui_button_info( "PXB_07", 611, 261, -1, -1, 7 ), // scroll text down
408 ui_button_info( "PXB_08", 0, 408, -1, -1, 8 ), // exit
409 ui_button_info( "PXB_09", 62, 372, -1, -1, 9 ), // rankings webpage
410 ui_button_info( "PXB_10", 122, 372, -1, -1, 10 ), // pilot info
411 ui_button_info( "PXB_11", 171, 372, -1, -1, 11 ), // find player
412 ui_button_info( "PXB_12", 505, 408, -1, -1, 12 ), // help
413 ui_button_info( "PXB_13", 561, 388, -1, -1, 13 ), // games list
417 ui_button_info( "2_PXB_00", 2, 166, -1, -1, 0 ), // scroll player list up
418 ui_button_info( "2_PXB_01", 2, 534, -1, -1, 1 ), // scroll player list down
419 ui_button_info( "2_PXB_02", 29, 616, -1, -1, 2 ), // rankings webpage
420 ui_button_info( "2_PXB_03", 114, 616, -1, -1, 3 ), // pilot info
421 ui_button_info( "2_PXB_04", 184, 616, -1, -1, 4 ), // find player
422 ui_button_info( "2_PXB_05", 2, 709, -1, -1, 5 ), // motd
423 ui_button_info( "2_PXB_06", 528, 119, -1, -1, 6 ), // join channel
424 ui_button_info( "2_PXB_07", 528, 175, -1, -1, 7 ), // join private channel
425 ui_button_info( "2_PXB_08", 989, 112, -1, -1, 8 ), // scroll channels up
426 ui_button_info( "2_PXB_09", 989, 170, -1, -1, 9 ), // scroll channels down
427 ui_button_info( "2_PXB_10", 984, 240, -1, -1, 10 ), // scroll text up
428 ui_button_info( "2_PXB_11", 984, 568, -1, -1, 11 ), // scroll text down
429 ui_button_info( "2_PXB_12", 771, 696, -1, -1, 12 ), // exit
430 ui_button_info( "2_PXB_13", 853, 691, -1, -1, 13 ), // help
432 ui_button_info( "2_PXB_14", 917, 691, -1, -1, 14 ), // games list
438 // define MULTI_PXO_NUM_TEXT 18
439 #define MULTI_PXO_NUM_TEXT 16
440 UI_XSTR Multi_pxo_text[GR_NUM_RESOLUTIONS][MULTI_PXO_NUM_TEXT] = {
442 {"Web", 1313, 20, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_RANKINGS].button},
443 {"Ranking", 1314, 6, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_RANKINGS].button},
444 {"Pilot", 1310, 68, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_PINFO].button},
445 {"Info", 1311, 72, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_PINFO].button},
446 {"Find", 1315, 119, 415, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_FIND].button},
447 {"Motd", 1316, 36, 456, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_MOTD].button},
448 {"Join", 1505, 291, 100, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN].button},
449 {"Channel", 1317, 266, 112, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN].button},
450 {"Join", 1506, 291, 134, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN_PRIV].button},
451 {"Private", 1318, 273, 146, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN_PRIV].button},
452 {"Exit", 1416, 493, 424, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_EXIT].button},
453 {"Help", 928, 535, 416, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_HELP].button},
454 {"Games", 1319, 579, 416, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_GAMES].button},
455 {"Players", 1269, 29, 102, UI_XSTR_COLOR_GREEN, -1, NULL},
456 // {"Squad", 1320, 110, 101, UI_XSTR_COLOR_GREEN, -1, NULL},
457 // {"Channels", 1321, 369, 76, UI_XSTR_COLOR_GREEN, -1, NULL},
458 {"Players", 1269, 507, 90, UI_XSTR_COLOR_GREEN, -1, NULL},
459 {"Games", 1319, 568, 90, UI_XSTR_COLOR_GREEN, -1, NULL}
462 {"Web", 1313, 32, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_RANKINGS].button},
463 {"Ranking", 1314, 9, 674, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_RANKINGS].button},
464 {"Pilot", 1310, 109, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_PINFO].button},
465 {"Info", 1311, 115, 674, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_PINFO].button},
466 {"Find", 1315, 190, 664, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_FIND].button},
467 {"Motd", 1316, 58, 729, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_MOTD].button},
468 {"Join", 1505, 488, 129, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN].button},
469 {"Channel", 1317, 461, 139, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN].button},
470 {"Join", 1506, 487, 184, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN_PRIV].button},
471 {"Private", 1318, 467, 194, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN_PRIV].button},
472 {"Exit", 1416, 789, 678, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_EXIT].button},
473 {"Help", 928, 857, 667, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_HELP].button},
474 {"Games", 1319, 917, 667, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_GAMES].button},
475 {"Players", 1269, 47, 163, UI_XSTR_COLOR_GREEN, -1, NULL},
476 // {"Squad", 1320, 176, 163, UI_XSTR_COLOR_GREEN, -1, NULL},
477 // {"Channels", 1321, 591, 86, UI_XSTR_COLOR_GREEN, -1, NULL},
478 {"Players", 1269, 852, 109, UI_XSTR_COLOR_GREEN, -1, NULL},
479 {"Games", 1319, 926, 109, UI_XSTR_COLOR_GREEN, -1, NULL}
484 char Multi_pxo_bitmap_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
488 char Multi_pxo_mask_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
493 UI_WINDOW Multi_pxo_window;
494 int Multi_pxo_bitmap = -1;
495 int Multi_pxo_palette = -1;
499 #define MULTI_PXO_ANIM_FNAME "pxologo"
501 #define MULTI_PXO_ANIM_X 0
502 #define MULTI_PXO_ANIM_Y 4
504 #define MULTI_PXO_ANIM_X 5
505 #define MULTI_PXO_ANIM_Y 10
507 anim *Multi_pxo_anim = NULL;
508 anim_instance *Multi_pxo_anim_instance = NULL;
510 // rankings last clicked time
511 #define MULTI_PXO_RANK_TIME (5.0f)
512 float Multi_pxo_ranking_last = -1.0f;
515 int Multi_pxo_must_connect = 0; // if we still need to connect
516 int Multi_pxo_connected = 0; // if we are connected
517 int Multi_pxo_must_validate = 0; // if we need to validate on the tracker
518 int Multi_pxo_must_autojoin = 1; // still need to autojoin a channel
519 int Multi_pxo_must_verify_version = 1; // only do it once per instance of freespace
522 #define MULTI_PXO_MODE_NORMAL 0 // normal mode
523 #define MULTI_PXO_MODE_PRIVATE 1 // private channel popup
524 #define MULTI_PXO_MODE_FIND 2 // find player popup
525 #define MULTI_PXO_MODE_PINFO 3 // pilot info popup
526 int Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
528 // our nick for this session
529 char Multi_pxo_nick[NAME_LENGTH+1];
531 // check for button presses
532 void multi_pxo_check_buttons();
534 // handle a button press
535 void multi_pxo_button_pressed(int n);
537 // condition function for popup_do_with_condition for connected to Parallax Online
538 // return 10 : on successful connect
539 int multi_pxo_connect_do();
541 // attempt to connect to Parallax Online, return success or fail
542 int multi_pxo_connect();
544 // run the networking functions for the PXO API
545 void multi_pxo_api_process();
547 // process a "nick" change event
548 void multi_pxo_process_nick_change(char *data);
550 // run normally (no popups)
551 void multi_pxo_do_normal();
553 // blit everything on the "normal" screen
554 void multi_pxo_blit_all();
556 // process common stuff
557 void multi_pxo_process_common();
559 // get selected player information
560 void multi_pxo_get_data(char *name);
562 // handle being kicked
563 void multi_pxo_handle_kick();
565 // handle being disconnected
566 void multi_pxo_handle_disconnect();
568 // return string2, which is the first substring of string 1 without a space
569 // it is safe to pass the same pointer for both parameters
570 void multi_pxo_strip_space(char *string1,char *string2, const int str2_len);
572 // fire up the given URL
573 void multi_pxo_url(char *url);
575 // load/set the palette
576 void multi_pxo_load_palette();
578 // unload the palette
579 void multi_pxo_unload_palette();
581 // if we're currently on a private channel
582 int multi_pxo_on_private_channel();
584 // convert string 1 into string 2, substituting underscores for spaces
585 void multi_pxo_underscore_nick(char *string1, char *string2, const int str2_len);
587 // if the command is a potential "nick" command
588 int multi_pxo_is_nick_command(char *msg);
591 // status bar stuff -----------------------------------------------
592 int Multi_pxo_status_coords[GR_NUM_RESOLUTIONS][4] = {
605 // the status text itself
606 char Multi_pxo_status_text[255];
608 // set the status text
609 void multi_pxo_set_status_text(const char *txt);
611 // blit the status text
612 void multi_pxo_blit_status_text();
615 // channel related stuff -------------------------------------------
616 #define MAX_CHANNEL_NAME_LEN 32
617 #define MAX_CHANNEL_DESCRIPT_LEN 120
619 // some convenient macros
620 #define SWITCHING_CHANNELS() (Multi_pxo_channel_switch.num_users != -1)
621 #define ON_CHANNEL() (Multi_pxo_channel_current.num_users != -1)
623 typedef struct pxo_channel {
624 pxo_channel *next,*prev; // next and previous items in the list
625 char name[MAX_CHANNEL_NAME_LEN+1]; // name
626 char desc[MAX_CHANNEL_DESCRIPT_LEN+1]; // description
627 short num_users; // # users, or -1 if not in use
628 short num_servers; // the # of servers registered on this channel
631 // last channel we were on before going to the game list screen
632 char Multi_pxo_channel_last[MAX_CHANNEL_NAME_LEN+1] = "";
633 int Multi_pxo_use_last_channel = 0;
635 // all channels which are prefixed with this are "lobby" channels
636 #define MULTI_PXO_AUTOJOIN_PREFIX "#lobby"
638 // join this channel to get put in an appropriate lobby channel
639 #define MULTI_PXO_AUTOJOIN_CHANNEL "#autoselect"
641 int Multi_pxo_chan_coords[GR_NUM_RESOLUTIONS][4] = {
654 // this is the offset from the RIGHT side of the channel box
655 #define CHAN_PLAYERS_COLUMN 0
656 #define CHAN_GAMES_COLUMN 1
657 static int Multi_pxo_chan_column_offsets[GR_NUM_RESOLUTIONS][2] = {
662 #define CHANNEL_REFRESH_TIME (75.0f)
663 float Multi_pxo_channel_last_refresh = -1.0f;
665 #define CHANNEL_SERVER_REFRESH_TIME (35.0f)
666 float Multi_pxo_channel_server_refresh = -1.0f;
668 int Multi_pxo_max_chan_display[GR_NUM_RESOLUTIONS] = {
673 UI_BUTTON Multi_pxo_channel_button;
675 // head of the list of available (displayed) channels
676 pxo_channel *Multi_pxo_channels = NULL;
677 int Multi_pxo_channel_count = 0;
679 // item we're going to start displaying at
680 pxo_channel *Multi_pxo_channel_start = NULL;
681 int Multi_pxo_channel_start_index = -1;
683 // items we've currently got selected
684 pxo_channel *Multi_pxo_channel_select = NULL;
686 // channel we're currently connected to, num_users == -1, if we're not connected
687 pxo_channel Multi_pxo_channel_current;
689 // channel we're currently trying to change to, num_users == -1, if we're not trying to change channels
690 pxo_channel Multi_pxo_channel_switch;
692 // get a list of channels on the server (clear any old list as well)
693 void multi_pxo_get_channels();
695 // clear the old channel list
696 void multi_pxo_clear_channels();
698 // parse the input string and make a list of new channels
699 void multi_pxo_make_channels(char *chan_str);
701 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
702 pxo_channel *multi_pxo_add_channel(char *name, pxo_channel **list);
704 // lookup a channel with the specified name
705 pxo_channel *multi_pxo_find_channel(char *name, pxo_channel *list);
707 // process the channel list (select, etc)
708 void multi_pxo_process_channels();
710 // display the channel list
711 void multi_pxo_blit_channels();
713 // scroll channel list up
714 void multi_pxo_scroll_channels_up();
716 // scroll channel list down
717 void multi_pxo_scroll_channels_down();
719 // attempt to join a channel
720 void multi_pxo_join_channel(pxo_channel *chan);
722 // handle any processing details if we're currently trying to join a channel
723 void multi_pxo_handle_channel_change();
725 // autojoin an appropriate channel
726 void multi_pxo_autojoin();
728 // does the string match the "autojoin" prefic
729 int multi_pxo_is_autojoin(char *name);
731 // send a request to refresh our channel server counts
732 void multi_pxo_channel_refresh_servers();
734 // refresh current channel server count
735 void multi_pxo_channel_refresh_current();
738 // player related stuff -------------------------------------------
739 #define MAX_PLAYER_NAME_LEN 32
741 typedef struct player_list {
742 player_list *next,*prev;
743 char name[MAX_PLAYER_NAME_LEN+1];
746 // channel list region
747 int Multi_pxo_player_coords[GR_NUM_RESOLUTIONS][4] = {
760 int Multi_pxo_max_player_display[GR_NUM_RESOLUTIONS] = {
764 UI_BUTTON Multi_pxo_player_button;
767 // UI_SLIDER2 Multi_pxo_player_slider;
770 int Multi_pxo_player_slider_coords[GR_NUM_RESOLUTIONS][4] = {
778 const char *Multi_pxo_player_slider_name[GR_NUM_RESOLUTIONS] = {
780 "2_slider" // GR_1024
784 // head of the list of players in this channel
785 player_list *Multi_pxo_players = NULL;
786 int Multi_pxo_player_count = 0;
788 // item we're going to start displaying at
789 player_list *Multi_pxo_player_start = NULL;
790 // int Multi_pxo_player_start_index = -1;
792 // items we've currently got selected
793 player_list *Multi_pxo_player_select = NULL;
795 // clear the old player list
796 void multi_pxo_clear_players();
798 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
799 player_list *multi_pxo_add_player(char *name);
801 // remove a player with the given name
802 void multi_pxo_del_player(char *name);
804 // try and find a player with the given name, return a pointer to his entry (or NULL)
805 player_list *multi_pxo_find_player(char *name);
807 // process the player list (select, etc)
808 void multi_pxo_process_players();
810 // display the player list
811 void multi_pxo_blit_players();
813 // scroll player list up
814 void multi_pxo_scroll_players_up();
816 // scroll player list down
817 void multi_pxo_scroll_players_down();
819 // get the absolute index of the displayed items which our currently selected one is
820 int multi_pxo_get_select_index();
826 // add a bunch of bogus players
828 for(int idx=0; idx<Dc_arg_int; idx++){
829 SDL_snprintf(name, SDL_arraysize(name), "player %d", idx);
830 multi_pxo_add_player(name);
834 // chat text stuff -----------------------------------------
835 #define MAX_CHAT_LINES 60
836 #define MAX_CHAT_LINE_LEN 256
838 int Multi_pxo_chat_title_y[GR_NUM_RESOLUTIONS] = {
847 int Multi_pxo_chat_coords[GR_NUM_RESOLUTIONS][4] = {
860 int Multi_pxo_input_coords[GR_NUM_RESOLUTIONS][4] = {
873 int Multi_pxo_max_chat_display[GR_NUM_RESOLUTIONS] = {
882 // all messages from the server are prefixed with this
883 #define MULTI_PXO_SERVER_PREFIX "*** "
885 // the "has left" message from the server
886 #define MULTI_PXO_HAS_LEFT "has left"
889 #define CHAT_MODE_NORMAL 0 // normal chat from someone
890 #define CHAT_MODE_SERVER 1 // is from the server, display appropriately
891 #define CHAT_MODE_CARRY 2 // is a carryover from a previous line
892 #define CHAT_MODE_PRIVATE 3 // is a private message
893 #define CHAT_MODE_CHANNEL_SWITCH 4 // "switching channels" message - draw in red
894 #define CHAT_MODE_MOTD 5 // message of the day from the chat server
896 typedef struct chat_line {
897 chat_line *next,*prev;
898 char text[MAX_CHAT_LINE_LEN+1];
902 // the chat linked list itself
903 chat_line *Multi_pxo_chat = NULL;
905 // the current add line
906 chat_line *Multi_pxo_chat_add = NULL;
908 // the current line to start displaying from
909 chat_line *Multi_pxo_chat_start = NULL;
910 int Multi_pxo_chat_start_index = -1;
912 // input box for text
913 UI_INPUTBOX Multi_pxo_chat_input;
917 UI_SLIDER2 Multi_pxo_chat_slider;
919 int Multi_pxo_chat_slider_coords[GR_NUM_RESOLUTIONS][4] = {
928 const char *Multi_pxo_chat_slider_name[GR_NUM_RESOLUTIONS] = {
934 // how many chat lines we have
935 int Multi_pxo_chat_count = 0;
937 // extra delay time when switching channels
938 #define MULTI_PXO_SWITCH_DELAY_TIME 2000
939 int Multi_pxo_switch_delay = -1;
941 // initialize and create the chat text linked list
942 void multi_pxo_chat_init();
944 // free up all chat list stuff
945 void multi_pxo_chat_free();
947 // clear all lines of chat text in the chat area
948 void multi_pxo_chat_clear();
950 // blit the chat text
951 void multi_pxo_chat_blit();
953 // add a line of text
954 void multi_pxo_chat_add_line(char *txt,int mode);
956 // process an incoming line of text
957 void multi_pxo_chat_process_incoming(const char *txt,int mode = CHAT_MODE_NORMAL);
959 // scroll to the very bottom of the chat area
960 void multi_pxo_goto_bottom();
962 // check whether we can scroll down or not
963 int multi_pxo_can_scroll_down();
965 static int Can_scroll_down = 0;
967 // scroll the text up
968 void multi_pxo_scroll_chat_up();
970 // scroll the text down
971 void multi_pxo_scroll_chat_down();
973 // process chat controls
974 void multi_pxo_chat_process();
976 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
977 const char *multi_pxo_chat_is_private(const char *txt);
979 // if the text came from the server
980 int multi_pxo_is_server_text(const char *txt);
982 // if the text is message of the day text
983 int multi_pxo_is_motd_text(const char *txt);
985 // if the text is the end of motd text
986 int multi_pxo_is_end_of_motd_text(const char *txt);
988 // if the text is a "has left message" from the server
989 int multi_pxo_chat_is_left_message(const char *txt);
991 // recalculate the chat start index, and adjust the slider properly
992 void multi_pxo_chat_adjust_start();
995 // motd stuff ---------------------------------------------------------
996 #define MAX_PXO_MOTD_LEN 1024
997 #define PXO_MOTD_BLINK_TIME 500
998 char Pxo_motd[1024] = "";
999 int Pxo_motd_end = 0;
1000 int Pxo_motd_read = 0;
1001 int Pxo_motd_blink_stamp = -1;
1002 int Pxo_motd_blink_on = 0;
1003 int Pxo_motd_blinked_already = 0;
1005 // initialize motd when going into this screen
1006 void multi_pxo_motd_init();
1008 // set the motd text
1009 void multi_pxo_motd_add_text(const char *text);
1012 void multi_pxo_set_end_of_motd();
1014 // display the motd dialog
1015 void multi_pxo_motd_dialog();
1017 // call to maybe blink the motd button
1018 void multi_pxo_motd_maybe_blit();
1021 // common dialog stuff ------------------------------------------------
1022 const char *Multi_pxo_com_fname[GR_NUM_RESOLUTIONS] = {
1026 const char *Multi_pxo_com_mask_fname[GR_NUM_RESOLUTIONS] = {
1032 int Multi_pxo_com_coords[GR_NUM_RESOLUTIONS][2] = {
1046 int Multi_pxo_com_input_coords[GR_NUM_RESOLUTIONS][4] = {
1059 #define MULTI_PXO_COM_NUM_BUTTONS 2
1060 #define MULTI_PXO_COM_CANCEL 0
1061 #define MULTI_PXO_COM_OK 1
1063 ui_button_info Multi_pxo_com_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_BUTTONS] = {
1066 ui_button_info("PXP_00", 573, 192, -1, -1, 0),
1067 ui_button_info("PXP_01", 573, 226, -1, -1, 1)
1069 ui_button_info("PXP_00", 494, 182, -1, -1, 0),
1070 ui_button_info("PXP_01", 525, 221, -1, -1, 1)
1074 ui_button_info("2_PXP_00", 917, 308, -1, -1, 0),
1075 ui_button_info("2_PXP_01", 917, 361, -1, -1, 1)
1080 #define MULTI_PXO_COM_NUM_TEXT 2
1081 UI_XSTR Multi_pxo_com_text[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_TEXT] = {
1083 { "&Cancel", 645, 510, 204, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_CANCEL].button },
1084 { "&Ok", 669, 548, 233, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_OK].button }
1087 { "&Cancel", 645, 847, 327, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_CANCEL].button },
1088 { "&Ok", 669, 877, 372, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_OK].button }
1093 int Multi_pxo_com_bitmap = -1;
1094 UI_WINDOW Multi_pxo_com_window;
1095 UI_INPUTBOX Multi_pxo_com_input;
1097 // text on the "top" half of the dialog display area
1098 char Multi_pxo_com_top_text[255];
1100 // text on the "middle" portion of the dialog display area
1101 char Multi_pxo_com_middle_text[255];
1103 // text on the "bottom" half of the dialog display area
1104 char Multi_pxo_com_bottom_text[255];
1106 int Multi_pxo_com_top_text_coords[GR_NUM_RESOLUTIONS][2] = {
1114 int Multi_pxo_com_middle_text_y[GR_NUM_RESOLUTIONS] = {
1118 int Multi_pxo_com_bottom_text_y[GR_NUM_RESOLUTIONS] = {
1123 // initialize the common dialog with the passed max input length
1124 void multi_pxo_com_init();
1126 // close down the common dialog
1127 void multi_pxo_com_close();
1129 // blit all text lines, top, middle, bottoms
1130 void multi_pxo_com_blit_text();
1132 // set the top text, shortening as necessary
1133 void multi_pxo_com_set_top_text(const char *txt);
1135 // set the middle text, shortening as necessary
1136 void multi_pxo_com_set_middle_text(const char *txt);
1138 // set the bottom text, shortening as necessary
1139 void multi_pxo_com_set_bottom_text(const char *txt);
1142 // private channel join stuff -----------------------------------------
1143 #define MULTI_PXO_PRIV_MAX_TEXT_LEN 30
1145 // max private channel name length
1146 char Multi_pxo_priv_chan[MULTI_PXO_PRIV_MAX_TEXT_LEN+100];
1148 // return code, set to something other than -1 if we're supposed to return
1149 int Multi_pxo_priv_return_code = -1;
1151 // initialize the popup
1152 void multi_pxo_priv_init();
1154 // close down the popup
1155 void multi_pxo_priv_close();
1157 // run the popup, 0 if still running, -1 if cancel, 1 if ok
1158 int multi_pxo_priv_popup();
1160 // process button presses
1161 void multi_pxo_priv_process_buttons();
1163 // handle a button press
1164 void multi_pxo_priv_button_pressed(int n);
1166 // process the inputbox
1167 void multi_pxo_priv_process_input();
1170 // find player stuff -----------------------------------------
1172 char Multi_pxo_find_channel[MAX_CHANNEL_NAME_LEN+1];
1174 // return code, set to something other than -1 if we're supposed to return
1175 int Multi_pxo_find_return_code = -1;
1177 // initialize the popup
1178 void multi_pxo_find_init();
1180 // close down the popup
1181 void multi_pxo_find_close();
1183 // run the popup, 0 if still running, -1 if cancel, 1 if ok
1184 int multi_pxo_find_popup();
1186 // process button presses
1187 void multi_pxo_find_process_buttons();
1189 // handle a button press
1190 void multi_pxo_find_button_pressed(int n);
1192 // process the inputbox
1193 void multi_pxo_find_process_input();
1195 // process search mode if applicable
1196 void multi_pxo_find_search_process();
1199 // player info stuff -----------------------------------------
1200 const char *Multi_pxo_pinfo_fname[GR_NUM_RESOLUTIONS] = {
1204 const char *Multi_pxo_pinfo_mask_fname[GR_NUM_RESOLUTIONS] = {
1210 #define MULTI_PXO_PINFO_NUM_BUTTONS 2
1211 #define MULTI_PXO_PINFO_MEDALS 0
1212 #define MULTI_PXO_PINFO_OK 1
1214 ui_button_info Multi_pxo_pinfo_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_BUTTONS] = {
1217 ui_button_info("PI2_00", 328, 446, 319, 433, 0),
1218 ui_button_info("PI2_01", 376, 446, 382, 433, 1),
1220 ui_button_info("PI2_00", 286, 359, -1, -1, 0),
1221 ui_button_info("PI2_01", 341, 359, -1, -1, 1)
1225 ui_button_info("2_PI2_00", 525, 714, 510, 695, 0),
1226 ui_button_info("2_PI2_01", 601, 714, 611, 695, 1),
1232 #define MULTI_PXO_PINFO_NUM_TEXT 2
1233 UI_XSTR Multi_pxo_pinfo_text[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_TEXT] = {
1235 { "Medals", 1037, 319, 433, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_MEDALS].button },
1236 { "Ok", 345, 382, 433, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_OK].button },
1239 { "Medals", 1037, 510, 695, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_MEDALS].button },
1240 { "Ok", 345, 611, 695, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_OK].button },
1245 int Multi_pxo_pinfo_bitmap = -1;
1246 UI_WINDOW Multi_pxo_pinfo_window;
1248 vmt_stats_struct Multi_pxo_pinfo;
1249 player Multi_pxo_pinfo_player;
1251 int Multi_pxo_retrieve_mode = -1;
1253 char Multi_pxo_retrieve_name[MAX_PLAYER_NAME_LEN+1];
1254 char Multi_pxo_retrieve_id[128];
1256 // stats label stuff
1257 #define MULTI_PXO_PINFO_NUM_LABELS 18
1259 int Multi_pxo_pinfo_coords[GR_NUM_RESOLUTIONS][4] = {
1271 int Multi_pxo_pinfo_val_x[GR_NUM_RESOLUTIONS] = {
1276 char *Multi_pxo_pinfo_stats_labels[MULTI_PXO_PINFO_NUM_LABELS];
1287 "Primary shots fired",
1288 "Primary shots hit",
1290 "Secondary shots fired",
1291 "Secondary shots hit",
1293 "Primary friendly hits",
1294 "Primary friendly hit %",
1295 "Secondary friendly hits",
1296 "Secondary friendly hit %"
1301 char Multi_pxo_pinfo_vals[MULTI_PXO_PINFO_NUM_LABELS][50];
1303 int Multi_pxo_pinfo_stats_spacing[MULTI_PXO_PINFO_NUM_LABELS] = {
1304 10,20,10,10,20,10,10,20,10,10,20,10,10,20,10,20,10,0
1307 // popup conditional functions, returns 10 on successful get of stats
1308 int multi_pxo_pinfo_cond();
1310 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
1311 int multi_pxo_pinfo_get(char *name);
1313 // fire up the stats view popup
1314 int multi_pxo_pinfo_popup();
1316 // build the stats labels values
1317 void multi_pxo_pinfo_build_vals();
1319 // initialize the popup
1320 void multi_pxo_pinfo_init();
1323 void multi_pxo_pinfo_close();
1325 // blit all the stats on this screen
1326 void multi_pxo_pinfo_blit();
1328 // run the medals screen
1329 void multi_pxo_run_medals();
1331 // notify stuff stuff -----------------------------------------
1332 #define MULTI_PXO_NOTIFY_TIME 4000
1333 #define MULTI_PXO_NOTIFY_Y 435
1335 char Multi_pxo_notify_text[255];
1336 int Multi_pxo_notify_stamp = -1;
1338 // add a notification string
1339 void multi_pxo_notify_add(const char *txt);
1341 // blit and process the notification string
1342 void multi_pxo_notify_blit();
1345 // help screen stuff -----------------------------------------
1347 const char *Multi_pxo_help_fname[GR_NUM_RESOLUTIONS] = {
1351 const char *Multi_pxo_help_mask_fname[GR_NUM_RESOLUTIONS] = {
1356 #define MULTI_PXO_HELP_NUM_BUTTONS 3
1357 #define MULTI_PXO_HELP_PREV 0
1358 #define MULTI_PXO_HELP_NEXT 1
1359 #define MULTI_PXO_HELP_CONTINUE 2
1361 ui_button_info Multi_pxo_help_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_BUTTONS] = {
1364 ui_button_info("PXH_00", 15, 389, -1, -1, 0),
1365 ui_button_info("PXH_01", 60, 389, -1, -1, 1),
1366 ui_button_info("PXH_02", 574, 431, 571, 413, 2),
1368 ui_button_info("PXH_00", 71, 373, -1, -1, 0),
1369 ui_button_info("PXH_01", 121, 373, -1, -1, 1),
1370 ui_button_info("PXH_02", 554, 411, -1, -1, 2)
1374 ui_button_info("2_PXH_00", 24, 622, -1, -1, 0),
1375 ui_button_info("2_PXH_01", 96, 622, -1, -1, 1),
1376 ui_button_info("2_PXH_02", 919, 689, 928, 663, 2),
1381 #define MULTI_PXO_HELP_NUM_TEXT 1
1382 UI_XSTR Multi_pxo_help_text[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_TEXT] = {
1384 {"Continue", 1069, 571, 413, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[0][MULTI_PXO_HELP_CONTINUE].button },
1387 {"Continue", 1069, 928, 663, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[1][MULTI_PXO_HELP_CONTINUE].button },
1393 #define MULTI_PXO_HELP_FILE "pxohelp.txt"
1394 #define MULTI_PXO_MAX_LINES_PP 57
1395 #define MULTI_PXO_MAX_PAGES 3
1397 int Multi_pxo_help_coords[GR_NUM_RESOLUTIONS][2] = {
1406 int Multi_pxo_chars_per_line[GR_NUM_RESOLUTIONS] = {
1411 int Multi_pxo_lines_pp[GR_NUM_RESOLUTIONS] = {
1417 typedef struct help_page {
1418 char *text[MULTI_PXO_MAX_LINES_PP];
1422 help_page Multi_pxo_help_pages[MULTI_PXO_MAX_PAGES];
1423 // int Multi_pxo_help_loaded = 0;
1425 int Multi_pxo_help_num_pages = 0;
1427 int Multi_pxo_help_bitmap = -1;
1428 UI_WINDOW Multi_pxo_help_window;
1430 // current page we're on
1431 int Multi_pxo_help_cur = 0;
1433 // load the help file up
1434 void multi_pxo_help_load();
1436 // blit the current page
1437 void multi_pxo_help_blit_page();
1439 // process button presses
1440 void multi_pxo_help_process_buttons();
1443 void multi_pxo_help_button_pressed(int n);
1446 // http banner stuff ---------------------------------------------
1447 InetGetFile *Multi_pxo_ban_get = NULL;
1450 #define PXO_BANNERS_CONFIG_FILE "pxobanners.cfg"
1452 // coords to display banners at
1453 int Pxo_ban_coords[GR_NUM_RESOLUTIONS][4] = {
1463 #define PXO_BAN_MODE_LIST_STARTUP 0 // start downloading list
1464 #define PXO_BAN_MODE_LIST 1 // downloading list
1465 #define PXO_BAN_MODE_IMAGES_STARTUP 2 // start downloading images
1466 #define PXO_BAN_MODE_IMAGES 3 // downloading images
1467 #define PXO_BAN_MODE_IMAGES_DONE 4 // done downloading everything - now maybe load an image
1468 #define PXO_BAN_MODE_IDLE 5 // done with everything - doing nothing
1469 #define PXO_BAN_MODE_CHOOSE_RANDOM 6 // choose a bitmap we've already downloaded at random
1471 // interface button for detecting clicks
1472 UI_BUTTON Multi_pxo_ban_button;
1475 typedef struct pxo_banner {
1476 char ban_file[MAX_FILENAME_LEN+1]; // base filename of the banner
1477 char ban_file_url[MULTI_OPTIONS_STRING_LEN+1]; // full url of the file to get (convenient)
1478 char ban_url[MULTI_OPTIONS_STRING_LEN+1]; // url to go to when clicked
1479 int ban_bitmap; // banner bitmap
1482 // active pxo banner
1483 pxo_banner Multi_pxo_banner;
1486 int Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
1489 void multi_pxo_ban_init();
1491 // process http download details
1492 void multi_pxo_ban_process();
1495 void multi_pxo_ban_close();
1497 // parse the banners file and maybe fill in Multi_pxo_dl_file[]
1498 void multi_pxo_ban_parse_banner_file(int choose_existing);
1500 // any bitmap or info or whatever
1501 void multi_pxo_ban_draw();
1503 // called when the URL button is clicked
1504 void multi_pxo_ban_clicked();
1507 // ----------------------------------------------------------------------------------------------------
1511 // initialize the PXO screen
1512 void multi_pxo_init(int use_last_channel)
1516 // load the background bitmap
1517 Multi_pxo_bitmap = bm_load(Multi_pxo_bitmap_fname[gr_screen.res]);
1518 if(Multi_pxo_bitmap < 0){
1519 // we failed to load the bitmap - this is very bad
1523 // load up the private channel bitmap
1524 Multi_pxo_com_bitmap = bm_load(Multi_pxo_com_fname[gr_screen.res]);
1525 SDL_assert(Multi_pxo_com_bitmap != -1);
1527 // create the interface window
1528 Multi_pxo_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1529 Multi_pxo_window.set_mask_bmap(Multi_pxo_mask_fname[gr_screen.res]);
1531 // multiplayer screen common palettes
1532 multi_pxo_load_palette();
1534 // create the interface buttons
1535 for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
1536 // create the object
1537 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);
1539 // set the sound to play when highlighted
1540 Multi_pxo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1542 // set the ani for the button
1543 Multi_pxo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_buttons[gr_screen.res][idx].filename);
1546 Multi_pxo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_buttons[gr_screen.res][idx].hotspot);
1551 for(idx=0; idx<MULTI_PXO_NUM_TEXT; idx++){
1552 Multi_pxo_window.add_XSTR(&Multi_pxo_text[gr_screen.res][idx]);
1556 if(use_last_channel && strlen(Multi_pxo_channel_last)){
1557 Multi_pxo_use_last_channel = 1;
1559 SDL_zero(Multi_pxo_channel_last);
1560 Multi_pxo_use_last_channel = 0;
1563 // make all scrolling buttons repeatable
1564 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_UP].button.repeatable(1);
1565 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_DOWN].button.repeatable(1);
1566 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_UP].button.repeatable(1);
1567 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_DOWN].button.repeatable(1);
1568 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_UP].button.repeatable(1);
1569 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_DOWN].button.repeatable(1);
1571 // set the mouseover cursor if it loaded ok
1572 if (Web_cursor_bitmap > 0) {
1573 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_RANKINGS].button.set_custom_cursor_bmap(Web_cursor_bitmap);
1576 // create the channel list select button and hide it
1577 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);
1578 Multi_pxo_channel_button.hide();
1580 // create the player list select button and hide it
1581 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);
1582 Multi_pxo_player_button.hide();
1584 // create the chat input box
1585 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);
1586 Multi_pxo_chat_input.set_focus();
1588 // create the banner button and hide it
1589 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);
1590 Multi_pxo_ban_button.hide();
1592 // create the player list slider
1593 // 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);
1596 // create the chat slider
1597 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);
1600 // set our connection status so that we do the right stuff next frame
1601 Multi_pxo_must_validate = 1;
1602 Multi_pxo_must_connect = 0;
1603 Multi_pxo_connected = 0;
1605 // channel we're currently connected to
1606 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
1607 Multi_pxo_channel_current.num_users = -1;
1609 // channel we're currently trying to change to, or NULL if nont
1610 memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
1611 Multi_pxo_channel_switch.num_users = -1;
1613 // last time clicked the url button (so we don't have repeats)
1614 Multi_pxo_ranking_last = -1.0f;
1616 // channel switching extra time delay stamp
1617 Multi_pxo_switch_delay = -1;
1619 // our nick for this session
1620 multi_pxo_underscore_nick(Player->callsign, Multi_pxo_nick, SDL_arraysize(Multi_pxo_nick));
1622 // clear the channel list
1623 multi_pxo_clear_channels();
1625 // clear the player list
1626 multi_pxo_clear_players();
1628 // initialize the chat system
1629 multi_pxo_chat_init();
1632 multi_pxo_ban_init();
1634 // load the animation up
1635 if (gr_screen.res == GR_1024) {
1636 char anim_filename[32] = "2_";
1637 SDL_strlcat(anim_filename, MULTI_PXO_ANIM_FNAME, SDL_arraysize(anim_filename));
1638 Multi_pxo_anim = anim_load(anim_filename);
1640 // if hi-res is not there, fallback to low
1641 if (Multi_pxo_anim == NULL) {
1642 Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME);
1645 Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME);
1648 // clear the status text
1649 multi_pxo_set_status_text("");
1651 // last refresh time
1652 Multi_pxo_channel_last_refresh = -1.0f;
1654 // server count last refresh time
1655 Multi_pxo_channel_server_refresh = -1.0f;
1658 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1661 multi_pxo_motd_init();
1663 // make sure we autojoin
1664 Multi_pxo_must_autojoin = 1;
1666 // clear all tracker channel related strings
1667 SDL_zero(Multi_fs_tracker_channel);
1668 SDL_zero(Multi_fs_tracker_filter);
1671 // do frame for the PXO screen
1674 pxo_channel priv_chan;
1677 if(Multi_pxo_connected) {
1678 multi_pxo_api_process();
1681 // process common stuff
1682 multi_pxo_process_common();
1684 switch(Multi_pxo_mode){
1685 // private channel join mode
1686 case MULTI_PXO_MODE_PRIVATE:
1687 switch(multi_pxo_priv_popup()){
1692 // user hit "cancel"
1694 // return to normal mode
1695 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1700 // setup some information
1701 memset(&priv_chan,0,sizeof(pxo_channel));
1702 priv_chan.num_users = 0;
1703 SDL_strlcpy(priv_chan.name, Multi_pxo_priv_chan, SDL_arraysize(priv_chan.name));
1705 // see if we know about this channel already
1706 multi_pxo_join_channel(&priv_chan);
1708 // return to normal mode
1709 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1715 case MULTI_PXO_MODE_FIND:
1716 switch(multi_pxo_find_popup()){
1721 // user hit "cancel"
1723 // return to normal mode
1724 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1729 // return to normal mode
1730 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1732 // if there is a valid channel name try and join it
1733 if(strlen(Multi_pxo_find_channel) && !SWITCHING_CHANNELS()){
1737 memset(&join,0,sizeof(pxo_channel));
1739 SDL_strlcpy(join.name, Multi_pxo_find_channel, SDL_arraysize(join.name));
1742 multi_pxo_join_channel(&join);
1748 case MULTI_PXO_MODE_PINFO:
1749 switch (multi_pxo_pinfo_popup()) {
1754 // used closed popup
1756 // return to normal mode
1757 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1762 case MULTI_PXO_MODE_NORMAL:
1763 multi_pxo_do_normal();
1768 // close the PXO screen
1769 void multi_pxo_close()
1771 // unload any bitmaps
1772 bm_unload(Multi_pxo_bitmap);
1773 bm_unload(Multi_pxo_com_bitmap);
1775 // record the last channel we were on, if any
1776 SDL_zero(Multi_fs_tracker_channel);
1777 SDL_zero(Multi_fs_tracker_filter);
1779 if( ON_CHANNEL() && strlen(Multi_pxo_channel_current.name) ){
1781 SDL_strlcpy(Multi_fs_tracker_channel, Multi_pxo_channel_current.name, SDL_arraysize(Multi_fs_tracker_channel));
1784 SDL_strlcpy(Multi_fs_tracker_filter, Multi_pxo_channel_current.name, SDL_arraysize(Multi_fs_tracker_filter));
1787 // disconnect from the server
1788 DisconnectFromChatServer();
1789 Multi_pxo_connected = 0;
1791 // unload the animation
1792 anim_release_all_instances(GS_STATE_PXO);
1793 Multi_pxo_anim_instance = NULL;
1794 if(Multi_pxo_anim != NULL){
1795 anim_free(Multi_pxo_anim);
1796 Multi_pxo_anim = NULL;
1799 // unload the palette for this screen
1800 multi_pxo_unload_palette();
1802 // destroy the UI_WINDOW
1803 Multi_pxo_window.destroy();
1805 // clear the channel list
1806 multi_pxo_clear_channels();
1808 // close the chat system
1809 multi_pxo_chat_free();
1812 multi_pxo_ban_close();
1815 static void login_failed_callback(int choice)
1820 nprintf(("Network","PXO CANCEL\n"));
1822 // flip his "pxo" bit temporarily and push him to the join game screen
1823 Multi_options_g.pxo = 0;
1824 gameseq_post_event(GS_EVENT_MULTI_JOIN_GAME);
1825 } else if (choice == 1) {
1826 nprintf(("Network","PXO CREATE\n"));
1828 // fire up the given URL
1829 multi_pxo_url(Multi_options_g.pxo_create_url);
1830 } else if (choice == 2) {
1831 nprintf(("Network","PXO VERIFY\n"));
1833 // fire up the given URL
1834 multi_pxo_url(Multi_options_g.pxo_verify_url);
1837 // go back to the main hall
1838 gameseq_post_event(GS_EVENT_MAIN_MENU);
1841 // run normally (no popups)
1842 void multi_pxo_do_normal()
1845 int k = Multi_pxo_window.process();
1847 // if the animation isn't playing, start it up
1848 if((Multi_pxo_anim_instance == NULL) && (Multi_pxo_anim != NULL)){
1849 anim_play_struct aps;
1851 // fire up the animation
1852 anim_play_init(&aps, Multi_pxo_anim, MULTI_PXO_ANIM_X, MULTI_PXO_ANIM_Y);
1853 aps.screen_id = GS_STATE_PXO;
1854 aps.framerate_independent = 1;
1856 Multi_pxo_anim_instance = anim_play(&aps);
1859 // process any keypresses
1862 gamesnd_play_iface(SND_USER_SELECT);
1863 gameseq_post_event(GS_EVENT_MAIN_MENU);
1867 // check for button presses
1868 multi_pxo_check_buttons();
1870 // if we're not in a chatroom, disable and hide the chat input box
1872 Multi_pxo_chat_input.hide();
1873 Multi_pxo_chat_input.disable();
1875 Multi_pxo_chat_input.enable();
1876 Multi_pxo_chat_input.unhide();
1880 multi_pxo_blit_all();
1885 // verify version # now (only once per Freespace instance)
1886 if(Multi_pxo_must_verify_version){
1887 switch(multi_update_gobaby()){
1888 // everything is cool. Move along
1889 case MULTI_UPDATE_CONTINUE:
1890 Multi_pxo_must_verify_version = 0;
1893 // go back to the main menu
1894 case MULTI_UPDATE_MAIN_MENU:
1895 gameseq_post_event(GS_EVENT_MAIN_MENU);
1897 // unset these so we don't do anything else PXO related
1898 Multi_pxo_must_validate = 0;
1899 Multi_pxo_must_connect = 0;
1902 // freespace will be shutting down shortly
1903 case MULTI_UPDATE_SHUTTING_DOWN:
1908 // if we need to get tracker info for ourselves, do so
1909 if(Multi_pxo_must_validate){
1910 // initialize the master tracker API for Freespace
1911 multi_fs_tracker_init();
1913 // validate the current player with the master tracker (will create the pilot on the MT if necessary)
1914 validate_code = multi_fs_tracker_validate(0);
1915 if(validate_code != 1){
1916 // show an error popup if it failed (not cancelled by the user)
1917 if (validate_code == 0) {
1918 popup_callback(login_failed_callback, PF_USE_AFFIRMATIVE_ICON | PF_WEB_CURSOR_1 | PF_WEB_CURSOR_2, 3,
1920 XSTR("&Create Acct",936),
1921 XSTR("&Verify Acct",937),
1922 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));
1926 Multi_pxo_must_connect = 0;
1927 Multi_pxo_must_validate = 0;
1929 // now we have to conenct to PXO
1931 Multi_pxo_must_connect = 1;
1932 Multi_pxo_must_validate = 0;
1936 // if we need to connect, do so now
1937 if(Multi_pxo_must_connect){
1938 // for now, just try once
1939 Multi_pxo_connected = multi_pxo_connect();
1941 // if we successfully connected, send a request for a list of channels on the server
1942 if(Multi_pxo_connected){
1943 multi_pxo_get_channels();
1946 multi_pxo_set_status_text(XSTR("Retrieving Public Channels",939));
1949 multi_pxo_set_status_text(XSTR("Failed to connect to Parallax Online",940));
1952 // no longer need to connect
1953 Multi_pxo_must_connect = 0;
1957 // blit everything on the "normal" screen
1958 void multi_pxo_blit_all()
1960 // draw the background, etc
1962 // GR_MAYBE_CLEAR_RES(Multi_pxo_bitmap);
1963 int bmap = Multi_pxo_bitmap;
1968 bm_get_info( bmap, &bmw, &bmh);
1969 if((bmw != gr_screen.max_w) || (bmh != gr_screen.max_h)){
1976 if(Multi_pxo_bitmap != -1){
1977 gr_set_bitmap(Multi_pxo_bitmap);
1980 Multi_pxo_window.draw();
1982 // display the channel list
1983 multi_pxo_blit_channels();
1985 // display the player list
1986 multi_pxo_blit_players();
1988 // blit the chat text
1989 multi_pxo_chat_blit();
1991 // blit the status text
1992 multi_pxo_blit_status_text();
1994 // blit and process the notification string
1995 multi_pxo_notify_blit();
1997 // any bitmap or info or whatever
1998 multi_pxo_ban_draw();
2000 // draw any motd stuff
2001 multi_pxo_motd_maybe_blit();
2003 // if we have a valid animation handle, play it
2004 if(Multi_pxo_anim_instance != NULL){
2005 anim_render_all(GS_STATE_PXO,flFrametime);
2009 // process common stuff
2010 void multi_pxo_process_common()
2012 // process the channel list (select, etc)
2013 multi_pxo_process_channels();
2015 // process the player list (select, etc)
2016 multi_pxo_process_players();
2018 // process chat controls
2019 multi_pxo_chat_process();
2021 // process http download details
2022 multi_pxo_ban_process();
2025 // get selected player information
2026 void multi_pxo_get_data(char *name)
2030 // handle being kicked
2031 void multi_pxo_handle_kick()
2033 // remove ourselves from the room
2034 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2035 Multi_pxo_channel_current.num_users = -1;
2038 multi_pxo_chat_clear();
2040 // clear the old player list
2041 multi_pxo_clear_players();
2043 // add a notification string
2044 multi_pxo_notify_add(XSTR("You have been kicked",941));
2047 // handle being disconnected
2048 void multi_pxo_handle_disconnect()
2050 if ( popup_active() ) {
2051 popup_change_text(XSTR("You have been disconnected from the server",942));
2053 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been disconnected from the server",942));
2054 gameseq_post_event(GS_EVENT_MAIN_MENU);
2058 // return string2, which is the first substring of string 1 without a space
2059 // it is safe to pass the same pointer for both parameters
2060 void multi_pxo_strip_space(char *string1, char *string2, const int str2_len)
2065 // copy the original
2066 SDL_strlcpy(midway, string1, SDL_arraysize(midway));
2067 tok = strtok(midway," ");
2069 SDL_strlcpy(string2, tok, str2_len);
2071 SDL_strlcpy(string2, "", str2_len);
2075 // fire up the given URL
2076 void multi_pxo_url(char *url)
2078 if ( platform_open_url(url) ) {
2079 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,XSTR("Warning\nCould not locate/launch default Internet Browser",943));
2083 // load/set the palette
2084 void multi_pxo_load_palette()
2087 //#ifndef HARDWARE_ONLY
2088 // palette_use_bm_palette(Multi_pxo_palette);
2092 // unload the palette
2093 void multi_pxo_unload_palette()
2095 // unload the palette if it exists
2096 if(Multi_pxo_palette != -1){
2097 bm_release(Multi_pxo_palette);
2098 Multi_pxo_palette = -1;
2102 // if we're currently on a private channel
2103 int multi_pxo_on_private_channel()
2105 // if we're connected to a channel with the "+" symbol on front
2106 if(ON_CHANNEL() && (Multi_pxo_channel_current.name[0] == '+')){
2110 // otherwise return falos
2114 // convert string 1 into string 2, substituting underscores for spaces
2115 void multi_pxo_underscore_nick(char *string1, char *string2, const int str2_len)
2117 char nick_temp[512];
2120 // don't do anything if we have bogus string
2121 if((string1 == NULL) || (string2 == NULL)){
2125 // copy the nickname
2126 SDL_strlcpy(nick_temp, string1, SDL_arraysize(nick_temp));
2128 // get the first token
2129 tok = strtok(nick_temp, " ");
2131 SDL_strlcpy(string2, tok, str2_len);
2133 // get the next token
2134 tok = strtok(NULL," ");
2137 SDL_strlcat(string2, "_", str2_len);
2138 SDL_strlcat(string2, tok, str2_len);
2141 tok = strtok(NULL," ");
2144 SDL_strlcpy(string2, string1, str2_len);
2148 // if the command is a potential "nick" command
2149 int multi_pxo_is_nick_command(char *msg)
2154 // get the first token in the message
2155 SDL_strlcpy(tmp, msg, SDL_arraysize(tmp));
2156 tok = strtok(tmp," ");
2158 // can't be a nick message
2162 return !SDL_strcasecmp(tok,NOX("/nick"));
2165 // check for button presses
2166 void multi_pxo_check_buttons()
2170 // go through all buttons
2171 for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
2172 if(Multi_pxo_buttons[gr_screen.res][idx].button.pressed()){
2173 multi_pxo_button_pressed(idx);
2179 // handle a button press
2180 void multi_pxo_button_pressed(int n)
2183 case MULTI_PXO_EXIT:
2184 gamesnd_play_iface(SND_USER_SELECT);
2185 gameseq_post_event(GS_EVENT_MAIN_MENU);
2188 case MULTI_PXO_CHAN_UP:
2189 multi_pxo_scroll_channels_up();
2192 case MULTI_PXO_CHAN_DOWN:
2193 multi_pxo_scroll_channels_down();
2196 case MULTI_PXO_TEXT_UP:
2197 multi_pxo_scroll_chat_up();
2200 case MULTI_PXO_TEXT_DOWN:
2201 multi_pxo_scroll_chat_down();
2204 case MULTI_PXO_PLIST_UP:
2205 multi_pxo_scroll_players_up();
2206 multi_pxo_chat_adjust_start();
2209 case MULTI_PXO_PLIST_DOWN:
2210 multi_pxo_scroll_players_down();
2211 multi_pxo_chat_adjust_start();
2214 case MULTI_PXO_JOIN:
2215 // if there are no channels to join, let the user know
2216 if((Multi_pxo_channel_count == 0) || (Multi_pxo_channels == NULL)){
2217 gamesnd_play_iface(SND_GENERAL_FAIL);
2218 multi_pxo_notify_add(XSTR("No channels!",944));
2222 // if we're not already trying to join, allow this
2223 if(!SWITCHING_CHANNELS() && (Multi_pxo_channel_select != NULL)){
2224 gamesnd_play_iface(SND_USER_SELECT);
2225 multi_pxo_join_channel(Multi_pxo_channel_select);
2227 multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2228 gamesnd_play_iface(SND_GENERAL_FAIL);
2232 case MULTI_PXO_GAMES:
2233 // move to the join game screen as normally (temporary!)
2234 gameseq_post_event( GS_EVENT_MULTI_JOIN_GAME );
2237 case MULTI_PXO_JOIN_PRIV:
2238 // if we're not already trying to join, allow this
2239 if(!SWITCHING_CHANNELS()){
2240 gamesnd_play_iface(SND_USER_SELECT);
2242 // fire up the private join popup
2243 multi_pxo_priv_popup();
2245 multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2246 gamesnd_play_iface(SND_GENERAL_FAIL);
2250 case MULTI_PXO_FIND:
2251 gamesnd_play_iface(SND_USER_SELECT);
2253 // fire up the find join popup
2254 multi_pxo_find_popup();
2257 case MULTI_PXO_HELP:
2258 gamesnd_play_iface(SND_USER_SELECT);
2259 gameseq_post_event(GS_EVENT_PXO_HELP);
2262 case MULTI_PXO_PINFO:
2265 // if we have a guy selected, try and get his info
2266 if(Multi_pxo_player_select != NULL){
2267 // if we successfully got info for this guy
2268 if(multi_pxo_pinfo_get(Multi_pxo_player_select->name)){
2270 multi_pxo_pinfo_popup();
2272 // if we didn't get stats for this guy.
2274 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);
2275 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,stats);
2278 gamesnd_play_iface(SND_GENERAL_FAIL);
2282 case MULTI_PXO_RANKINGS:
2283 // make sure he doesn't click it too many times
2284 if((Multi_pxo_ranking_last < 0.0f) || ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_ranking_last) > MULTI_PXO_RANK_TIME) ){
2285 gamesnd_play_iface(SND_USER_SELECT);
2288 multi_pxo_url(Multi_options_g.pxo_rank_url);
2290 // mark the time down
2291 Multi_pxo_ranking_last = f2fl(timer_get_fixed_seconds());
2293 gamesnd_play_iface(SND_GENERAL_FAIL);
2297 case MULTI_PXO_MOTD:
2298 // maybe fire up the pxo motd dialog
2299 multi_pxo_motd_dialog();
2305 // condition function for popup_do_with_condition for connected to Parallax Online
2306 int mpxo_failed = 0;
2307 int multi_pxo_connect_do()
2310 char id_string[255] = "";
2311 char ip_string[255] = "";
2313 // if we already tried and failed, sit around until the user presses cancel
2315 // try and connect to the server
2318 // build the tracker id string
2319 SDL_snprintf(id_string, SDL_arraysize(id_string), "%s %s", Multi_tracker_id_string, Player->callsign);
2321 // build the ip string
2322 SDL_snprintf(ip_string, SDL_arraysize(ip_string), "%s:%d", Multi_options_g.pxo_ip, PXO_CHAT_PORT);
2324 // connect to the server
2325 ret_code = ConnectToChatServer(ip_string, Multi_pxo_nick, id_string);
2327 // give some time to the pxo api.
2328 multi_pxo_api_process();
2331 // already connected, return success
2335 // failed to connect, return fail
2338 popup_change_text(XSTR("Failed to connect to Parallax Online!", 947));
2341 // connected, return success
2354 // popup loop which does an autojoin of a public channel. Returns when the autojoin process is complete
2355 int multi_pxo_autojoin_do()
2357 pxo_channel last_channel;
2359 // if we need to autojoin, do so now
2360 if(Multi_pxo_must_autojoin){
2361 Multi_pxo_must_autojoin = 0;
2363 // if we're supposed to be using a (valid) "last" channel, do so
2364 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){
2366 memset(&last_channel, 0, sizeof(pxo_channel));
2367 last_channel.num_users = 0;
2368 SDL_strlcpy(last_channel.name, Multi_pxo_channel_last, SDL_arraysize(last_channel.name));
2371 multi_pxo_join_channel(&last_channel);
2373 nprintf(("Network","PXO : using last channel\n"));
2375 multi_pxo_autojoin();
2377 nprintf(("Network","PXO : using autojoin channel\n"));
2379 multi_pxo_get_channels();
2382 // give some time to the pxo api.
2383 multi_pxo_api_process();
2384 multi_pxo_process_common();
2386 // next value is not -1 when actually switching channels, so keep processing by returning 0.
2387 if ( SWITCHING_CHANNELS() ){
2391 // couldn't switch channel for some reason. bail out with -1
2392 if ( !ON_CHANNEL() ){
2400 // attempt to connect to Parallax Online, return success or fail
2401 int multi_pxo_connect()
2404 char join_fail_str[256];
2406 // intiialize chat api
2409 // set us to "must autojoin"
2410 Multi_pxo_must_autojoin = 1;
2412 // run the connect dialog/popup
2414 if(popup_till_condition(multi_pxo_connect_do, XSTR("&Cancel", 779), XSTR("Logging into Parallax Online",949)) == 10){
2417 // if we're going to use the "last" channel
2418 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){
2419 SDL_strlcpy(join_str, XSTR("Joining last channel (",982), SDL_arraysize(join_str));
2420 SDL_strlcat(join_str, Multi_pxo_channel_last + 1, SDL_arraysize(join_str));
2421 SDL_strlcat(join_str, ")", SDL_arraysize(join_str));
2423 SDL_strlcpy(join_fail_str, XSTR("Unable to join last channel", 983), SDL_arraysize(join_fail_str));
2425 SDL_strlcpy(join_str, XSTR("Autojoining public channel", 984), SDL_arraysize(join_str));
2426 SDL_strlcpy(join_fail_str, XSTR("Unable to autojoin public channel", 985), SDL_arraysize(join_fail_str));
2429 // once connected, we should do an autojoin before allowing the guy to continue.
2430 rval = popup_till_condition( multi_pxo_autojoin_do, XSTR("&Cancel", 779), join_str );
2435 popup( PF_USE_AFFIRMATIVE_ICON, 1, XSTR("OK", 1492), join_fail_str);
2438 // otherwise disconnect just to be safe
2439 DisconnectFromChatServer();
2440 gameseq_post_event(GS_EVENT_MAIN_MENU);
2442 // did not successfully connect
2446 // run the networking functions for the PXO API
2447 void multi_pxo_api_process()
2452 pxo_channel *lookup;
2455 // give some time to psnet
2456 PSNET_TOP_LAYER_PROCESS();
2459 // give some time to the game tracker API
2462 // give some time to the user tracker API
2465 // get any incoming text
2471 // process the chat line
2472 multi_pxo_chat_process_incoming(p);
2476 // get any incoming channel list stuff
2477 p = GetChannelList();
2480 // nprintf(("Network","%s\n",p));
2481 multi_pxo_make_channels(p);
2484 // process any chat commands
2485 cmd = GetChatCommand();
2488 switch(cmd->command)
2490 case CC_USER_JOINING:
2491 // add a user, if he doesn't already exist
2492 if(multi_pxo_find_player(cmd->data) == NULL){
2493 multi_pxo_add_player(cmd->data);
2496 // increase the player count
2498 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2500 lookup->num_users++;
2505 case CC_USER_LEAVING:
2507 multi_pxo_del_player(cmd->data);
2509 // add a text message
2510 SDL_snprintf(msg_str, SDL_arraysize(msg_str), XSTR("*** %s has left", 950), cmd->data);
2511 multi_pxo_chat_process_incoming(msg_str);
2513 // decrease the player count
2515 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2517 lookup->num_users--;
2522 case CC_DISCONNECTED:
2523 multi_pxo_handle_disconnect();
2527 multi_pxo_handle_kick();
2530 case CC_NICKCHANGED:
2531 // process a nick change
2532 multi_pxo_process_nick_change(cmd->data);
2535 case CC_YOURCHANNEL:
2536 // copy the current channel info, and unset the switching status
2537 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2538 Multi_pxo_channel_switch.num_users = -1;
2540 SetNewChatChannel(NULL);
2542 SDL_strlcpy(Multi_pxo_channel_current.name, cmd->data, SDL_arraysize(Multi_pxo_channel_current.name));
2544 // if we don't already have this guy on the list, add him
2545 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2547 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2548 lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2551 // set the user count to be 0
2553 lookup->num_users = 0;
2556 // set our "last" channel to be this one
2557 SDL_strlcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name, SDL_arraysize(Multi_pxo_channel_last));
2559 // refresh current channel server count
2560 multi_pxo_channel_refresh_current();
2562 // clear the chat area
2563 // multi_pxo_chat_clear();
2570 cmd = GetChatCommand();
2573 // handle any processing details if we're currently trying to join a channel
2574 multi_pxo_handle_channel_change();
2577 // process a "nick" change event
2578 void multi_pxo_process_nick_change(char *data)
2581 player_list *lookup;
2583 // get the new string
2584 from = strtok(data," ");
2585 to = strtok(NULL,"");
2586 if((from != NULL) && (to != NULL)){
2587 lookup = multi_pxo_find_player(from);
2589 SDL_strlcpy(lookup->name, to, SDL_arraysize(lookup->name));
2591 // if this is also my nick, change it
2592 if(!SDL_strcasecmp(Multi_pxo_nick,from)){
2593 SDL_strlcpy(Multi_pxo_nick, to, SDL_arraysize(Multi_pxo_nick));
2599 // autojoin an appropriate channel
2600 void multi_pxo_autojoin()
2604 memset(&sw,0,sizeof(pxo_channel));
2606 SDL_strlcpy(sw.name, MULTI_PXO_AUTOJOIN_CHANNEL, SDL_arraysize(sw.name));
2608 // if we found a valid room, attempt to join it
2609 multi_pxo_join_channel(&sw);
2612 // does the string match the "autojoin" prefic
2613 int multi_pxo_is_autojoin(char *name)
2615 // check to see if the name is long enough
2616 if(strlen(name) < strlen(MULTI_PXO_AUTOJOIN_PREFIX)){
2620 // check to see if the first n chars match
2621 return !SDL_strncasecmp(name,MULTI_PXO_AUTOJOIN_PREFIX,strlen(MULTI_PXO_AUTOJOIN_PREFIX));
2624 // called from the game tracker API - server count update for a channel
2625 void multi_pxo_channel_count_update(char *name,int count)
2627 pxo_channel *lookup;
2629 // lookup the channel name on the normal list
2631 lookup = multi_pxo_find_channel(name,Multi_pxo_channels);
2633 lookup->num_servers = (ushort)count;
2635 nprintf(("Network","PXO : updated channel %s server count to %d\n",name,count));
2639 // status bar stuff -----------------------------------------------
2641 // set the status text
2642 void multi_pxo_set_status_text(const char *txt)
2645 SDL_strlcpy(Multi_pxo_status_text, txt, SDL_arraysize(Multi_pxo_status_text));
2647 // make sure it fits properly
2648 gr_force_fit_string(Multi_pxo_status_text, 254, Multi_pxo_status_coords[gr_screen.res][2]);
2651 // blit the status text
2652 void multi_pxo_blit_status_text()
2656 // center and draw the text
2657 if(strlen(Multi_pxo_status_text)) {
2658 gr_set_color_fast(&Color_bright);
2659 gr_get_string_size(&w, NULL, Multi_pxo_status_text);
2660 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);
2665 // channel related stuff -------------------------------------------
2667 // get a list of channels on the server
2668 void multi_pxo_get_channels()
2670 SendChatString(NOX("/list"));
2673 // clear the old channel list
2674 void multi_pxo_clear_channels()
2676 pxo_channel *moveup,*backup;
2678 // only clear a non-null list
2679 if(Multi_pxo_channels != NULL){
2681 moveup = Multi_pxo_channels;
2686 moveup = moveup->next;
2688 // free the struct itself
2691 } while(moveup != Multi_pxo_channels);
2692 Multi_pxo_channels = NULL;
2695 // head of the list of available channels
2696 Multi_pxo_channels = NULL;
2697 Multi_pxo_channel_count = 0;
2699 // item we're going to start displaying at
2700 Multi_pxo_channel_start = NULL;
2701 Multi_pxo_channel_start_index = -1;
2703 // items we've currently got selected
2704 Multi_pxo_channel_select = NULL;
2708 // parse the input string and make a list of new channels
2709 void multi_pxo_make_channels(char *chan_str)
2711 char *name_tok,*user_tok,*desc_tok;
2713 pxo_channel *lookup;
2716 nprintf(("Network","Making some channels!\n"));
2718 // clear the channel list
2719 // multi_pxo_clear_channels();
2721 // set the last get time
2722 Multi_pxo_channel_last_refresh = f2fl(timer_get_fixed_seconds());
2724 name_tok = strtok(chan_str," ");
2725 if(name_tok == NULL){
2730 // parse the user count token
2731 user_tok = strtok(NULL," ");
2733 // parse the channel description token
2734 desc_tok = strtok(NULL,"$");
2736 // something invalid in the data, return here.....
2737 if((name_tok == NULL) || (user_tok == NULL) || (desc_tok == NULL)){
2741 // get the # of users
2742 num_users = (ubyte)atoi(user_tok);
2744 // if the # of users is > 0, or its not an autojoin, place it on the display list
2745 if((num_users > 0) || !multi_pxo_is_autojoin(name_tok)){
2746 // see if it exists already, and if so, just update the user count
2747 lookup = multi_pxo_find_channel(name_tok,Multi_pxo_channels);
2750 lookup->num_users = (short)num_users;
2754 res = multi_pxo_add_channel(name_tok,&Multi_pxo_channels);
2756 //Multi_pxo_channel_count++;
2757 res->num_users = (short)num_users;
2758 SDL_strlcpy(res->desc, desc_tok, SDL_arraysize(res->desc));
2763 // get the next name token
2764 name_tok = strtok(NULL," ");
2765 } while(name_tok != NULL);
2767 // if we need to autojoin, do so now
2768 //if(Multi_pxo_must_autojoin){
2769 // Multi_pxo_must_autojoin = 0;
2771 // multi_pxo_autojoin();
2775 multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
2777 // if we haven't refreshed server counts yet, do it now
2778 if(Multi_pxo_channel_server_refresh < 0.0f){
2779 multi_pxo_channel_refresh_servers();
2782 // if we don't already have this guy on the list, add him
2784 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2786 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2787 multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2792 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2793 pxo_channel *multi_pxo_add_channel(char *name,pxo_channel **list)
2795 pxo_channel *new_channel;
2797 // try and allocate a new pxo_channel struct
2798 new_channel = (pxo_channel *)malloc(sizeof(pxo_channel));
2799 if ( new_channel == NULL ) {
2800 nprintf(("Network", "Cannot allocate space for new pxo_channel structure\n"));
2803 memset(new_channel,0,sizeof(pxo_channel));
2804 // try and allocate a string for the channel name
2805 SDL_strlcpy(new_channel->name, name, SDL_arraysize(new_channel->name));
2807 // insert it on the list
2808 if ( *list != NULL ) {
2809 new_channel->next = (*list)->next;
2810 new_channel->next->prev = new_channel;
2811 (*list)->next = new_channel;
2812 new_channel->prev = *list;
2814 *list = new_channel;
2815 (*list)->next = (*list)->prev = *list;
2818 Multi_pxo_channel_count++;
2822 // lookup a channel with the specified name
2823 pxo_channel *multi_pxo_find_channel(char *name,pxo_channel *list)
2825 pxo_channel *moveup;
2827 // look the sucker up
2833 if(!SDL_strcasecmp(name,moveup->name)){
2837 moveup = moveup->next;
2838 } while((moveup != list) && (moveup != NULL));
2843 // process the channel list (select, etc)
2844 void multi_pxo_process_channels()
2849 // if we don't have a start item, but the list is non-null
2850 if((Multi_pxo_channel_start == NULL) && (Multi_pxo_channels != NULL)){
2851 Multi_pxo_channel_start = Multi_pxo_channels;
2852 Multi_pxo_channel_start_index = 0;
2855 // if we don't have a selected item, but the list is non-null
2856 if((Multi_pxo_channel_select == NULL) && (Multi_pxo_channels != NULL)){
2857 Multi_pxo_channel_select = Multi_pxo_channels;
2860 multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2863 // if the "switch" delay timestamp is set, see if it has expired
2864 if((Multi_pxo_switch_delay != -1) && timestamp_elapsed(Multi_pxo_switch_delay)){
2865 Multi_pxo_switch_delay = -1;
2868 // see if we have a mouse click on the channel region
2869 if(Multi_pxo_channel_button.pressed()){
2870 Multi_pxo_channel_button.get_mouse_pos(NULL,&my);
2872 // index from the top
2873 item_index = my / 10;
2875 // select the item if possible
2876 if((item_index + Multi_pxo_channel_start_index) < Multi_pxo_channel_count){
2877 Multi_pxo_channel_select = Multi_pxo_channel_start;
2878 for(idx=0;idx<item_index;idx++){
2879 Multi_pxo_channel_select = Multi_pxo_channel_select->next;
2883 multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2887 // last refresh time
2888 if((Multi_pxo_channel_last_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_last_refresh) > CHANNEL_REFRESH_TIME) ){
2890 multi_pxo_set_status_text(XSTR("Refreshing Public Channel List",952));
2892 // get a list of channels on the server (clear any old list as well)
2893 multi_pxo_get_channels();
2896 Multi_pxo_channel_last_refresh = -1.0f;
2898 nprintf(("Network","Refreshing channels\n"));
2901 // if we haven't updated our server channel counts in a while, do so again
2902 // last refresh time
2903 if((Multi_pxo_channel_server_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_server_refresh) > CHANNEL_SERVER_REFRESH_TIME) ){
2904 // refresh server counts
2905 // multi_pxo_set_status_text("Refreshing Public Channel Server Counts");
2907 // do it _NOW_ I"M RIGHT HERE KILL ME WHAT ARE YOU WAITING FOR DO IT KILL ME DO IT NOW!
2908 multi_pxo_channel_refresh_servers();
2912 // send a request to refresh our channel server counts
2913 void multi_pxo_channel_refresh_servers()
2915 pxo_channel *lookup;
2916 filter_game_list_struct filter;
2918 // traverse the list of existing channels we know about and query the game tracker about them
2919 lookup = Multi_pxo_channels;
2924 if(strlen(lookup->name)){
2926 memset(&filter,0,sizeof(filter_game_list_struct));
2927 SDL_strlcpy(filter.channel, lookup->name, SDL_arraysize(filter.channel));
2930 RequestGameCountWithFilter(&filter);
2934 lookup = lookup->next;
2935 } while((lookup != NULL) && (lookup != Multi_pxo_channels));
2938 Multi_pxo_channel_server_refresh = f2fl(timer_get_fixed_seconds());
2941 // refresh current channel server count
2942 void multi_pxo_channel_refresh_current()
2944 // send a request for a server count on this channel
2945 if(strlen(Multi_pxo_channel_current.name)){
2947 filter_game_list_struct filter;
2948 memset(&filter,0,sizeof(filter_game_list_struct));
2949 SDL_strlcpy(filter.channel, Multi_pxo_channel_current.name, SDL_arraysize(filter.channel));
2952 RequestGameCountWithFilter(&filter);
2956 // display the channel list
2957 void multi_pxo_blit_channels()
2959 pxo_channel *moveup;
2960 char chan_name[255];
2961 char chan_users[15];
2962 char chan_servers[15];
2963 int user_w,server_w;
2964 int disp_count,y_start;
2966 // blit as many channels as we can
2968 y_start = Multi_pxo_chan_coords[gr_screen.res][1];
2969 moveup = Multi_pxo_channel_start;
2974 // if this is the currently selected item, highlight it
2975 if(moveup == Multi_pxo_channel_select){
2976 gr_set_color_fast(&Color_bright);
2978 // otherwise draw it normally
2980 gr_set_color_fast(&Color_normal);
2983 // get the # of users on the channel
2984 SDL_snprintf(chan_users, SDL_arraysize(chan_users), "%d", moveup->num_users);
2986 // get the width of the user count string
2987 gr_get_string_size(&user_w, NULL, chan_users);
2989 // get the # of servers on the channel
2990 SDL_snprintf(chan_servers, SDL_arraysize(chan_servers), "%d", moveup->num_servers);
2992 // get the width of the user count string
2993 gr_get_string_size(&server_w, NULL, chan_servers);
2995 // make sure the name fits
2996 SDL_assert(moveup->name);
2997 SDL_strlcpy(chan_name, moveup->name, SDL_arraysize(chan_name));
2998 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]);
3001 gr_string(Multi_pxo_chan_coords[gr_screen.res][0], y_start, chan_name + 1);
3002 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);
3003 gr_set_color_fast(&Color_bright);
3004 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);
3006 // increment the displayed count
3011 moveup = moveup->next;
3012 } while((moveup != Multi_pxo_channels) && (disp_count < Multi_pxo_max_chan_display[gr_screen.res]));
3015 // scroll channel list up
3016 void multi_pxo_scroll_channels_up()
3018 // if we're already at the head of the list, do nothing
3019 if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start == Multi_pxo_channels)){
3020 gamesnd_play_iface(SND_GENERAL_FAIL);
3024 // otherwise move up one
3025 Multi_pxo_channel_start = Multi_pxo_channel_start->prev;
3026 Multi_pxo_channel_start_index--;
3027 gamesnd_play_iface(SND_USER_SELECT);
3030 // scroll channel list down
3031 void multi_pxo_scroll_channels_down()
3033 // if we're already at the tail of the list, do nothing
3034 if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start->next == Multi_pxo_channels)){
3035 gamesnd_play_iface(SND_GENERAL_FAIL);
3039 // if we can't scroll further without going past the end of the viewable list, don't
3040 if((Multi_pxo_channel_start_index + Multi_pxo_max_chan_display[gr_screen.res]) >= Multi_pxo_channel_count){
3041 gamesnd_play_iface(SND_GENERAL_FAIL);
3045 // otherwise move down one
3046 Multi_pxo_channel_start = Multi_pxo_channel_start->next;
3047 Multi_pxo_channel_start_index++;
3048 gamesnd_play_iface(SND_USER_SELECT);
3051 // attempt to join a channel
3052 void multi_pxo_join_channel(pxo_channel *chan)
3054 char switch_msg[256];
3056 // if we're already on this channel, do nothing
3057 if(ON_CHANNEL() && !SDL_strcasecmp(chan->name,Multi_pxo_channel_current.name)){
3061 // if we're already trying to join a channel, do nothing
3062 if(SWITCHING_CHANNELS()){
3066 // try and join the channel
3067 switch(SetNewChatChannel(chan->name)){
3073 // decrement the count of our current channel
3074 pxo_channel *lookup;
3075 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
3077 lookup->num_users--;
3080 // set our current channel as none
3081 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
3082 Multi_pxo_channel_current.num_users = -1;
3084 multi_pxo_set_status_text(XSTR("Switching channels",953));
3087 memcpy(&Multi_pxo_channel_switch,chan,sizeof(pxo_channel));
3089 // clear the player list
3090 multi_pxo_clear_players();
3092 // display a line of text indicating that we're switching channels
3093 if(strlen(Multi_pxo_channel_switch.name) > 1){
3094 SDL_snprintf(switch_msg, SDL_arraysize(switch_msg), "[Switching to channel %s]", Multi_pxo_channel_switch.name + 1);
3096 SDL_snprintf(switch_msg, SDL_arraysize(switch_msg), "[Switching to channel %s]", Multi_pxo_channel_switch.name);
3098 multi_pxo_chat_process_incoming(switch_msg, CHAT_MODE_CHANNEL_SWITCH);
3106 // handle any processing details if we're currently trying to join a channel
3107 void multi_pxo_handle_channel_change()
3109 // if we're not switching channels, do nothing
3110 if(!SWITCHING_CHANNELS()){
3114 // if we are, check the status
3115 switch(SetNewChatChannel(NULL)){
3118 // unset our switching struct
3119 memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
3120 Multi_pxo_channel_switch.num_users = -1;
3123 multi_pxo_set_status_text(XSTR("No channel (error while switching)",954));
3130 // successfully changed
3132 // copy the current channel info, and unset the switching status
3133 memcpy(&Multi_pxo_channel_current,&Multi_pxo_channel_switch,sizeof(pxo_channel));
3134 Multi_pxo_channel_switch.num_users = -1;
3136 // set our "last" channel
3137 SDL_strlcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name, SDL_arraysize(Multi_pxo_channel_last));
3140 multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
3142 // if we don't already have this guy on the list, add him
3143 pxo_channel *lookup;
3144 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
3146 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
3147 lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
3150 // set the user count to be 1 (just me)
3152 lookup->num_users = 1;
3155 // clear the chat area
3156 // multi_pxo_chat_clear();
3158 // set the "switch" delay timestamp
3159 Multi_pxo_switch_delay = timestamp(MULTI_PXO_SWITCH_DELAY_TIME);
3161 // refresh current channel server count
3162 multi_pxo_channel_refresh_current();
3168 // player related stuff -------------------------------------------
3170 // clear the old player list
3171 void multi_pxo_clear_players()
3173 player_list *moveup,*backup;
3175 // if the list is null, don't free it up
3176 if(Multi_pxo_players != NULL){
3178 moveup = Multi_pxo_players;
3183 moveup = moveup->next;
3185 // free the struct itself
3188 } while(moveup != Multi_pxo_players);
3189 Multi_pxo_players = NULL;
3193 Multi_pxo_player_start = NULL;
3194 // Multi_pxo_player_start_index = -1;
3195 Multi_pxo_player_select = NULL;
3198 // Multi_pxo_player_slider.set_numberItems(0);
3200 // add a bunch of bogus players
3203 for(int idx=0;idx<30;idx++){
3204 sprintf(str,"player%d",idx);
3205 multi_pxo_add_player(str);
3210 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
3211 player_list *multi_pxo_add_player(char *name)
3213 player_list *new_player;
3215 // try and allocate a new player_list struct
3216 new_player = (player_list *)malloc(sizeof(player_list));
3217 if ( new_player == NULL ) {
3218 nprintf(("Network", "Cannot allocate space for new player_list structure\n"));
3221 // try and allocate a string for the channel name
3222 SDL_strlcpy(new_player->name, name, SDL_arraysize(new_player->name));
3224 // insert it on the list
3225 if ( Multi_pxo_players != NULL ) {
3226 new_player->next = Multi_pxo_players->next;
3227 new_player->next->prev = new_player;
3228 Multi_pxo_players->next = new_player;
3229 new_player->prev = Multi_pxo_players;
3231 Multi_pxo_players = new_player;
3232 Multi_pxo_players->next = Multi_pxo_players->prev = Multi_pxo_players;
3235 // increment the start position
3236 if(Multi_pxo_player_start != NULL){
3237 // Multi_pxo_player_start_index++;
3241 Multi_pxo_player_count++;
3243 // update the count on the slider
3244 // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3249 // remove a player with the given name
3250 void multi_pxo_del_player(char *name)
3252 player_list *lookup;
3254 // try and find this guy
3255 lookup = Multi_pxo_players;
3260 // if we found a match, delete it
3261 if(!SDL_strcasecmp(name,lookup->name)){
3262 // if this is the only item on the list, free stuff up
3263 if(lookup->next == lookup){
3264 SDL_assert(lookup == Multi_pxo_players);
3266 Multi_pxo_players = NULL;
3267 multi_pxo_clear_players();
3269 // otherwise, just delete it
3271 lookup->next->prev = lookup->prev;
3272 lookup->prev->next = lookup->next;
3274 // if this was our selected item, unselect it
3275 if((lookup == Multi_pxo_player_select) && (Multi_pxo_player_select != NULL)){
3276 Multi_pxo_player_select = Multi_pxo_player_select->next;
3279 // if this was our point to start viewing from, select another
3280 if(lookup == Multi_pxo_player_start){
3281 // if this is the head of the list, move up one
3282 if(Multi_pxo_players == lookup){
3283 Multi_pxo_player_start = Multi_pxo_player_start->next;
3284 // Multi_pxo_player_start_index = 0;
3286 // otherwise move back
3288 Multi_pxo_player_start = Multi_pxo_player_start->prev;
3289 // Multi_pxo_player_start_index++;
3293 // if this is the head of the list, move it up
3294 if(lookup == Multi_pxo_players){
3295 Multi_pxo_players = Multi_pxo_players->next;
3299 lookup->next = NULL;
3300 lookup->prev = NULL;
3305 Multi_pxo_player_count--;
3306 SDL_assert(Multi_pxo_player_count >= 0);
3308 // update the count on the slider
3309 // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3310 // Multi_pxo_player_slider.force_currentItem(Multi_pxo_player_start_index);
3317 lookup = lookup->next;
3318 } while((lookup != NULL) && (lookup != Multi_pxo_players));
3321 // try and find a player with the given name, return a pointer to his entry (or NULL)
3322 player_list *multi_pxo_find_player(char *name)
3324 player_list *lookup;
3326 // look through all players
3327 lookup = Multi_pxo_players;
3332 if(!SDL_strcasecmp(name,lookup->name)){
3336 lookup = lookup->next;
3337 } while((lookup != NULL) && (lookup != Multi_pxo_players));
3343 // process the player list (select, etc)
3344 void multi_pxo_process_players()
3347 player_list *lookup;
3349 // if we don't have a start item, but the list is non-null
3350 if((Multi_pxo_player_start == NULL) && (Multi_pxo_players != NULL)){
3351 Multi_pxo_player_start = Multi_pxo_players;
3352 // Multi_pxo_player_start_index = 0;
3354 // update the slider
3355 // Multi_pxo_player_slider.set_currentItem(Multi_pxo_player_start_index);
3358 // if we don't have a selected item, but the list is non-null
3359 if((Multi_pxo_player_select == NULL) && (Multi_pxo_players != NULL)){
3360 Multi_pxo_player_select = Multi_pxo_players;
3363 // see if we have a mouse click on the channel region
3364 if(Multi_pxo_player_button.pressed()){
3365 Multi_pxo_player_button.get_mouse_pos(NULL,&my);
3367 // index from the top
3368 item_index = my / 10;
3370 // select the item if possible
3371 lookup = Multi_pxo_player_start;
3376 if(item_index == 0){
3377 Multi_pxo_player_select = Multi_pxo_player_start;
3381 // move to the next item
3382 lookup = lookup->next;
3385 // if this item is our guy
3386 if((item_index == 0) && (lookup != Multi_pxo_players)){
3387 Multi_pxo_player_select = lookup;
3390 } while((lookup != Multi_pxo_players) && (item_index > 0));
3394 // display the player list
3395 void multi_pxo_blit_players()
3397 player_list *moveup;
3398 char player_name[255];
3399 int disp_count,y_start;
3401 // blit as many channels as we can
3403 y_start = Multi_pxo_player_coords[gr_screen.res][1];
3404 moveup = Multi_pxo_player_start;
3409 // if this is the currently selected item, highlight it
3410 if(moveup == Multi_pxo_player_select){
3411 gr_set_color_fast(&Color_bright);
3413 // otherwise draw it normally
3415 gr_set_color_fast(&Color_normal);
3418 // make sure the string fits
3419 SDL_strlcpy(player_name, moveup->name, SDL_arraysize(player_name));
3420 gr_force_fit_string(player_name, 254, Multi_pxo_player_coords[gr_screen.res][2]);
3423 gr_string(Multi_pxo_player_coords[gr_screen.res][0], y_start, player_name);
3425 // increment the displayed count
3430 moveup = moveup->next;
3431 } while((moveup != Multi_pxo_players) && (disp_count < Multi_pxo_max_player_display[gr_screen.res]));
3434 // scroll player list up
3435 void multi_pxo_scroll_players_up()
3437 // if we're already at the head of the list, do nothing
3438 if((Multi_pxo_player_start == NULL) || (Multi_pxo_player_start == Multi_pxo_players)){
3439 gamesnd_play_iface(SND_GENERAL_FAIL);
3443 // otherwise move up one
3444 Multi_pxo_player_start = Multi_pxo_player_start->prev;
3445 // Multi_pxo_player_start_index--;
3446 // SDL_assert(Multi_pxo_player_start_index >= 0);
3448 gamesnd_play_iface(SND_USER_SELECT);
3451 // scroll player list down
3452 void multi_pxo_scroll_players_down()
3454 player_list *lookup;
3457 // see if its okay to scroll down
3458 lookup = Multi_pxo_player_start;
3459 if(lookup == NULL ){
3460 gamesnd_play_iface(SND_GENERAL_FAIL);
3464 while(lookup->next != Multi_pxo_players){
3465 lookup = lookup->next;
3469 // if we can move down
3470 if(count >= Multi_pxo_max_player_display[gr_screen.res]){
3471 Multi_pxo_player_start = Multi_pxo_player_start->next;
3473 // Multi_pxo_player_start_index++;
3475 gamesnd_play_iface(SND_USER_SELECT);
3477 gamesnd_play_iface(SND_GENERAL_FAIL);
3482 // chat text stuff -----------------------------------------
3484 // initialize and create the chat text linked list
3485 void multi_pxo_chat_init()
3488 chat_line *new_line;
3491 Multi_pxo_chat = NULL;
3492 Multi_pxo_chat_add = NULL;
3493 Multi_pxo_chat_start = NULL;
3494 Multi_pxo_chat_start_index = -1;
3496 // create the lines in a non-circular doubly linked list
3497 for(idx=0;idx<MAX_CHAT_LINES;idx++){
3498 new_line = (chat_line*)malloc(sizeof(chat_line));
3500 // clear the line out
3501 SDL_assert(new_line != NULL);
3502 if(new_line == NULL){
3505 memset(new_line,0,sizeof(chat_line));
3506 new_line->prev = NULL;
3507 new_line->next = NULL;
3509 // insert it into the (empty) list
3510 if(Multi_pxo_chat == NULL){
3511 Multi_pxo_chat = new_line;
3513 // insert it onto the (non-empty) list
3515 Multi_pxo_chat->prev = new_line;
3516 new_line->next = Multi_pxo_chat;
3517 Multi_pxo_chat = new_line;
3521 // start adding chat lines at the beginning of the list
3522 Multi_pxo_chat_add = Multi_pxo_chat;
3525 // free up all chat list stuff
3526 void multi_pxo_chat_free()
3528 chat_line *moveup, *backup;
3530 // free all items up
3531 moveup = Multi_pxo_chat;
3532 while(moveup != NULL){
3534 moveup = moveup->next;
3540 Multi_pxo_chat = NULL;
3541 Multi_pxo_chat_add = NULL;
3542 Multi_pxo_chat_start = NULL;
3543 Multi_pxo_chat_start_index = -1;
3544 Multi_pxo_chat_count = 0;
3546 Multi_pxo_chat_slider.set_numberItems(0);
3550 // clear all lines of chat text in the chat area
3551 void multi_pxo_chat_clear()
3555 // clear the text in all the lines
3556 moveup = Multi_pxo_chat;
3557 while(moveup != NULL){
3558 SDL_zero(moveup->text);
3559 moveup = moveup->next;
3562 // how many chat lines we have
3563 Multi_pxo_chat_count = 0;
3565 // start adding chat lines at the beginning of the list
3566 Multi_pxo_chat_add = Multi_pxo_chat;
3569 // add a line of text
3570 void multi_pxo_chat_add_line(char *txt, int mode)
3575 SDL_assert(Multi_pxo_chat_add != NULL);
3576 SDL_strlcpy(Multi_pxo_chat_add->text, txt, SDL_arraysize(Multi_pxo_chat_add->text));
3577 Multi_pxo_chat_add->mode = mode;
3579 // if we're at the end of the list, move the front item down
3580 if(Multi_pxo_chat_add->next == NULL) {
3581 // store the new "head" of the list
3582 temp = Multi_pxo_chat->next;
3584 // move the current head to the end of the list
3585 Multi_pxo_chat_add->next = Multi_pxo_chat;
3587 Multi_pxo_chat->prev = Multi_pxo_chat_add;
3588 Multi_pxo_chat->next = NULL;
3590 // reset the head of the list
3591 Multi_pxo_chat = temp;
3593 // set the new add line
3594 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3595 SDL_zero(Multi_pxo_chat_add->text);
3596 Multi_pxo_chat_add->mode = CHAT_MODE_NORMAL;
3598 // if we're not at the end of the list, just move up by one
3600 // set the new add line
3601 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3604 // if we've reached max chat lines, don't increment
3605 if(Multi_pxo_chat_count < MAX_CHAT_LINES) {
3606 Multi_pxo_chat_count++;
3611 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
3614 // force the position, in case we arent at the bottom of the list
3617 // move to the bottom of the chat area
3620 multi_pxo_goto_bottom();
3623 // if we have more than the # of lines displayable
3624 if(Multi_pxo_chat_count >= MULTI_PXO_MAX_CHAT_DISPLAY){
3626 multi_pxo_goto_bottom();
3629 multi_pxo_goto_bottom();
3632 // process an incoming line of text
3633 void multi_pxo_chat_process_incoming(const char *txt,int mode)
3635 char msg_total[512],line[512];
3638 char *p_str[20]; // the initial line (unindented)
3639 const char *priv_ptr = NULL;
3641 // filter out "has left" channel messages, when switching channels
3642 if((SWITCHING_CHANNELS() || ((Multi_pxo_switch_delay != -1) && !timestamp_elapsed(Multi_pxo_switch_delay))) &&
3643 multi_pxo_chat_is_left_message(txt)){
3647 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3648 priv_ptr = multi_pxo_chat_is_private(txt);
3649 if(priv_ptr != NULL){
3650 SDL_strlcpy(msg_total, priv_ptr, SDL_arraysize(msg_total));
3652 SDL_strlcpy(msg_total, txt, SDL_arraysize(msg_total));
3655 // determine what mode to display this text in
3657 // if this is private chat
3658 if(priv_ptr != NULL){
3659 mode = CHAT_MODE_PRIVATE;
3663 // if this is a server message
3664 if(multi_pxo_is_server_text(txt)){
3665 mode = CHAT_MODE_SERVER;
3667 // if this is a MOTD
3668 else if(multi_pxo_is_motd_text(txt)){
3671 SDL_strlcpy(msg_total, txt + strlen(PXO_CHAT_MOTD_PREFIX), SDL_arraysize(msg_total));
3673 mode = CHAT_MODE_MOTD;
3676 multi_pxo_motd_add_text(txt);
3680 // if this is the end of motd text
3681 else if(multi_pxo_is_end_of_motd_text(txt)){
3683 multi_pxo_set_end_of_motd();
3689 // split the text up into as many lines as necessary
3690 n_lines = split_str(msg_total, Multi_pxo_chat_coords[gr_screen.res][2] - 5, n_chars, p_str, 3);
3691 SDL_assert((n_lines != -1) && (n_lines <= 20));
3692 if((n_lines < 0) || (n_lines > 20)) {
3696 // if the string fits on one line
3698 multi_pxo_chat_add_line(msg_total,mode);
3700 // don't pad with extra spaces if its from the server
3702 if(mode != CHAT_MODE_SERVER){
3703 multi_pxo_chat_add_line("",CHAT_MODE_NORMAL);
3707 // if the string was split into multiple lines
3709 // add the first line
3710 memcpy(line,p_str[0],n_chars[0]);
3711 line[n_chars[0]] = '\0';
3712 multi_pxo_chat_add_line(line,mode);
3714 // copy the rest of the lines
3715 for(idx=1; idx<n_lines; idx++){
3716 memcpy(line,p_str[idx],n_chars[idx]);
3717 line[n_chars[idx]] = '\0';
3719 // unless the current mode is server or "switching channels", make all these CHAT_MODE_CARRY
3720 if((mode != CHAT_MODE_SERVER) && (mode != CHAT_MODE_CHANNEL_SWITCH)){
3721 mode = CHAT_MODE_CARRY;
3723 multi_pxo_chat_add_line(line, mode);
3728 // blit the chat text
3729 void multi_pxo_chat_blit()
3732 int disp_count,token_width;
3738 // blit the title line
3740 if(strlen(Multi_pxo_channel_current.name) > 1){
3741 SDL_snprintf(title, SDL_arraysize(title), XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name+1); // [[ <who> on <channel> ]]
3743 SDL_snprintf(title, SDL_arraysize(title), XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name); // [[ <who> on <channel> ]]
3746 SDL_strlcpy(title, XSTR("Parallax Online - No Channel", 956), SDL_arraysize(title));
3748 gr_force_fit_string(title, 254, Multi_pxo_chat_coords[gr_screen.res][2] - 10);
3749 gr_get_string_size(&token_width,NULL,title);
3750 gr_set_color_fast(&Color_normal);
3751 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);
3753 // blit all active lines of text
3754 moveup = Multi_pxo_chat_start;
3756 y_start = Multi_pxo_chat_coords[gr_screen.res][1];
3757 while((moveup != NULL) && (moveup != Multi_pxo_chat_add) && (disp_count < (Multi_pxo_max_chat_display[gr_screen.res]))){
3758 switch(moveup->mode){
3759 // if this is text from the server, display it all "bright"
3760 case CHAT_MODE_SERVER:
3761 gr_set_color_fast(&Color_bright);
3762 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3765 // if this is motd, display it all "bright"
3766 case CHAT_MODE_MOTD:
3767 gr_set_color_fast(&Color_bright_white);
3768 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3771 // normal mode, just highlight the server
3772 case CHAT_MODE_PRIVATE:
3773 case CHAT_MODE_NORMAL:
3774 SDL_strlcpy(piece, moveup->text, SDL_arraysize(piece));
3775 tok = strtok(piece," ");
3777 // get the width of just the first "piece"
3778 gr_get_string_size(&token_width, NULL, tok);
3781 gr_set_color_fast(&Color_bright);
3782 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, tok);
3784 // draw the rest of the string normally
3785 tok = strtok(NULL,"");
3787 gr_set_color_fast(&Color_normal);
3788 gr_string(Multi_pxo_chat_coords[gr_screen.res][0] + token_width + 6, y_start, tok);
3793 // carry mode, display with no highlight
3794 case CHAT_MODE_CARRY:
3795 gr_set_color_fast(&Color_normal);
3796 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3799 // "switching channels mode", display it bright
3800 case CHAT_MODE_CHANNEL_SWITCH:
3801 gr_set_color_fast(&Color_bright);
3802 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3807 moveup = moveup->next;
3812 if ((moveup != Multi_pxo_chat_add) && (moveup != NULL)) {
3813 Can_scroll_down = 1;
3815 Can_scroll_down = 0;
3819 // scroll to the very bottom of the chat area
3820 void multi_pxo_goto_bottom()
3825 if (Multi_pxo_chat == NULL) {
3829 // if we have less than the displayable amount of lines, do nothing
3830 if(Multi_pxo_chat_count <= Multi_pxo_max_chat_display[gr_screen.res]){
3831 Multi_pxo_chat_start = Multi_pxo_chat;
3834 // nothing to do for the slider
3835 Multi_pxo_chat_slider.set_numberItems(0);
3840 if (!Can_scroll_down)
3842 // otherwise move back the right # of items
3843 backup = Multi_pxo_chat_add;
3844 for(idx=0; idx<Multi_pxo_max_chat_display[gr_screen.res]; idx++){
3845 SDL_assert(backup->prev != NULL);
3846 backup = backup->prev;
3849 Multi_pxo_chat_start = backup;
3851 // fixup the start index
3852 multi_pxo_chat_adjust_start();
3856 // scroll the text up
3857 void multi_pxo_scroll_chat_up()
3859 // if we're already at the top of the list, don't do anything
3860 if ((Multi_pxo_chat_start == NULL) || (Multi_pxo_chat_start == Multi_pxo_chat)) {
3861 gamesnd_play_iface(SND_GENERAL_FAIL);
3865 // otherwise move up one
3866 Multi_pxo_chat_start = Multi_pxo_chat_start->prev;
3868 multi_pxo_chat_adjust_start();
3870 gamesnd_play_iface(SND_USER_SELECT);
3873 // returns 1 if we can scroll down, 0 otherwise
3874 int multi_pxo_can_scroll_down()
3879 // see if its okay to scroll down
3880 lookup = Multi_pxo_chat_start;
3881 if (lookup == NULL) {
3882 // gamesnd_play_iface(SND_GENERAL_FAIL);
3886 while (lookup != Multi_pxo_chat_add) {
3887 lookup = lookup->next;
3891 // check if we can move down, return accordingly
3892 if (count > Multi_pxo_max_chat_display[gr_screen.res]) {
3899 // scroll the text down
3900 void multi_pxo_scroll_chat_down()
3902 // if we can move down
3903 if (multi_pxo_can_scroll_down()) {
3904 Multi_pxo_chat_start = Multi_pxo_chat_start->next;
3905 multi_pxo_chat_adjust_start();
3906 gamesnd_play_iface(SND_USER_SELECT);
3908 gamesnd_play_iface(SND_GENERAL_FAIL);
3912 // process chat controls
3913 void multi_pxo_chat_process()
3915 char *remainder = NULL;
3916 const char *result = NULL;
3918 int msg_pixel_width;
3920 // if the chat line is getting too long, fire off the message, putting the last
3921 // word on the next input line.
3923 Multi_pxo_chat_input.get_text(msg);
3925 // determine if the width of the string in pixels is > than the inputbox width -- if so,
3926 // then send the message
3927 gr_get_string_size(&msg_pixel_width, NULL, msg);
3928 // if ( msg_pixel_width >= (Chatbox_inputbox_w - Player->short_callsign_width) ) {
3929 if ( msg_pixel_width >= (Multi_pxo_input_coords[gr_screen.res][2])) {
3930 remainder = strrchr(msg, ' ');
3936 // if we're connected to a channel, send the chat to the server
3938 result = SendChatString(msg,1);
3940 multi_pxo_chat_process_incoming(result);
3943 // display any remainder of text on the next line
3944 Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3946 Multi_pxo_chat_input.set_text("");
3948 } else if((Multi_pxo_chat_input.pressed() && (strlen(msg) > 0)) || (strlen(msg) >= MAX_CHAT_LINE_LEN)) {
3949 // tack on the null terminator in the boundary case
3950 int x = strlen(msg);
3951 if(x >= MAX_CHAT_LINE_LEN){
3952 msg[MAX_CHAT_LINE_LEN-1] = '\0';
3955 // ignore "/nick" commands
3956 if(multi_pxo_is_nick_command(msg)){
3957 Multi_pxo_chat_input.set_text("");
3961 // send the chat to the server
3962 // if we're connected to a channel, send the chat to the server
3964 result = SendChatString(msg,1);
3966 multi_pxo_chat_process_incoming(result);
3969 // display any remainder of text on the next line
3970 Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3972 Multi_pxo_chat_input.set_text("");
3977 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3980 // NOTE : DO NOT LOCALIZE THESE STRINGS!!!! THEY ARE CONSTANTS WHICH ARE CHECKED AGAINST
3981 // PXO CHAT SERVER DATA. THEY CANNOT CHANGE!!!
3982 #define PMSG_FROM "private message from "
3983 #define PMSG_TO "private message to "
3984 const char *multi_pxo_chat_is_private(const char *txt)
3987 if( strlen(txt) > strlen( PMSG_FROM ) ){
3988 // otherwise do a comparison
3989 if(!SDL_strncasecmp( txt, PMSG_FROM, strlen(PMSG_FROM) )){
3990 return &txt[strlen( PMSG_FROM )];
3995 if(strlen(txt) > strlen( PMSG_TO )){
3996 // otherwise do a comparison
3997 if(!SDL_strncasecmp(txt,PMSG_TO,strlen(PMSG_TO))){
3998 return &txt[strlen(PMSG_TO)];
4006 // if the text came from the server
4007 int multi_pxo_is_server_text(const char *txt)
4009 // if the message is prefaced by a ***
4010 if((strlen(txt) >= strlen(MULTI_PXO_SERVER_PREFIX)) && !strncmp(txt, MULTI_PXO_SERVER_PREFIX, strlen(MULTI_PXO_SERVER_PREFIX))){
4017 // if the text is message of the day text
4018 int multi_pxo_is_motd_text(const char *txt)
4020 // if we're not on a channel, and this is not a channel switching message assume its coming from a server
4021 if((strlen(txt) >= strlen(PXO_CHAT_MOTD_PREFIX)) && !strncmp(txt, PXO_CHAT_MOTD_PREFIX, strlen(PXO_CHAT_MOTD_PREFIX))){
4028 // if the text is the end of motd text
4029 int multi_pxo_is_end_of_motd_text(const char *txt)
4031 // if we're not on a channel, and this is not a channel switching message assume its coming from a server
4032 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))){
4039 // if the text is a "has left message" from the server
4040 int multi_pxo_chat_is_left_message(const char *txt)
4042 char last_portion[100];
4044 // if the text is not server text
4045 if(!multi_pxo_is_server_text(txt)){
4049 // check to see if the last portion is the correct wording
4050 SDL_zero(last_portion);
4051 if((strlen(txt) > strlen(MULTI_PXO_HAS_LEFT)) && !strcmp(&txt[strlen(txt) - strlen(MULTI_PXO_HAS_LEFT)], MULTI_PXO_HAS_LEFT)){
4055 // check the end of the line
4059 // recalculate the chat start index, and adjust the slider properly
4060 void multi_pxo_chat_adjust_start()
4064 // if we have no chat
4065 if (Multi_pxo_chat == NULL) {
4066 Multi_pxo_chat_start_index = -1;
4071 Multi_pxo_chat_start_index = 0;
4072 moveup = Multi_pxo_chat;
4073 while((moveup != Multi_pxo_chat_start) && (moveup != NULL)){
4074 Multi_pxo_chat_start_index++;
4075 moveup = moveup->next;
4078 // set the slider index
4079 Multi_pxo_chat_slider.force_currentItem(Multi_pxo_chat_start_index);
4083 // motd stuff ---------------------------------------------------------
4085 // initialize motd when going into this screen
4086 void multi_pxo_motd_init()
4088 // zero the motd string
4089 SDL_strlcpy(Pxo_motd, "", SDL_arraysize(Pxo_motd));
4091 // haven't gotten it yet
4094 // haven't read it yet either
4098 // set the motd text
4099 void multi_pxo_motd_add_text(const char *text)
4101 int cur_len = strlen(Pxo_motd);
4109 // make sure its motd text
4110 SDL_assert(multi_pxo_is_motd_text(text));
4111 if(!multi_pxo_is_motd_text(text)){
4115 // if its a 0 line motd
4116 if(strlen(text) <= strlen(PXO_CHAT_MOTD_PREFIX)){
4120 // add text to the motd
4121 new_len = strlen(text + strlen(PXO_CHAT_MOTD_PREFIX)) - 1;
4122 if((cur_len + new_len + 1) < MAX_PXO_MOTD_LEN){
4123 SDL_strlcat(Pxo_motd, text + strlen(PXO_CHAT_MOTD_PREFIX) + 1, SDL_arraysize(Pxo_motd));
4124 SDL_strlcat(Pxo_motd, "\n", SDL_arraysize(Pxo_motd));
4125 mprintf(("MOTD ADD : %s\n", Pxo_motd));
4130 void multi_pxo_set_end_of_motd()
4135 mprintf(("MOTD ALL : %s\n", Pxo_motd));
4139 // do we have an old MOTD file laying around? If so, read it in and see if its the same
4143 // checksum the current motd
4144 new_chksum = cf_add_chksum_long(0, Pxo_motd, strlen(Pxo_motd));
4146 // checksum the old motd if its lying around
4147 CFILE *in = cfopen("oldmotd.txt", "rb");
4149 // read the old checksum
4150 old_chksum = cfread_uint(in);
4153 // same checksum? no blink
4154 if(new_chksum == old_chksum){
4159 // write out the motd for next time
4160 if(strlen(Pxo_motd)){
4161 CFILE *out = cfopen("oldmotd.txt", "wb", CFILE_NORMAL, CF_TYPE_DATA);
4163 // write all the text
4164 cfwrite_uint(new_chksum, out);
4166 // close the outfile
4171 // set the blink stamp
4172 Pxo_motd_blink_stamp = -1;
4174 Pxo_motd_blink_on = 0;
4175 if(!Pxo_motd_blinked_already){
4176 Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4177 Pxo_motd_blink_on = 1;
4181 Pxo_motd_blinked_already = 1;
4184 // display the motd dialog
4185 void multi_pxo_motd_dialog()
4187 // mark the motd as read
4190 // simple popup, with a slider
4191 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, Pxo_motd);
4194 // call to maybe blink the motd button
4195 void multi_pxo_motd_maybe_blit()
4198 // if we got the end of the motd, and he hasn't read it yet
4199 if(Pxo_motd_end && !Pxo_motd_read && (Pxo_motd_blink_stamp != -1)){
4200 // if the timestamp elapsed, flip the blink flag
4201 if(timestamp_elapsed(Pxo_motd_blink_stamp)){
4202 Pxo_motd_blink_on = !Pxo_motd_blink_on;
4203 Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4207 if(Pxo_motd_blink_on){
4208 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_MOTD].button.draw_forced(2);
4215 // common dialog stuff ------------------------------------------------
4217 int Multi_pxo_searching = 0;
4219 // initialize the common dialog with the passed max input length
4220 void multi_pxo_com_init(int input_len)
4224 // create the interface window
4225 Multi_pxo_com_window.create(0, 0, gr_screen.max_w,gr_screen.max_h, 0);
4226 Multi_pxo_com_window.set_mask_bmap(Multi_pxo_com_mask_fname[gr_screen.res]);
4228 // create the interface buttons
4229 for(idx=0; idx<MULTI_PXO_COM_NUM_BUTTONS; idx++){
4230 // create the object
4231 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);
4233 // set the sound to play when highlighted
4234 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4236 // set the ani for the button
4237 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_com_buttons[gr_screen.res][idx].filename);
4240 Multi_pxo_com_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_com_buttons[gr_screen.res][idx].hotspot);
4245 for(idx=0; idx<MULTI_PXO_COM_NUM_TEXT; idx++){
4246 Multi_pxo_com_window.add_XSTR(&Multi_pxo_com_text[gr_screen.res][idx]);
4250 // create the input box
4251 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);
4252 Multi_pxo_com_input.set_focus();
4254 // clear all text lines
4255 SDL_zero(Multi_pxo_com_bottom_text);
4256 SDL_zero(Multi_pxo_com_middle_text);
4257 SDL_zero(Multi_pxo_com_top_text);
4260 // close down the common dialog
4261 void multi_pxo_com_close()
4263 // destroy the UI_WINDOW
4264 Multi_pxo_com_window.destroy();
4267 // blit all text lines, top, middle, bottoms
4268 void multi_pxo_com_blit_text()
4270 // blit top, middle and bottom text if possible
4271 if(strlen(Multi_pxo_com_top_text) > 0){
4272 gr_set_color_fast(&Color_bright);
4273 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);
4275 if(strlen(Multi_pxo_com_middle_text) > 0){
4276 gr_set_color_fast(&Color_bright);
4277 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);
4279 if(strlen(Multi_pxo_com_bottom_text) > 0){
4280 gr_set_color_fast(&Color_bright);
4281 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);
4285 // set the top text, shortening as necessary
4286 void multi_pxo_com_set_top_text(const char *txt)
4288 if((txt != NULL) && strlen(txt)){
4289 SDL_strlcpy(Multi_pxo_com_top_text, txt, SDL_arraysize(Multi_pxo_com_top_text));
4290 gr_force_fit_string(Multi_pxo_com_top_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4294 // set the middle text, shortening as necessary
4295 void multi_pxo_com_set_middle_text(const char *txt)
4297 if((txt != NULL) && strlen(txt)){
4298 SDL_strlcpy(Multi_pxo_com_middle_text, txt, SDL_arraysize(Multi_pxo_com_middle_text));
4299 gr_force_fit_string(Multi_pxo_com_middle_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4303 // set the bottom text, shortening as necessary
4304 void multi_pxo_com_set_bottom_text(const char *txt)
4306 if((txt != NULL) && strlen(txt)){
4307 SDL_strlcpy(Multi_pxo_com_bottom_text, txt, SDL_arraysize(Multi_pxo_com_bottom_text));
4308 gr_force_fit_string(Multi_pxo_com_bottom_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4313 // private channel join stuff -----------------------------------------
4315 // initialize the popup
4316 void multi_pxo_priv_init()
4318 SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE);
4320 // initialize the common dialog with the passed max input length
4321 multi_pxo_com_init(MULTI_PXO_PRIV_MAX_TEXT_LEN);
4323 // initialize the return code
4324 Multi_pxo_priv_return_code = -1;
4326 // mark us as running
4327 Multi_pxo_mode = MULTI_PXO_MODE_PRIVATE;
4330 multi_pxo_com_set_middle_text(XSTR("Type the name of the channel to join/create",961));
4333 // close down the popup
4334 void multi_pxo_priv_close()
4336 // close down the common dialog
4337 multi_pxo_com_close();
4339 // mark us as not running any more
4340 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4343 // run the popup, 0 if still running, -1 if cancel, 1 if ok
4344 int multi_pxo_priv_popup()
4348 // if we're not already running, initialize stuff
4349 if(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE){
4351 multi_pxo_priv_init();
4353 // return "still running"
4357 k = Multi_pxo_com_window.process();
4359 // process keypresses
4361 // like hitting the cancel button
4363 Multi_pxo_priv_return_code = 0;
4367 // process button presses
4368 multi_pxo_priv_process_buttons();
4370 // process the inputbox
4371 multi_pxo_priv_process_input();
4373 // blit the background
4374 multi_pxo_blit_all();
4378 gr_set_bitmap(Multi_pxo_com_bitmap);
4379 gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4380 Multi_pxo_com_window.draw();
4382 // blit all text lines, top, middle, bottoms
4383 multi_pxo_com_blit_text();
4387 // check the return code
4388 switch(Multi_pxo_priv_return_code){
4389 // still in progress
4395 multi_pxo_priv_close();
4400 multi_pxo_priv_close();
4407 // process button presses
4408 void multi_pxo_priv_process_buttons()
4412 // check all buttons
4413 for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4414 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4415 multi_pxo_priv_button_pressed(idx);
4421 // handle a button press
4422 void multi_pxo_priv_button_pressed(int n)
4424 char priv_chan_name[128];
4427 case MULTI_PXO_COM_CANCEL:
4428 Multi_pxo_priv_return_code = 0;
4431 case MULTI_PXO_COM_OK:
4432 Multi_pxo_com_input.get_text(priv_chan_name);
4433 multi_pxo_strip_space(priv_chan_name, priv_chan_name, SDL_arraysize(priv_chan_name));
4435 // if its a 0 length string, interpret as a cancel
4436 if(strlen(priv_chan_name) <= 0){
4437 Multi_pxo_priv_return_code = 0;
4441 Multi_pxo_priv_return_code = 1;
4446 // process the inputbox
4447 void multi_pxo_priv_process_input()
4449 char priv_chan_name[128];
4451 // see if the user has pressed enter
4452 if(Multi_pxo_com_input.pressed()){
4453 Multi_pxo_com_input.get_text(priv_chan_name);
4454 multi_pxo_strip_space(priv_chan_name, priv_chan_name, SDL_arraysize(priv_chan_name));
4456 // if its a 0 length string, interpret as a cancel
4457 if(strlen(priv_chan_name) <= 0){
4458 Multi_pxo_priv_return_code = 0;
4462 // otherwise interpret as "accept"
4463 Multi_pxo_priv_return_code = 1;
4465 // add in the "+" which indicates a private room
4466 SDL_strlcpy(Multi_pxo_priv_chan, "+", SDL_arraysize(Multi_pxo_priv_chan));
4467 SDL_strlcat(Multi_pxo_priv_chan, priv_chan_name, SDL_arraysize(Multi_pxo_priv_chan));
4471 // find player stuff -----------------------------------------
4473 char name_lookup[255];
4475 // initialize the popup
4476 void multi_pxo_find_init()
4478 SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_FIND);
4480 // initialize the common dialog with the passed max input length
4481 multi_pxo_com_init(MAX_PLAYER_NAME_LEN);
4483 // return code, set to something other than -1 if we're supposed to return
4484 Multi_pxo_find_return_code = -1;
4486 // mark us as running
4487 Multi_pxo_mode = MULTI_PXO_MODE_FIND;
4489 // not searching yet
4490 Multi_pxo_searching = 0;
4493 multi_pxo_com_set_top_text(XSTR("Enter user to be found",962));
4496 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4499 SDL_strlcpy(name_lookup, "", SDL_arraysize(name_lookup));
4502 // close down the popup
4503 void multi_pxo_find_close()
4505 // close down the common dialog
4506 multi_pxo_com_close();
4508 // mark us as not running any more
4509 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4512 // run the popup, 0 if still running, -1 if cancel, 1 if ok
4513 int multi_pxo_find_popup()
4517 // if we're not already running, initialize stuff
4518 if(Multi_pxo_mode != MULTI_PXO_MODE_FIND){
4520 multi_pxo_find_init();
4522 // return "still running"
4526 k = Multi_pxo_com_window.process();
4528 // process keypresses
4530 // like hitting the cancel button
4532 Multi_pxo_find_return_code = 0;
4536 // process button presses
4537 multi_pxo_find_process_buttons();
4539 // process the inputbox
4540 multi_pxo_find_process_input();
4542 // process search mode if applicable
4543 multi_pxo_find_search_process();
4545 // blit the background
4546 multi_pxo_blit_all();
4550 gr_set_bitmap(Multi_pxo_com_bitmap);
4551 gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4552 Multi_pxo_com_window.draw();
4554 // blit any text lines
4555 multi_pxo_com_blit_text();
4559 // check the return code
4560 switch(Multi_pxo_find_return_code){
4561 // still in progress
4567 // close the popup down
4568 multi_pxo_find_close();
4573 // close the popup down
4574 multi_pxo_find_close();
4576 // if we have a channel, join it now if possible
4577 if(strlen(Multi_pxo_find_channel) > 0){
4578 pxo_channel *lookup;
4579 lookup = multi_pxo_find_channel(Multi_pxo_find_channel,Multi_pxo_channels);
4581 // if we couldn't find it, don't join
4583 multi_pxo_join_channel(lookup);
4592 // process button presses
4593 void multi_pxo_find_process_buttons()
4597 // check all buttons
4598 for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4599 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4600 multi_pxo_find_button_pressed(idx);
4606 // handle a button press
4607 void multi_pxo_find_button_pressed(int n)
4610 case MULTI_PXO_COM_CANCEL:
4611 Multi_pxo_find_return_code = 0;
4614 case MULTI_PXO_COM_OK:
4615 Multi_pxo_find_return_code = 1;
4620 // process the inputbox
4621 void multi_pxo_find_process_input()
4623 // see if the user has pressed enter
4624 if(Multi_pxo_com_input.pressed()){
4625 // if we're not already in search mode
4626 if(!Multi_pxo_searching){
4628 SDL_zero(Multi_pxo_com_middle_text);
4629 SDL_zero(Multi_pxo_com_bottom_text);
4631 Multi_pxo_com_input.get_text(name_lookup);
4632 multi_pxo_strip_space(name_lookup, name_lookup, SDL_arraysize(name_lookup));
4634 // never search with a zero length string
4635 if(strlen(name_lookup) > 0){
4636 char search_text[512];
4638 // put us in search mode
4639 Multi_pxo_searching = 1;
4642 GetChannelByUser(name_lookup);
4645 SDL_snprintf(search_text, SDL_arraysize(search_text), XSTR("Searching for %s", 963), name_lookup);
4646 multi_pxo_com_set_top_text(search_text);
4650 SDL_zero(Multi_pxo_com_top_text);
4656 // process search mode if applicable
4657 void multi_pxo_find_search_process()
4661 // if we're not searching for anything, return
4662 if(!Multi_pxo_searching){
4666 // otherwise check to see if we've found him
4667 channel = GetChannelByUser(NULL);
4669 // if we've got a result, let the user know
4671 // if he couldn't be found
4672 if(channel == (char *)-1){
4673 multi_pxo_com_set_middle_text(XSTR("User not found",964));
4674 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4676 if(channel[0] == '*'){
4677 multi_pxo_com_set_middle_text(XSTR("Player is logged in but is not on a channel",965));
4678 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4682 // if this guy is on a public channel, display which one
4683 if(channel[0] == '#'){
4684 SDL_snprintf(p_text, SDL_arraysize(p_text), XSTR("Found %s on :", 966), name_lookup);
4686 // display the results
4687 multi_pxo_com_set_middle_text(p_text);
4688 multi_pxo_com_set_bottom_text(channel+1);
4690 // mark down the channel name so we know where to find him
4691 SDL_strlcpy(Multi_pxo_find_channel, channel, SDL_arraysize(Multi_pxo_find_channel));
4692 // strip out trailing whitespace
4693 if(Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] == ' '){
4694 Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] = '\0';
4697 // if this is a private channel
4698 else if(channel[0] == '+'){
4699 SDL_snprintf(p_text, SDL_arraysize(p_text), XSTR("Found %s on a private channel", 967), name_lookup);
4700 multi_pxo_com_set_middle_text(p_text);
4702 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4707 // unset search mode
4708 Multi_pxo_searching = 0;
4710 // clear the inputbox
4711 Multi_pxo_com_input.set_text("");
4716 // player info stuff -----------------------------------------
4718 // popup conditional functions, returns 10 on successful get of stats
4719 int multi_pxo_pinfo_cond()
4722 char temp_string[255];
4725 // process common stuff
4726 multi_pxo_process_common();
4728 // run the networking functions for the PXO API
4729 multi_pxo_api_process();
4731 // process depending on what mode we're in
4732 switch(Multi_pxo_retrieve_mode){
4733 // getting his player tracker id
4735 // if the thing is non-null, do something
4736 ret_string = GetTrackerIdByUser(Multi_pxo_retrieve_name);
4737 if(ret_string != NULL){
4738 // user not-online/not found
4739 if(ret_string == (char *)-1){
4743 // user not a tracker pilot
4744 if(!SDL_strcasecmp(ret_string,"-1")){
4748 // otherwise parse into his id and callsign
4749 SDL_strlcpy(temp_string, ret_string, SDL_arraysize(temp_string));
4750 tok = strtok(temp_string," ");
4754 SDL_strlcpy(Multi_pxo_retrieve_id, tok, SDL_arraysize(Multi_pxo_retrieve_id));
4757 tok = strtok(NULL,"");
4759 SDL_strlcpy(Multi_pxo_retrieve_name, tok, SDL_arraysize(Multi_pxo_retrieve_name));
4766 // failure of some kind or another
4771 Multi_pxo_retrieve_mode = 1;
4776 // initial call to get his stats
4778 // change the popup text
4779 popup_change_text(XSTR("Getting player stats",968));
4782 memset(&Multi_pxo_pinfo, 0, sizeof(Multi_pxo_pinfo));
4783 SDL_strlcpy(Multi_pxo_pinfo.pilot_name, Multi_pxo_retrieve_name, SDL_arraysize(Multi_pxo_pinfo.pilot_name));
4784 SDL_strlcpy(Multi_pxo_pinfo.tracker_id, Multi_pxo_retrieve_id, SDL_arraysize(Multi_pxo_pinfo.tracker_id));
4786 // make the initial call to the API
4787 GetFSPilotData((vmt_stats_struct*)0xffffffff,NULL,NULL,0);
4788 if(GetFSPilotData(&Multi_pxo_pinfo,Multi_pxo_retrieve_name,Multi_pxo_retrieve_id,0) != 0){
4791 // if the call went through, set the mode to 2
4793 Multi_pxo_retrieve_mode = 2;
4797 // busy retrieving his stats
4799 switch(GetFSPilotData(NULL,NULL,NULL,0)){
4800 // timeout, fail, cancel
4818 // return not done yet
4822 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
4823 int multi_pxo_pinfo_get(char *name)
4826 Multi_pxo_retrieve_mode = 0;
4827 SDL_strlcpy(Multi_pxo_retrieve_name, name, SDL_arraysize(Multi_pxo_retrieve_name));
4828 switch(popup_till_condition(multi_pxo_pinfo_cond,XSTR("&Cancel", 779),XSTR("Retrieving player tracker id",969))){
4833 // failed to get his tracker id
4837 // failed to get his stats
4842 // we didn't get the stats
4846 // build the stats labels values
4847 void multi_pxo_pinfo_build_vals()
4849 vmt_stats_struct *fs = &Multi_pxo_pinfo;
4851 SDL_zero(Multi_pxo_pinfo_vals);
4854 SDL_strlcpy(Multi_pxo_pinfo_vals[0], fs->pilot_name, SDL_arraysize(Multi_pxo_pinfo_vals[0]));
4855 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]));
4858 multi_sg_rank_build_name(Ranks[fs->rank].name, Multi_pxo_pinfo_vals[1], SDL_arraysize(Multi_pxo_pinfo_vals[1]));
4859 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]));
4862 SDL_snprintf(Multi_pxo_pinfo_vals[2], SDL_arraysize(Multi_pxo_pinfo_vals[2]), "%d", fs->kill_count);
4865 SDL_snprintf(Multi_pxo_pinfo_vals[3], SDL_arraysize(Multi_pxo_pinfo_vals[3]), "%d", fs->assists);
4868 SDL_snprintf(Multi_pxo_pinfo_vals[4], SDL_arraysize(Multi_pxo_pinfo_vals[4]), "%d", fs->kill_count - fs->kill_count_ok);
4871 SDL_snprintf(Multi_pxo_pinfo_vals[5], SDL_arraysize(Multi_pxo_pinfo_vals[5]), "%d", (int)fs->missions_flown);
4874 game_format_time(fl2f((float)fs->flight_time), Multi_pxo_pinfo_vals[6], SDL_arraysize(Multi_pxo_pinfo_vals[6]));
4877 if(fs->last_flown == 0){
4878 SDL_strlcpy(Multi_pxo_pinfo_vals[7], XSTR("No missions flown", 970), SDL_arraysize(Multi_pxo_pinfo_vals[7]));
4880 tm *tmr = gmtime((time_t*)&fs->last_flown);
4882 strftime(Multi_pxo_pinfo_vals[7],30,"%m/%d/%y %H:%M",tmr);
4884 SDL_strlcpy(Multi_pxo_pinfo_vals[7], "", SDL_arraysize(Multi_pxo_pinfo_vals[7]));
4888 // primary shots fired
4889 SDL_snprintf(Multi_pxo_pinfo_vals[8], SDL_arraysize(Multi_pxo_pinfo_vals[8]), "%d", (int)fs->p_shots_fired);
4891 // primary shots hit
4892 SDL_snprintf(Multi_pxo_pinfo_vals[9], SDL_arraysize(Multi_pxo_pinfo_vals[9]), "%d", (int)fs->p_shots_hit);
4895 if(fs->p_shots_fired > 0){
4896 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));
4898 SDL_strlcpy(Multi_pxo_pinfo_vals[10], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[10]));
4901 // secondary shots fired
4902 SDL_snprintf(Multi_pxo_pinfo_vals[11], SDL_arraysize(Multi_pxo_pinfo_vals[11]), "%d", (int)fs->s_shots_fired);
4904 // secondary shots hit
4905 SDL_snprintf(Multi_pxo_pinfo_vals[12], SDL_arraysize(Multi_pxo_pinfo_vals[12]), "%d", (int)fs->s_shots_hit);
4907 // secondary hit pct
4908 if(fs->s_shots_fired > 0){
4909 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));
4911 SDL_strlcpy(Multi_pxo_pinfo_vals[13], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[13]));
4914 // primary friendly hits
4915 SDL_snprintf(Multi_pxo_pinfo_vals[14], SDL_arraysize(Multi_pxo_pinfo_vals[14]), "%d", fs->p_bonehead_hits);
4917 // primary friendly hit %
4918 if(fs->p_shots_fired > 0){
4919 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)));
4921 SDL_strlcpy(Multi_pxo_pinfo_vals[15], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[15]));
4924 // secondary friendly hits
4925 SDL_snprintf(Multi_pxo_pinfo_vals[16], SDL_arraysize(Multi_pxo_pinfo_vals[16]), "%d", fs->s_bonehead_hits);
4927 // secondary friendly hit %
4928 if(fs->s_shots_fired > 0){
4929 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)));
4931 SDL_strlcpy(Multi_pxo_pinfo_vals[17], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[17]));
4935 // initialize the popup
4936 void multi_pxo_pinfo_init()
4940 SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_PINFO);
4942 // mark us as running
4943 Multi_pxo_mode = MULTI_PXO_MODE_PINFO;
4945 // create the interface window
4946 Multi_pxo_pinfo_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4947 Multi_pxo_pinfo_window.set_mask_bmap(Multi_pxo_pinfo_mask_fname[gr_screen.res]);
4949 Multi_pxo_pinfo_bitmap = bm_load(Multi_pxo_pinfo_fname[gr_screen.res]);
4950 SDL_assert(Multi_pxo_pinfo_bitmap != -1);
4952 // create the interface buttons
4953 for(idx=0; idx<MULTI_PXO_PINFO_NUM_BUTTONS; idx++){
4954 // create the object
4955 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);
4957 // set the sound to play when highlighted
4958 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4960 // set the ani for the button
4961 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_pinfo_buttons[gr_screen.res][idx].filename);
4964 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_pinfo_buttons[gr_screen.res][idx].hotspot);
4969 for(idx=0; idx<MULTI_PXO_PINFO_NUM_TEXT; idx++){
4970 Multi_pxo_pinfo_window.add_XSTR(&Multi_pxo_pinfo_text[gr_screen.res][idx]);
4974 // set up the stats labels
4975 Multi_pxo_pinfo_stats_labels[0] = strdup(XSTR("Name", 1532));
4976 Multi_pxo_pinfo_stats_labels[1] = strdup(XSTR("Rank", 1533));
4977 Multi_pxo_pinfo_stats_labels[2] = strdup(XSTR("Kills", 1534));
4978 Multi_pxo_pinfo_stats_labels[3] = strdup(XSTR("Assists", 1535));
4979 Multi_pxo_pinfo_stats_labels[4] = strdup(XSTR("Friendly kills", 1536));
4980 Multi_pxo_pinfo_stats_labels[5] = strdup(XSTR("Missions flown", 1537));
4981 Multi_pxo_pinfo_stats_labels[6] = strdup(XSTR("Flight time", 1538));
4982 Multi_pxo_pinfo_stats_labels[7] = strdup(XSTR("Last flown", 1539));
4983 Multi_pxo_pinfo_stats_labels[8] = strdup(XSTR("Primary shots fired", 1540));
4984 Multi_pxo_pinfo_stats_labels[9] = strdup(XSTR("Primary shots hit", 1541));
4985 Multi_pxo_pinfo_stats_labels[10] = strdup(XSTR("Primary hit %", 1542));
4986 Multi_pxo_pinfo_stats_labels[11] = strdup(XSTR("Secondary shots fired", 1543));
4987 Multi_pxo_pinfo_stats_labels[12] = strdup(XSTR("Secondary shots hit", 1544));
4988 Multi_pxo_pinfo_stats_labels[13] = strdup(XSTR("Secondary hit %", 1545));
4989 Multi_pxo_pinfo_stats_labels[14] = strdup(XSTR("Primary friendly hits", 1546));
4990 Multi_pxo_pinfo_stats_labels[15] = strdup(XSTR("Primary friendly hit %", 1547));
4991 Multi_pxo_pinfo_stats_labels[16] = strdup(XSTR("Secondary friendly hits", 1548));
4992 Multi_pxo_pinfo_stats_labels[17] = strdup(XSTR("Secondary friendly hit %", 1549));
4994 // build the stats labels values
4995 multi_pxo_pinfo_build_vals();
4999 int multi_pxo_pinfo_popup()
5003 // if we're not already running, initialize stuff
5004 if (Multi_pxo_mode != MULTI_PXO_MODE_PINFO) {
5006 multi_pxo_pinfo_init();
5008 // return "still running"
5012 k = Multi_pxo_pinfo_window.process();
5014 // process common stuff
5015 multi_pxo_process_common();
5017 // run the networking functions for the PXO API
5018 multi_pxo_api_process();
5020 // check to see if he pressed escp
5021 if(k == SDLK_ESCAPE){
5022 multi_pxo_pinfo_close();
5026 // if he pressed the ok button
5027 if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_OK].button.pressed()){
5028 multi_pxo_pinfo_close();
5032 // if he pressed the medals buttons, run the medals screen
5033 if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_MEDALS].button.pressed()){
5035 game_feature_not_in_demo_popup();
5037 multi_pxo_run_medals();
5043 // blit everything on the "normal" screen
5044 multi_pxo_blit_all();
5046 // blit our own stuff
5048 gr_set_bitmap(Multi_pxo_pinfo_bitmap);
5054 Multi_pxo_pinfo_window.draw();
5056 // blit the stats themselves
5057 multi_pxo_pinfo_blit();
5067 void multi_pxo_pinfo_close()
5071 // destroy the UI_WINDOW
5072 Multi_pxo_pinfo_window.destroy();
5074 // unload the bitmap
5075 if(Multi_pxo_pinfo_bitmap != -1){
5076 bm_unload(Multi_pxo_pinfo_bitmap);
5079 // free the stats labels strings
5080 for (i=0; i<MULTI_PXO_PINFO_NUM_LABELS; i++) {
5081 free(Multi_pxo_pinfo_stats_labels[i]);
5084 // mark us as not running any more
5085 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
5088 // blit all the stats on this screen
5089 void multi_pxo_pinfo_blit()
5094 // blit all the labels
5095 y_start = Multi_pxo_pinfo_coords[gr_screen.res][1];
5096 for(idx=0; idx<MULTI_PXO_PINFO_NUM_LABELS; idx++){
5098 gr_set_color_fast(&Color_bright);
5099 gr_string(Multi_pxo_pinfo_coords[gr_screen.res][0], y_start, Multi_pxo_pinfo_stats_labels[idx]);
5101 // blit the label's value
5102 gr_set_color_fast(&Color_normal);
5103 gr_string(Multi_pxo_pinfo_val_x[gr_screen.res], y_start, Multi_pxo_pinfo_vals[idx]);
5106 y_start += Multi_pxo_pinfo_stats_spacing[idx];
5110 // run the medals screen
5111 void multi_pxo_run_medals()
5115 // process common stuff
5116 multi_pxo_process_common();
5118 // run the networking functions for the PXO API
5119 multi_pxo_api_process();
5121 // initialize the freespace data and the player struct
5122 multi_stats_tracker_to_fs(&Multi_pxo_pinfo, &Multi_pxo_pinfo_player.stats);
5123 SDL_strlcpy(Multi_pxo_pinfo_player.callsign, Multi_pxo_pinfo.pilot_name, SDL_arraysize(Multi_pxo_pinfo_player.callsign));
5125 // initialize the medals screen
5126 medal_main_init(&Multi_pxo_pinfo_player, MM_POPUP);
5128 // run the medals screen until it says that it should be closed
5130 // set frametime and run common functions
5131 game_set_frametime(-1);
5132 game_do_state_common(gameseq_get_state());
5134 // run the medals screen
5135 ret_code = medal_main_do();
5138 // close the medals screen down
5141 // reset the palette
5142 multi_pxo_load_palette();
5146 // notify stuff stuff -----------------------------------------
5148 // add a notification string
5149 void multi_pxo_notify_add(const char *txt)
5152 SDL_strlcpy(Multi_pxo_notify_text, txt, SDL_arraysize(Multi_pxo_notify_text));
5154 // set the timestamp
5155 Multi_pxo_notify_stamp = timestamp(MULTI_PXO_NOTIFY_TIME);
5158 // blit and process the notification string
5159 void multi_pxo_notify_blit()
5163 // if the timestamp is -1, do nothing
5164 if(Multi_pxo_notify_stamp == -1){
5168 // if it has expired, do nothing
5169 if(timestamp_elapsed(Multi_pxo_notify_stamp)){
5170 Multi_pxo_notify_stamp = -1;
5173 // otherwise blit the text
5174 gr_set_color_fast(&Color_bright);
5175 gr_get_string_size(&w,NULL,Multi_pxo_notify_text);
5176 gr_string((gr_screen.max_w - w)/2,MULTI_PXO_NOTIFY_Y,Multi_pxo_notify_text);
5180 // initialize the PXO help screen
5181 void multi_pxo_help_init()
5185 // load the background bitmap
5186 Multi_pxo_help_bitmap = bm_load(Multi_pxo_help_fname[gr_screen.res]);
5187 if(Multi_pxo_help_bitmap < 0){
5188 // we failed to load the bitmap - this is very bad
5191 // create the interface window
5192 Multi_pxo_help_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
5193 Multi_pxo_help_window.set_mask_bmap(Multi_pxo_help_mask_fname[gr_screen.res]);
5195 // create the interface buttons
5196 for(idx=0; idx<MULTI_PXO_HELP_NUM_BUTTONS; idx++){
5197 // create the object
5198 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);
5200 // set the sound to play when highlighted
5201 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
5203 // set the ani for the button
5204 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_help_buttons[gr_screen.res][idx].filename);
5207 Multi_pxo_help_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_help_buttons[gr_screen.res][idx].hotspot);
5212 for(idx=0; idx<MULTI_PXO_HELP_NUM_TEXT; idx++){
5213 Multi_pxo_help_window.add_XSTR(&Multi_pxo_help_text[gr_screen.res][idx]);
5217 // if we haven't already loaded in the text, do so
5218 // if(!Multi_pxo_help_loaded){
5219 multi_pxo_help_load();
5222 // set the current page to 0
5223 Multi_pxo_help_cur = 0;
5226 // do frame for PXO help
5227 void multi_pxo_help_do()
5230 if(Multi_pxo_connected){
5231 multi_pxo_api_process();
5234 // process common stuff
5235 multi_pxo_process_common();
5237 int k = Multi_pxo_help_window.process();
5239 // process any keypresses
5242 gamesnd_play_iface(SND_USER_SELECT);
5243 gameseq_post_event(GS_EVENT_PXO);
5247 // process button presses
5248 multi_pxo_help_process_buttons();
5250 // draw the background, etc
5252 GR_MAYBE_CLEAR_RES(Multi_pxo_help_bitmap);
5253 if(Multi_pxo_help_bitmap != -1){
5254 gr_set_bitmap(Multi_pxo_help_bitmap);
5257 Multi_pxo_help_window.draw();
5259 // blit the current page
5260 multi_pxo_help_blit_page();
5266 // close the pxo screen
5267 void multi_pxo_help_close()
5271 // unload any bitmaps
5272 bm_unload(Multi_pxo_help_bitmap);
5274 // destroy the UI_WINDOW
5275 Multi_pxo_help_window.destroy();
5278 for(idx=0; idx<Multi_pxo_help_num_pages; idx++){
5279 for(idx2=0; idx2<Multi_pxo_help_pages[idx].num_lines; idx2++){
5281 if(Multi_pxo_help_pages[idx].text[idx2] != NULL){
5282 free(Multi_pxo_help_pages[idx].text[idx2]);
5283 Multi_pxo_help_pages[idx].text[idx2] = NULL;
5289 // load the help file up
5290 void multi_pxo_help_load()
5295 // if its already loaded, do nothing
5296 // if(Multi_pxo_help_loaded){
5300 // read in the text file
5302 in = cfopen(MULTI_PXO_HELP_FILE,"rt",CFILE_NORMAL,CF_TYPE_DATA);
5303 SDL_assert(in != NULL);
5308 Multi_pxo_help_num_pages = 0;
5310 // blast all the help pages clear
5311 memset(Multi_pxo_help_pages, 0, sizeof(help_page) * MULTI_PXO_MAX_PAGES);
5312 Multi_pxo_help_num_pages = 0;
5313 cp = &Multi_pxo_help_pages[0];
5317 cp->text[cp->num_lines] = (char*)malloc(Multi_pxo_chars_per_line[gr_screen.res]);
5318 if(cp->text[cp->num_lines] == NULL){
5322 // read in the next line
5323 cfgets(cp->text[cp->num_lines++], Multi_pxo_chars_per_line[gr_screen.res], in);
5325 // skip to the next page if necessary
5326 if(cp->num_lines == Multi_pxo_lines_pp[gr_screen.res]){
5327 Multi_pxo_help_num_pages++;
5328 SDL_assert(Multi_pxo_help_num_pages < MULTI_PXO_MAX_PAGES);
5329 if(Multi_pxo_help_num_pages >= MULTI_PXO_MAX_PAGES){
5330 Multi_pxo_help_num_pages--;
5333 cp = &Multi_pxo_help_pages[Multi_pxo_help_num_pages];
5340 // mark the help as having been loaded
5341 // Multi_pxo_help_loaded = 1;
5344 // blit the current page
5345 void multi_pxo_help_blit_page()
5350 help_page *cp = &Multi_pxo_help_pages[Multi_pxo_help_cur];
5353 y_start = Multi_pxo_help_coords[gr_screen.res][1];
5354 for(idx=0;idx<cp->num_lines;idx++){
5355 // if the first symbol is "@", highlight the line
5356 if(cp->text[idx][0] == '@'){
5357 gr_set_color_fast(&Color_bright);
5360 gr_set_color_fast(&Color_normal);
5365 gr_string(Multi_pxo_help_coords[gr_screen.res][0], y_start, cp->text[idx] + start_pos);
5367 // increment the y location
5372 // process button presses
5373 void multi_pxo_help_process_buttons()
5377 // process all buttons
5378 for(idx=0;idx<MULTI_PXO_HELP_NUM_BUTTONS;idx++){
5379 if(Multi_pxo_help_buttons[gr_screen.res][idx].button.pressed()){
5380 multi_pxo_help_button_pressed(idx);
5387 void multi_pxo_help_button_pressed(int n)
5390 case MULTI_PXO_HELP_PREV:
5391 // if we're already at page 0, do nothing
5392 if(Multi_pxo_help_cur == 0){
5393 gamesnd_play_iface(SND_GENERAL_FAIL);
5395 Multi_pxo_help_cur--;
5396 gamesnd_play_iface(SND_USER_SELECT);
5400 case MULTI_PXO_HELP_NEXT:
5401 // if we're already at max pages, do nothing
5402 if(Multi_pxo_help_cur == Multi_pxo_help_num_pages){
5403 gamesnd_play_iface(SND_GENERAL_FAIL);
5405 Multi_pxo_help_cur++;
5406 gamesnd_play_iface(SND_USER_SELECT);
5410 case MULTI_PXO_HELP_CONTINUE:
5411 gamesnd_play_iface(SND_USER_SELECT);
5412 gameseq_post_event(GS_EVENT_PXO);
5417 // http banner stuff ---------------------------------------------
5420 void multi_pxo_ban_init()
5423 // zero the active banner bitmap
5424 Multi_pxo_banner.ban_bitmap = -1;
5426 // are we doing banners at all?
5427 if(os_config_read_uint(NULL, "PXOBanners", 1)){
5428 // if we're already in idle mode, we're done downloading for this instance of freespace. pick a random image we already have
5429 if(Multi_pxo_ban_mode == PXO_BAN_MODE_IDLE){
5430 Multi_pxo_ban_mode = PXO_BAN_MODE_CHOOSE_RANDOM;
5434 // set ourselves to startup mode
5435 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
5436 Multi_pxo_ban_get = NULL;
5438 // set ourselves to idle mode
5439 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5440 Multi_pxo_ban_get = NULL;
5444 // zero the active banner bitmap
5445 SDL_zero(Multi_pxo_banner);
5446 Multi_pxo_banner.ban_bitmap = -1;
5449 // process http download details
5450 void multi_pxo_ban_process()
5452 char url_string[512] = "";
5453 char local_file[MAX_PATH_LEN] = "";
5461 switch(Multi_pxo_ban_mode){
5462 // start downloading list
5463 case PXO_BAN_MODE_LIST_STARTUP:
5465 SDL_snprintf(url_string, SDL_arraysize(url_string), "%s/%s", Multi_options_g.pxo_banner_url, PXO_BANNERS_CONFIG_FILE);
5468 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, PXO_BANNERS_CONFIG_FILE);
5470 // try creating the file get object
5471 Multi_pxo_ban_get = new InetGetFile(url_string, local_file);
5474 if(Multi_pxo_ban_get == NULL){
5475 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5478 // go to the downloading list mode
5479 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST;
5483 case PXO_BAN_MODE_LIST:
5485 if(Multi_pxo_ban_get->IsFileError()){
5486 delete Multi_pxo_ban_get;
5487 Multi_pxo_ban_get = NULL;
5488 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5492 // connecting, receiving
5493 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5498 if(Multi_pxo_ban_get->IsFileReceived()){
5499 delete Multi_pxo_ban_get;
5500 Multi_pxo_ban_get = NULL;
5501 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_STARTUP;
5505 // start downloading files
5506 case PXO_BAN_MODE_IMAGES_STARTUP:
5507 // first thing - parse the banners file and pick a file
5508 multi_pxo_ban_parse_banner_file(0);
5510 // if we have no active file, we're done
5511 if((strlen(Multi_pxo_banner.ban_file) <= 0) || (strlen(Multi_pxo_banner.ban_file_url) <= 0)){
5512 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5516 // if the file already exists, we're done
5517 if(cf_exist(Multi_pxo_banner.ban_file, CF_TYPE_MULTI_CACHE)){
5518 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5522 // otherwise try and download it
5523 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, Multi_pxo_banner.ban_file);
5524 // try creating the file get object
5525 Multi_pxo_ban_get = new InetGetFile(Multi_pxo_banner.ban_file_url, local_file);
5528 if(Multi_pxo_ban_get == NULL){
5529 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5532 // go to the downloading images mode
5533 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES;
5536 // downloading files
5537 case PXO_BAN_MODE_IMAGES:
5539 if(Multi_pxo_ban_get->IsFileError()){
5540 delete Multi_pxo_ban_get;
5541 Multi_pxo_ban_get = NULL;
5542 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5546 // connecting, receiving
5547 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5552 if(Multi_pxo_ban_get->IsFileReceived()){
5553 delete Multi_pxo_ban_get;
5554 Multi_pxo_ban_get = NULL;
5555 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5559 // done downloading - maybe load an image
5560 case PXO_BAN_MODE_IMAGES_DONE:
5561 // make sure we have a valid filename
5562 // SDL_assert(strlen(Multi_pxo_banner.ban_file) > 0);
5563 if(strlen(Multi_pxo_banner.ban_file) > 0){
5564 Multi_pxo_banner.ban_bitmap = bm_load(Multi_pxo_banner.ban_file);
5568 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5571 // idle (done with EVERYTHING)
5572 case PXO_BAN_MODE_IDLE:
5573 // if the banner button was clicked
5574 if(Multi_pxo_ban_button.pressed()){
5575 multi_pxo_ban_clicked();
5579 case PXO_BAN_MODE_CHOOSE_RANDOM:
5580 // first thing - parse the banners file and pick a file
5581 multi_pxo_ban_parse_banner_file(1);
5583 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5589 void multi_pxo_ban_close()
5591 // if we have a currently active transfer
5592 if(Multi_pxo_ban_get != NULL){
5593 Multi_pxo_ban_get->AbortGet();
5594 delete Multi_pxo_ban_get;
5595 Multi_pxo_ban_get = NULL;
5598 // if we have a loaded bitmap, unload it
5599 if(Multi_pxo_banner.ban_bitmap != -1){
5600 bm_unload(Multi_pxo_banner.ban_bitmap);
5601 Multi_pxo_banner.ban_bitmap = -1;
5605 // parse the banners file and maybe fill in Multi_pxo_dl_file
5606 void multi_pxo_ban_parse_banner_file(int choose_existing)
5608 char file_url[MAX_PATH_LEN] = "";
5609 char banners[10][MAX_PATH_LEN];
5610 char urls[10][MAX_PATH_LEN];
5613 int num_banners, idx;
5614 CFILE *in = cfopen(PXO_BANNERS_CONFIG_FILE, "rt", CFILE_NORMAL, CF_TYPE_MULTI_CACHE);
5616 SDL_zero(Multi_pxo_banner);
5617 Multi_pxo_banner.ban_bitmap = -1;
5624 // clear all strings
5628 // get the global banner url
5629 if(cfgets(file_url, SDL_arraysize(file_url), in) == NULL){
5631 cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5634 drop_leading_white_space(file_url);
5635 drop_trailing_white_space(file_url);
5637 // otherwise read in
5639 while(num_banners < 10){
5640 // try and get the pcx
5641 if(cfgets(banners[num_banners], SDL_arraysize(banners[0]), in) == NULL){
5644 // try and get the url
5645 if(cfgets(urls[num_banners], SDL_arraysize(urls[0]), in) == NULL){
5649 // strip off trailing and leading whitespace
5650 drop_leading_white_space(banners[num_banners]);
5651 drop_trailing_white_space(banners[num_banners]);
5652 drop_leading_white_space(urls[num_banners]);
5653 drop_trailing_white_space(urls[num_banners]);
5663 if(num_banners <= 0){
5667 // if we're only selecting files which already exist (previously downloaded)
5668 if(choose_existing){
5670 for(idx=0; idx<10; idx++){
5674 // build a list of existing files
5676 for(idx=0; idx<num_banners; idx++){
5677 if(cf_exist(banners[idx], CF_TYPE_MULTI_CACHE)){
5684 if(exist_count <= 0){
5689 int select = (int)frand_range(0.0f, (float)exist_count);
5690 if(select >= exist_count){
5691 select = exist_count - 1;
5696 for(idx=0; idx<exist_count; idx++){
5706 if(idx < exist_count){
5708 SDL_strlcpy(Multi_pxo_banner.ban_file, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file));
5710 // get the full file url
5711 SDL_strlcpy(Multi_pxo_banner.ban_file_url, file_url, SDL_arraysize(Multi_pxo_banner.ban_file_url));
5712 SDL_strlcat(Multi_pxo_banner.ban_file_url, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file_url));
5714 // url of where to go to when clicked
5715 SDL_strlcpy(Multi_pxo_banner.ban_url, urls[idx], SDL_arraysize(Multi_pxo_banner.ban_url));
5718 // randomly pick a file for download
5720 idx = (int)frand_range(0.0f, (float)num_banners);
5722 if(idx >= num_banners){
5723 idx = num_banners - 1;
5730 SDL_strlcpy(Multi_pxo_banner.ban_file, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file));
5732 // get the full file url
5733 SDL_strlcpy(Multi_pxo_banner.ban_file_url, file_url, SDL_arraysize(Multi_pxo_banner.ban_file_url));
5734 SDL_strlcat(Multi_pxo_banner.ban_file_url, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file_url));
5736 // url of where to go to when clicked
5737 SDL_strlcpy(Multi_pxo_banner.ban_url, urls[idx], SDL_arraysize(Multi_pxo_banner.ban_url));
5740 // delete the banner config file
5741 // cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5744 // any bitmap or info or whatever
5745 void multi_pxo_ban_draw()
5747 // if we have a valid bitmap
5748 if(Multi_pxo_banner.ban_bitmap >= 0){
5749 // if the mouse is over the banner button, highlight with a rectangle
5750 if(Multi_pxo_ban_button.is_mouse_on()){
5751 gr_set_color_fast(&Color_bright_blue);
5752 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);
5755 // draw the bitmap itself
5756 gr_set_bitmap(Multi_pxo_banner.ban_bitmap);
5757 gr_bitmap(Pxo_ban_coords[gr_screen.res][0], Pxo_ban_coords[gr_screen.res][1]);
5761 // called when the URL button is clicked
5762 void multi_pxo_ban_clicked()
5764 // if we have a valid bitmap and URL, launch the URL
5765 if((Multi_pxo_banner.ban_bitmap >= 0) && (strlen(Multi_pxo_banner.ban_url) > 0)){
5766 multi_pxo_url(Multi_pxo_banner.ban_url);