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 int Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
527 // our nick for this session
528 char Multi_pxo_nick[NAME_LENGTH+1];
530 // check for button presses
531 void multi_pxo_check_buttons();
533 // handle a button press
534 void multi_pxo_button_pressed(int n);
536 // condition function for popup_do_with_condition for connected to Parallax Online
537 // return 10 : on successful connect
538 int multi_pxo_connect_do();
540 // attempt to connect to Parallax Online, return success or fail
541 int multi_pxo_connect();
543 // run the networking functions for the PXO API
544 void multi_pxo_api_process();
546 // process a "nick" change event
547 void multi_pxo_process_nick_change(char *data);
549 // run normally (no popups)
550 void multi_pxo_do_normal();
552 // blit everything on the "normal" screen
553 void multi_pxo_blit_all();
555 // process common stuff
556 void multi_pxo_process_common();
558 // get selected player information
559 void multi_pxo_get_data(char *name);
561 // handle being kicked
562 void multi_pxo_handle_kick();
564 // handle being disconnected
565 void multi_pxo_handle_disconnect();
567 // return string2, which is the first substring of string 1 without a space
568 // it is safe to pass the same pointer for both parameters
569 void multi_pxo_strip_space(char *string1,char *string2, const int str2_len);
571 // fire up the given URL
572 void multi_pxo_url(char *url);
574 // load/set the palette
575 void multi_pxo_load_palette();
577 // unload the palette
578 void multi_pxo_unload_palette();
580 // if we're currently on a private channel
581 int multi_pxo_on_private_channel();
583 // convert string 1 into string 2, substituting underscores for spaces
584 void multi_pxo_underscore_nick(char *string1, char *string2, const int str2_len);
586 // if the command is a potential "nick" command
587 int multi_pxo_is_nick_command(char *msg);
590 // status bar stuff -----------------------------------------------
591 int Multi_pxo_status_coords[GR_NUM_RESOLUTIONS][4] = {
604 // the status text itself
605 char Multi_pxo_status_text[255];
607 // set the status text
608 void multi_pxo_set_status_text(const char *txt);
610 // blit the status text
611 void multi_pxo_blit_status_text();
614 // channel related stuff -------------------------------------------
615 #define MAX_CHANNEL_NAME_LEN 32
616 #define MAX_CHANNEL_DESCRIPT_LEN 120
618 // some convenient macros
619 #define SWITCHING_CHANNELS() (Multi_pxo_channel_switch.num_users != -1)
620 #define ON_CHANNEL() (Multi_pxo_channel_current.num_users != -1)
622 typedef struct pxo_channel {
623 pxo_channel *next,*prev; // next and previous items in the list
624 char name[MAX_CHANNEL_NAME_LEN+1]; // name
625 char desc[MAX_CHANNEL_DESCRIPT_LEN+1]; // description
626 short num_users; // # users, or -1 if not in use
627 short num_servers; // the # of servers registered on this channel
630 // last channel we were on before going to the game list screen
631 char Multi_pxo_channel_last[MAX_CHANNEL_NAME_LEN+1] = "";
632 int Multi_pxo_use_last_channel = 0;
634 // all channels which are prefixed with this are "lobby" channels
635 #define MULTI_PXO_AUTOJOIN_PREFIX "#lobby"
637 // join this channel to get put in an appropriate lobby channel
638 #define MULTI_PXO_AUTOJOIN_CHANNEL "#autoselect"
640 int Multi_pxo_chan_coords[GR_NUM_RESOLUTIONS][4] = {
653 // this is the offset from the RIGHT side of the channel box
654 #define CHAN_PLAYERS_COLUMN 0
655 #define CHAN_GAMES_COLUMN 1
656 static int Multi_pxo_chan_column_offsets[GR_NUM_RESOLUTIONS][2] = {
661 #define CHANNEL_REFRESH_TIME (75.0f)
662 float Multi_pxo_channel_last_refresh = -1.0f;
664 #define CHANNEL_SERVER_REFRESH_TIME (35.0f)
665 float Multi_pxo_channel_server_refresh = -1.0f;
667 int Multi_pxo_max_chan_display[GR_NUM_RESOLUTIONS] = {
672 UI_BUTTON Multi_pxo_channel_button;
674 // head of the list of available (displayed) channels
675 pxo_channel *Multi_pxo_channels = NULL;
676 int Multi_pxo_channel_count = 0;
678 // item we're going to start displaying at
679 pxo_channel *Multi_pxo_channel_start = NULL;
680 int Multi_pxo_channel_start_index = -1;
682 // items we've currently got selected
683 pxo_channel *Multi_pxo_channel_select = NULL;
685 // channel we're currently connected to, num_users == -1, if we're not connected
686 pxo_channel Multi_pxo_channel_current;
688 // channel we're currently trying to change to, num_users == -1, if we're not trying to change channels
689 pxo_channel Multi_pxo_channel_switch;
691 // get a list of channels on the server (clear any old list as well)
692 void multi_pxo_get_channels();
694 // clear the old channel list
695 void multi_pxo_clear_channels();
697 // parse the input string and make a list of new channels
698 void multi_pxo_make_channels(char *chan_str);
700 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
701 pxo_channel *multi_pxo_add_channel(char *name, pxo_channel **list);
703 // lookup a channel with the specified name
704 pxo_channel *multi_pxo_find_channel(char *name, pxo_channel *list);
706 // process the channel list (select, etc)
707 void multi_pxo_process_channels();
709 // display the channel list
710 void multi_pxo_blit_channels();
712 // scroll channel list up
713 void multi_pxo_scroll_channels_up();
715 // scroll channel list down
716 void multi_pxo_scroll_channels_down();
718 // attempt to join a channel
719 void multi_pxo_join_channel(pxo_channel *chan);
721 // handle any processing details if we're currently trying to join a channel
722 void multi_pxo_handle_channel_change();
724 // autojoin an appropriate channel
725 void multi_pxo_autojoin();
727 // does the string match the "autojoin" prefic
728 int multi_pxo_is_autojoin(char *name);
730 // send a request to refresh our channel server counts
731 void multi_pxo_channel_refresh_servers();
733 // refresh current channel server count
734 void multi_pxo_channel_refresh_current();
737 // player related stuff -------------------------------------------
738 #define MAX_PLAYER_NAME_LEN 32
740 typedef struct player_list {
741 player_list *next,*prev;
742 char name[MAX_PLAYER_NAME_LEN+1];
745 // channel list region
746 int Multi_pxo_player_coords[GR_NUM_RESOLUTIONS][4] = {
759 int Multi_pxo_max_player_display[GR_NUM_RESOLUTIONS] = {
763 UI_BUTTON Multi_pxo_player_button;
766 // UI_SLIDER2 Multi_pxo_player_slider;
769 int Multi_pxo_player_slider_coords[GR_NUM_RESOLUTIONS][4] = {
777 const char *Multi_pxo_player_slider_name[GR_NUM_RESOLUTIONS] = {
779 "2_slider" // GR_1024
783 // head of the list of players in this channel
784 player_list *Multi_pxo_players = NULL;
785 int Multi_pxo_player_count = 0;
787 // item we're going to start displaying at
788 player_list *Multi_pxo_player_start = NULL;
789 // int Multi_pxo_player_start_index = -1;
791 // items we've currently got selected
792 player_list *Multi_pxo_player_select = NULL;
794 // clear the old player list
795 void multi_pxo_clear_players();
797 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
798 player_list *multi_pxo_add_player(char *name);
800 // remove a player with the given name
801 void multi_pxo_del_player(char *name);
803 // try and find a player with the given name, return a pointer to his entry (or NULL)
804 player_list *multi_pxo_find_player(char *name);
806 // process the player list (select, etc)
807 void multi_pxo_process_players();
809 // display the player list
810 void multi_pxo_blit_players();
812 // scroll player list up
813 void multi_pxo_scroll_players_up();
815 // scroll player list down
816 void multi_pxo_scroll_players_down();
818 // get the absolute index of the displayed items which our currently selected one is
819 int multi_pxo_get_select_index();
825 // add a bunch of bogus players
827 for(int idx=0; idx<Dc_arg_int; idx++){
828 SDL_snprintf(name, SDL_arraysize(name), "player %d", idx);
829 multi_pxo_add_player(name);
833 // chat text stuff -----------------------------------------
834 #define MAX_CHAT_LINES 60
835 #define MAX_CHAT_LINE_LEN 256
837 int Multi_pxo_chat_title_y[GR_NUM_RESOLUTIONS] = {
846 int Multi_pxo_chat_coords[GR_NUM_RESOLUTIONS][4] = {
859 int Multi_pxo_input_coords[GR_NUM_RESOLUTIONS][4] = {
872 int Multi_pxo_max_chat_display[GR_NUM_RESOLUTIONS] = {
881 // all messages from the server are prefixed with this
882 #define MULTI_PXO_SERVER_PREFIX "*** "
884 // the "has left" message from the server
885 #define MULTI_PXO_HAS_LEFT "has left"
888 #define CHAT_MODE_NORMAL 0 // normal chat from someone
889 #define CHAT_MODE_SERVER 1 // is from the server, display appropriately
890 #define CHAT_MODE_CARRY 2 // is a carryover from a previous line
891 #define CHAT_MODE_PRIVATE 3 // is a private message
892 #define CHAT_MODE_CHANNEL_SWITCH 4 // "switching channels" message - draw in red
893 #define CHAT_MODE_MOTD 5 // message of the day from the chat server
895 typedef struct chat_line {
896 chat_line *next,*prev;
897 char text[MAX_CHAT_LINE_LEN+1];
901 // the chat linked list itself
902 chat_line *Multi_pxo_chat = NULL;
904 // the current add line
905 chat_line *Multi_pxo_chat_add = NULL;
907 // the current line to start displaying from
908 chat_line *Multi_pxo_chat_start = NULL;
909 int Multi_pxo_chat_start_index = -1;
911 // input box for text
912 UI_INPUTBOX Multi_pxo_chat_input;
916 UI_SLIDER2 Multi_pxo_chat_slider;
918 int Multi_pxo_chat_slider_coords[GR_NUM_RESOLUTIONS][4] = {
927 const char *Multi_pxo_chat_slider_name[GR_NUM_RESOLUTIONS] = {
933 // how many chat lines we have
934 int Multi_pxo_chat_count = 0;
936 // extra delay time when switching channels
937 #define MULTI_PXO_SWITCH_DELAY_TIME 2000
938 int Multi_pxo_switch_delay = -1;
940 // initialize and create the chat text linked list
941 void multi_pxo_chat_init();
943 // free up all chat list stuff
944 void multi_pxo_chat_free();
946 // clear all lines of chat text in the chat area
947 void multi_pxo_chat_clear();
949 // blit the chat text
950 void multi_pxo_chat_blit();
952 // add a line of text
953 void multi_pxo_chat_add_line(char *txt,int mode);
955 // process an incoming line of text
956 void multi_pxo_chat_process_incoming(const char *txt,int mode = CHAT_MODE_NORMAL);
958 // scroll to the very bottom of the chat area
959 void multi_pxo_goto_bottom();
961 // check whether we can scroll down or not
962 int multi_pxo_can_scroll_down();
964 static int Can_scroll_down = 0;
966 // scroll the text up
967 void multi_pxo_scroll_chat_up();
969 // scroll the text down
970 void multi_pxo_scroll_chat_down();
972 // process chat controls
973 void multi_pxo_chat_process();
975 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
976 const char *multi_pxo_chat_is_private(const char *txt);
978 // if the text came from the server
979 int multi_pxo_is_server_text(const char *txt);
981 // if the text is message of the day text
982 int multi_pxo_is_motd_text(const char *txt);
984 // if the text is the end of motd text
985 int multi_pxo_is_end_of_motd_text(const char *txt);
987 // if the text is a "has left message" from the server
988 int multi_pxo_chat_is_left_message(const char *txt);
990 // recalculate the chat start index, and adjust the slider properly
991 void multi_pxo_chat_adjust_start();
994 // motd stuff ---------------------------------------------------------
995 #define MAX_PXO_MOTD_LEN 1024
996 #define PXO_MOTD_BLINK_TIME 500
997 char Pxo_motd[1024] = "";
998 int Pxo_motd_end = 0;
999 int Pxo_motd_read = 0;
1000 int Pxo_motd_blink_stamp = -1;
1001 int Pxo_motd_blink_on = 0;
1002 int Pxo_motd_blinked_already = 0;
1004 // initialize motd when going into this screen
1005 void multi_pxo_motd_init();
1007 // set the motd text
1008 void multi_pxo_motd_add_text(const char *text);
1011 void multi_pxo_set_end_of_motd();
1013 // display the motd dialog
1014 void multi_pxo_motd_dialog();
1016 // call to maybe blink the motd button
1017 void multi_pxo_motd_maybe_blit();
1020 // common dialog stuff ------------------------------------------------
1021 const char *Multi_pxo_com_fname[GR_NUM_RESOLUTIONS] = {
1025 const char *Multi_pxo_com_mask_fname[GR_NUM_RESOLUTIONS] = {
1031 int Multi_pxo_com_coords[GR_NUM_RESOLUTIONS][2] = {
1045 int Multi_pxo_com_input_coords[GR_NUM_RESOLUTIONS][4] = {
1058 #define MULTI_PXO_COM_NUM_BUTTONS 2
1059 #define MULTI_PXO_COM_CANCEL 0
1060 #define MULTI_PXO_COM_OK 1
1062 ui_button_info Multi_pxo_com_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_BUTTONS] = {
1065 ui_button_info("PXP_00", 573, 192, -1, -1, 0),
1066 ui_button_info("PXP_01", 573, 226, -1, -1, 1)
1068 ui_button_info("PXP_00", 494, 182, -1, -1, 0),
1069 ui_button_info("PXP_01", 525, 221, -1, -1, 1)
1073 ui_button_info("2_PXP_00", 917, 308, -1, -1, 0),
1074 ui_button_info("2_PXP_01", 917, 361, -1, -1, 1)
1079 #define MULTI_PXO_COM_NUM_TEXT 2
1080 UI_XSTR Multi_pxo_com_text[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_TEXT] = {
1082 { "&Cancel", 645, 510, 204, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_CANCEL].button },
1083 { "&Ok", 669, 548, 233, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_OK].button }
1086 { "&Cancel", 645, 847, 327, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_CANCEL].button },
1087 { "&Ok", 669, 877, 372, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_OK].button }
1092 int Multi_pxo_com_bitmap = -1;
1093 UI_WINDOW Multi_pxo_com_window;
1094 UI_INPUTBOX Multi_pxo_com_input;
1096 // text on the "top" half of the dialog display area
1097 char Multi_pxo_com_top_text[255];
1099 // text on the "middle" portion of the dialog display area
1100 char Multi_pxo_com_middle_text[255];
1102 // text on the "bottom" half of the dialog display area
1103 char Multi_pxo_com_bottom_text[255];
1105 int Multi_pxo_com_top_text_coords[GR_NUM_RESOLUTIONS][2] = {
1113 int Multi_pxo_com_middle_text_y[GR_NUM_RESOLUTIONS] = {
1117 int Multi_pxo_com_bottom_text_y[GR_NUM_RESOLUTIONS] = {
1122 // initialize the common dialog with the passed max input length
1123 void multi_pxo_com_init();
1125 // close down the common dialog
1126 void multi_pxo_com_close();
1128 // blit all text lines, top, middle, bottoms
1129 void multi_pxo_com_blit_text();
1131 // set the top text, shortening as necessary
1132 void multi_pxo_com_set_top_text(const char *txt);
1134 // set the middle text, shortening as necessary
1135 void multi_pxo_com_set_middle_text(const char *txt);
1137 // set the bottom text, shortening as necessary
1138 void multi_pxo_com_set_bottom_text(const char *txt);
1141 // private channel join stuff -----------------------------------------
1142 #define MULTI_PXO_PRIV_MAX_TEXT_LEN 30
1144 // max private channel name length
1145 char Multi_pxo_priv_chan[MULTI_PXO_PRIV_MAX_TEXT_LEN+100];
1147 // return code, set to something other than -1 if we're supposed to return
1148 int Multi_pxo_priv_return_code = -1;
1150 // initialize the popup
1151 void multi_pxo_priv_init();
1153 // close down the popup
1154 void multi_pxo_priv_close();
1156 // run the popup, 0 if still running, -1 if cancel, 1 if ok
1157 int multi_pxo_priv_popup();
1159 // process button presses
1160 void multi_pxo_priv_process_buttons();
1162 // handle a button press
1163 void multi_pxo_priv_button_pressed(int n);
1165 // process the inputbox
1166 void multi_pxo_priv_process_input();
1169 // find player stuff -----------------------------------------
1171 char Multi_pxo_find_channel[MAX_CHANNEL_NAME_LEN+1];
1173 // return code, set to something other than -1 if we're supposed to return
1174 int Multi_pxo_find_return_code = -1;
1176 // initialize the popup
1177 void multi_pxo_find_init();
1179 // close down the popup
1180 void multi_pxo_find_close();
1182 // run the popup, 0 if still running, -1 if cancel, 1 if ok
1183 int multi_pxo_find_popup();
1185 // process button presses
1186 void multi_pxo_find_process_buttons();
1188 // handle a button press
1189 void multi_pxo_find_button_pressed(int n);
1191 // process the inputbox
1192 void multi_pxo_find_process_input();
1194 // process search mode if applicable
1195 void multi_pxo_find_search_process();
1198 // player info stuff -----------------------------------------
1199 const char *Multi_pxo_pinfo_fname[GR_NUM_RESOLUTIONS] = {
1203 const char *Multi_pxo_pinfo_mask_fname[GR_NUM_RESOLUTIONS] = {
1209 #define MULTI_PXO_PINFO_NUM_BUTTONS 2
1210 #define MULTI_PXO_PINFO_MEDALS 0
1211 #define MULTI_PXO_PINFO_OK 1
1213 ui_button_info Multi_pxo_pinfo_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_BUTTONS] = {
1216 ui_button_info("PI2_00", 328, 446, 319, 433, 0),
1217 ui_button_info("PI2_01", 376, 446, 382, 433, 1),
1219 ui_button_info("PI2_00", 286, 359, -1, -1, 0),
1220 ui_button_info("PI2_01", 341, 359, -1, -1, 1)
1224 ui_button_info("2_PI2_00", 525, 714, 510, 695, 0),
1225 ui_button_info("2_PI2_01", 601, 714, 611, 695, 1),
1231 #define MULTI_PXO_PINFO_NUM_TEXT 2
1232 UI_XSTR Multi_pxo_pinfo_text[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_TEXT] = {
1234 { "Medals", 1037, 319, 433, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_MEDALS].button },
1235 { "Ok", 345, 382, 433, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_OK].button },
1238 { "Medals", 1037, 510, 695, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_MEDALS].button },
1239 { "Ok", 345, 611, 695, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_OK].button },
1244 int Multi_pxo_pinfo_bitmap = -1;
1245 UI_WINDOW Multi_pxo_pinfo_window;
1247 vmt_stats_struct Multi_pxo_pinfo;
1248 player Multi_pxo_pinfo_player;
1250 int Multi_pxo_retrieve_mode = -1;
1252 char Multi_pxo_retrieve_name[MAX_PLAYER_NAME_LEN+1];
1253 char Multi_pxo_retrieve_id[128];
1255 // stats label stuff
1256 #define MULTI_PXO_PINFO_NUM_LABELS 18
1258 int Multi_pxo_pinfo_coords[GR_NUM_RESOLUTIONS][4] = {
1270 int Multi_pxo_pinfo_val_x[GR_NUM_RESOLUTIONS] = {
1275 char *Multi_pxo_pinfo_stats_labels[MULTI_PXO_PINFO_NUM_LABELS];
1286 "Primary shots fired",
1287 "Primary shots hit",
1289 "Secondary shots fired",
1290 "Secondary shots hit",
1292 "Primary friendly hits",
1293 "Primary friendly hit %",
1294 "Secondary friendly hits",
1295 "Secondary friendly hit %"
1300 char Multi_pxo_pinfo_vals[MULTI_PXO_PINFO_NUM_LABELS][50];
1302 int Multi_pxo_pinfo_stats_spacing[MULTI_PXO_PINFO_NUM_LABELS] = {
1303 10,20,10,10,20,10,10,20,10,10,20,10,10,20,10,20,10,0
1306 // popup conditional functions, returns 10 on successful get of stats
1307 int multi_pxo_pinfo_cond();
1309 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
1310 int multi_pxo_pinfo_get(char *name);
1312 // fire up the stats view popup
1313 void multi_pxo_pinfo_show();
1315 // build the stats labels values
1316 void multi_pxo_pinfo_build_vals();
1318 // initialize the popup
1319 void multi_pxo_pinfo_init();
1322 int multi_pxo_pinfo_do();
1325 void multi_pxo_pinfo_close();
1327 // blit all the stats on this screen
1328 void multi_pxo_pinfo_blit();
1330 // run the medals screen
1331 void multi_pxo_run_medals();
1333 // notify stuff stuff -----------------------------------------
1334 #define MULTI_PXO_NOTIFY_TIME 4000
1335 #define MULTI_PXO_NOTIFY_Y 435
1337 char Multi_pxo_notify_text[255];
1338 int Multi_pxo_notify_stamp = -1;
1340 // add a notification string
1341 void multi_pxo_notify_add(const char *txt);
1343 // blit and process the notification string
1344 void multi_pxo_notify_blit();
1347 // help screen stuff -----------------------------------------
1349 const char *Multi_pxo_help_fname[GR_NUM_RESOLUTIONS] = {
1353 const char *Multi_pxo_help_mask_fname[GR_NUM_RESOLUTIONS] = {
1358 #define MULTI_PXO_HELP_NUM_BUTTONS 3
1359 #define MULTI_PXO_HELP_PREV 0
1360 #define MULTI_PXO_HELP_NEXT 1
1361 #define MULTI_PXO_HELP_CONTINUE 2
1363 ui_button_info Multi_pxo_help_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_BUTTONS] = {
1366 ui_button_info("PXH_00", 15, 389, -1, -1, 0),
1367 ui_button_info("PXH_01", 60, 389, -1, -1, 1),
1368 ui_button_info("PXH_02", 574, 431, 571, 413, 2),
1370 ui_button_info("PXH_00", 71, 373, -1, -1, 0),
1371 ui_button_info("PXH_01", 121, 373, -1, -1, 1),
1372 ui_button_info("PXH_02", 554, 411, -1, -1, 2)
1376 ui_button_info("2_PXH_00", 24, 622, -1, -1, 0),
1377 ui_button_info("2_PXH_01", 96, 622, -1, -1, 1),
1378 ui_button_info("2_PXH_02", 919, 689, 928, 663, 2),
1383 #define MULTI_PXO_HELP_NUM_TEXT 1
1384 UI_XSTR Multi_pxo_help_text[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_TEXT] = {
1386 {"Continue", 1069, 571, 413, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[0][MULTI_PXO_HELP_CONTINUE].button },
1389 {"Continue", 1069, 928, 663, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[1][MULTI_PXO_HELP_CONTINUE].button },
1395 #define MULTI_PXO_HELP_FILE "pxohelp.txt"
1396 #define MULTI_PXO_MAX_LINES_PP 57
1397 #define MULTI_PXO_MAX_PAGES 3
1399 int Multi_pxo_help_coords[GR_NUM_RESOLUTIONS][2] = {
1408 int Multi_pxo_chars_per_line[GR_NUM_RESOLUTIONS] = {
1413 int Multi_pxo_lines_pp[GR_NUM_RESOLUTIONS] = {
1419 typedef struct help_page {
1420 char *text[MULTI_PXO_MAX_LINES_PP];
1424 help_page Multi_pxo_help_pages[MULTI_PXO_MAX_PAGES];
1425 // int Multi_pxo_help_loaded = 0;
1427 int Multi_pxo_help_num_pages = 0;
1429 int Multi_pxo_help_bitmap = -1;
1430 UI_WINDOW Multi_pxo_help_window;
1432 // current page we're on
1433 int Multi_pxo_help_cur = 0;
1435 // load the help file up
1436 void multi_pxo_help_load();
1438 // blit the current page
1439 void multi_pxo_help_blit_page();
1441 // process button presses
1442 void multi_pxo_help_process_buttons();
1445 void multi_pxo_help_button_pressed(int n);
1448 // http banner stuff ---------------------------------------------
1449 InetGetFile *Multi_pxo_ban_get = NULL;
1452 #define PXO_BANNERS_CONFIG_FILE "pxobanners.cfg"
1454 // coords to display banners at
1455 int Pxo_ban_coords[GR_NUM_RESOLUTIONS][4] = {
1465 #define PXO_BAN_MODE_LIST_STARTUP 0 // start downloading list
1466 #define PXO_BAN_MODE_LIST 1 // downloading list
1467 #define PXO_BAN_MODE_IMAGES_STARTUP 2 // start downloading images
1468 #define PXO_BAN_MODE_IMAGES 3 // downloading images
1469 #define PXO_BAN_MODE_IMAGES_DONE 4 // done downloading everything - now maybe load an image
1470 #define PXO_BAN_MODE_IDLE 5 // done with everything - doing nothing
1471 #define PXO_BAN_MODE_CHOOSE_RANDOM 6 // choose a bitmap we've already downloaded at random
1473 // interface button for detecting clicks
1474 UI_BUTTON Multi_pxo_ban_button;
1477 typedef struct pxo_banner {
1478 char ban_file[MAX_FILENAME_LEN+1]; // base filename of the banner
1479 char ban_file_url[MULTI_OPTIONS_STRING_LEN+1]; // full url of the file to get (convenient)
1480 char ban_url[MULTI_OPTIONS_STRING_LEN+1]; // url to go to when clicked
1481 int ban_bitmap; // banner bitmap
1484 // active pxo banner
1485 pxo_banner Multi_pxo_banner;
1488 int Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
1491 void multi_pxo_ban_init();
1493 // process http download details
1494 void multi_pxo_ban_process();
1497 void multi_pxo_ban_close();
1499 // parse the banners file and maybe fill in Multi_pxo_dl_file[]
1500 void multi_pxo_ban_parse_banner_file(int choose_existing);
1502 // any bitmap or info or whatever
1503 void multi_pxo_ban_draw();
1505 // called when the URL button is clicked
1506 void multi_pxo_ban_clicked();
1509 // ----------------------------------------------------------------------------------------------------
1513 // initialize the PXO screen
1514 void multi_pxo_init(int use_last_channel)
1518 // load the background bitmap
1519 Multi_pxo_bitmap = bm_load(Multi_pxo_bitmap_fname[gr_screen.res]);
1520 if(Multi_pxo_bitmap < 0){
1521 // we failed to load the bitmap - this is very bad
1525 // load up the private channel bitmap
1526 Multi_pxo_com_bitmap = bm_load(Multi_pxo_com_fname[gr_screen.res]);
1527 SDL_assert(Multi_pxo_com_bitmap != -1);
1529 // create the interface window
1530 Multi_pxo_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1531 Multi_pxo_window.set_mask_bmap(Multi_pxo_mask_fname[gr_screen.res]);
1533 // multiplayer screen common palettes
1534 multi_pxo_load_palette();
1536 // create the interface buttons
1537 for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
1538 // create the object
1539 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);
1541 // set the sound to play when highlighted
1542 Multi_pxo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1544 // set the ani for the button
1545 Multi_pxo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_buttons[gr_screen.res][idx].filename);
1548 Multi_pxo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_buttons[gr_screen.res][idx].hotspot);
1553 for(idx=0; idx<MULTI_PXO_NUM_TEXT; idx++){
1554 Multi_pxo_window.add_XSTR(&Multi_pxo_text[gr_screen.res][idx]);
1558 if(use_last_channel && strlen(Multi_pxo_channel_last)){
1559 Multi_pxo_use_last_channel = 1;
1561 SDL_zero(Multi_pxo_channel_last);
1562 Multi_pxo_use_last_channel = 0;
1565 // make all scrolling buttons repeatable
1566 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_UP].button.repeatable(1);
1567 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_DOWN].button.repeatable(1);
1568 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_UP].button.repeatable(1);
1569 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_DOWN].button.repeatable(1);
1570 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_UP].button.repeatable(1);
1571 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_DOWN].button.repeatable(1);
1573 // set the mouseover cursor if it loaded ok
1574 if (Web_cursor_bitmap > 0) {
1575 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_RANKINGS].button.set_custom_cursor_bmap(Web_cursor_bitmap);
1578 // create the channel list select button and hide it
1579 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);
1580 Multi_pxo_channel_button.hide();
1582 // create the player list select button and hide it
1583 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);
1584 Multi_pxo_player_button.hide();
1586 // create the chat input box
1587 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);
1588 Multi_pxo_chat_input.set_focus();
1590 // create the banner button and hide it
1591 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);
1592 Multi_pxo_ban_button.hide();
1594 // create the player list slider
1595 // 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);
1598 // create the chat slider
1599 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);
1602 // set our connection status so that we do the right stuff next frame
1603 Multi_pxo_must_validate = 1;
1604 Multi_pxo_must_connect = 0;
1605 Multi_pxo_connected = 0;
1607 // channel we're currently connected to
1608 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
1609 Multi_pxo_channel_current.num_users = -1;
1611 // channel we're currently trying to change to, or NULL if nont
1612 memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
1613 Multi_pxo_channel_switch.num_users = -1;
1615 // last time clicked the url button (so we don't have repeats)
1616 Multi_pxo_ranking_last = -1.0f;
1618 // channel switching extra time delay stamp
1619 Multi_pxo_switch_delay = -1;
1621 // our nick for this session
1622 multi_pxo_underscore_nick(Player->callsign, Multi_pxo_nick, SDL_arraysize(Multi_pxo_nick));
1624 // clear the channel list
1625 multi_pxo_clear_channels();
1627 // clear the player list
1628 multi_pxo_clear_players();
1630 // initialize the chat system
1631 multi_pxo_chat_init();
1634 multi_pxo_ban_init();
1636 // load the animation up
1637 if (gr_screen.res == GR_1024) {
1638 char anim_filename[32] = "2_";
1639 SDL_strlcat(anim_filename, MULTI_PXO_ANIM_FNAME, SDL_arraysize(anim_filename));
1640 Multi_pxo_anim = anim_load(anim_filename);
1642 // if hi-res is not there, fallback to low
1643 if (Multi_pxo_anim == NULL) {
1644 Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME);
1647 Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME);
1650 // clear the status text
1651 multi_pxo_set_status_text("");
1653 // last refresh time
1654 Multi_pxo_channel_last_refresh = -1.0f;
1656 // server count last refresh time
1657 Multi_pxo_channel_server_refresh = -1.0f;
1660 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1663 multi_pxo_motd_init();
1665 // make sure we autojoin
1666 Multi_pxo_must_autojoin = 1;
1668 // clear all tracker channel related strings
1669 SDL_zero(Multi_fs_tracker_channel);
1670 SDL_zero(Multi_fs_tracker_filter);
1673 // do frame for the PXO screen
1676 pxo_channel priv_chan;
1679 if(Multi_pxo_connected) {
1680 multi_pxo_api_process();
1683 // process common stuff
1684 multi_pxo_process_common();
1686 switch(Multi_pxo_mode){
1687 // private channel join mode
1688 case MULTI_PXO_MODE_PRIVATE:
1689 switch(multi_pxo_priv_popup()){
1694 // user hit "cancel"
1696 // return to normal mode
1697 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1702 // setup some information
1703 memset(&priv_chan,0,sizeof(pxo_channel));
1704 priv_chan.num_users = 0;
1705 SDL_strlcpy(priv_chan.name, Multi_pxo_priv_chan, SDL_arraysize(priv_chan.name));
1707 // see if we know about this channel already
1708 multi_pxo_join_channel(&priv_chan);
1710 // return to normal mode
1711 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1717 case MULTI_PXO_MODE_FIND:
1718 switch(multi_pxo_find_popup()){
1723 // user hit "cancel"
1725 // return to normal mode
1726 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1731 // return to normal mode
1732 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1734 // if there is a valid channel name try and join it
1735 if(strlen(Multi_pxo_find_channel) && !SWITCHING_CHANNELS()){
1739 memset(&join,0,sizeof(pxo_channel));
1741 SDL_strlcpy(join.name, Multi_pxo_find_channel, SDL_arraysize(join.name));
1744 multi_pxo_join_channel(&join);
1750 case MULTI_PXO_MODE_NORMAL:
1751 multi_pxo_do_normal();
1756 // close the PXO screen
1757 void multi_pxo_close()
1759 // unload any bitmaps
1760 bm_unload(Multi_pxo_bitmap);
1761 bm_unload(Multi_pxo_com_bitmap);
1763 // record the last channel we were on, if any
1764 SDL_zero(Multi_fs_tracker_channel);
1765 SDL_zero(Multi_fs_tracker_filter);
1767 if( ON_CHANNEL() && strlen(Multi_pxo_channel_current.name) ){
1769 SDL_strlcpy(Multi_fs_tracker_channel, Multi_pxo_channel_current.name, SDL_arraysize(Multi_fs_tracker_channel));
1772 SDL_strlcpy(Multi_fs_tracker_filter, Multi_pxo_channel_current.name, SDL_arraysize(Multi_fs_tracker_filter));
1775 // disconnect from the server
1776 DisconnectFromChatServer();
1777 Multi_pxo_connected = 0;
1779 // unload the animation
1780 anim_release_all_instances(GS_STATE_PXO);
1781 Multi_pxo_anim_instance = NULL;
1782 if(Multi_pxo_anim != NULL){
1783 anim_free(Multi_pxo_anim);
1784 Multi_pxo_anim = NULL;
1787 // unload the palette for this screen
1788 multi_pxo_unload_palette();
1790 // destroy the UI_WINDOW
1791 Multi_pxo_window.destroy();
1793 // clear the channel list
1794 multi_pxo_clear_channels();
1796 // close the chat system
1797 multi_pxo_chat_free();
1800 multi_pxo_ban_close();
1803 static void login_failed_callback(int choice)
1808 nprintf(("Network","PXO CANCEL\n"));
1810 // flip his "pxo" bit temporarily and push him to the join game screen
1811 Multi_options_g.pxo = 0;
1812 gameseq_post_event(GS_EVENT_MULTI_JOIN_GAME);
1813 } else if (choice == 1) {
1814 nprintf(("Network","PXO CREATE\n"));
1816 // fire up the given URL
1817 multi_pxo_url(Multi_options_g.pxo_create_url);
1818 } else if (choice == 2) {
1819 nprintf(("Network","PXO VERIFY\n"));
1821 // fire up the given URL
1822 multi_pxo_url(Multi_options_g.pxo_verify_url);
1825 // go back to the main hall
1826 gameseq_post_event(GS_EVENT_MAIN_MENU);
1829 // run normally (no popups)
1830 void multi_pxo_do_normal()
1833 int k = Multi_pxo_window.process();
1835 // if the animation isn't playing, start it up
1836 if((Multi_pxo_anim_instance == NULL) && (Multi_pxo_anim != NULL)){
1837 anim_play_struct aps;
1839 // fire up the animation
1840 anim_play_init(&aps, Multi_pxo_anim, MULTI_PXO_ANIM_X, MULTI_PXO_ANIM_Y);
1841 aps.screen_id = GS_STATE_PXO;
1842 aps.framerate_independent = 1;
1844 Multi_pxo_anim_instance = anim_play(&aps);
1847 // process any keypresses
1850 gamesnd_play_iface(SND_USER_SELECT);
1851 gameseq_post_event(GS_EVENT_MAIN_MENU);
1855 // check for button presses
1856 multi_pxo_check_buttons();
1858 // if we're not in a chatroom, disable and hide the chat input box
1860 Multi_pxo_chat_input.hide();
1861 Multi_pxo_chat_input.disable();
1863 Multi_pxo_chat_input.enable();
1864 Multi_pxo_chat_input.unhide();
1868 multi_pxo_blit_all();
1873 // verify version # now (only once per Freespace instance)
1874 if(Multi_pxo_must_verify_version){
1875 switch(multi_update_gobaby()){
1876 // everything is cool. Move along
1877 case MULTI_UPDATE_CONTINUE:
1878 Multi_pxo_must_verify_version = 0;
1881 // go back to the main menu
1882 case MULTI_UPDATE_MAIN_MENU:
1883 gameseq_post_event(GS_EVENT_MAIN_MENU);
1885 // unset these so we don't do anything else PXO related
1886 Multi_pxo_must_validate = 0;
1887 Multi_pxo_must_connect = 0;
1890 // freespace will be shutting down shortly
1891 case MULTI_UPDATE_SHUTTING_DOWN:
1896 // if we need to get tracker info for ourselves, do so
1897 if(Multi_pxo_must_validate){
1898 // initialize the master tracker API for Freespace
1899 multi_fs_tracker_init();
1901 // validate the current player with the master tracker (will create the pilot on the MT if necessary)
1902 validate_code = multi_fs_tracker_validate(0);
1903 if(validate_code != 1){
1904 // show an error popup if it failed (not cancelled by the user)
1905 if (validate_code == 0) {
1906 popup_callback(login_failed_callback, PF_USE_AFFIRMATIVE_ICON | PF_WEB_CURSOR_1 | PF_WEB_CURSOR_2, 3,
1908 XSTR("&Create Acct",936),
1909 XSTR("&Verify Acct",937),
1910 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));
1914 Multi_pxo_must_connect = 0;
1915 Multi_pxo_must_validate = 0;
1917 // now we have to conenct to PXO
1919 Multi_pxo_must_connect = 1;
1920 Multi_pxo_must_validate = 0;
1924 // if we need to connect, do so now
1925 if(Multi_pxo_must_connect){
1926 // for now, just try once
1927 Multi_pxo_connected = multi_pxo_connect();
1929 // if we successfully connected, send a request for a list of channels on the server
1930 if(Multi_pxo_connected){
1931 multi_pxo_get_channels();
1934 multi_pxo_set_status_text(XSTR("Retrieving Public Channels",939));
1937 multi_pxo_set_status_text(XSTR("Failed to connect to Parallax Online",940));
1940 // no longer need to connect
1941 Multi_pxo_must_connect = 0;
1945 // blit everything on the "normal" screen
1946 void multi_pxo_blit_all()
1948 // draw the background, etc
1950 // GR_MAYBE_CLEAR_RES(Multi_pxo_bitmap);
1951 int bmap = Multi_pxo_bitmap;
1956 bm_get_info( bmap, &bmw, &bmh);
1957 if((bmw != gr_screen.max_w) || (bmh != gr_screen.max_h)){
1964 if(Multi_pxo_bitmap != -1){
1965 gr_set_bitmap(Multi_pxo_bitmap);
1968 Multi_pxo_window.draw();
1970 // display the channel list
1971 multi_pxo_blit_channels();
1973 // display the player list
1974 multi_pxo_blit_players();
1976 // blit the chat text
1977 multi_pxo_chat_blit();
1979 // blit the status text
1980 multi_pxo_blit_status_text();
1982 // blit and process the notification string
1983 multi_pxo_notify_blit();
1985 // any bitmap or info or whatever
1986 multi_pxo_ban_draw();
1988 // draw any motd stuff
1989 multi_pxo_motd_maybe_blit();
1991 // if we have a valid animation handle, play it
1992 if(Multi_pxo_anim_instance != NULL){
1993 anim_render_all(GS_STATE_PXO,flFrametime);
1997 // process common stuff
1998 void multi_pxo_process_common()
2000 // process the channel list (select, etc)
2001 multi_pxo_process_channels();
2003 // process the player list (select, etc)
2004 multi_pxo_process_players();
2006 // process chat controls
2007 multi_pxo_chat_process();
2009 // process http download details
2010 multi_pxo_ban_process();
2013 // get selected player information
2014 void multi_pxo_get_data(char *name)
2018 // handle being kicked
2019 void multi_pxo_handle_kick()
2021 // remove ourselves from the room
2022 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2023 Multi_pxo_channel_current.num_users = -1;
2026 multi_pxo_chat_clear();
2028 // clear the old player list
2029 multi_pxo_clear_players();
2031 // add a notification string
2032 multi_pxo_notify_add(XSTR("You have been kicked",941));
2035 // handle being disconnected
2036 void multi_pxo_handle_disconnect()
2038 if ( popup_active() ) {
2039 popup_change_text(XSTR("You have been disconnected from the server",942));
2041 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been disconnected from the server",942));
2042 gameseq_post_event(GS_EVENT_MAIN_MENU);
2046 // return string2, which is the first substring of string 1 without a space
2047 // it is safe to pass the same pointer for both parameters
2048 void multi_pxo_strip_space(char *string1, char *string2, const int str2_len)
2053 // copy the original
2054 SDL_strlcpy(midway, string1, SDL_arraysize(midway));
2055 tok = strtok(midway," ");
2057 SDL_strlcpy(string2, tok, str2_len);
2059 SDL_strlcpy(string2, "", str2_len);
2063 // fire up the given URL
2064 void multi_pxo_url(char *url)
2066 if ( platform_open_url(url) ) {
2067 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,XSTR("Warning\nCould not locate/launch default Internet Browser",943));
2071 // load/set the palette
2072 void multi_pxo_load_palette()
2075 //#ifndef HARDWARE_ONLY
2076 // palette_use_bm_palette(Multi_pxo_palette);
2080 // unload the palette
2081 void multi_pxo_unload_palette()
2083 // unload the palette if it exists
2084 if(Multi_pxo_palette != -1){
2085 bm_release(Multi_pxo_palette);
2086 Multi_pxo_palette = -1;
2090 // if we're currently on a private channel
2091 int multi_pxo_on_private_channel()
2093 // if we're connected to a channel with the "+" symbol on front
2094 if(ON_CHANNEL() && (Multi_pxo_channel_current.name[0] == '+')){
2098 // otherwise return falos
2102 // convert string 1 into string 2, substituting underscores for spaces
2103 void multi_pxo_underscore_nick(char *string1, char *string2, const int str2_len)
2105 char nick_temp[512];
2108 // don't do anything if we have bogus string
2109 if((string1 == NULL) || (string2 == NULL)){
2113 // copy the nickname
2114 SDL_strlcpy(nick_temp, string1, SDL_arraysize(nick_temp));
2116 // get the first token
2117 tok = strtok(nick_temp, " ");
2119 SDL_strlcpy(string2, tok, str2_len);
2121 // get the next token
2122 tok = strtok(NULL," ");
2125 SDL_strlcat(string2, "_", str2_len);
2126 SDL_strlcat(string2, tok, str2_len);
2129 tok = strtok(NULL," ");
2132 SDL_strlcpy(string2, string1, str2_len);
2136 // if the command is a potential "nick" command
2137 int multi_pxo_is_nick_command(char *msg)
2142 // get the first token in the message
2143 SDL_strlcpy(tmp, msg, SDL_arraysize(tmp));
2144 tok = strtok(tmp," ");
2146 // can't be a nick message
2150 return !SDL_strcasecmp(tok,NOX("/nick"));
2153 // check for button presses
2154 void multi_pxo_check_buttons()
2158 // go through all buttons
2159 for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
2160 if(Multi_pxo_buttons[gr_screen.res][idx].button.pressed()){
2161 multi_pxo_button_pressed(idx);
2167 // handle a button press
2168 void multi_pxo_button_pressed(int n)
2171 case MULTI_PXO_EXIT:
2172 gamesnd_play_iface(SND_USER_SELECT);
2173 gameseq_post_event(GS_EVENT_MAIN_MENU);
2176 case MULTI_PXO_CHAN_UP:
2177 multi_pxo_scroll_channels_up();
2180 case MULTI_PXO_CHAN_DOWN:
2181 multi_pxo_scroll_channels_down();
2184 case MULTI_PXO_TEXT_UP:
2185 multi_pxo_scroll_chat_up();
2188 case MULTI_PXO_TEXT_DOWN:
2189 multi_pxo_scroll_chat_down();
2192 case MULTI_PXO_PLIST_UP:
2193 multi_pxo_scroll_players_up();
2194 multi_pxo_chat_adjust_start();
2197 case MULTI_PXO_PLIST_DOWN:
2198 multi_pxo_scroll_players_down();
2199 multi_pxo_chat_adjust_start();
2202 case MULTI_PXO_JOIN:
2203 // if there are no channels to join, let the user know
2204 if((Multi_pxo_channel_count == 0) || (Multi_pxo_channels == NULL)){
2205 gamesnd_play_iface(SND_GENERAL_FAIL);
2206 multi_pxo_notify_add(XSTR("No channels!",944));
2210 // if we're not already trying to join, allow this
2211 if(!SWITCHING_CHANNELS() && (Multi_pxo_channel_select != NULL)){
2212 gamesnd_play_iface(SND_USER_SELECT);
2213 multi_pxo_join_channel(Multi_pxo_channel_select);
2215 multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2216 gamesnd_play_iface(SND_GENERAL_FAIL);
2220 case MULTI_PXO_GAMES:
2221 // move to the join game screen as normally (temporary!)
2222 gameseq_post_event( GS_EVENT_MULTI_JOIN_GAME );
2225 case MULTI_PXO_JOIN_PRIV:
2226 // if we're not already trying to join, allow this
2227 if(!SWITCHING_CHANNELS()){
2228 gamesnd_play_iface(SND_USER_SELECT);
2230 // fire up the private join popup
2231 multi_pxo_priv_popup();
2233 multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2234 gamesnd_play_iface(SND_GENERAL_FAIL);
2238 case MULTI_PXO_FIND:
2239 gamesnd_play_iface(SND_USER_SELECT);
2241 // fire up the find join popup
2242 multi_pxo_find_popup();
2245 case MULTI_PXO_HELP:
2246 gamesnd_play_iface(SND_USER_SELECT);
2247 gameseq_post_event(GS_EVENT_PXO_HELP);
2250 case MULTI_PXO_PINFO:
2253 // if we have a guy selected, try and get his info
2254 if(Multi_pxo_player_select != NULL){
2255 // if we successfully got info for this guy
2256 if(multi_pxo_pinfo_get(Multi_pxo_player_select->name)){
2258 multi_pxo_pinfo_show();
2260 // if we didn't get stats for this guy.
2262 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);
2263 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,stats);
2266 gamesnd_play_iface(SND_GENERAL_FAIL);
2270 case MULTI_PXO_RANKINGS:
2271 // make sure he doesn't click it too many times
2272 if((Multi_pxo_ranking_last < 0.0f) || ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_ranking_last) > MULTI_PXO_RANK_TIME) ){
2273 gamesnd_play_iface(SND_USER_SELECT);
2276 multi_pxo_url(Multi_options_g.pxo_rank_url);
2278 // mark the time down
2279 Multi_pxo_ranking_last = f2fl(timer_get_fixed_seconds());
2281 gamesnd_play_iface(SND_GENERAL_FAIL);
2285 case MULTI_PXO_MOTD:
2286 // maybe fire up the pxo motd dialog
2287 multi_pxo_motd_dialog();
2293 // condition function for popup_do_with_condition for connected to Parallax Online
2294 int mpxo_failed = 0;
2295 int multi_pxo_connect_do()
2298 char id_string[255] = "";
2299 char ip_string[255] = "";
2301 // if we already tried and failed, sit around until the user presses cancel
2303 // try and connect to the server
2306 // build the tracker id string
2307 SDL_snprintf(id_string, SDL_arraysize(id_string), "%s %s", Multi_tracker_id_string, Player->callsign);
2309 // build the ip string
2310 SDL_snprintf(ip_string, SDL_arraysize(ip_string), "%s:%d", Multi_options_g.pxo_ip, PXO_CHAT_PORT);
2312 // connect to the server
2313 ret_code = ConnectToChatServer(ip_string, Multi_pxo_nick, id_string);
2315 // give some time to the pxo api.
2316 multi_pxo_api_process();
2319 // already connected, return success
2323 // failed to connect, return fail
2326 popup_change_text(XSTR("Failed to connect to Parallax Online!", 947));
2329 // connected, return success
2342 // popup loop which does an autojoin of a public channel. Returns when the autojoin process is complete
2343 int multi_pxo_autojoin_do()
2345 pxo_channel last_channel;
2347 // if we need to autojoin, do so now
2348 if(Multi_pxo_must_autojoin){
2349 Multi_pxo_must_autojoin = 0;
2351 // if we're supposed to be using a (valid) "last" channel, do so
2352 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){
2354 memset(&last_channel, 0, sizeof(pxo_channel));
2355 last_channel.num_users = 0;
2356 SDL_strlcpy(last_channel.name, Multi_pxo_channel_last, SDL_arraysize(last_channel.name));
2359 multi_pxo_join_channel(&last_channel);
2361 nprintf(("Network","PXO : using last channel\n"));
2363 multi_pxo_autojoin();
2365 nprintf(("Network","PXO : using autojoin channel\n"));
2367 multi_pxo_get_channels();
2370 // give some time to the pxo api.
2371 multi_pxo_api_process();
2372 multi_pxo_process_common();
2374 // next value is not -1 when actually switching channels, so keep processing by returning 0.
2375 if ( SWITCHING_CHANNELS() ){
2379 // couldn't switch channel for some reason. bail out with -1
2380 if ( !ON_CHANNEL() ){
2388 // attempt to connect to Parallax Online, return success or fail
2389 int multi_pxo_connect()
2392 char join_fail_str[256];
2394 // intiialize chat api
2397 // set us to "must autojoin"
2398 Multi_pxo_must_autojoin = 1;
2400 // run the connect dialog/popup
2402 if(popup_till_condition(multi_pxo_connect_do, XSTR("&Cancel", 779), XSTR("Logging into Parallax Online",949)) == 10){
2405 // if we're going to use the "last" channel
2406 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){
2407 SDL_strlcpy(join_str, XSTR("Joining last channel (",982), SDL_arraysize(join_str));
2408 SDL_strlcat(join_str, Multi_pxo_channel_last + 1, SDL_arraysize(join_str));
2409 SDL_strlcat(join_str, ")", SDL_arraysize(join_str));
2411 SDL_strlcpy(join_fail_str, XSTR("Unable to join last channel", 983), SDL_arraysize(join_fail_str));
2413 SDL_strlcpy(join_str, XSTR("Autojoining public channel", 984), SDL_arraysize(join_str));
2414 SDL_strlcpy(join_fail_str, XSTR("Unable to autojoin public channel", 985), SDL_arraysize(join_fail_str));
2417 // once connected, we should do an autojoin before allowing the guy to continue.
2418 rval = popup_till_condition( multi_pxo_autojoin_do, XSTR("&Cancel", 779), join_str );
2423 popup( PF_USE_AFFIRMATIVE_ICON, 1, XSTR("OK", 1492), join_fail_str);
2426 // otherwise disconnect just to be safe
2427 DisconnectFromChatServer();
2428 gameseq_post_event(GS_EVENT_MAIN_MENU);
2430 // did not successfully connect
2434 // run the networking functions for the PXO API
2435 void multi_pxo_api_process()
2440 pxo_channel *lookup;
2443 // give some time to psnet
2444 PSNET_TOP_LAYER_PROCESS();
2447 // give some time to the game tracker API
2450 // give some time to the user tracker API
2453 // get any incoming text
2459 // process the chat line
2460 multi_pxo_chat_process_incoming(p);
2464 // get any incoming channel list stuff
2465 p = GetChannelList();
2468 // nprintf(("Network","%s\n",p));
2469 multi_pxo_make_channels(p);
2472 // process any chat commands
2473 cmd = GetChatCommand();
2476 switch(cmd->command)
2478 case CC_USER_JOINING:
2479 // add a user, if he doesn't already exist
2480 if(multi_pxo_find_player(cmd->data) == NULL){
2481 multi_pxo_add_player(cmd->data);
2484 // increase the player count
2486 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2488 lookup->num_users++;
2493 case CC_USER_LEAVING:
2495 multi_pxo_del_player(cmd->data);
2497 // add a text message
2498 SDL_snprintf(msg_str, SDL_arraysize(msg_str), XSTR("*** %s has left", 950), cmd->data);
2499 multi_pxo_chat_process_incoming(msg_str);
2501 // decrease the player count
2503 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2505 lookup->num_users--;
2510 case CC_DISCONNECTED:
2511 multi_pxo_handle_disconnect();
2515 multi_pxo_handle_kick();
2518 case CC_NICKCHANGED:
2519 // process a nick change
2520 multi_pxo_process_nick_change(cmd->data);
2523 case CC_YOURCHANNEL:
2524 // copy the current channel info, and unset the switching status
2525 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2526 Multi_pxo_channel_switch.num_users = -1;
2528 SetNewChatChannel(NULL);
2530 SDL_strlcpy(Multi_pxo_channel_current.name, cmd->data, SDL_arraysize(Multi_pxo_channel_current.name));
2532 // if we don't already have this guy on the list, add him
2533 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2535 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2536 lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2539 // set the user count to be 0
2541 lookup->num_users = 0;
2544 // set our "last" channel to be this one
2545 SDL_strlcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name, SDL_arraysize(Multi_pxo_channel_last));
2547 // refresh current channel server count
2548 multi_pxo_channel_refresh_current();
2550 // clear the chat area
2551 // multi_pxo_chat_clear();
2558 cmd = GetChatCommand();
2561 // handle any processing details if we're currently trying to join a channel
2562 multi_pxo_handle_channel_change();
2565 // process a "nick" change event
2566 void multi_pxo_process_nick_change(char *data)
2569 player_list *lookup;
2571 // get the new string
2572 from = strtok(data," ");
2573 to = strtok(NULL,"");
2574 if((from != NULL) && (to != NULL)){
2575 lookup = multi_pxo_find_player(from);
2577 SDL_strlcpy(lookup->name, to, SDL_arraysize(lookup->name));
2579 // if this is also my nick, change it
2580 if(!SDL_strcasecmp(Multi_pxo_nick,from)){
2581 SDL_strlcpy(Multi_pxo_nick, to, SDL_arraysize(Multi_pxo_nick));
2587 // autojoin an appropriate channel
2588 void multi_pxo_autojoin()
2592 memset(&sw,0,sizeof(pxo_channel));
2594 SDL_strlcpy(sw.name, MULTI_PXO_AUTOJOIN_CHANNEL, SDL_arraysize(sw.name));
2596 // if we found a valid room, attempt to join it
2597 multi_pxo_join_channel(&sw);
2600 // does the string match the "autojoin" prefic
2601 int multi_pxo_is_autojoin(char *name)
2603 // check to see if the name is long enough
2604 if(strlen(name) < strlen(MULTI_PXO_AUTOJOIN_PREFIX)){
2608 // check to see if the first n chars match
2609 return !SDL_strncasecmp(name,MULTI_PXO_AUTOJOIN_PREFIX,strlen(MULTI_PXO_AUTOJOIN_PREFIX));
2612 // called from the game tracker API - server count update for a channel
2613 void multi_pxo_channel_count_update(char *name,int count)
2615 pxo_channel *lookup;
2617 // lookup the channel name on the normal list
2619 lookup = multi_pxo_find_channel(name,Multi_pxo_channels);
2621 lookup->num_servers = (ushort)count;
2623 nprintf(("Network","PXO : updated channel %s server count to %d\n",name,count));
2627 // status bar stuff -----------------------------------------------
2629 // set the status text
2630 void multi_pxo_set_status_text(const char *txt)
2633 SDL_strlcpy(Multi_pxo_status_text, txt, SDL_arraysize(Multi_pxo_status_text));
2635 // make sure it fits properly
2636 gr_force_fit_string(Multi_pxo_status_text, 254, Multi_pxo_status_coords[gr_screen.res][2]);
2639 // blit the status text
2640 void multi_pxo_blit_status_text()
2644 // center and draw the text
2645 if(strlen(Multi_pxo_status_text)) {
2646 gr_set_color_fast(&Color_bright);
2647 gr_get_string_size(&w, NULL, Multi_pxo_status_text);
2648 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);
2653 // channel related stuff -------------------------------------------
2655 // get a list of channels on the server
2656 void multi_pxo_get_channels()
2658 SendChatString(NOX("/list"));
2661 // clear the old channel list
2662 void multi_pxo_clear_channels()
2664 pxo_channel *moveup,*backup;
2666 // only clear a non-null list
2667 if(Multi_pxo_channels != NULL){
2669 moveup = Multi_pxo_channels;
2674 moveup = moveup->next;
2676 // free the struct itself
2679 } while(moveup != Multi_pxo_channels);
2680 Multi_pxo_channels = NULL;
2683 // head of the list of available channels
2684 Multi_pxo_channels = NULL;
2685 Multi_pxo_channel_count = 0;
2687 // item we're going to start displaying at
2688 Multi_pxo_channel_start = NULL;
2689 Multi_pxo_channel_start_index = -1;
2691 // items we've currently got selected
2692 Multi_pxo_channel_select = NULL;
2696 // parse the input string and make a list of new channels
2697 void multi_pxo_make_channels(char *chan_str)
2699 char *name_tok,*user_tok,*desc_tok;
2701 pxo_channel *lookup;
2704 nprintf(("Network","Making some channels!\n"));
2706 // clear the channel list
2707 // multi_pxo_clear_channels();
2709 // set the last get time
2710 Multi_pxo_channel_last_refresh = f2fl(timer_get_fixed_seconds());
2712 name_tok = strtok(chan_str," ");
2713 if(name_tok == NULL){
2718 // parse the user count token
2719 user_tok = strtok(NULL," ");
2721 // parse the channel description token
2722 desc_tok = strtok(NULL,"$");
2724 // something invalid in the data, return here.....
2725 if((name_tok == NULL) || (user_tok == NULL) || (desc_tok == NULL)){
2729 // get the # of users
2730 num_users = (ubyte)atoi(user_tok);
2732 // if the # of users is > 0, or its not an autojoin, place it on the display list
2733 if((num_users > 0) || !multi_pxo_is_autojoin(name_tok)){
2734 // see if it exists already, and if so, just update the user count
2735 lookup = multi_pxo_find_channel(name_tok,Multi_pxo_channels);
2738 lookup->num_users = (short)num_users;
2742 res = multi_pxo_add_channel(name_tok,&Multi_pxo_channels);
2744 //Multi_pxo_channel_count++;
2745 res->num_users = (short)num_users;
2746 SDL_strlcpy(res->desc, desc_tok, SDL_arraysize(res->desc));
2751 // get the next name token
2752 name_tok = strtok(NULL," ");
2753 } while(name_tok != NULL);
2755 // if we need to autojoin, do so now
2756 //if(Multi_pxo_must_autojoin){
2757 // Multi_pxo_must_autojoin = 0;
2759 // multi_pxo_autojoin();
2763 multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
2765 // if we haven't refreshed server counts yet, do it now
2766 if(Multi_pxo_channel_server_refresh < 0.0f){
2767 multi_pxo_channel_refresh_servers();
2770 // if we don't already have this guy on the list, add him
2772 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2774 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2775 multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2780 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2781 pxo_channel *multi_pxo_add_channel(char *name,pxo_channel **list)
2783 pxo_channel *new_channel;
2785 // try and allocate a new pxo_channel struct
2786 new_channel = (pxo_channel *)malloc(sizeof(pxo_channel));
2787 if ( new_channel == NULL ) {
2788 nprintf(("Network", "Cannot allocate space for new pxo_channel structure\n"));
2791 memset(new_channel,0,sizeof(pxo_channel));
2792 // try and allocate a string for the channel name
2793 SDL_strlcpy(new_channel->name, name, SDL_arraysize(new_channel->name));
2795 // insert it on the list
2796 if ( *list != NULL ) {
2797 new_channel->next = (*list)->next;
2798 new_channel->next->prev = new_channel;
2799 (*list)->next = new_channel;
2800 new_channel->prev = *list;
2802 *list = new_channel;
2803 (*list)->next = (*list)->prev = *list;
2806 Multi_pxo_channel_count++;
2810 // lookup a channel with the specified name
2811 pxo_channel *multi_pxo_find_channel(char *name,pxo_channel *list)
2813 pxo_channel *moveup;
2815 // look the sucker up
2821 if(!SDL_strcasecmp(name,moveup->name)){
2825 moveup = moveup->next;
2826 } while((moveup != list) && (moveup != NULL));
2831 // process the channel list (select, etc)
2832 void multi_pxo_process_channels()
2837 // if we don't have a start item, but the list is non-null
2838 if((Multi_pxo_channel_start == NULL) && (Multi_pxo_channels != NULL)){
2839 Multi_pxo_channel_start = Multi_pxo_channels;
2840 Multi_pxo_channel_start_index = 0;
2843 // if we don't have a selected item, but the list is non-null
2844 if((Multi_pxo_channel_select == NULL) && (Multi_pxo_channels != NULL)){
2845 Multi_pxo_channel_select = Multi_pxo_channels;
2848 multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2851 // if the "switch" delay timestamp is set, see if it has expired
2852 if((Multi_pxo_switch_delay != -1) && timestamp_elapsed(Multi_pxo_switch_delay)){
2853 Multi_pxo_switch_delay = -1;
2856 // see if we have a mouse click on the channel region
2857 if(Multi_pxo_channel_button.pressed()){
2858 Multi_pxo_channel_button.get_mouse_pos(NULL,&my);
2860 // index from the top
2861 item_index = my / 10;
2863 // select the item if possible
2864 if((item_index + Multi_pxo_channel_start_index) < Multi_pxo_channel_count){
2865 Multi_pxo_channel_select = Multi_pxo_channel_start;
2866 for(idx=0;idx<item_index;idx++){
2867 Multi_pxo_channel_select = Multi_pxo_channel_select->next;
2871 multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2875 // last refresh time
2876 if((Multi_pxo_channel_last_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_last_refresh) > CHANNEL_REFRESH_TIME) ){
2878 multi_pxo_set_status_text(XSTR("Refreshing Public Channel List",952));
2880 // get a list of channels on the server (clear any old list as well)
2881 multi_pxo_get_channels();
2884 Multi_pxo_channel_last_refresh = -1.0f;
2886 nprintf(("Network","Refreshing channels\n"));
2889 // if we haven't updated our server channel counts in a while, do so again
2890 // last refresh time
2891 if((Multi_pxo_channel_server_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_server_refresh) > CHANNEL_SERVER_REFRESH_TIME) ){
2892 // refresh server counts
2893 // multi_pxo_set_status_text("Refreshing Public Channel Server Counts");
2895 // do it _NOW_ I"M RIGHT HERE KILL ME WHAT ARE YOU WAITING FOR DO IT KILL ME DO IT NOW!
2896 multi_pxo_channel_refresh_servers();
2900 // send a request to refresh our channel server counts
2901 void multi_pxo_channel_refresh_servers()
2903 pxo_channel *lookup;
2904 filter_game_list_struct filter;
2906 // traverse the list of existing channels we know about and query the game tracker about them
2907 lookup = Multi_pxo_channels;
2912 if(strlen(lookup->name)){
2914 memset(&filter,0,sizeof(filter_game_list_struct));
2915 SDL_strlcpy(filter.channel, lookup->name, SDL_arraysize(filter.channel));
2918 RequestGameCountWithFilter(&filter);
2922 lookup = lookup->next;
2923 } while((lookup != NULL) && (lookup != Multi_pxo_channels));
2926 Multi_pxo_channel_server_refresh = f2fl(timer_get_fixed_seconds());
2929 // refresh current channel server count
2930 void multi_pxo_channel_refresh_current()
2932 // send a request for a server count on this channel
2933 if(strlen(Multi_pxo_channel_current.name)){
2935 filter_game_list_struct filter;
2936 memset(&filter,0,sizeof(filter_game_list_struct));
2937 SDL_strlcpy(filter.channel, Multi_pxo_channel_current.name, SDL_arraysize(filter.channel));
2940 RequestGameCountWithFilter(&filter);
2944 // display the channel list
2945 void multi_pxo_blit_channels()
2947 pxo_channel *moveup;
2948 char chan_name[255];
2949 char chan_users[15];
2950 char chan_servers[15];
2951 int user_w,server_w;
2952 int disp_count,y_start;
2954 // blit as many channels as we can
2956 y_start = Multi_pxo_chan_coords[gr_screen.res][1];
2957 moveup = Multi_pxo_channel_start;
2962 // if this is the currently selected item, highlight it
2963 if(moveup == Multi_pxo_channel_select){
2964 gr_set_color_fast(&Color_bright);
2966 // otherwise draw it normally
2968 gr_set_color_fast(&Color_normal);
2971 // get the # of users on the channel
2972 SDL_snprintf(chan_users, SDL_arraysize(chan_users), "%d", moveup->num_users);
2974 // get the width of the user count string
2975 gr_get_string_size(&user_w, NULL, chan_users);
2977 // get the # of servers on the channel
2978 SDL_snprintf(chan_servers, SDL_arraysize(chan_servers), "%d", moveup->num_servers);
2980 // get the width of the user count string
2981 gr_get_string_size(&server_w, NULL, chan_servers);
2983 // make sure the name fits
2984 SDL_assert(moveup->name);
2985 SDL_strlcpy(chan_name, moveup->name, SDL_arraysize(chan_name));
2986 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]);
2989 gr_string(Multi_pxo_chan_coords[gr_screen.res][0], y_start, chan_name + 1);
2990 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);
2991 gr_set_color_fast(&Color_bright);
2992 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);
2994 // increment the displayed count
2999 moveup = moveup->next;
3000 } while((moveup != Multi_pxo_channels) && (disp_count < Multi_pxo_max_chan_display[gr_screen.res]));
3003 // scroll channel list up
3004 void multi_pxo_scroll_channels_up()
3006 // if we're already at the head of the list, do nothing
3007 if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start == Multi_pxo_channels)){
3008 gamesnd_play_iface(SND_GENERAL_FAIL);
3012 // otherwise move up one
3013 Multi_pxo_channel_start = Multi_pxo_channel_start->prev;
3014 Multi_pxo_channel_start_index--;
3015 gamesnd_play_iface(SND_USER_SELECT);
3018 // scroll channel list down
3019 void multi_pxo_scroll_channels_down()
3021 // if we're already at the tail of the list, do nothing
3022 if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start->next == Multi_pxo_channels)){
3023 gamesnd_play_iface(SND_GENERAL_FAIL);
3027 // if we can't scroll further without going past the end of the viewable list, don't
3028 if((Multi_pxo_channel_start_index + Multi_pxo_max_chan_display[gr_screen.res]) >= Multi_pxo_channel_count){
3029 gamesnd_play_iface(SND_GENERAL_FAIL);
3033 // otherwise move down one
3034 Multi_pxo_channel_start = Multi_pxo_channel_start->next;
3035 Multi_pxo_channel_start_index++;
3036 gamesnd_play_iface(SND_USER_SELECT);
3039 // attempt to join a channel
3040 void multi_pxo_join_channel(pxo_channel *chan)
3042 char switch_msg[256];
3044 // if we're already on this channel, do nothing
3045 if(ON_CHANNEL() && !SDL_strcasecmp(chan->name,Multi_pxo_channel_current.name)){
3049 // if we're already trying to join a channel, do nothing
3050 if(SWITCHING_CHANNELS()){
3054 // try and join the channel
3055 switch(SetNewChatChannel(chan->name)){
3061 // decrement the count of our current channel
3062 pxo_channel *lookup;
3063 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
3065 lookup->num_users--;
3068 // set our current channel as none
3069 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
3070 Multi_pxo_channel_current.num_users = -1;
3072 multi_pxo_set_status_text(XSTR("Switching channels",953));
3075 memcpy(&Multi_pxo_channel_switch,chan,sizeof(pxo_channel));
3077 // clear the player list
3078 multi_pxo_clear_players();
3080 // display a line of text indicating that we're switching channels
3081 if(strlen(Multi_pxo_channel_switch.name) > 1){
3082 SDL_snprintf(switch_msg, SDL_arraysize(switch_msg), "[Switching to channel %s]", Multi_pxo_channel_switch.name + 1);
3084 SDL_snprintf(switch_msg, SDL_arraysize(switch_msg), "[Switching to channel %s]", Multi_pxo_channel_switch.name);
3086 multi_pxo_chat_process_incoming(switch_msg, CHAT_MODE_CHANNEL_SWITCH);
3094 // handle any processing details if we're currently trying to join a channel
3095 void multi_pxo_handle_channel_change()
3097 // if we're not switching channels, do nothing
3098 if(!SWITCHING_CHANNELS()){
3102 // if we are, check the status
3103 switch(SetNewChatChannel(NULL)){
3106 // unset our switching struct
3107 memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
3108 Multi_pxo_channel_switch.num_users = -1;
3111 multi_pxo_set_status_text(XSTR("No channel (error while switching)",954));
3118 // successfully changed
3120 // copy the current channel info, and unset the switching status
3121 memcpy(&Multi_pxo_channel_current,&Multi_pxo_channel_switch,sizeof(pxo_channel));
3122 Multi_pxo_channel_switch.num_users = -1;
3124 // set our "last" channel
3125 SDL_strlcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name, SDL_arraysize(Multi_pxo_channel_last));
3128 multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
3130 // if we don't already have this guy on the list, add him
3131 pxo_channel *lookup;
3132 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
3134 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
3135 lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
3138 // set the user count to be 1 (just me)
3140 lookup->num_users = 1;
3143 // clear the chat area
3144 // multi_pxo_chat_clear();
3146 // set the "switch" delay timestamp
3147 Multi_pxo_switch_delay = timestamp(MULTI_PXO_SWITCH_DELAY_TIME);
3149 // refresh current channel server count
3150 multi_pxo_channel_refresh_current();
3156 // player related stuff -------------------------------------------
3158 // clear the old player list
3159 void multi_pxo_clear_players()
3161 player_list *moveup,*backup;
3163 // if the list is null, don't free it up
3164 if(Multi_pxo_players != NULL){
3166 moveup = Multi_pxo_players;
3171 moveup = moveup->next;
3173 // free the struct itself
3176 } while(moveup != Multi_pxo_players);
3177 Multi_pxo_players = NULL;
3181 Multi_pxo_player_start = NULL;
3182 // Multi_pxo_player_start_index = -1;
3183 Multi_pxo_player_select = NULL;
3186 // Multi_pxo_player_slider.set_numberItems(0);
3188 // add a bunch of bogus players
3191 for(int idx=0;idx<30;idx++){
3192 sprintf(str,"player%d",idx);
3193 multi_pxo_add_player(str);
3198 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
3199 player_list *multi_pxo_add_player(char *name)
3201 player_list *new_player;
3203 // try and allocate a new player_list struct
3204 new_player = (player_list *)malloc(sizeof(player_list));
3205 if ( new_player == NULL ) {
3206 nprintf(("Network", "Cannot allocate space for new player_list structure\n"));
3209 // try and allocate a string for the channel name
3210 SDL_strlcpy(new_player->name, name, SDL_arraysize(new_player->name));
3212 // insert it on the list
3213 if ( Multi_pxo_players != NULL ) {
3214 new_player->next = Multi_pxo_players->next;
3215 new_player->next->prev = new_player;
3216 Multi_pxo_players->next = new_player;
3217 new_player->prev = Multi_pxo_players;
3219 Multi_pxo_players = new_player;
3220 Multi_pxo_players->next = Multi_pxo_players->prev = Multi_pxo_players;
3223 // increment the start position
3224 if(Multi_pxo_player_start != NULL){
3225 // Multi_pxo_player_start_index++;
3229 Multi_pxo_player_count++;
3231 // update the count on the slider
3232 // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3237 // remove a player with the given name
3238 void multi_pxo_del_player(char *name)
3240 player_list *lookup;
3242 // try and find this guy
3243 lookup = Multi_pxo_players;
3248 // if we found a match, delete it
3249 if(!SDL_strcasecmp(name,lookup->name)){
3250 // if this is the only item on the list, free stuff up
3251 if(lookup->next == lookup){
3252 SDL_assert(lookup == Multi_pxo_players);
3254 Multi_pxo_players = NULL;
3255 multi_pxo_clear_players();
3257 // otherwise, just delete it
3259 lookup->next->prev = lookup->prev;
3260 lookup->prev->next = lookup->next;
3262 // if this was our selected item, unselect it
3263 if((lookup == Multi_pxo_player_select) && (Multi_pxo_player_select != NULL)){
3264 Multi_pxo_player_select = Multi_pxo_player_select->next;
3267 // if this was our point to start viewing from, select another
3268 if(lookup == Multi_pxo_player_start){
3269 // if this is the head of the list, move up one
3270 if(Multi_pxo_players == lookup){
3271 Multi_pxo_player_start = Multi_pxo_player_start->next;
3272 // Multi_pxo_player_start_index = 0;
3274 // otherwise move back
3276 Multi_pxo_player_start = Multi_pxo_player_start->prev;
3277 // Multi_pxo_player_start_index++;
3281 // if this is the head of the list, move it up
3282 if(lookup == Multi_pxo_players){
3283 Multi_pxo_players = Multi_pxo_players->next;
3287 lookup->next = NULL;
3288 lookup->prev = NULL;
3293 Multi_pxo_player_count--;
3294 SDL_assert(Multi_pxo_player_count >= 0);
3296 // update the count on the slider
3297 // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3298 // Multi_pxo_player_slider.force_currentItem(Multi_pxo_player_start_index);
3305 lookup = lookup->next;
3306 } while((lookup != NULL) && (lookup != Multi_pxo_players));
3309 // try and find a player with the given name, return a pointer to his entry (or NULL)
3310 player_list *multi_pxo_find_player(char *name)
3312 player_list *lookup;
3314 // look through all players
3315 lookup = Multi_pxo_players;
3320 if(!SDL_strcasecmp(name,lookup->name)){
3324 lookup = lookup->next;
3325 } while((lookup != NULL) && (lookup != Multi_pxo_players));
3331 // process the player list (select, etc)
3332 void multi_pxo_process_players()
3335 player_list *lookup;
3337 // if we don't have a start item, but the list is non-null
3338 if((Multi_pxo_player_start == NULL) && (Multi_pxo_players != NULL)){
3339 Multi_pxo_player_start = Multi_pxo_players;
3340 // Multi_pxo_player_start_index = 0;
3342 // update the slider
3343 // Multi_pxo_player_slider.set_currentItem(Multi_pxo_player_start_index);
3346 // if we don't have a selected item, but the list is non-null
3347 if((Multi_pxo_player_select == NULL) && (Multi_pxo_players != NULL)){
3348 Multi_pxo_player_select = Multi_pxo_players;
3351 // see if we have a mouse click on the channel region
3352 if(Multi_pxo_player_button.pressed()){
3353 Multi_pxo_player_button.get_mouse_pos(NULL,&my);
3355 // index from the top
3356 item_index = my / 10;
3358 // select the item if possible
3359 lookup = Multi_pxo_player_start;
3364 if(item_index == 0){
3365 Multi_pxo_player_select = Multi_pxo_player_start;
3369 // move to the next item
3370 lookup = lookup->next;
3373 // if this item is our guy
3374 if((item_index == 0) && (lookup != Multi_pxo_players)){
3375 Multi_pxo_player_select = lookup;
3378 } while((lookup != Multi_pxo_players) && (item_index > 0));
3382 // display the player list
3383 void multi_pxo_blit_players()
3385 player_list *moveup;
3386 char player_name[255];
3387 int disp_count,y_start;
3389 // blit as many channels as we can
3391 y_start = Multi_pxo_player_coords[gr_screen.res][1];
3392 moveup = Multi_pxo_player_start;
3397 // if this is the currently selected item, highlight it
3398 if(moveup == Multi_pxo_player_select){
3399 gr_set_color_fast(&Color_bright);
3401 // otherwise draw it normally
3403 gr_set_color_fast(&Color_normal);
3406 // make sure the string fits
3407 SDL_strlcpy(player_name, moveup->name, SDL_arraysize(player_name));
3408 gr_force_fit_string(player_name, 254, Multi_pxo_player_coords[gr_screen.res][2]);
3411 gr_string(Multi_pxo_player_coords[gr_screen.res][0], y_start, player_name);
3413 // increment the displayed count
3418 moveup = moveup->next;
3419 } while((moveup != Multi_pxo_players) && (disp_count < Multi_pxo_max_player_display[gr_screen.res]));
3422 // scroll player list up
3423 void multi_pxo_scroll_players_up()
3425 // if we're already at the head of the list, do nothing
3426 if((Multi_pxo_player_start == NULL) || (Multi_pxo_player_start == Multi_pxo_players)){
3427 gamesnd_play_iface(SND_GENERAL_FAIL);
3431 // otherwise move up one
3432 Multi_pxo_player_start = Multi_pxo_player_start->prev;
3433 // Multi_pxo_player_start_index--;
3434 // SDL_assert(Multi_pxo_player_start_index >= 0);
3436 gamesnd_play_iface(SND_USER_SELECT);
3439 // scroll player list down
3440 void multi_pxo_scroll_players_down()
3442 player_list *lookup;
3445 // see if its okay to scroll down
3446 lookup = Multi_pxo_player_start;
3447 if(lookup == NULL ){
3448 gamesnd_play_iface(SND_GENERAL_FAIL);
3452 while(lookup->next != Multi_pxo_players){
3453 lookup = lookup->next;
3457 // if we can move down
3458 if(count >= Multi_pxo_max_player_display[gr_screen.res]){
3459 Multi_pxo_player_start = Multi_pxo_player_start->next;
3461 // Multi_pxo_player_start_index++;
3463 gamesnd_play_iface(SND_USER_SELECT);
3465 gamesnd_play_iface(SND_GENERAL_FAIL);
3470 // chat text stuff -----------------------------------------
3472 // initialize and create the chat text linked list
3473 void multi_pxo_chat_init()
3476 chat_line *new_line;
3479 Multi_pxo_chat = NULL;
3480 Multi_pxo_chat_add = NULL;
3481 Multi_pxo_chat_start = NULL;
3482 Multi_pxo_chat_start_index = -1;
3484 // create the lines in a non-circular doubly linked list
3485 for(idx=0;idx<MAX_CHAT_LINES;idx++){
3486 new_line = (chat_line*)malloc(sizeof(chat_line));
3488 // clear the line out
3489 SDL_assert(new_line != NULL);
3490 if(new_line == NULL){
3493 memset(new_line,0,sizeof(chat_line));
3494 new_line->prev = NULL;
3495 new_line->next = NULL;
3497 // insert it into the (empty) list
3498 if(Multi_pxo_chat == NULL){
3499 Multi_pxo_chat = new_line;
3501 // insert it onto the (non-empty) list
3503 Multi_pxo_chat->prev = new_line;
3504 new_line->next = Multi_pxo_chat;
3505 Multi_pxo_chat = new_line;
3509 // start adding chat lines at the beginning of the list
3510 Multi_pxo_chat_add = Multi_pxo_chat;
3513 // free up all chat list stuff
3514 void multi_pxo_chat_free()
3516 chat_line *moveup, *backup;
3518 // free all items up
3519 moveup = Multi_pxo_chat;
3520 while(moveup != NULL){
3522 moveup = moveup->next;
3528 Multi_pxo_chat = NULL;
3529 Multi_pxo_chat_add = NULL;
3530 Multi_pxo_chat_start = NULL;
3531 Multi_pxo_chat_start_index = -1;
3532 Multi_pxo_chat_count = 0;
3534 Multi_pxo_chat_slider.set_numberItems(0);
3538 // clear all lines of chat text in the chat area
3539 void multi_pxo_chat_clear()
3543 // clear the text in all the lines
3544 moveup = Multi_pxo_chat;
3545 while(moveup != NULL){
3546 SDL_zero(moveup->text);
3547 moveup = moveup->next;
3550 // how many chat lines we have
3551 Multi_pxo_chat_count = 0;
3553 // start adding chat lines at the beginning of the list
3554 Multi_pxo_chat_add = Multi_pxo_chat;
3557 // add a line of text
3558 void multi_pxo_chat_add_line(char *txt, int mode)
3563 SDL_assert(Multi_pxo_chat_add != NULL);
3564 SDL_strlcpy(Multi_pxo_chat_add->text, txt, SDL_arraysize(Multi_pxo_chat_add->text));
3565 Multi_pxo_chat_add->mode = mode;
3567 // if we're at the end of the list, move the front item down
3568 if(Multi_pxo_chat_add->next == NULL) {
3569 // store the new "head" of the list
3570 temp = Multi_pxo_chat->next;
3572 // move the current head to the end of the list
3573 Multi_pxo_chat_add->next = Multi_pxo_chat;
3575 Multi_pxo_chat->prev = Multi_pxo_chat_add;
3576 Multi_pxo_chat->next = NULL;
3578 // reset the head of the list
3579 Multi_pxo_chat = temp;
3581 // set the new add line
3582 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3583 SDL_zero(Multi_pxo_chat_add->text);
3584 Multi_pxo_chat_add->mode = CHAT_MODE_NORMAL;
3586 // if we're not at the end of the list, just move up by one
3588 // set the new add line
3589 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3592 // if we've reached max chat lines, don't increment
3593 if(Multi_pxo_chat_count < MAX_CHAT_LINES) {
3594 Multi_pxo_chat_count++;
3599 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
3602 // force the position, in case we arent at the bottom of the list
3605 // move to the bottom of the chat area
3608 multi_pxo_goto_bottom();
3611 // if we have more than the # of lines displayable
3612 if(Multi_pxo_chat_count >= MULTI_PXO_MAX_CHAT_DISPLAY){
3614 multi_pxo_goto_bottom();
3617 multi_pxo_goto_bottom();
3620 // process an incoming line of text
3621 void multi_pxo_chat_process_incoming(const char *txt,int mode)
3623 char msg_total[512],line[512];
3626 char *p_str[20]; // the initial line (unindented)
3627 const char *priv_ptr = NULL;
3629 // filter out "has left" channel messages, when switching channels
3630 if((SWITCHING_CHANNELS() || ((Multi_pxo_switch_delay != -1) && !timestamp_elapsed(Multi_pxo_switch_delay))) &&
3631 multi_pxo_chat_is_left_message(txt)){
3635 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3636 priv_ptr = multi_pxo_chat_is_private(txt);
3637 if(priv_ptr != NULL){
3638 SDL_strlcpy(msg_total, priv_ptr, SDL_arraysize(msg_total));
3640 SDL_strlcpy(msg_total, txt, SDL_arraysize(msg_total));
3643 // determine what mode to display this text in
3645 // if this is private chat
3646 if(priv_ptr != NULL){
3647 mode = CHAT_MODE_PRIVATE;
3651 // if this is a server message
3652 if(multi_pxo_is_server_text(txt)){
3653 mode = CHAT_MODE_SERVER;
3655 // if this is a MOTD
3656 else if(multi_pxo_is_motd_text(txt)){
3659 SDL_strlcpy(msg_total, txt + strlen(PXO_CHAT_MOTD_PREFIX), SDL_arraysize(msg_total));
3661 mode = CHAT_MODE_MOTD;
3664 multi_pxo_motd_add_text(txt);
3668 // if this is the end of motd text
3669 else if(multi_pxo_is_end_of_motd_text(txt)){
3671 multi_pxo_set_end_of_motd();
3677 // split the text up into as many lines as necessary
3678 n_lines = split_str(msg_total, Multi_pxo_chat_coords[gr_screen.res][2] - 5, n_chars, p_str, 3);
3679 SDL_assert((n_lines != -1) && (n_lines <= 20));
3680 if((n_lines < 0) || (n_lines > 20)) {
3684 // if the string fits on one line
3686 multi_pxo_chat_add_line(msg_total,mode);
3688 // don't pad with extra spaces if its from the server
3690 if(mode != CHAT_MODE_SERVER){
3691 multi_pxo_chat_add_line("",CHAT_MODE_NORMAL);
3695 // if the string was split into multiple lines
3697 // add the first line
3698 memcpy(line,p_str[0],n_chars[0]);
3699 line[n_chars[0]] = '\0';
3700 multi_pxo_chat_add_line(line,mode);
3702 // copy the rest of the lines
3703 for(idx=1; idx<n_lines; idx++){
3704 memcpy(line,p_str[idx],n_chars[idx]);
3705 line[n_chars[idx]] = '\0';
3707 // unless the current mode is server or "switching channels", make all these CHAT_MODE_CARRY
3708 if((mode != CHAT_MODE_SERVER) && (mode != CHAT_MODE_CHANNEL_SWITCH)){
3709 mode = CHAT_MODE_CARRY;
3711 multi_pxo_chat_add_line(line, mode);
3716 // blit the chat text
3717 void multi_pxo_chat_blit()
3720 int disp_count,token_width;
3726 // blit the title line
3728 if(strlen(Multi_pxo_channel_current.name) > 1){
3729 SDL_snprintf(title, SDL_arraysize(title), XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name+1); // [[ <who> on <channel> ]]
3731 SDL_snprintf(title, SDL_arraysize(title), XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name); // [[ <who> on <channel> ]]
3734 SDL_strlcpy(title, XSTR("Parallax Online - No Channel", 956), SDL_arraysize(title));
3736 gr_force_fit_string(title, 254, Multi_pxo_chat_coords[gr_screen.res][2] - 10);
3737 gr_get_string_size(&token_width,NULL,title);
3738 gr_set_color_fast(&Color_normal);
3739 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);
3741 // blit all active lines of text
3742 moveup = Multi_pxo_chat_start;
3744 y_start = Multi_pxo_chat_coords[gr_screen.res][1];
3745 while((moveup != NULL) && (moveup != Multi_pxo_chat_add) && (disp_count < (Multi_pxo_max_chat_display[gr_screen.res]))){
3746 switch(moveup->mode){
3747 // if this is text from the server, display it all "bright"
3748 case CHAT_MODE_SERVER:
3749 gr_set_color_fast(&Color_bright);
3750 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3753 // if this is motd, display it all "bright"
3754 case CHAT_MODE_MOTD:
3755 gr_set_color_fast(&Color_bright_white);
3756 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3759 // normal mode, just highlight the server
3760 case CHAT_MODE_PRIVATE:
3761 case CHAT_MODE_NORMAL:
3762 SDL_strlcpy(piece, moveup->text, SDL_arraysize(piece));
3763 tok = strtok(piece," ");
3765 // get the width of just the first "piece"
3766 gr_get_string_size(&token_width, NULL, tok);
3769 gr_set_color_fast(&Color_bright);
3770 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, tok);
3772 // draw the rest of the string normally
3773 tok = strtok(NULL,"");
3775 gr_set_color_fast(&Color_normal);
3776 gr_string(Multi_pxo_chat_coords[gr_screen.res][0] + token_width + 6, y_start, tok);
3781 // carry mode, display with no highlight
3782 case CHAT_MODE_CARRY:
3783 gr_set_color_fast(&Color_normal);
3784 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3787 // "switching channels mode", display it bright
3788 case CHAT_MODE_CHANNEL_SWITCH:
3789 gr_set_color_fast(&Color_bright);
3790 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3795 moveup = moveup->next;
3800 if ((moveup != Multi_pxo_chat_add) && (moveup != NULL)) {
3801 Can_scroll_down = 1;
3803 Can_scroll_down = 0;
3807 // scroll to the very bottom of the chat area
3808 void multi_pxo_goto_bottom()
3813 if (Multi_pxo_chat == NULL) {
3817 // if we have less than the displayable amount of lines, do nothing
3818 if(Multi_pxo_chat_count <= Multi_pxo_max_chat_display[gr_screen.res]){
3819 Multi_pxo_chat_start = Multi_pxo_chat;
3822 // nothing to do for the slider
3823 Multi_pxo_chat_slider.set_numberItems(0);
3828 if (!Can_scroll_down)
3830 // otherwise move back the right # of items
3831 backup = Multi_pxo_chat_add;
3832 for(idx=0; idx<Multi_pxo_max_chat_display[gr_screen.res]; idx++){
3833 SDL_assert(backup->prev != NULL);
3834 backup = backup->prev;
3837 Multi_pxo_chat_start = backup;
3839 // fixup the start index
3840 multi_pxo_chat_adjust_start();
3844 // scroll the text up
3845 void multi_pxo_scroll_chat_up()
3847 // if we're already at the top of the list, don't do anything
3848 if ((Multi_pxo_chat_start == NULL) || (Multi_pxo_chat_start == Multi_pxo_chat)) {
3849 gamesnd_play_iface(SND_GENERAL_FAIL);
3853 // otherwise move up one
3854 Multi_pxo_chat_start = Multi_pxo_chat_start->prev;
3856 multi_pxo_chat_adjust_start();
3858 gamesnd_play_iface(SND_USER_SELECT);
3861 // returns 1 if we can scroll down, 0 otherwise
3862 int multi_pxo_can_scroll_down()
3867 // see if its okay to scroll down
3868 lookup = Multi_pxo_chat_start;
3869 if (lookup == NULL) {
3870 // gamesnd_play_iface(SND_GENERAL_FAIL);
3874 while (lookup != Multi_pxo_chat_add) {
3875 lookup = lookup->next;
3879 // check if we can move down, return accordingly
3880 if (count > Multi_pxo_max_chat_display[gr_screen.res]) {
3887 // scroll the text down
3888 void multi_pxo_scroll_chat_down()
3890 // if we can move down
3891 if (multi_pxo_can_scroll_down()) {
3892 Multi_pxo_chat_start = Multi_pxo_chat_start->next;
3893 multi_pxo_chat_adjust_start();
3894 gamesnd_play_iface(SND_USER_SELECT);
3896 gamesnd_play_iface(SND_GENERAL_FAIL);
3900 // process chat controls
3901 void multi_pxo_chat_process()
3903 char *remainder = NULL;
3904 const char *result = NULL;
3906 int msg_pixel_width;
3908 // if the chat line is getting too long, fire off the message, putting the last
3909 // word on the next input line.
3911 Multi_pxo_chat_input.get_text(msg);
3913 // determine if the width of the string in pixels is > than the inputbox width -- if so,
3914 // then send the message
3915 gr_get_string_size(&msg_pixel_width, NULL, msg);
3916 // if ( msg_pixel_width >= (Chatbox_inputbox_w - Player->short_callsign_width) ) {
3917 if ( msg_pixel_width >= (Multi_pxo_input_coords[gr_screen.res][2])) {
3918 remainder = strrchr(msg, ' ');
3924 // if we're connected to a channel, send the chat to the server
3926 result = SendChatString(msg,1);
3928 multi_pxo_chat_process_incoming(result);
3931 // display any remainder of text on the next line
3932 Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3934 Multi_pxo_chat_input.set_text("");
3936 } else if((Multi_pxo_chat_input.pressed() && (strlen(msg) > 0)) || (strlen(msg) >= MAX_CHAT_LINE_LEN)) {
3937 // tack on the null terminator in the boundary case
3938 int x = strlen(msg);
3939 if(x >= MAX_CHAT_LINE_LEN){
3940 msg[MAX_CHAT_LINE_LEN-1] = '\0';
3943 // ignore "/nick" commands
3944 if(multi_pxo_is_nick_command(msg)){
3945 Multi_pxo_chat_input.set_text("");
3949 // send the chat to the server
3950 // if we're connected to a channel, send the chat to the server
3952 result = SendChatString(msg,1);
3954 multi_pxo_chat_process_incoming(result);
3957 // display any remainder of text on the next line
3958 Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3960 Multi_pxo_chat_input.set_text("");
3965 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3968 // NOTE : DO NOT LOCALIZE THESE STRINGS!!!! THEY ARE CONSTANTS WHICH ARE CHECKED AGAINST
3969 // PXO CHAT SERVER DATA. THEY CANNOT CHANGE!!!
3970 #define PMSG_FROM "private message from "
3971 #define PMSG_TO "private message to "
3972 const char *multi_pxo_chat_is_private(const char *txt)
3975 if( strlen(txt) > strlen( PMSG_FROM ) ){
3976 // otherwise do a comparison
3977 if(!SDL_strncasecmp( txt, PMSG_FROM, strlen(PMSG_FROM) )){
3978 return &txt[strlen( PMSG_FROM )];
3983 if(strlen(txt) > strlen( PMSG_TO )){
3984 // otherwise do a comparison
3985 if(!SDL_strncasecmp(txt,PMSG_TO,strlen(PMSG_TO))){
3986 return &txt[strlen(PMSG_TO)];
3994 // if the text came from the server
3995 int multi_pxo_is_server_text(const char *txt)
3997 // if the message is prefaced by a ***
3998 if((strlen(txt) >= strlen(MULTI_PXO_SERVER_PREFIX)) && !strncmp(txt, MULTI_PXO_SERVER_PREFIX, strlen(MULTI_PXO_SERVER_PREFIX))){
4005 // if the text is message of the day text
4006 int multi_pxo_is_motd_text(const char *txt)
4008 // if we're not on a channel, and this is not a channel switching message assume its coming from a server
4009 if((strlen(txt) >= strlen(PXO_CHAT_MOTD_PREFIX)) && !strncmp(txt, PXO_CHAT_MOTD_PREFIX, strlen(PXO_CHAT_MOTD_PREFIX))){
4016 // if the text is the end of motd text
4017 int multi_pxo_is_end_of_motd_text(const char *txt)
4019 // if we're not on a channel, and this is not a channel switching message assume its coming from a server
4020 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))){
4027 // if the text is a "has left message" from the server
4028 int multi_pxo_chat_is_left_message(const char *txt)
4030 char last_portion[100];
4032 // if the text is not server text
4033 if(!multi_pxo_is_server_text(txt)){
4037 // check to see if the last portion is the correct wording
4038 SDL_zero(last_portion);
4039 if((strlen(txt) > strlen(MULTI_PXO_HAS_LEFT)) && !strcmp(&txt[strlen(txt) - strlen(MULTI_PXO_HAS_LEFT)], MULTI_PXO_HAS_LEFT)){
4043 // check the end of the line
4047 // recalculate the chat start index, and adjust the slider properly
4048 void multi_pxo_chat_adjust_start()
4052 // if we have no chat
4053 if (Multi_pxo_chat == NULL) {
4054 Multi_pxo_chat_start_index = -1;
4059 Multi_pxo_chat_start_index = 0;
4060 moveup = Multi_pxo_chat;
4061 while((moveup != Multi_pxo_chat_start) && (moveup != NULL)){
4062 Multi_pxo_chat_start_index++;
4063 moveup = moveup->next;
4066 // set the slider index
4067 Multi_pxo_chat_slider.force_currentItem(Multi_pxo_chat_start_index);
4071 // motd stuff ---------------------------------------------------------
4073 // initialize motd when going into this screen
4074 void multi_pxo_motd_init()
4076 // zero the motd string
4077 SDL_strlcpy(Pxo_motd, "", SDL_arraysize(Pxo_motd));
4079 // haven't gotten it yet
4082 // haven't read it yet either
4086 // set the motd text
4087 void multi_pxo_motd_add_text(const char *text)
4089 int cur_len = strlen(Pxo_motd);
4097 // make sure its motd text
4098 SDL_assert(multi_pxo_is_motd_text(text));
4099 if(!multi_pxo_is_motd_text(text)){
4103 // if its a 0 line motd
4104 if(strlen(text) <= strlen(PXO_CHAT_MOTD_PREFIX)){
4108 // add text to the motd
4109 new_len = strlen(text + strlen(PXO_CHAT_MOTD_PREFIX)) - 1;
4110 if((cur_len + new_len + 1) < MAX_PXO_MOTD_LEN){
4111 SDL_strlcat(Pxo_motd, text + strlen(PXO_CHAT_MOTD_PREFIX) + 1, SDL_arraysize(Pxo_motd));
4112 SDL_strlcat(Pxo_motd, "\n", SDL_arraysize(Pxo_motd));
4113 mprintf(("MOTD ADD : %s\n", Pxo_motd));
4118 void multi_pxo_set_end_of_motd()
4123 mprintf(("MOTD ALL : %s\n", Pxo_motd));
4127 // do we have an old MOTD file laying around? If so, read it in and see if its the same
4131 // checksum the current motd
4132 new_chksum = cf_add_chksum_long(0, Pxo_motd, strlen(Pxo_motd));
4134 // checksum the old motd if its lying around
4135 CFILE *in = cfopen("oldmotd.txt", "rb");
4137 // read the old checksum
4138 old_chksum = cfread_uint(in);
4141 // same checksum? no blink
4142 if(new_chksum == old_chksum){
4147 // write out the motd for next time
4148 if(strlen(Pxo_motd)){
4149 CFILE *out = cfopen("oldmotd.txt", "wb", CFILE_NORMAL, CF_TYPE_DATA);
4151 // write all the text
4152 cfwrite_uint(new_chksum, out);
4154 // close the outfile
4159 // set the blink stamp
4160 Pxo_motd_blink_stamp = -1;
4162 Pxo_motd_blink_on = 0;
4163 if(!Pxo_motd_blinked_already){
4164 Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4165 Pxo_motd_blink_on = 1;
4169 Pxo_motd_blinked_already = 1;
4172 // display the motd dialog
4173 void multi_pxo_motd_dialog()
4175 // mark the motd as read
4178 // simple popup, with a slider
4179 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, Pxo_motd);
4182 // call to maybe blink the motd button
4183 void multi_pxo_motd_maybe_blit()
4186 // if we got the end of the motd, and he hasn't read it yet
4187 if(Pxo_motd_end && !Pxo_motd_read && (Pxo_motd_blink_stamp != -1)){
4188 // if the timestamp elapsed, flip the blink flag
4189 if(timestamp_elapsed(Pxo_motd_blink_stamp)){
4190 Pxo_motd_blink_on = !Pxo_motd_blink_on;
4191 Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4195 if(Pxo_motd_blink_on){
4196 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_MOTD].button.draw_forced(2);
4203 // common dialog stuff ------------------------------------------------
4205 int Multi_pxo_searching = 0;
4207 // initialize the common dialog with the passed max input length
4208 void multi_pxo_com_init(int input_len)
4212 // create the interface window
4213 Multi_pxo_com_window.create(0, 0, gr_screen.max_w,gr_screen.max_h, 0);
4214 Multi_pxo_com_window.set_mask_bmap(Multi_pxo_com_mask_fname[gr_screen.res]);
4216 // create the interface buttons
4217 for(idx=0; idx<MULTI_PXO_COM_NUM_BUTTONS; idx++){
4218 // create the object
4219 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);
4221 // set the sound to play when highlighted
4222 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4224 // set the ani for the button
4225 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_com_buttons[gr_screen.res][idx].filename);
4228 Multi_pxo_com_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_com_buttons[gr_screen.res][idx].hotspot);
4233 for(idx=0; idx<MULTI_PXO_COM_NUM_TEXT; idx++){
4234 Multi_pxo_com_window.add_XSTR(&Multi_pxo_com_text[gr_screen.res][idx]);
4238 // create the input box
4239 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);
4240 Multi_pxo_com_input.set_focus();
4242 // clear all text lines
4243 SDL_zero(Multi_pxo_com_bottom_text);
4244 SDL_zero(Multi_pxo_com_middle_text);
4245 SDL_zero(Multi_pxo_com_top_text);
4248 // close down the common dialog
4249 void multi_pxo_com_close()
4251 // destroy the UI_WINDOW
4252 Multi_pxo_com_window.destroy();
4255 // blit all text lines, top, middle, bottoms
4256 void multi_pxo_com_blit_text()
4258 // blit top, middle and bottom text if possible
4259 if(strlen(Multi_pxo_com_top_text) > 0){
4260 gr_set_color_fast(&Color_bright);
4261 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);
4263 if(strlen(Multi_pxo_com_middle_text) > 0){
4264 gr_set_color_fast(&Color_bright);
4265 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);
4267 if(strlen(Multi_pxo_com_bottom_text) > 0){
4268 gr_set_color_fast(&Color_bright);
4269 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);
4273 // set the top text, shortening as necessary
4274 void multi_pxo_com_set_top_text(const char *txt)
4276 if((txt != NULL) && strlen(txt)){
4277 SDL_strlcpy(Multi_pxo_com_top_text, txt, SDL_arraysize(Multi_pxo_com_top_text));
4278 gr_force_fit_string(Multi_pxo_com_top_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4282 // set the middle text, shortening as necessary
4283 void multi_pxo_com_set_middle_text(const char *txt)
4285 if((txt != NULL) && strlen(txt)){
4286 SDL_strlcpy(Multi_pxo_com_middle_text, txt, SDL_arraysize(Multi_pxo_com_middle_text));
4287 gr_force_fit_string(Multi_pxo_com_middle_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4291 // set the bottom text, shortening as necessary
4292 void multi_pxo_com_set_bottom_text(const char *txt)
4294 if((txt != NULL) && strlen(txt)){
4295 SDL_strlcpy(Multi_pxo_com_bottom_text, txt, SDL_arraysize(Multi_pxo_com_bottom_text));
4296 gr_force_fit_string(Multi_pxo_com_bottom_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4301 // private channel join stuff -----------------------------------------
4303 // initialize the popup
4304 void multi_pxo_priv_init()
4306 SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE);
4308 // initialize the common dialog with the passed max input length
4309 multi_pxo_com_init(MULTI_PXO_PRIV_MAX_TEXT_LEN);
4311 // initialize the return code
4312 Multi_pxo_priv_return_code = -1;
4314 // mark us as running
4315 Multi_pxo_mode = MULTI_PXO_MODE_PRIVATE;
4318 multi_pxo_com_set_middle_text(XSTR("Type the name of the channel to join/create",961));
4321 // close down the popup
4322 void multi_pxo_priv_close()
4324 // close down the common dialog
4325 multi_pxo_com_close();
4327 // mark us as not running any more
4328 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4331 // run the popup, 0 if still running, -1 if cancel, 1 if ok
4332 int multi_pxo_priv_popup()
4336 // if we're not already running, initialize stuff
4337 if(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE){
4339 multi_pxo_priv_init();
4341 // return "still running"
4345 k = Multi_pxo_com_window.process();
4347 // process keypresses
4349 // like hitting the cancel button
4351 Multi_pxo_priv_return_code = 0;
4355 // process button presses
4356 multi_pxo_priv_process_buttons();
4358 // process the inputbox
4359 multi_pxo_priv_process_input();
4361 // blit the background
4362 multi_pxo_blit_all();
4366 gr_set_bitmap(Multi_pxo_com_bitmap);
4367 gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4368 Multi_pxo_com_window.draw();
4370 // blit all text lines, top, middle, bottoms
4371 multi_pxo_com_blit_text();
4375 // check the return code
4376 switch(Multi_pxo_priv_return_code){
4377 // still in progress
4383 multi_pxo_priv_close();
4388 multi_pxo_priv_close();
4395 // process button presses
4396 void multi_pxo_priv_process_buttons()
4400 // check all buttons
4401 for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4402 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4403 multi_pxo_priv_button_pressed(idx);
4409 // handle a button press
4410 void multi_pxo_priv_button_pressed(int n)
4412 char priv_chan_name[128];
4415 case MULTI_PXO_COM_CANCEL:
4416 Multi_pxo_priv_return_code = 0;
4419 case MULTI_PXO_COM_OK:
4420 Multi_pxo_com_input.get_text(priv_chan_name);
4421 multi_pxo_strip_space(priv_chan_name, priv_chan_name, SDL_arraysize(priv_chan_name));
4423 // if its a 0 length string, interpret as a cancel
4424 if(strlen(priv_chan_name) <= 0){
4425 Multi_pxo_priv_return_code = 0;
4429 Multi_pxo_priv_return_code = 1;
4434 // process the inputbox
4435 void multi_pxo_priv_process_input()
4437 char priv_chan_name[128];
4439 // see if the user has pressed enter
4440 if(Multi_pxo_com_input.pressed()){
4441 Multi_pxo_com_input.get_text(priv_chan_name);
4442 multi_pxo_strip_space(priv_chan_name, priv_chan_name, SDL_arraysize(priv_chan_name));
4444 // if its a 0 length string, interpret as a cancel
4445 if(strlen(priv_chan_name) <= 0){
4446 Multi_pxo_priv_return_code = 0;
4450 // otherwise interpret as "accept"
4451 Multi_pxo_priv_return_code = 1;
4453 // add in the "+" which indicates a private room
4454 SDL_strlcpy(Multi_pxo_priv_chan, "+", SDL_arraysize(Multi_pxo_priv_chan));
4455 SDL_strlcat(Multi_pxo_priv_chan, priv_chan_name, SDL_arraysize(Multi_pxo_priv_chan));
4459 // find player stuff -----------------------------------------
4461 char name_lookup[255];
4463 // initialize the popup
4464 void multi_pxo_find_init()
4466 SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_FIND);
4468 // initialize the common dialog with the passed max input length
4469 multi_pxo_com_init(MAX_PLAYER_NAME_LEN);
4471 // return code, set to something other than -1 if we're supposed to return
4472 Multi_pxo_find_return_code = -1;
4474 // mark us as running
4475 Multi_pxo_mode = MULTI_PXO_MODE_FIND;
4477 // not searching yet
4478 Multi_pxo_searching = 0;
4481 multi_pxo_com_set_top_text(XSTR("Enter user to be found",962));
4484 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4487 SDL_strlcpy(name_lookup, "", SDL_arraysize(name_lookup));
4490 // close down the popup
4491 void multi_pxo_find_close()
4493 // close down the common dialog
4494 multi_pxo_com_close();
4496 // mark us as not running any more
4497 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4500 // run the popup, 0 if still running, -1 if cancel, 1 if ok
4501 int multi_pxo_find_popup()
4505 // if we're not already running, initialize stuff
4506 if(Multi_pxo_mode != MULTI_PXO_MODE_FIND){
4508 multi_pxo_find_init();
4510 // return "still running"
4514 k = Multi_pxo_com_window.process();
4516 // process keypresses
4518 // like hitting the cancel button
4520 Multi_pxo_find_return_code = 0;
4524 // process button presses
4525 multi_pxo_find_process_buttons();
4527 // process the inputbox
4528 multi_pxo_find_process_input();
4530 // process search mode if applicable
4531 multi_pxo_find_search_process();
4533 // blit the background
4534 multi_pxo_blit_all();
4538 gr_set_bitmap(Multi_pxo_com_bitmap);
4539 gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4540 Multi_pxo_com_window.draw();
4542 // blit any text lines
4543 multi_pxo_com_blit_text();
4547 // check the return code
4548 switch(Multi_pxo_find_return_code){
4549 // still in progress
4555 // close the popup down
4556 multi_pxo_find_close();
4561 // close the popup down
4562 multi_pxo_find_close();
4564 // if we have a channel, join it now if possible
4565 if(strlen(Multi_pxo_find_channel) > 0){
4566 pxo_channel *lookup;
4567 lookup = multi_pxo_find_channel(Multi_pxo_find_channel,Multi_pxo_channels);
4569 // if we couldn't find it, don't join
4571 multi_pxo_join_channel(lookup);
4580 // process button presses
4581 void multi_pxo_find_process_buttons()
4585 // check all buttons
4586 for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4587 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4588 multi_pxo_find_button_pressed(idx);
4594 // handle a button press
4595 void multi_pxo_find_button_pressed(int n)
4598 case MULTI_PXO_COM_CANCEL:
4599 Multi_pxo_find_return_code = 0;
4602 case MULTI_PXO_COM_OK:
4603 Multi_pxo_find_return_code = 1;
4608 // process the inputbox
4609 void multi_pxo_find_process_input()
4611 // see if the user has pressed enter
4612 if(Multi_pxo_com_input.pressed()){
4613 // if we're not already in search mode
4614 if(!Multi_pxo_searching){
4616 SDL_zero(Multi_pxo_com_middle_text);
4617 SDL_zero(Multi_pxo_com_bottom_text);
4619 Multi_pxo_com_input.get_text(name_lookup);
4620 multi_pxo_strip_space(name_lookup, name_lookup, SDL_arraysize(name_lookup));
4622 // never search with a zero length string
4623 if(strlen(name_lookup) > 0){
4624 char search_text[512];
4626 // put us in search mode
4627 Multi_pxo_searching = 1;
4630 GetChannelByUser(name_lookup);
4633 SDL_snprintf(search_text, SDL_arraysize(search_text), XSTR("Searching for %s", 963), name_lookup);
4634 multi_pxo_com_set_top_text(search_text);
4638 SDL_zero(Multi_pxo_com_top_text);
4644 // process search mode if applicable
4645 void multi_pxo_find_search_process()
4649 // if we're not searching for anything, return
4650 if(!Multi_pxo_searching){
4654 // otherwise check to see if we've found him
4655 channel = GetChannelByUser(NULL);
4657 // if we've got a result, let the user know
4659 // if he couldn't be found
4660 if(channel == (char *)-1){
4661 multi_pxo_com_set_middle_text(XSTR("User not found",964));
4662 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4664 if(channel[0] == '*'){
4665 multi_pxo_com_set_middle_text(XSTR("Player is logged in but is not on a channel",965));
4666 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4670 // if this guy is on a public channel, display which one
4671 if(channel[0] == '#'){
4672 SDL_snprintf(p_text, SDL_arraysize(p_text), XSTR("Found %s on :", 966), name_lookup);
4674 // display the results
4675 multi_pxo_com_set_middle_text(p_text);
4676 multi_pxo_com_set_bottom_text(channel+1);
4678 // mark down the channel name so we know where to find him
4679 SDL_strlcpy(Multi_pxo_find_channel, channel, SDL_arraysize(Multi_pxo_find_channel));
4680 // strip out trailing whitespace
4681 if(Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] == ' '){
4682 Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] = '\0';
4685 // if this is a private channel
4686 else if(channel[0] == '+'){
4687 SDL_snprintf(p_text, SDL_arraysize(p_text), XSTR("Found %s on a private channel", 967), name_lookup);
4688 multi_pxo_com_set_middle_text(p_text);
4690 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4695 // unset search mode
4696 Multi_pxo_searching = 0;
4698 // clear the inputbox
4699 Multi_pxo_com_input.set_text("");
4704 // player info stuff -----------------------------------------
4706 // popup conditional functions, returns 10 on successful get of stats
4707 int multi_pxo_pinfo_cond()
4710 char temp_string[255];
4713 // process common stuff
4714 multi_pxo_process_common();
4716 // run the networking functions for the PXO API
4717 multi_pxo_api_process();
4719 // process depending on what mode we're in
4720 switch(Multi_pxo_retrieve_mode){
4721 // getting his player tracker id
4723 // if the thing is non-null, do something
4724 ret_string = GetTrackerIdByUser(Multi_pxo_retrieve_name);
4725 if(ret_string != NULL){
4726 // user not-online/not found
4727 if(ret_string == (char *)-1){
4731 // user not a tracker pilot
4732 if(!SDL_strcasecmp(ret_string,"-1")){
4736 // otherwise parse into his id and callsign
4737 SDL_strlcpy(temp_string, ret_string, SDL_arraysize(temp_string));
4738 tok = strtok(temp_string," ");
4742 SDL_strlcpy(Multi_pxo_retrieve_id, tok, SDL_arraysize(Multi_pxo_retrieve_id));
4745 tok = strtok(NULL,"");
4747 SDL_strlcpy(Multi_pxo_retrieve_name, tok, SDL_arraysize(Multi_pxo_retrieve_name));
4754 // failure of some kind or another
4759 Multi_pxo_retrieve_mode = 1;
4764 // initial call to get his stats
4766 // change the popup text
4767 popup_change_text(XSTR("Getting player stats",968));
4770 memset(&Multi_pxo_pinfo, 0, sizeof(Multi_pxo_pinfo));
4771 SDL_strlcpy(Multi_pxo_pinfo.pilot_name, Multi_pxo_retrieve_name, SDL_arraysize(Multi_pxo_pinfo.pilot_name));
4772 SDL_strlcpy(Multi_pxo_pinfo.tracker_id, Multi_pxo_retrieve_id, SDL_arraysize(Multi_pxo_pinfo.tracker_id));
4774 // make the initial call to the API
4775 GetFSPilotData((vmt_stats_struct*)0xffffffff,NULL,NULL,0);
4776 if(GetFSPilotData(&Multi_pxo_pinfo,Multi_pxo_retrieve_name,Multi_pxo_retrieve_id,0) != 0){
4779 // if the call went through, set the mode to 2
4781 Multi_pxo_retrieve_mode = 2;
4785 // busy retrieving his stats
4787 switch(GetFSPilotData(NULL,NULL,NULL,0)){
4788 // timeout, fail, cancel
4806 // return not done yet
4810 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
4811 int multi_pxo_pinfo_get(char *name)
4814 Multi_pxo_retrieve_mode = 0;
4815 SDL_strlcpy(Multi_pxo_retrieve_name, name, SDL_arraysize(Multi_pxo_retrieve_name));
4816 switch(popup_till_condition(multi_pxo_pinfo_cond,XSTR("&Cancel", 779),XSTR("Retrieving player tracker id",969))){
4821 // failed to get his tracker id
4825 // failed to get his stats
4830 // we didn't get the stats
4834 // fire up the stats view popup
4835 void multi_pxo_pinfo_show()
4837 // initialize the popup
4838 multi_pxo_pinfo_init();
4842 game_set_frametime(GS_STATE_PXO);
4843 } while(!multi_pxo_pinfo_do());
4845 // close down the popup
4846 multi_pxo_pinfo_close();
4849 // build the stats labels values
4850 void multi_pxo_pinfo_build_vals()
4852 vmt_stats_struct *fs = &Multi_pxo_pinfo;
4854 SDL_zero(Multi_pxo_pinfo_vals);
4857 SDL_strlcpy(Multi_pxo_pinfo_vals[0], fs->pilot_name, SDL_arraysize(Multi_pxo_pinfo_vals[0]));
4858 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]));
4861 multi_sg_rank_build_name(Ranks[fs->rank].name, Multi_pxo_pinfo_vals[1], SDL_arraysize(Multi_pxo_pinfo_vals[1]));
4862 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]));
4865 SDL_snprintf(Multi_pxo_pinfo_vals[2], SDL_arraysize(Multi_pxo_pinfo_vals[2]), "%d", fs->kill_count);
4868 SDL_snprintf(Multi_pxo_pinfo_vals[3], SDL_arraysize(Multi_pxo_pinfo_vals[3]), "%d", fs->assists);
4871 SDL_snprintf(Multi_pxo_pinfo_vals[4], SDL_arraysize(Multi_pxo_pinfo_vals[4]), "%d", fs->kill_count - fs->kill_count_ok);
4874 SDL_snprintf(Multi_pxo_pinfo_vals[5], SDL_arraysize(Multi_pxo_pinfo_vals[5]), "%d", (int)fs->missions_flown);
4877 game_format_time(fl2f((float)fs->flight_time), Multi_pxo_pinfo_vals[6], SDL_arraysize(Multi_pxo_pinfo_vals[6]));
4880 if(fs->last_flown == 0){
4881 SDL_strlcpy(Multi_pxo_pinfo_vals[7], XSTR("No missions flown", 970), SDL_arraysize(Multi_pxo_pinfo_vals[7]));
4883 tm *tmr = gmtime((time_t*)&fs->last_flown);
4885 strftime(Multi_pxo_pinfo_vals[7],30,"%m/%d/%y %H:%M",tmr);
4887 SDL_strlcpy(Multi_pxo_pinfo_vals[7], "", SDL_arraysize(Multi_pxo_pinfo_vals[7]));
4891 // primary shots fired
4892 SDL_snprintf(Multi_pxo_pinfo_vals[8], SDL_arraysize(Multi_pxo_pinfo_vals[8]), "%d", (int)fs->p_shots_fired);
4894 // primary shots hit
4895 SDL_snprintf(Multi_pxo_pinfo_vals[9], SDL_arraysize(Multi_pxo_pinfo_vals[9]), "%d", (int)fs->p_shots_hit);
4898 if(fs->p_shots_fired > 0){
4899 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));
4901 SDL_strlcpy(Multi_pxo_pinfo_vals[10], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[10]));
4904 // secondary shots fired
4905 SDL_snprintf(Multi_pxo_pinfo_vals[11], SDL_arraysize(Multi_pxo_pinfo_vals[11]), "%d", (int)fs->s_shots_fired);
4907 // secondary shots hit
4908 SDL_snprintf(Multi_pxo_pinfo_vals[12], SDL_arraysize(Multi_pxo_pinfo_vals[12]), "%d", (int)fs->s_shots_hit);
4910 // secondary hit pct
4911 if(fs->s_shots_fired > 0){
4912 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));
4914 SDL_strlcpy(Multi_pxo_pinfo_vals[13], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[13]));
4917 // primary friendly hits
4918 SDL_snprintf(Multi_pxo_pinfo_vals[14], SDL_arraysize(Multi_pxo_pinfo_vals[14]), "%d", fs->p_bonehead_hits);
4920 // primary friendly hit %
4921 if(fs->p_shots_fired > 0){
4922 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)));
4924 SDL_strlcpy(Multi_pxo_pinfo_vals[15], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[15]));
4927 // secondary friendly hits
4928 SDL_snprintf(Multi_pxo_pinfo_vals[16], SDL_arraysize(Multi_pxo_pinfo_vals[16]), "%d", fs->s_bonehead_hits);
4930 // secondary friendly hit %
4931 if(fs->s_shots_fired > 0){
4932 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)));
4934 SDL_strlcpy(Multi_pxo_pinfo_vals[17], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[17]));
4938 // initialize the popup
4939 void multi_pxo_pinfo_init()
4943 // create the interface window
4944 Multi_pxo_pinfo_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4945 Multi_pxo_pinfo_window.set_mask_bmap(Multi_pxo_pinfo_mask_fname[gr_screen.res]);
4947 Multi_pxo_pinfo_bitmap = bm_load(Multi_pxo_pinfo_fname[gr_screen.res]);
4948 SDL_assert(Multi_pxo_pinfo_bitmap != -1);
4950 // create the interface buttons
4951 for(idx=0; idx<MULTI_PXO_PINFO_NUM_BUTTONS; idx++){
4952 // create the object
4953 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);
4955 // set the sound to play when highlighted
4956 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4958 // set the ani for the button
4959 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_pinfo_buttons[gr_screen.res][idx].filename);
4962 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_pinfo_buttons[gr_screen.res][idx].hotspot);
4967 for(idx=0; idx<MULTI_PXO_PINFO_NUM_TEXT; idx++){
4968 Multi_pxo_pinfo_window.add_XSTR(&Multi_pxo_pinfo_text[gr_screen.res][idx]);
4972 // set up the stats labels
4973 Multi_pxo_pinfo_stats_labels[0] = strdup(XSTR("Name", 1532));
4974 Multi_pxo_pinfo_stats_labels[1] = strdup(XSTR("Rank", 1533));
4975 Multi_pxo_pinfo_stats_labels[2] = strdup(XSTR("Kills", 1534));
4976 Multi_pxo_pinfo_stats_labels[3] = strdup(XSTR("Assists", 1535));
4977 Multi_pxo_pinfo_stats_labels[4] = strdup(XSTR("Friendly kills", 1536));
4978 Multi_pxo_pinfo_stats_labels[5] = strdup(XSTR("Missions flown", 1537));
4979 Multi_pxo_pinfo_stats_labels[6] = strdup(XSTR("Flight time", 1538));
4980 Multi_pxo_pinfo_stats_labels[7] = strdup(XSTR("Last flown", 1539));
4981 Multi_pxo_pinfo_stats_labels[8] = strdup(XSTR("Primary shots fired", 1540));
4982 Multi_pxo_pinfo_stats_labels[9] = strdup(XSTR("Primary shots hit", 1541));
4983 Multi_pxo_pinfo_stats_labels[10] = strdup(XSTR("Primary hit %", 1542));
4984 Multi_pxo_pinfo_stats_labels[11] = strdup(XSTR("Secondary shots fired", 1543));
4985 Multi_pxo_pinfo_stats_labels[12] = strdup(XSTR("Secondary shots hit", 1544));
4986 Multi_pxo_pinfo_stats_labels[13] = strdup(XSTR("Secondary hit %", 1545));
4987 Multi_pxo_pinfo_stats_labels[14] = strdup(XSTR("Primary friendly hits", 1546));
4988 Multi_pxo_pinfo_stats_labels[15] = strdup(XSTR("Primary friendly hit %", 1547));
4989 Multi_pxo_pinfo_stats_labels[16] = strdup(XSTR("Secondary friendly hits", 1548));
4990 Multi_pxo_pinfo_stats_labels[17] = strdup(XSTR("Secondary friendly hit %", 1549));
4992 // build the stats labels values
4993 multi_pxo_pinfo_build_vals();
4997 int multi_pxo_pinfo_do()
4999 int k = Multi_pxo_pinfo_window.process();
5001 // process common stuff
5002 multi_pxo_process_common();
5004 // run the networking functions for the PXO API
5005 multi_pxo_api_process();
5007 // check to see if he pressed escp
5008 if(k == SDLK_ESCAPE){
5012 // if he pressed the ok button
5013 if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_OK].button.pressed()){
5017 // if he pressed the medals buttons, run the medals screen
5018 if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_MEDALS].button.pressed()){
5020 game_feature_not_in_demo_popup();
5022 multi_pxo_run_medals();
5028 // blit everything on the "normal" screen
5029 multi_pxo_blit_all();
5031 // blit our own stuff
5033 gr_set_bitmap(Multi_pxo_pinfo_bitmap);
5039 Multi_pxo_pinfo_window.draw();
5041 // blit the stats themselves
5042 multi_pxo_pinfo_blit();
5052 void multi_pxo_pinfo_close()
5056 // destroy the UI_WINDOW
5057 Multi_pxo_pinfo_window.destroy();
5059 // unload the bitmap
5060 if(Multi_pxo_pinfo_bitmap != -1){
5061 bm_unload(Multi_pxo_pinfo_bitmap);
5064 // free the stats labels strings
5065 for (i=0; i<MULTI_PXO_PINFO_NUM_LABELS; i++) {
5066 free(Multi_pxo_pinfo_stats_labels[i]);
5070 // blit all the stats on this screen
5071 void multi_pxo_pinfo_blit()
5076 // blit all the labels
5077 y_start = Multi_pxo_pinfo_coords[gr_screen.res][1];
5078 for(idx=0; idx<MULTI_PXO_PINFO_NUM_LABELS; idx++){
5080 gr_set_color_fast(&Color_bright);
5081 gr_string(Multi_pxo_pinfo_coords[gr_screen.res][0], y_start, Multi_pxo_pinfo_stats_labels[idx]);
5083 // blit the label's value
5084 gr_set_color_fast(&Color_normal);
5085 gr_string(Multi_pxo_pinfo_val_x[gr_screen.res], y_start, Multi_pxo_pinfo_vals[idx]);
5088 y_start += Multi_pxo_pinfo_stats_spacing[idx];
5092 // run the medals screen
5093 void multi_pxo_run_medals()
5097 // process common stuff
5098 multi_pxo_process_common();
5100 // run the networking functions for the PXO API
5101 multi_pxo_api_process();
5103 // initialize the freespace data and the player struct
5104 multi_stats_tracker_to_fs(&Multi_pxo_pinfo, &Multi_pxo_pinfo_player.stats);
5105 SDL_strlcpy(Multi_pxo_pinfo_player.callsign, Multi_pxo_pinfo.pilot_name, SDL_arraysize(Multi_pxo_pinfo_player.callsign));
5107 // initialize the medals screen
5108 medal_main_init(&Multi_pxo_pinfo_player, MM_POPUP);
5110 // run the medals screen until it says that it should be closed
5112 // set frametime and run common functions
5113 game_set_frametime(-1);
5114 game_do_state_common(gameseq_get_state());
5116 // run the medals screen
5117 ret_code = medal_main_do();
5120 // close the medals screen down
5123 // reset the palette
5124 multi_pxo_load_palette();
5128 // notify stuff stuff -----------------------------------------
5130 // add a notification string
5131 void multi_pxo_notify_add(const char *txt)
5134 SDL_strlcpy(Multi_pxo_notify_text, txt, SDL_arraysize(Multi_pxo_notify_text));
5136 // set the timestamp
5137 Multi_pxo_notify_stamp = timestamp(MULTI_PXO_NOTIFY_TIME);
5140 // blit and process the notification string
5141 void multi_pxo_notify_blit()
5145 // if the timestamp is -1, do nothing
5146 if(Multi_pxo_notify_stamp == -1){
5150 // if it has expired, do nothing
5151 if(timestamp_elapsed(Multi_pxo_notify_stamp)){
5152 Multi_pxo_notify_stamp = -1;
5155 // otherwise blit the text
5156 gr_set_color_fast(&Color_bright);
5157 gr_get_string_size(&w,NULL,Multi_pxo_notify_text);
5158 gr_string((gr_screen.max_w - w)/2,MULTI_PXO_NOTIFY_Y,Multi_pxo_notify_text);
5162 // initialize the PXO help screen
5163 void multi_pxo_help_init()
5167 // load the background bitmap
5168 Multi_pxo_help_bitmap = bm_load(Multi_pxo_help_fname[gr_screen.res]);
5169 if(Multi_pxo_help_bitmap < 0){
5170 // we failed to load the bitmap - this is very bad
5173 // create the interface window
5174 Multi_pxo_help_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
5175 Multi_pxo_help_window.set_mask_bmap(Multi_pxo_help_mask_fname[gr_screen.res]);
5177 // create the interface buttons
5178 for(idx=0; idx<MULTI_PXO_HELP_NUM_BUTTONS; idx++){
5179 // create the object
5180 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);
5182 // set the sound to play when highlighted
5183 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
5185 // set the ani for the button
5186 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_help_buttons[gr_screen.res][idx].filename);
5189 Multi_pxo_help_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_help_buttons[gr_screen.res][idx].hotspot);
5194 for(idx=0; idx<MULTI_PXO_HELP_NUM_TEXT; idx++){
5195 Multi_pxo_help_window.add_XSTR(&Multi_pxo_help_text[gr_screen.res][idx]);
5199 // if we haven't already loaded in the text, do so
5200 // if(!Multi_pxo_help_loaded){
5201 multi_pxo_help_load();
5204 // set the current page to 0
5205 Multi_pxo_help_cur = 0;
5208 // do frame for PXO help
5209 void multi_pxo_help_do()
5212 if(Multi_pxo_connected){
5213 multi_pxo_api_process();
5216 // process common stuff
5217 multi_pxo_process_common();
5219 int k = Multi_pxo_help_window.process();
5221 // process any keypresses
5224 gamesnd_play_iface(SND_USER_SELECT);
5225 gameseq_post_event(GS_EVENT_PXO);
5229 // process button presses
5230 multi_pxo_help_process_buttons();
5232 // draw the background, etc
5234 GR_MAYBE_CLEAR_RES(Multi_pxo_help_bitmap);
5235 if(Multi_pxo_help_bitmap != -1){
5236 gr_set_bitmap(Multi_pxo_help_bitmap);
5239 Multi_pxo_help_window.draw();
5241 // blit the current page
5242 multi_pxo_help_blit_page();
5248 // close the pxo screen
5249 void multi_pxo_help_close()
5253 // unload any bitmaps
5254 bm_unload(Multi_pxo_help_bitmap);
5256 // destroy the UI_WINDOW
5257 Multi_pxo_help_window.destroy();
5260 for(idx=0; idx<Multi_pxo_help_num_pages; idx++){
5261 for(idx2=0; idx2<Multi_pxo_help_pages[idx].num_lines; idx2++){
5263 if(Multi_pxo_help_pages[idx].text[idx2] != NULL){
5264 free(Multi_pxo_help_pages[idx].text[idx2]);
5265 Multi_pxo_help_pages[idx].text[idx2] = NULL;
5271 // load the help file up
5272 void multi_pxo_help_load()
5277 // if its already loaded, do nothing
5278 // if(Multi_pxo_help_loaded){
5282 // read in the text file
5284 in = cfopen(MULTI_PXO_HELP_FILE,"rt",CFILE_NORMAL,CF_TYPE_DATA);
5285 SDL_assert(in != NULL);
5290 Multi_pxo_help_num_pages = 0;
5292 // blast all the help pages clear
5293 memset(Multi_pxo_help_pages, 0, sizeof(help_page) * MULTI_PXO_MAX_PAGES);
5294 Multi_pxo_help_num_pages = 0;
5295 cp = &Multi_pxo_help_pages[0];
5299 cp->text[cp->num_lines] = (char*)malloc(Multi_pxo_chars_per_line[gr_screen.res]);
5300 if(cp->text[cp->num_lines] == NULL){
5304 // read in the next line
5305 cfgets(cp->text[cp->num_lines++], Multi_pxo_chars_per_line[gr_screen.res], in);
5307 // skip to the next page if necessary
5308 if(cp->num_lines == Multi_pxo_lines_pp[gr_screen.res]){
5309 Multi_pxo_help_num_pages++;
5310 SDL_assert(Multi_pxo_help_num_pages < MULTI_PXO_MAX_PAGES);
5311 if(Multi_pxo_help_num_pages >= MULTI_PXO_MAX_PAGES){
5312 Multi_pxo_help_num_pages--;
5315 cp = &Multi_pxo_help_pages[Multi_pxo_help_num_pages];
5322 // mark the help as having been loaded
5323 // Multi_pxo_help_loaded = 1;
5326 // blit the current page
5327 void multi_pxo_help_blit_page()
5332 help_page *cp = &Multi_pxo_help_pages[Multi_pxo_help_cur];
5335 y_start = Multi_pxo_help_coords[gr_screen.res][1];
5336 for(idx=0;idx<cp->num_lines;idx++){
5337 // if the first symbol is "@", highlight the line
5338 if(cp->text[idx][0] == '@'){
5339 gr_set_color_fast(&Color_bright);
5342 gr_set_color_fast(&Color_normal);
5347 gr_string(Multi_pxo_help_coords[gr_screen.res][0], y_start, cp->text[idx] + start_pos);
5349 // increment the y location
5354 // process button presses
5355 void multi_pxo_help_process_buttons()
5359 // process all buttons
5360 for(idx=0;idx<MULTI_PXO_HELP_NUM_BUTTONS;idx++){
5361 if(Multi_pxo_help_buttons[gr_screen.res][idx].button.pressed()){
5362 multi_pxo_help_button_pressed(idx);
5369 void multi_pxo_help_button_pressed(int n)
5372 case MULTI_PXO_HELP_PREV:
5373 // if we're already at page 0, do nothing
5374 if(Multi_pxo_help_cur == 0){
5375 gamesnd_play_iface(SND_GENERAL_FAIL);
5377 Multi_pxo_help_cur--;
5378 gamesnd_play_iface(SND_USER_SELECT);
5382 case MULTI_PXO_HELP_NEXT:
5383 // if we're already at max pages, do nothing
5384 if(Multi_pxo_help_cur == Multi_pxo_help_num_pages){
5385 gamesnd_play_iface(SND_GENERAL_FAIL);
5387 Multi_pxo_help_cur++;
5388 gamesnd_play_iface(SND_USER_SELECT);
5392 case MULTI_PXO_HELP_CONTINUE:
5393 gamesnd_play_iface(SND_USER_SELECT);
5394 gameseq_post_event(GS_EVENT_PXO);
5399 // http banner stuff ---------------------------------------------
5402 void multi_pxo_ban_init()
5405 // zero the active banner bitmap
5406 Multi_pxo_banner.ban_bitmap = -1;
5408 // are we doing banners at all?
5409 if(os_config_read_uint(NULL, "PXOBanners", 1)){
5410 // if we're already in idle mode, we're done downloading for this instance of freespace. pick a random image we already have
5411 if(Multi_pxo_ban_mode == PXO_BAN_MODE_IDLE){
5412 Multi_pxo_ban_mode = PXO_BAN_MODE_CHOOSE_RANDOM;
5416 // set ourselves to startup mode
5417 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
5418 Multi_pxo_ban_get = NULL;
5420 // set ourselves to idle mode
5421 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5422 Multi_pxo_ban_get = NULL;
5426 // zero the active banner bitmap
5427 SDL_zero(Multi_pxo_banner);
5428 Multi_pxo_banner.ban_bitmap = -1;
5431 // process http download details
5432 void multi_pxo_ban_process()
5434 char url_string[512] = "";
5435 char local_file[MAX_PATH_LEN] = "";
5443 switch(Multi_pxo_ban_mode){
5444 // start downloading list
5445 case PXO_BAN_MODE_LIST_STARTUP:
5447 SDL_snprintf(url_string, SDL_arraysize(url_string), "%s/%s", Multi_options_g.pxo_banner_url, PXO_BANNERS_CONFIG_FILE);
5450 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, PXO_BANNERS_CONFIG_FILE);
5452 // try creating the file get object
5453 Multi_pxo_ban_get = new InetGetFile(url_string, local_file);
5456 if(Multi_pxo_ban_get == NULL){
5457 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5460 // go to the downloading list mode
5461 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST;
5465 case PXO_BAN_MODE_LIST:
5467 if(Multi_pxo_ban_get->IsFileError()){
5468 delete Multi_pxo_ban_get;
5469 Multi_pxo_ban_get = NULL;
5470 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5474 // connecting, receiving
5475 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5480 if(Multi_pxo_ban_get->IsFileReceived()){
5481 delete Multi_pxo_ban_get;
5482 Multi_pxo_ban_get = NULL;
5483 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_STARTUP;
5487 // start downloading files
5488 case PXO_BAN_MODE_IMAGES_STARTUP:
5489 // first thing - parse the banners file and pick a file
5490 multi_pxo_ban_parse_banner_file(0);
5492 // if we have no active file, we're done
5493 if((strlen(Multi_pxo_banner.ban_file) <= 0) || (strlen(Multi_pxo_banner.ban_file_url) <= 0)){
5494 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5498 // if the file already exists, we're done
5499 if(cf_exist(Multi_pxo_banner.ban_file, CF_TYPE_MULTI_CACHE)){
5500 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5504 // otherwise try and download it
5505 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, Multi_pxo_banner.ban_file);
5506 // try creating the file get object
5507 Multi_pxo_ban_get = new InetGetFile(Multi_pxo_banner.ban_file_url, local_file);
5510 if(Multi_pxo_ban_get == NULL){
5511 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5514 // go to the downloading images mode
5515 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES;
5518 // downloading files
5519 case PXO_BAN_MODE_IMAGES:
5521 if(Multi_pxo_ban_get->IsFileError()){
5522 delete Multi_pxo_ban_get;
5523 Multi_pxo_ban_get = NULL;
5524 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5528 // connecting, receiving
5529 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5534 if(Multi_pxo_ban_get->IsFileReceived()){
5535 delete Multi_pxo_ban_get;
5536 Multi_pxo_ban_get = NULL;
5537 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5541 // done downloading - maybe load an image
5542 case PXO_BAN_MODE_IMAGES_DONE:
5543 // make sure we have a valid filename
5544 // SDL_assert(strlen(Multi_pxo_banner.ban_file) > 0);
5545 if(strlen(Multi_pxo_banner.ban_file) > 0){
5546 Multi_pxo_banner.ban_bitmap = bm_load(Multi_pxo_banner.ban_file);
5550 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5553 // idle (done with EVERYTHING)
5554 case PXO_BAN_MODE_IDLE:
5555 // if the banner button was clicked
5556 if(Multi_pxo_ban_button.pressed()){
5557 multi_pxo_ban_clicked();
5561 case PXO_BAN_MODE_CHOOSE_RANDOM:
5562 // first thing - parse the banners file and pick a file
5563 multi_pxo_ban_parse_banner_file(1);
5565 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5571 void multi_pxo_ban_close()
5573 // if we have a currently active transfer
5574 if(Multi_pxo_ban_get != NULL){
5575 Multi_pxo_ban_get->AbortGet();
5576 delete Multi_pxo_ban_get;
5577 Multi_pxo_ban_get = NULL;
5580 // if we have a loaded bitmap, unload it
5581 if(Multi_pxo_banner.ban_bitmap != -1){
5582 bm_unload(Multi_pxo_banner.ban_bitmap);
5583 Multi_pxo_banner.ban_bitmap = -1;
5587 // parse the banners file and maybe fill in Multi_pxo_dl_file
5588 void multi_pxo_ban_parse_banner_file(int choose_existing)
5590 char file_url[MAX_PATH_LEN] = "";
5591 char banners[10][MAX_PATH_LEN];
5592 char urls[10][MAX_PATH_LEN];
5595 int num_banners, idx;
5596 CFILE *in = cfopen(PXO_BANNERS_CONFIG_FILE, "rt", CFILE_NORMAL, CF_TYPE_MULTI_CACHE);
5598 SDL_zero(Multi_pxo_banner);
5599 Multi_pxo_banner.ban_bitmap = -1;
5606 // clear all strings
5610 // get the global banner url
5611 if(cfgets(file_url, SDL_arraysize(file_url), in) == NULL){
5613 cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5616 drop_leading_white_space(file_url);
5617 drop_trailing_white_space(file_url);
5619 // otherwise read in
5621 while(num_banners < 10){
5622 // try and get the pcx
5623 if(cfgets(banners[num_banners], SDL_arraysize(banners[0]), in) == NULL){
5626 // try and get the url
5627 if(cfgets(urls[num_banners], SDL_arraysize(urls[0]), in) == NULL){
5631 // strip off trailing and leading whitespace
5632 drop_leading_white_space(banners[num_banners]);
5633 drop_trailing_white_space(banners[num_banners]);
5634 drop_leading_white_space(urls[num_banners]);
5635 drop_trailing_white_space(urls[num_banners]);
5645 if(num_banners <= 0){
5649 // if we're only selecting files which already exist (previously downloaded)
5650 if(choose_existing){
5652 for(idx=0; idx<10; idx++){
5656 // build a list of existing files
5658 for(idx=0; idx<num_banners; idx++){
5659 if(cf_exist(banners[idx], CF_TYPE_MULTI_CACHE)){
5666 if(exist_count <= 0){
5671 int select = (int)frand_range(0.0f, (float)exist_count);
5672 if(select >= exist_count){
5673 select = exist_count - 1;
5678 for(idx=0; idx<exist_count; idx++){
5688 if(idx < exist_count){
5690 SDL_strlcpy(Multi_pxo_banner.ban_file, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file));
5692 // get the full file url
5693 SDL_strlcpy(Multi_pxo_banner.ban_file_url, file_url, SDL_arraysize(Multi_pxo_banner.ban_file_url));
5694 SDL_strlcat(Multi_pxo_banner.ban_file_url, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file_url));
5696 // url of where to go to when clicked
5697 SDL_strlcpy(Multi_pxo_banner.ban_url, urls[idx], SDL_arraysize(Multi_pxo_banner.ban_url));
5700 // randomly pick a file for download
5702 idx = (int)frand_range(0.0f, (float)num_banners);
5704 if(idx >= num_banners){
5705 idx = num_banners - 1;
5712 SDL_strlcpy(Multi_pxo_banner.ban_file, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file));
5714 // get the full file url
5715 SDL_strlcpy(Multi_pxo_banner.ban_file_url, file_url, SDL_arraysize(Multi_pxo_banner.ban_file_url));
5716 SDL_strlcat(Multi_pxo_banner.ban_file_url, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file_url));
5718 // url of where to go to when clicked
5719 SDL_strlcpy(Multi_pxo_banner.ban_url, urls[idx], SDL_arraysize(Multi_pxo_banner.ban_url));
5722 // delete the banner config file
5723 // cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5726 // any bitmap or info or whatever
5727 void multi_pxo_ban_draw()
5729 // if we have a valid bitmap
5730 if(Multi_pxo_banner.ban_bitmap >= 0){
5731 // if the mouse is over the banner button, highlight with a rectangle
5732 if(Multi_pxo_ban_button.is_mouse_on()){
5733 gr_set_color_fast(&Color_bright_blue);
5734 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);
5737 // draw the bitmap itself
5738 gr_set_bitmap(Multi_pxo_banner.ban_bitmap);
5739 gr_bitmap(Pxo_ban_coords[gr_screen.res][0], Pxo_ban_coords[gr_screen.res][1]);
5743 // called when the URL button is clicked
5744 void multi_pxo_ban_clicked()
5746 // if we have a valid bitmap and URL, launch the URL
5747 if((Multi_pxo_banner.ban_bitmap >= 0) && (strlen(Multi_pxo_banner.ban_url) > 0)){
5748 multi_pxo_url(Multi_pxo_banner.ban_url);