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"
500 #define MULTI_PXO_ANIM_X 0
501 #define MULTI_PXO_ANIM_Y 4
502 anim *Multi_pxo_anim = NULL;
503 anim_instance *Multi_pxo_anim_instance = NULL;
505 // rankings last clicked time
506 #define MULTI_PXO_RANK_TIME (5.0f)
507 float Multi_pxo_ranking_last = -1.0f;
510 int Multi_pxo_must_connect = 0; // if we still need to connect
511 int Multi_pxo_connected = 0; // if we are connected
512 int Multi_pxo_must_validate = 0; // if we need to validate on the tracker
513 int Multi_pxo_must_autojoin = 1; // still need to autojoin a channel
514 int Multi_pxo_must_verify_version = 1; // only do it once per instance of freespace
517 #define MULTI_PXO_MODE_NORMAL 0 // normal mode
518 #define MULTI_PXO_MODE_PRIVATE 1 // private channel popup
519 #define MULTI_PXO_MODE_FIND 2 // find player popup
520 int Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
522 // our nick for this session
523 char Multi_pxo_nick[NAME_LENGTH+1];
525 // check for button presses
526 void multi_pxo_check_buttons();
528 // handle a button press
529 void multi_pxo_button_pressed(int n);
531 // condition function for popup_do_with_condition for connected to Parallax Online
532 // return 10 : on successful connect
533 int multi_pxo_connect_do();
535 // attempt to connect to Parallax Online, return success or fail
536 int multi_pxo_connect();
538 // run the networking functions for the PXO API
539 void multi_pxo_api_process();
541 // process a "nick" change event
542 void multi_pxo_process_nick_change(char *data);
544 // run normally (no popups)
545 void multi_pxo_do_normal();
547 // blit everything on the "normal" screen
548 void multi_pxo_blit_all();
550 // process common stuff
551 void multi_pxo_process_common();
553 // get selected player information
554 void multi_pxo_get_data(char *name);
556 // handle being kicked
557 void multi_pxo_handle_kick();
559 // handle being disconnected
560 void multi_pxo_handle_disconnect();
562 // return string2, which is the first substring of string 1 without a space
563 // it is safe to pass the same pointer for both parameters
564 void multi_pxo_strip_space(char *string1,char *string2, const int str2_len);
566 // fire up the given URL
567 void multi_pxo_url(char *url);
569 // load/set the palette
570 void multi_pxo_load_palette();
572 // unload the palette
573 void multi_pxo_unload_palette();
575 // if we're currently on a private channel
576 int multi_pxo_on_private_channel();
578 // convert string 1 into string 2, substituting underscores for spaces
579 void multi_pxo_underscore_nick(char *string1, char *string2, const int str2_len);
581 // if the command is a potential "nick" command
582 int multi_pxo_is_nick_command(char *msg);
585 // status bar stuff -----------------------------------------------
586 int Multi_pxo_status_coords[GR_NUM_RESOLUTIONS][4] = {
599 // the status text itself
600 char Multi_pxo_status_text[255];
602 // set the status text
603 void multi_pxo_set_status_text(const char *txt);
605 // blit the status text
606 void multi_pxo_blit_status_text();
609 // channel related stuff -------------------------------------------
610 #define MAX_CHANNEL_NAME_LEN 32
611 #define MAX_CHANNEL_DESCRIPT_LEN 120
613 // some convenient macros
614 #define SWITCHING_CHANNELS() (Multi_pxo_channel_switch.num_users != -1)
615 #define ON_CHANNEL() (Multi_pxo_channel_current.num_users != -1)
617 typedef struct pxo_channel {
618 pxo_channel *next,*prev; // next and previous items in the list
619 char name[MAX_CHANNEL_NAME_LEN+1]; // name
620 char desc[MAX_CHANNEL_DESCRIPT_LEN+1]; // description
621 short num_users; // # users, or -1 if not in use
622 short num_servers; // the # of servers registered on this channel
625 // last channel we were on before going to the game list screen
626 char Multi_pxo_channel_last[MAX_CHANNEL_NAME_LEN+1] = "";
627 int Multi_pxo_use_last_channel = 0;
629 // all channels which are prefixed with this are "lobby" channels
630 #define MULTI_PXO_AUTOJOIN_PREFIX "#lobby"
632 // join this channel to get put in an appropriate lobby channel
633 #define MULTI_PXO_AUTOJOIN_CHANNEL "#autoselect"
635 int Multi_pxo_chan_coords[GR_NUM_RESOLUTIONS][4] = {
648 // this is the offset from the RIGHT side of the channel box
649 #define CHAN_PLAYERS_COLUMN 0
650 #define CHAN_GAMES_COLUMN 1
651 static int Multi_pxo_chan_column_offsets[GR_NUM_RESOLUTIONS][2] = {
656 #define CHANNEL_REFRESH_TIME (75.0f)
657 float Multi_pxo_channel_last_refresh = -1.0f;
659 #define CHANNEL_SERVER_REFRESH_TIME (35.0f)
660 float Multi_pxo_channel_server_refresh = -1.0f;
662 int Multi_pxo_max_chan_display[GR_NUM_RESOLUTIONS] = {
667 UI_BUTTON Multi_pxo_channel_button;
669 // head of the list of available (displayed) channels
670 pxo_channel *Multi_pxo_channels = NULL;
671 int Multi_pxo_channel_count = 0;
673 // item we're going to start displaying at
674 pxo_channel *Multi_pxo_channel_start = NULL;
675 int Multi_pxo_channel_start_index = -1;
677 // items we've currently got selected
678 pxo_channel *Multi_pxo_channel_select = NULL;
680 // channel we're currently connected to, num_users == -1, if we're not connected
681 pxo_channel Multi_pxo_channel_current;
683 // channel we're currently trying to change to, num_users == -1, if we're not trying to change channels
684 pxo_channel Multi_pxo_channel_switch;
686 // get a list of channels on the server (clear any old list as well)
687 void multi_pxo_get_channels();
689 // clear the old channel list
690 void multi_pxo_clear_channels();
692 // parse the input string and make a list of new channels
693 void multi_pxo_make_channels(char *chan_str);
695 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
696 pxo_channel *multi_pxo_add_channel(char *name, pxo_channel **list);
698 // lookup a channel with the specified name
699 pxo_channel *multi_pxo_find_channel(char *name, pxo_channel *list);
701 // process the channel list (select, etc)
702 void multi_pxo_process_channels();
704 // display the channel list
705 void multi_pxo_blit_channels();
707 // scroll channel list up
708 void multi_pxo_scroll_channels_up();
710 // scroll channel list down
711 void multi_pxo_scroll_channels_down();
713 // attempt to join a channel
714 void multi_pxo_join_channel(pxo_channel *chan);
716 // handle any processing details if we're currently trying to join a channel
717 void multi_pxo_handle_channel_change();
719 // autojoin an appropriate channel
720 void multi_pxo_autojoin();
722 // does the string match the "autojoin" prefic
723 int multi_pxo_is_autojoin(char *name);
725 // send a request to refresh our channel server counts
726 void multi_pxo_channel_refresh_servers();
728 // refresh current channel server count
729 void multi_pxo_channel_refresh_current();
732 // player related stuff -------------------------------------------
733 #define MAX_PLAYER_NAME_LEN 32
735 typedef struct player_list {
736 player_list *next,*prev;
737 char name[MAX_PLAYER_NAME_LEN+1];
740 // channel list region
741 int Multi_pxo_player_coords[GR_NUM_RESOLUTIONS][4] = {
754 int Multi_pxo_max_player_display[GR_NUM_RESOLUTIONS] = {
758 UI_BUTTON Multi_pxo_player_button;
761 // UI_SLIDER2 Multi_pxo_player_slider;
764 int Multi_pxo_player_slider_coords[GR_NUM_RESOLUTIONS][4] = {
772 const char *Multi_pxo_player_slider_name[GR_NUM_RESOLUTIONS] = {
774 "2_slider" // GR_1024
778 // head of the list of players in this channel
779 player_list *Multi_pxo_players = NULL;
780 int Multi_pxo_player_count = 0;
782 // item we're going to start displaying at
783 player_list *Multi_pxo_player_start = NULL;
784 // int Multi_pxo_player_start_index = -1;
786 // items we've currently got selected
787 player_list *Multi_pxo_player_select = NULL;
789 // clear the old player list
790 void multi_pxo_clear_players();
792 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
793 player_list *multi_pxo_add_player(char *name);
795 // remove a player with the given name
796 void multi_pxo_del_player(char *name);
798 // try and find a player with the given name, return a pointer to his entry (or NULL)
799 player_list *multi_pxo_find_player(char *name);
801 // process the player list (select, etc)
802 void multi_pxo_process_players();
804 // display the player list
805 void multi_pxo_blit_players();
807 // scroll player list up
808 void multi_pxo_scroll_players_up();
810 // scroll player list down
811 void multi_pxo_scroll_players_down();
813 // get the absolute index of the displayed items which our currently selected one is
814 int multi_pxo_get_select_index();
820 // add a bunch of bogus players
822 for(int idx=0; idx<Dc_arg_int; idx++){
823 SDL_snprintf(name, SDL_arraysize(name), "player %d", idx);
824 multi_pxo_add_player(name);
828 // chat text stuff -----------------------------------------
829 #define MAX_CHAT_LINES 60
830 #define MAX_CHAT_LINE_LEN 256
832 int Multi_pxo_chat_title_y[GR_NUM_RESOLUTIONS] = {
841 int Multi_pxo_chat_coords[GR_NUM_RESOLUTIONS][4] = {
854 int Multi_pxo_input_coords[GR_NUM_RESOLUTIONS][4] = {
867 int Multi_pxo_max_chat_display[GR_NUM_RESOLUTIONS] = {
876 // all messages from the server are prefixed with this
877 #define MULTI_PXO_SERVER_PREFIX "*** "
879 // the "has left" message from the server
880 #define MULTI_PXO_HAS_LEFT "has left"
883 #define CHAT_MODE_NORMAL 0 // normal chat from someone
884 #define CHAT_MODE_SERVER 1 // is from the server, display appropriately
885 #define CHAT_MODE_CARRY 2 // is a carryover from a previous line
886 #define CHAT_MODE_PRIVATE 3 // is a private message
887 #define CHAT_MODE_CHANNEL_SWITCH 4 // "switching channels" message - draw in red
888 #define CHAT_MODE_MOTD 5 // message of the day from the chat server
890 typedef struct chat_line {
891 chat_line *next,*prev;
892 char text[MAX_CHAT_LINE_LEN+1];
896 // the chat linked list itself
897 chat_line *Multi_pxo_chat = NULL;
899 // the current add line
900 chat_line *Multi_pxo_chat_add = NULL;
902 // the current line to start displaying from
903 chat_line *Multi_pxo_chat_start = NULL;
904 int Multi_pxo_chat_start_index = -1;
906 // input box for text
907 UI_INPUTBOX Multi_pxo_chat_input;
911 UI_SLIDER2 Multi_pxo_chat_slider;
913 int Multi_pxo_chat_slider_coords[GR_NUM_RESOLUTIONS][4] = {
922 const char *Multi_pxo_chat_slider_name[GR_NUM_RESOLUTIONS] = {
928 // how many chat lines we have
929 int Multi_pxo_chat_count = 0;
931 // extra delay time when switching channels
932 #define MULTI_PXO_SWITCH_DELAY_TIME 2000
933 int Multi_pxo_switch_delay = -1;
935 // initialize and create the chat text linked list
936 void multi_pxo_chat_init();
938 // free up all chat list stuff
939 void multi_pxo_chat_free();
941 // clear all lines of chat text in the chat area
942 void multi_pxo_chat_clear();
944 // blit the chat text
945 void multi_pxo_chat_blit();
947 // add a line of text
948 void multi_pxo_chat_add_line(char *txt,int mode);
950 // process an incoming line of text
951 void multi_pxo_chat_process_incoming(const char *txt,int mode = CHAT_MODE_NORMAL);
953 // scroll to the very bottom of the chat area
954 void multi_pxo_goto_bottom();
956 // check whether we can scroll down or not
957 int multi_pxo_can_scroll_down();
959 static int Can_scroll_down = 0;
961 // scroll the text up
962 void multi_pxo_scroll_chat_up();
964 // scroll the text down
965 void multi_pxo_scroll_chat_down();
967 // process chat controls
968 void multi_pxo_chat_process();
970 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
971 const char *multi_pxo_chat_is_private(const char *txt);
973 // if the text came from the server
974 int multi_pxo_is_server_text(const char *txt);
976 // if the text is message of the day text
977 int multi_pxo_is_motd_text(const char *txt);
979 // if the text is the end of motd text
980 int multi_pxo_is_end_of_motd_text(const char *txt);
982 // if the text is a "has left message" from the server
983 int multi_pxo_chat_is_left_message(const char *txt);
985 // recalculate the chat start index, and adjust the slider properly
986 void multi_pxo_chat_adjust_start();
989 // motd stuff ---------------------------------------------------------
990 #define MAX_PXO_MOTD_LEN 1024
991 #define PXO_MOTD_BLINK_TIME 500
992 char Pxo_motd[1024] = "";
993 int Pxo_motd_end = 0;
994 int Pxo_motd_read = 0;
995 int Pxo_motd_blink_stamp = -1;
996 int Pxo_motd_blink_on = 0;
997 int Pxo_motd_blinked_already = 0;
999 // initialize motd when going into this screen
1000 void multi_pxo_motd_init();
1002 // set the motd text
1003 void multi_pxo_motd_add_text(const char *text);
1006 void multi_pxo_set_end_of_motd();
1008 // display the motd dialog
1009 void multi_pxo_motd_dialog();
1011 // call to maybe blink the motd button
1012 void multi_pxo_motd_maybe_blit();
1015 // common dialog stuff ------------------------------------------------
1016 const char *Multi_pxo_com_fname[GR_NUM_RESOLUTIONS] = {
1020 const char *Multi_pxo_com_mask_fname[GR_NUM_RESOLUTIONS] = {
1026 int Multi_pxo_com_coords[GR_NUM_RESOLUTIONS][2] = {
1040 int Multi_pxo_com_input_coords[GR_NUM_RESOLUTIONS][4] = {
1053 #define MULTI_PXO_COM_NUM_BUTTONS 2
1054 #define MULTI_PXO_COM_CANCEL 0
1055 #define MULTI_PXO_COM_OK 1
1057 ui_button_info Multi_pxo_com_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_BUTTONS] = {
1060 ui_button_info("PXP_00", 573, 192, -1, -1, 0),
1061 ui_button_info("PXP_01", 573, 226, -1, -1, 1)
1063 ui_button_info("PXP_00", 494, 182, -1, -1, 0),
1064 ui_button_info("PXP_01", 525, 221, -1, -1, 1)
1068 ui_button_info("2_PXP_00", 917, 308, -1, -1, 0),
1069 ui_button_info("2_PXP_01", 917, 361, -1, -1, 1)
1074 #define MULTI_PXO_COM_NUM_TEXT 2
1075 UI_XSTR Multi_pxo_com_text[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_TEXT] = {
1077 { "&Cancel", 645, 510, 204, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_CANCEL].button },
1078 { "&Ok", 669, 548, 233, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[0][MULTI_PXO_COM_OK].button }
1081 { "&Cancel", 645, 847, 327, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_CANCEL].button },
1082 { "&Ok", 669, 877, 372, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_com_buttons[1][MULTI_PXO_COM_OK].button }
1087 int Multi_pxo_com_bitmap = -1;
1088 UI_WINDOW Multi_pxo_com_window;
1089 UI_INPUTBOX Multi_pxo_com_input;
1091 // text on the "top" half of the dialog display area
1092 char Multi_pxo_com_top_text[255];
1094 // text on the "middle" portion of the dialog display area
1095 char Multi_pxo_com_middle_text[255];
1097 // text on the "bottom" half of the dialog display area
1098 char Multi_pxo_com_bottom_text[255];
1100 int Multi_pxo_com_top_text_coords[GR_NUM_RESOLUTIONS][2] = {
1108 int Multi_pxo_com_middle_text_y[GR_NUM_RESOLUTIONS] = {
1112 int Multi_pxo_com_bottom_text_y[GR_NUM_RESOLUTIONS] = {
1117 // initialize the common dialog with the passed max input length
1118 void multi_pxo_com_init();
1120 // close down the common dialog
1121 void multi_pxo_com_close();
1123 // blit all text lines, top, middle, bottoms
1124 void multi_pxo_com_blit_text();
1126 // set the top text, shortening as necessary
1127 void multi_pxo_com_set_top_text(const char *txt);
1129 // set the middle text, shortening as necessary
1130 void multi_pxo_com_set_middle_text(const char *txt);
1132 // set the bottom text, shortening as necessary
1133 void multi_pxo_com_set_bottom_text(const char *txt);
1136 // private channel join stuff -----------------------------------------
1137 #define MULTI_PXO_PRIV_MAX_TEXT_LEN 30
1139 // max private channel name length
1140 char Multi_pxo_priv_chan[MULTI_PXO_PRIV_MAX_TEXT_LEN+100];
1142 // return code, set to something other than -1 if we're supposed to return
1143 int Multi_pxo_priv_return_code = -1;
1145 // initialize the popup
1146 void multi_pxo_priv_init();
1148 // close down the popup
1149 void multi_pxo_priv_close();
1151 // run the popup, 0 if still running, -1 if cancel, 1 if ok
1152 int multi_pxo_priv_popup();
1154 // process button presses
1155 void multi_pxo_priv_process_buttons();
1157 // handle a button press
1158 void multi_pxo_priv_button_pressed(int n);
1160 // process the inputbox
1161 void multi_pxo_priv_process_input();
1164 // find player stuff -----------------------------------------
1166 char Multi_pxo_find_channel[MAX_CHANNEL_NAME_LEN+1];
1168 // return code, set to something other than -1 if we're supposed to return
1169 int Multi_pxo_find_return_code = -1;
1171 // initialize the popup
1172 void multi_pxo_find_init();
1174 // close down the popup
1175 void multi_pxo_find_close();
1177 // run the popup, 0 if still running, -1 if cancel, 1 if ok
1178 int multi_pxo_find_popup();
1180 // process button presses
1181 void multi_pxo_find_process_buttons();
1183 // handle a button press
1184 void multi_pxo_find_button_pressed(int n);
1186 // process the inputbox
1187 void multi_pxo_find_process_input();
1189 // process search mode if applicable
1190 void multi_pxo_find_search_process();
1193 // player info stuff -----------------------------------------
1194 const char *Multi_pxo_pinfo_fname[GR_NUM_RESOLUTIONS] = {
1198 const char *Multi_pxo_pinfo_mask_fname[GR_NUM_RESOLUTIONS] = {
1204 #define MULTI_PXO_PINFO_NUM_BUTTONS 2
1205 #define MULTI_PXO_PINFO_MEDALS 0
1206 #define MULTI_PXO_PINFO_OK 1
1208 ui_button_info Multi_pxo_pinfo_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_BUTTONS] = {
1211 ui_button_info("PI2_00", 328, 446, 319, 433, 0),
1212 ui_button_info("PI2_01", 376, 446, 382, 433, 1),
1214 ui_button_info("PI2_00", 286, 359, -1, -1, 0),
1215 ui_button_info("PI2_01", 341, 359, -1, -1, 1)
1219 ui_button_info("2_PI2_00", 525, 714, 510, 695, 0),
1220 ui_button_info("2_PI2_01", 601, 714, 611, 695, 1),
1226 #define MULTI_PXO_PINFO_NUM_TEXT 2
1227 UI_XSTR Multi_pxo_pinfo_text[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_TEXT] = {
1229 { "Medals", 1037, 319, 433, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_MEDALS].button },
1230 { "Ok", 345, 382, 433, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_OK].button },
1233 { "Medals", 1037, 510, 695, UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_MEDALS].button },
1234 { "Ok", 345, 611, 695, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_OK].button },
1239 int Multi_pxo_pinfo_bitmap = -1;
1240 UI_WINDOW Multi_pxo_pinfo_window;
1242 vmt_stats_struct Multi_pxo_pinfo;
1243 player Multi_pxo_pinfo_player;
1245 int Multi_pxo_retrieve_mode = -1;
1247 char Multi_pxo_retrieve_name[MAX_PLAYER_NAME_LEN+1];
1248 char Multi_pxo_retrieve_id[128];
1250 // stats label stuff
1251 #define MULTI_PXO_PINFO_NUM_LABELS 18
1253 int Multi_pxo_pinfo_coords[GR_NUM_RESOLUTIONS][4] = {
1265 int Multi_pxo_pinfo_val_x[GR_NUM_RESOLUTIONS] = {
1270 char *Multi_pxo_pinfo_stats_labels[MULTI_PXO_PINFO_NUM_LABELS];
1281 "Primary shots fired",
1282 "Primary shots hit",
1284 "Secondary shots fired",
1285 "Secondary shots hit",
1287 "Primary friendly hits",
1288 "Primary friendly hit %",
1289 "Secondary friendly hits",
1290 "Secondary friendly hit %"
1295 char Multi_pxo_pinfo_vals[MULTI_PXO_PINFO_NUM_LABELS][50];
1297 int Multi_pxo_pinfo_stats_spacing[MULTI_PXO_PINFO_NUM_LABELS] = {
1298 10,20,10,10,20,10,10,20,10,10,20,10,10,20,10,20,10,0
1301 // popup conditional functions, returns 10 on successful get of stats
1302 int multi_pxo_pinfo_cond();
1304 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
1305 int multi_pxo_pinfo_get(char *name);
1307 // fire up the stats view popup
1308 void multi_pxo_pinfo_show();
1310 // build the stats labels values
1311 void multi_pxo_pinfo_build_vals();
1313 // initialize the popup
1314 void multi_pxo_pinfo_init();
1317 int multi_pxo_pinfo_do();
1320 void multi_pxo_pinfo_close();
1322 // blit all the stats on this screen
1323 void multi_pxo_pinfo_blit();
1325 // run the medals screen
1326 void multi_pxo_run_medals();
1328 // notify stuff stuff -----------------------------------------
1329 #define MULTI_PXO_NOTIFY_TIME 4000
1330 #define MULTI_PXO_NOTIFY_Y 435
1332 char Multi_pxo_notify_text[255];
1333 int Multi_pxo_notify_stamp = -1;
1335 // add a notification string
1336 void multi_pxo_notify_add(const char *txt);
1338 // blit and process the notification string
1339 void multi_pxo_notify_blit();
1342 // help screen stuff -----------------------------------------
1344 const char *Multi_pxo_help_fname[GR_NUM_RESOLUTIONS] = {
1348 const char *Multi_pxo_help_mask_fname[GR_NUM_RESOLUTIONS] = {
1353 #define MULTI_PXO_HELP_NUM_BUTTONS 3
1354 #define MULTI_PXO_HELP_PREV 0
1355 #define MULTI_PXO_HELP_NEXT 1
1356 #define MULTI_PXO_HELP_CONTINUE 2
1358 ui_button_info Multi_pxo_help_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_BUTTONS] = {
1361 ui_button_info("PXH_00", 15, 389, -1, -1, 0),
1362 ui_button_info("PXH_01", 60, 389, -1, -1, 1),
1363 ui_button_info("PXH_02", 574, 431, 571, 413, 2),
1365 ui_button_info("PXH_00", 71, 373, -1, -1, 0),
1366 ui_button_info("PXH_01", 121, 373, -1, -1, 1),
1367 ui_button_info("PXH_02", 554, 411, -1, -1, 2)
1371 ui_button_info("2_PXH_00", 24, 622, -1, -1, 0),
1372 ui_button_info("2_PXH_01", 96, 622, -1, -1, 1),
1373 ui_button_info("2_PXH_02", 919, 689, 928, 663, 2),
1378 #define MULTI_PXO_HELP_NUM_TEXT 1
1379 UI_XSTR Multi_pxo_help_text[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_TEXT] = {
1381 {"Continue", 1069, 571, 413, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[0][MULTI_PXO_HELP_CONTINUE].button },
1384 {"Continue", 1069, 928, 663, UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[1][MULTI_PXO_HELP_CONTINUE].button },
1390 #define MULTI_PXO_HELP_FILE "pxohelp.txt"
1391 #define MULTI_PXO_MAX_LINES_PP 57
1392 #define MULTI_PXO_MAX_PAGES 3
1394 int Multi_pxo_help_coords[GR_NUM_RESOLUTIONS][2] = {
1403 int Multi_pxo_chars_per_line[GR_NUM_RESOLUTIONS] = {
1408 int Multi_pxo_lines_pp[GR_NUM_RESOLUTIONS] = {
1414 typedef struct help_page {
1415 char *text[MULTI_PXO_MAX_LINES_PP];
1419 help_page Multi_pxo_help_pages[MULTI_PXO_MAX_PAGES];
1420 // int Multi_pxo_help_loaded = 0;
1422 int Multi_pxo_help_num_pages = 0;
1424 int Multi_pxo_help_bitmap = -1;
1425 UI_WINDOW Multi_pxo_help_window;
1427 // current page we're on
1428 int Multi_pxo_help_cur = 0;
1430 // load the help file up
1431 void multi_pxo_help_load();
1433 // blit the current page
1434 void multi_pxo_help_blit_page();
1436 // process button presses
1437 void multi_pxo_help_process_buttons();
1440 void multi_pxo_help_button_pressed(int n);
1443 // http banner stuff ---------------------------------------------
1444 InetGetFile *Multi_pxo_ban_get = NULL;
1447 #define PXO_BANNERS_CONFIG_FILE "pxobanners.cfg"
1449 // coords to display banners at
1450 int Pxo_ban_coords[GR_NUM_RESOLUTIONS][4] = {
1460 #define PXO_BAN_MODE_LIST_STARTUP 0 // start downloading list
1461 #define PXO_BAN_MODE_LIST 1 // downloading list
1462 #define PXO_BAN_MODE_IMAGES_STARTUP 2 // start downloading images
1463 #define PXO_BAN_MODE_IMAGES 3 // downloading images
1464 #define PXO_BAN_MODE_IMAGES_DONE 4 // done downloading everything - now maybe load an image
1465 #define PXO_BAN_MODE_IDLE 5 // done with everything - doing nothing
1466 #define PXO_BAN_MODE_CHOOSE_RANDOM 6 // choose a bitmap we've already downloaded at random
1468 // interface button for detecting clicks
1469 UI_BUTTON Multi_pxo_ban_button;
1472 typedef struct pxo_banner {
1473 char ban_file[MAX_FILENAME_LEN+1]; // base filename of the banner
1474 char ban_file_url[MULTI_OPTIONS_STRING_LEN+1]; // full url of the file to get (convenient)
1475 char ban_url[MULTI_OPTIONS_STRING_LEN+1]; // url to go to when clicked
1476 int ban_bitmap; // banner bitmap
1479 // active pxo banner
1480 pxo_banner Multi_pxo_banner;
1483 int Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
1486 void multi_pxo_ban_init();
1488 // process http download details
1489 void multi_pxo_ban_process();
1492 void multi_pxo_ban_close();
1494 // parse the banners file and maybe fill in Multi_pxo_dl_file[]
1495 void multi_pxo_ban_parse_banner_file(int choose_existing);
1497 // any bitmap or info or whatever
1498 void multi_pxo_ban_draw();
1500 // called when the URL button is clicked
1501 void multi_pxo_ban_clicked();
1504 // ----------------------------------------------------------------------------------------------------
1508 // initialize the PXO screen
1509 void multi_pxo_init(int use_last_channel)
1513 // load the background bitmap
1514 Multi_pxo_bitmap = bm_load(Multi_pxo_bitmap_fname[gr_screen.res]);
1515 if(Multi_pxo_bitmap < 0){
1516 // we failed to load the bitmap - this is very bad
1520 // load up the private channel bitmap
1521 Multi_pxo_com_bitmap = bm_load(Multi_pxo_com_fname[gr_screen.res]);
1522 SDL_assert(Multi_pxo_com_bitmap != -1);
1524 // create the interface window
1525 Multi_pxo_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1526 Multi_pxo_window.set_mask_bmap(Multi_pxo_mask_fname[gr_screen.res]);
1528 // multiplayer screen common palettes
1529 multi_pxo_load_palette();
1531 // create the interface buttons
1532 for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
1533 // create the object
1534 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);
1536 // set the sound to play when highlighted
1537 Multi_pxo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1539 // set the ani for the button
1540 Multi_pxo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_buttons[gr_screen.res][idx].filename);
1543 Multi_pxo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_buttons[gr_screen.res][idx].hotspot);
1548 for(idx=0; idx<MULTI_PXO_NUM_TEXT; idx++){
1549 Multi_pxo_window.add_XSTR(&Multi_pxo_text[gr_screen.res][idx]);
1553 if(use_last_channel && strlen(Multi_pxo_channel_last)){
1554 Multi_pxo_use_last_channel = 1;
1556 SDL_zero(Multi_pxo_channel_last);
1557 Multi_pxo_use_last_channel = 0;
1560 // make all scrolling buttons repeatable
1561 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_UP].button.repeatable(1);
1562 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_DOWN].button.repeatable(1);
1563 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_UP].button.repeatable(1);
1564 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_DOWN].button.repeatable(1);
1565 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_UP].button.repeatable(1);
1566 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_DOWN].button.repeatable(1);
1568 // set the mouseover cursor if it loaded ok
1569 if (Web_cursor_bitmap > 0) {
1570 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_RANKINGS].button.set_custom_cursor_bmap(Web_cursor_bitmap);
1573 // create the channel list select button and hide it
1574 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);
1575 Multi_pxo_channel_button.hide();
1577 // create the player list select button and hide it
1578 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);
1579 Multi_pxo_player_button.hide();
1581 // create the chat input box
1582 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);
1583 Multi_pxo_chat_input.set_focus();
1585 // create the banner button and hide it
1586 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);
1587 Multi_pxo_ban_button.hide();
1589 // create the player list slider
1590 // 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);
1593 // create the chat slider
1594 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);
1597 // set our connection status so that we do the right stuff next frame
1598 Multi_pxo_must_validate = 1;
1599 Multi_pxo_must_connect = 0;
1600 Multi_pxo_connected = 0;
1602 // channel we're currently connected to
1603 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
1604 Multi_pxo_channel_current.num_users = -1;
1606 // channel we're currently trying to change to, or NULL if nont
1607 memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
1608 Multi_pxo_channel_switch.num_users = -1;
1610 // last time clicked the url button (so we don't have repeats)
1611 Multi_pxo_ranking_last = -1.0f;
1613 // channel switching extra time delay stamp
1614 Multi_pxo_switch_delay = -1;
1616 // our nick for this session
1617 multi_pxo_underscore_nick(Player->callsign, Multi_pxo_nick, SDL_arraysize(Multi_pxo_nick));
1619 // clear the channel list
1620 multi_pxo_clear_channels();
1622 // clear the player list
1623 multi_pxo_clear_players();
1625 // initialize the chat system
1626 multi_pxo_chat_init();
1629 multi_pxo_ban_init();
1631 // load the animation up
1632 if (gr_screen.res == GR_1024) {
1633 char anim_filename[32] = "2_";
1634 SDL_strlcat(anim_filename, MULTI_PXO_ANIM_FNAME, SDL_arraysize(anim_filename));
1635 Multi_pxo_anim = anim_load(anim_filename);
1637 // if hi-res is not there, fallback to low
1638 if (Multi_pxo_anim == NULL) {
1639 Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME);
1642 Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME);
1645 // clear the status text
1646 multi_pxo_set_status_text("");
1648 // last refresh time
1649 Multi_pxo_channel_last_refresh = -1.0f;
1651 // server count last refresh time
1652 Multi_pxo_channel_server_refresh = -1.0f;
1655 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1658 multi_pxo_motd_init();
1660 // make sure we autojoin
1661 Multi_pxo_must_autojoin = 1;
1663 // clear all tracker channel related strings
1664 SDL_zero(Multi_fs_tracker_channel);
1665 SDL_zero(Multi_fs_tracker_filter);
1668 // do frame for the PXO screen
1671 pxo_channel priv_chan;
1674 if(Multi_pxo_connected) {
1675 multi_pxo_api_process();
1678 // process common stuff
1679 multi_pxo_process_common();
1681 switch(Multi_pxo_mode){
1682 // private channel join mode
1683 case MULTI_PXO_MODE_PRIVATE:
1684 switch(multi_pxo_priv_popup()){
1689 // user hit "cancel"
1691 // return to normal mode
1692 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1697 // setup some information
1698 memset(&priv_chan,0,sizeof(pxo_channel));
1699 priv_chan.num_users = 0;
1700 SDL_strlcpy(priv_chan.name, Multi_pxo_priv_chan, SDL_arraysize(priv_chan.name));
1702 // see if we know about this channel already
1703 multi_pxo_join_channel(&priv_chan);
1705 // return to normal mode
1706 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1712 case MULTI_PXO_MODE_FIND:
1713 switch(multi_pxo_find_popup()){
1718 // user hit "cancel"
1720 // return to normal mode
1721 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1726 // return to normal mode
1727 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1729 // if there is a valid channel name try and join it
1730 if(strlen(Multi_pxo_find_channel) && !SWITCHING_CHANNELS()){
1734 memset(&join,0,sizeof(pxo_channel));
1736 SDL_strlcpy(join.name, Multi_pxo_find_channel, SDL_arraysize(join.name));
1739 multi_pxo_join_channel(&join);
1745 case MULTI_PXO_MODE_NORMAL:
1746 multi_pxo_do_normal();
1751 // close the PXO screen
1752 void multi_pxo_close()
1754 // unload any bitmaps
1755 bm_unload(Multi_pxo_bitmap);
1756 bm_unload(Multi_pxo_com_bitmap);
1758 // record the last channel we were on, if any
1759 SDL_zero(Multi_fs_tracker_channel);
1760 SDL_zero(Multi_fs_tracker_filter);
1762 if( ON_CHANNEL() && strlen(Multi_pxo_channel_current.name) ){
1764 SDL_strlcpy(Multi_fs_tracker_channel, Multi_pxo_channel_current.name, SDL_arraysize(Multi_fs_tracker_channel));
1767 SDL_strlcpy(Multi_fs_tracker_filter, Multi_pxo_channel_current.name, SDL_arraysize(Multi_fs_tracker_filter));
1770 // disconnect from the server
1771 DisconnectFromChatServer();
1772 Multi_pxo_connected = 0;
1774 // unload the animation
1775 anim_release_all_instances(GS_STATE_PXO);
1776 Multi_pxo_anim_instance = NULL;
1777 if(Multi_pxo_anim != NULL){
1778 anim_free(Multi_pxo_anim);
1779 Multi_pxo_anim = NULL;
1782 // unload the palette for this screen
1783 multi_pxo_unload_palette();
1785 // destroy the UI_WINDOW
1786 Multi_pxo_window.destroy();
1788 // clear the channel list
1789 multi_pxo_clear_channels();
1791 // close the chat system
1792 multi_pxo_chat_free();
1795 multi_pxo_ban_close();
1798 // run normally (no popups)
1799 void multi_pxo_do_normal()
1802 int k = Multi_pxo_window.process();
1804 // if the animation isn't playing, start it up
1805 if((Multi_pxo_anim_instance == NULL) && (Multi_pxo_anim != NULL)){
1806 anim_play_struct aps;
1808 // fire up the animation
1809 anim_play_init(&aps, Multi_pxo_anim, MULTI_PXO_ANIM_X, MULTI_PXO_ANIM_Y);
1810 aps.screen_id = GS_STATE_PXO;
1811 aps.framerate_independent = 1;
1813 Multi_pxo_anim_instance = anim_play(&aps);
1816 // process any keypresses
1819 gamesnd_play_iface(SND_USER_SELECT);
1820 gameseq_post_event(GS_EVENT_MAIN_MENU);
1824 // check for button presses
1825 multi_pxo_check_buttons();
1827 // if we're not in a chatroom, disable and hide the chat input box
1829 Multi_pxo_chat_input.hide();
1830 Multi_pxo_chat_input.disable();
1832 Multi_pxo_chat_input.enable();
1833 Multi_pxo_chat_input.unhide();
1837 multi_pxo_blit_all();
1842 // verify version # now (only once per Freespace instance)
1843 if(Multi_pxo_must_verify_version){
1844 switch(multi_update_gobaby()){
1845 // everything is cool. Move along
1846 case MULTI_UPDATE_CONTINUE:
1847 Multi_pxo_must_verify_version = 0;
1850 // go back to the main menu
1851 case MULTI_UPDATE_MAIN_MENU:
1852 gameseq_post_event(GS_EVENT_MAIN_MENU);
1854 // unset these so we don't do anything else PXO related
1855 Multi_pxo_must_validate = 0;
1856 Multi_pxo_must_connect = 0;
1859 // freespace will be shutting down shortly
1860 case MULTI_UPDATE_SHUTTING_DOWN:
1865 // if we need to get tracker info for ourselves, do so
1866 if(Multi_pxo_must_validate){
1867 // initialize the master tracker API for Freespace
1868 multi_fs_tracker_init();
1870 // validate the current player with the master tracker (will create the pilot on the MT if necessary)
1871 validate_code = multi_fs_tracker_validate(0);
1872 if(validate_code != 1){
1873 // show an error popup if it failed (not cancelled by the user)
1874 if (validate_code == 0) {
1875 switch (popup(PF_USE_AFFIRMATIVE_ICON | PF_WEB_CURSOR_1 | PF_WEB_CURSOR_2, 3, POPUP_CANCEL,XSTR("&Create Acct",936), XSTR("&Verify Acct",937), XSTR("PXO Login not accepted. You may visit the Parallax Online website to create or verify your login. Or you may click Cancel to play without using the Parallax Online service. (You may switch back to Parallax Online from the Options Menu under the Multi tab.)",938))) {
1877 nprintf(("Network","PXO CANCEL\n"));
1879 // flip his "pxo" bit temporarily and push him to the join game screen
1880 Multi_options_g.pxo = 0;
1881 // Net_game_tcp_mode = NET_TCP;
1882 gameseq_post_event(GS_EVENT_MULTI_JOIN_GAME);
1886 nprintf(("Network","PXO CREATE\n"));
1887 // fire up the given URL
1888 multi_pxo_url(Multi_options_g.pxo_create_url);
1892 nprintf(("Network","PXO VERIFY\n"));
1893 // fire up the given URL
1894 multi_pxo_url(Multi_options_g.pxo_verify_url);
1899 // go back to the main hall
1900 gameseq_post_event(GS_EVENT_MAIN_MENU);
1902 Multi_pxo_must_connect = 0;
1903 Multi_pxo_must_validate = 0;
1905 // now we have to conenct to PXO
1907 Multi_pxo_must_connect = 1;
1908 Multi_pxo_must_validate = 0;
1912 // if we need to connect, do so now
1913 if(Multi_pxo_must_connect){
1914 // for now, just try once
1915 Multi_pxo_connected = multi_pxo_connect();
1917 // if we successfully connected, send a request for a list of channels on the server
1918 if(Multi_pxo_connected){
1919 multi_pxo_get_channels();
1922 multi_pxo_set_status_text(XSTR("Retrieving Public Channels",939));
1925 multi_pxo_set_status_text(XSTR("Failed to connect to Parallax Online",940));
1928 // no longer need to connect
1929 Multi_pxo_must_connect = 0;
1933 // blit everything on the "normal" screen
1934 void multi_pxo_blit_all()
1936 // draw the background, etc
1938 // GR_MAYBE_CLEAR_RES(Multi_pxo_bitmap);
1939 int bmap = Multi_pxo_bitmap;
1944 bm_get_info( bmap, &bmw, &bmh);
1945 if((bmw != gr_screen.max_w) || (bmh != gr_screen.max_h)){
1952 if(Multi_pxo_bitmap != -1){
1953 gr_set_bitmap(Multi_pxo_bitmap);
1956 Multi_pxo_window.draw();
1958 // display the channel list
1959 multi_pxo_blit_channels();
1961 // display the player list
1962 multi_pxo_blit_players();
1964 // blit the chat text
1965 multi_pxo_chat_blit();
1967 // blit the status text
1968 multi_pxo_blit_status_text();
1970 // blit and process the notification string
1971 multi_pxo_notify_blit();
1973 // any bitmap or info or whatever
1974 multi_pxo_ban_draw();
1976 // draw any motd stuff
1977 multi_pxo_motd_maybe_blit();
1979 // if we have a valid animation handle, play it
1980 if(Multi_pxo_anim_instance != NULL){
1981 anim_render_all(GS_STATE_PXO,flFrametime);
1985 // process common stuff
1986 void multi_pxo_process_common()
1988 // process the channel list (select, etc)
1989 multi_pxo_process_channels();
1991 // process the player list (select, etc)
1992 multi_pxo_process_players();
1994 // process chat controls
1995 multi_pxo_chat_process();
1997 // process http download details
1998 multi_pxo_ban_process();
2001 // get selected player information
2002 void multi_pxo_get_data(char *name)
2006 // handle being kicked
2007 void multi_pxo_handle_kick()
2009 // remove ourselves from the room
2010 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2011 Multi_pxo_channel_current.num_users = -1;
2014 multi_pxo_chat_clear();
2016 // clear the old player list
2017 multi_pxo_clear_players();
2019 // add a notification string
2020 multi_pxo_notify_add(XSTR("You have been kicked",941));
2023 // handle being disconnected
2024 void multi_pxo_handle_disconnect()
2026 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been disconnected from the server",942));
2027 gameseq_post_event(GS_EVENT_MAIN_MENU);
2030 // return string2, which is the first substring of string 1 without a space
2031 // it is safe to pass the same pointer for both parameters
2032 void multi_pxo_strip_space(char *string1, char *string2, const int str2_len)
2037 // copy the original
2038 SDL_strlcpy(midway, string1, SDL_arraysize(midway));
2039 tok = strtok(midway," ");
2041 SDL_strlcpy(string2, tok, str2_len);
2043 SDL_strlcpy(string2, "", str2_len);
2047 // fire up the given URL
2048 void multi_pxo_url(char *url)
2050 if ( platform_open_url(url) ) {
2051 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,XSTR("Warning\nCould not locate/launch default Internet Browser",943));
2055 // load/set the palette
2056 void multi_pxo_load_palette()
2059 //#ifndef HARDWARE_ONLY
2060 // palette_use_bm_palette(Multi_pxo_palette);
2064 // unload the palette
2065 void multi_pxo_unload_palette()
2067 // unload the palette if it exists
2068 if(Multi_pxo_palette != -1){
2069 bm_release(Multi_pxo_palette);
2070 Multi_pxo_palette = -1;
2074 // if we're currently on a private channel
2075 int multi_pxo_on_private_channel()
2077 // if we're connected to a channel with the "+" symbol on front
2078 if(ON_CHANNEL() && (Multi_pxo_channel_current.name[0] == '+')){
2082 // otherwise return falos
2086 // convert string 1 into string 2, substituting underscores for spaces
2087 void multi_pxo_underscore_nick(char *string1, char *string2, const int str2_len)
2089 char nick_temp[512];
2092 // don't do anything if we have bogus string
2093 if((string1 == NULL) || (string2 == NULL)){
2097 // copy the nickname
2098 SDL_strlcpy(nick_temp, string1, SDL_arraysize(nick_temp));
2100 // get the first token
2101 tok = strtok(nick_temp, " ");
2103 SDL_strlcpy(string2, tok, str2_len);
2105 // get the next token
2106 tok = strtok(NULL," ");
2109 SDL_strlcat(string2, "_", str2_len);
2110 SDL_strlcat(string2, tok, str2_len);
2113 tok = strtok(NULL," ");
2116 SDL_strlcpy(string2, string1, str2_len);
2120 // if the command is a potential "nick" command
2121 int multi_pxo_is_nick_command(char *msg)
2126 // get the first token in the message
2127 SDL_strlcpy(tmp, msg, SDL_arraysize(tmp));
2128 tok = strtok(tmp," ");
2130 // can't be a nick message
2134 return !SDL_strcasecmp(tok,NOX("/nick"));
2137 // check for button presses
2138 void multi_pxo_check_buttons()
2142 // go through all buttons
2143 for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
2144 if(Multi_pxo_buttons[gr_screen.res][idx].button.pressed()){
2145 multi_pxo_button_pressed(idx);
2151 // handle a button press
2152 void multi_pxo_button_pressed(int n)
2155 case MULTI_PXO_EXIT:
2156 gamesnd_play_iface(SND_USER_SELECT);
2157 gameseq_post_event(GS_EVENT_MAIN_MENU);
2160 case MULTI_PXO_CHAN_UP:
2161 multi_pxo_scroll_channels_up();
2164 case MULTI_PXO_CHAN_DOWN:
2165 multi_pxo_scroll_channels_down();
2168 case MULTI_PXO_TEXT_UP:
2169 multi_pxo_scroll_chat_up();
2172 case MULTI_PXO_TEXT_DOWN:
2173 multi_pxo_scroll_chat_down();
2176 case MULTI_PXO_PLIST_UP:
2177 multi_pxo_scroll_players_up();
2178 multi_pxo_chat_adjust_start();
2181 case MULTI_PXO_PLIST_DOWN:
2182 multi_pxo_scroll_players_down();
2183 multi_pxo_chat_adjust_start();
2186 case MULTI_PXO_JOIN:
2187 // if there are no channels to join, let the user know
2188 if((Multi_pxo_channel_count == 0) || (Multi_pxo_channels == NULL)){
2189 gamesnd_play_iface(SND_GENERAL_FAIL);
2190 multi_pxo_notify_add(XSTR("No channels!",944));
2194 // if we're not already trying to join, allow this
2195 if(!SWITCHING_CHANNELS() && (Multi_pxo_channel_select != NULL)){
2196 gamesnd_play_iface(SND_USER_SELECT);
2197 multi_pxo_join_channel(Multi_pxo_channel_select);
2199 multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2200 gamesnd_play_iface(SND_GENERAL_FAIL);
2204 case MULTI_PXO_GAMES:
2205 // move to the join game screen as normally (temporary!)
2206 gameseq_post_event( GS_EVENT_MULTI_JOIN_GAME );
2209 case MULTI_PXO_JOIN_PRIV:
2210 // if we're not already trying to join, allow this
2211 if(!SWITCHING_CHANNELS()){
2212 gamesnd_play_iface(SND_USER_SELECT);
2214 // fire up the private join popup
2215 multi_pxo_priv_popup();
2217 multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2218 gamesnd_play_iface(SND_GENERAL_FAIL);
2222 case MULTI_PXO_FIND:
2223 gamesnd_play_iface(SND_USER_SELECT);
2225 // fire up the find join popup
2226 multi_pxo_find_popup();
2229 case MULTI_PXO_HELP:
2230 gamesnd_play_iface(SND_USER_SELECT);
2231 gameseq_post_event(GS_EVENT_PXO_HELP);
2234 case MULTI_PXO_PINFO:
2237 // if we have a guy selected, try and get his info
2238 if(Multi_pxo_player_select != NULL){
2239 // if we successfully got info for this guy
2240 if(multi_pxo_pinfo_get(Multi_pxo_player_select->name)){
2242 multi_pxo_pinfo_show();
2244 // if we didn't get stats for this guy.
2246 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);
2247 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,stats);
2250 gamesnd_play_iface(SND_GENERAL_FAIL);
2254 case MULTI_PXO_RANKINGS:
2255 // make sure he doesn't click it too many times
2256 if((Multi_pxo_ranking_last < 0.0f) || ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_ranking_last) > MULTI_PXO_RANK_TIME) ){
2257 gamesnd_play_iface(SND_USER_SELECT);
2260 multi_pxo_url(Multi_options_g.pxo_rank_url);
2262 // mark the time down
2263 Multi_pxo_ranking_last = f2fl(timer_get_fixed_seconds());
2265 gamesnd_play_iface(SND_GENERAL_FAIL);
2269 case MULTI_PXO_MOTD:
2270 // maybe fire up the pxo motd dialog
2271 multi_pxo_motd_dialog();
2277 // condition function for popup_do_with_condition for connected to Parallax Online
2278 int mpxo_failed = 0;
2279 int multi_pxo_connect_do()
2282 char id_string[255] = "";
2283 char ip_string[255] = "";
2285 // if we already tried and failed, sit around until the user presses cancel
2287 // try and connect to the server
2290 // build the tracker id string
2291 SDL_snprintf(id_string, SDL_arraysize(id_string), "%s %s", Multi_tracker_id_string, Player->callsign);
2293 // build the ip string
2294 SDL_snprintf(ip_string, SDL_arraysize(ip_string), "%s:%d", Multi_options_g.pxo_ip, PXO_CHAT_PORT);
2296 // connect to the server
2297 ret_code = ConnectToChatServer(ip_string, Multi_pxo_nick, id_string);
2299 // give some time to the pxo api.
2300 multi_pxo_api_process();
2303 // already connected, return success
2307 // failed to connect, return fail
2310 popup_change_text(XSTR("Failed to connect to Parallax Online!", 947));
2313 // connected, return success
2326 // popup loop which does an autojoin of a public channel. Returns when the autojoin process is complete
2327 int multi_pxo_autojoin_do()
2329 pxo_channel last_channel;
2331 // if we need to autojoin, do so now
2332 if(Multi_pxo_must_autojoin){
2333 Multi_pxo_must_autojoin = 0;
2335 // if we're supposed to be using a (valid) "last" channel, do so
2336 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){
2338 memset(&last_channel, 0, sizeof(pxo_channel));
2339 last_channel.num_users = 0;
2340 SDL_strlcpy(last_channel.name, Multi_pxo_channel_last, SDL_arraysize(last_channel.name));
2343 multi_pxo_join_channel(&last_channel);
2345 nprintf(("Network","PXO : using last channel\n"));
2347 multi_pxo_autojoin();
2349 nprintf(("Network","PXO : using autojoin channel\n"));
2351 multi_pxo_get_channels();
2354 // give some time to the pxo api.
2355 multi_pxo_api_process();
2356 multi_pxo_process_common();
2358 // next value is not -1 when actually switching channels, so keep processing by returning 0.
2359 if ( SWITCHING_CHANNELS() ){
2363 // couldn't switch channel for some reason. bail out with -1
2364 if ( !ON_CHANNEL() ){
2372 // attempt to connect to Parallax Online, return success or fail
2373 int multi_pxo_connect()
2376 char join_fail_str[256];
2378 // intiialize chat api
2381 // set us to "must autojoin"
2382 Multi_pxo_must_autojoin = 1;
2384 // run the connect dialog/popup
2386 if(popup_till_condition(multi_pxo_connect_do, XSTR("&Cancel", 779), XSTR("Logging into Parallax Online",949)) == 10){
2389 // if we're going to use the "last" channel
2390 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){
2391 SDL_strlcpy(join_str, XSTR("Joining last channel (",982), SDL_arraysize(join_str));
2392 SDL_strlcat(join_str, Multi_pxo_channel_last + 1, SDL_arraysize(join_str));
2393 SDL_strlcat(join_str, ")", SDL_arraysize(join_str));
2395 SDL_strlcpy(join_fail_str, XSTR("Unable to join last channel", 983), SDL_arraysize(join_fail_str));
2397 SDL_strlcpy(join_str, XSTR("Autojoining public channel", 984), SDL_arraysize(join_str));
2398 SDL_strlcpy(join_fail_str, XSTR("Unable to autojoin public channel", 985), SDL_arraysize(join_fail_str));
2401 // once connected, we should do an autojoin before allowing the guy to continue.
2402 rval = popup_till_condition( multi_pxo_autojoin_do, XSTR("&Cancel", 779), join_str );
2407 popup( PF_USE_AFFIRMATIVE_ICON, 1, XSTR("OK", 1492), join_fail_str);
2410 // otherwise disconnect just to be safe
2411 DisconnectFromChatServer();
2412 gameseq_post_event(GS_EVENT_MAIN_MENU);
2414 // did not successfully connect
2418 // run the networking functions for the PXO API
2419 void multi_pxo_api_process()
2424 pxo_channel *lookup;
2426 // give some time to psnet
2427 PSNET_TOP_LAYER_PROCESS();
2429 // give some time to the game tracker API
2432 // give some time to the user tracker API
2435 // get any incoming text
2441 // process the chat line
2442 multi_pxo_chat_process_incoming(p);
2446 // get any incoming channel list stuff
2447 p = GetChannelList();
2450 // nprintf(("Network","%s\n",p));
2451 multi_pxo_make_channels(p);
2454 // process any chat commands
2455 cmd = GetChatCommand();
2458 switch(cmd->command)
2460 case CC_USER_JOINING:
2461 // add a user, if he doesn't already exist
2462 if(multi_pxo_find_player(cmd->data) == NULL){
2463 multi_pxo_add_player(cmd->data);
2466 // increase the player count
2468 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2470 lookup->num_users++;
2475 case CC_USER_LEAVING:
2477 multi_pxo_del_player(cmd->data);
2479 // add a text message
2480 SDL_snprintf(msg_str, SDL_arraysize(msg_str), XSTR("*** %s has left", 950), cmd->data);
2481 multi_pxo_chat_process_incoming(msg_str);
2483 // decrease the player count
2485 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2487 lookup->num_users--;
2492 case CC_DISCONNECTED:
2493 multi_pxo_handle_disconnect();
2497 multi_pxo_handle_kick();
2500 case CC_NICKCHANGED:
2501 // process a nick change
2502 multi_pxo_process_nick_change(cmd->data);
2505 case CC_YOURCHANNEL:
2506 // copy the current channel info, and unset the switching status
2507 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2508 Multi_pxo_channel_switch.num_users = -1;
2510 SetNewChatChannel(NULL);
2512 SDL_strlcpy(Multi_pxo_channel_current.name, cmd->data, SDL_arraysize(Multi_pxo_channel_current.name));
2514 // if we don't already have this guy on the list, add him
2515 pxo_channel *lookup;
2516 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2518 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2519 lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2522 // set the user count to be 0
2524 lookup->num_users = 0;
2527 // set our "last" channel to be this one
2528 SDL_strlcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name, SDL_arraysize(Multi_pxo_channel_last));
2530 // refresh current channel server count
2531 multi_pxo_channel_refresh_current();
2533 // clear the chat area
2534 // multi_pxo_chat_clear();
2541 cmd = GetChatCommand();
2544 // handle any processing details if we're currently trying to join a channel
2545 multi_pxo_handle_channel_change();
2548 // process a "nick" change event
2549 void multi_pxo_process_nick_change(char *data)
2552 player_list *lookup;
2554 // get the new string
2555 from = strtok(data," ");
2556 to = strtok(NULL,"");
2557 if((from != NULL) && (to != NULL)){
2558 lookup = multi_pxo_find_player(from);
2560 SDL_strlcpy(lookup->name, to, SDL_arraysize(lookup->name));
2562 // if this is also my nick, change it
2563 if(!SDL_strcasecmp(Multi_pxo_nick,from)){
2564 SDL_strlcpy(Multi_pxo_nick, to, SDL_arraysize(Multi_pxo_nick));
2570 // autojoin an appropriate channel
2571 void multi_pxo_autojoin()
2575 memset(&sw,0,sizeof(pxo_channel));
2577 SDL_strlcpy(sw.name, MULTI_PXO_AUTOJOIN_CHANNEL, SDL_arraysize(sw.name));
2579 // if we found a valid room, attempt to join it
2580 multi_pxo_join_channel(&sw);
2583 // does the string match the "autojoin" prefic
2584 int multi_pxo_is_autojoin(char *name)
2586 // check to see if the name is long enough
2587 if(strlen(name) < strlen(MULTI_PXO_AUTOJOIN_PREFIX)){
2591 // check to see if the first n chars match
2592 return !SDL_strncasecmp(name,MULTI_PXO_AUTOJOIN_PREFIX,strlen(MULTI_PXO_AUTOJOIN_PREFIX));
2595 // called from the game tracker API - server count update for a channel
2596 void multi_pxo_channel_count_update(char *name,int count)
2598 pxo_channel *lookup;
2600 // lookup the channel name on the normal list
2602 lookup = multi_pxo_find_channel(name,Multi_pxo_channels);
2604 lookup->num_servers = (ushort)count;
2606 nprintf(("Network","PXO : updated channel %s server count to %d\n",name,count));
2610 // status bar stuff -----------------------------------------------
2612 // set the status text
2613 void multi_pxo_set_status_text(const char *txt)
2616 SDL_strlcpy(Multi_pxo_status_text, txt, SDL_arraysize(Multi_pxo_status_text));
2618 // make sure it fits properly
2619 gr_force_fit_string(Multi_pxo_status_text, 254, Multi_pxo_status_coords[gr_screen.res][2]);
2622 // blit the status text
2623 void multi_pxo_blit_status_text()
2627 // center and draw the text
2628 if(strlen(Multi_pxo_status_text)) {
2629 gr_set_color_fast(&Color_bright);
2630 gr_get_string_size(&w, NULL, Multi_pxo_status_text);
2631 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);
2636 // channel related stuff -------------------------------------------
2638 // get a list of channels on the server
2639 void multi_pxo_get_channels()
2641 SendChatString(NOX("/list"));
2644 // clear the old channel list
2645 void multi_pxo_clear_channels()
2647 pxo_channel *moveup,*backup;
2649 // only clear a non-null list
2650 if(Multi_pxo_channels != NULL){
2652 moveup = Multi_pxo_channels;
2657 moveup = moveup->next;
2659 // free the struct itself
2662 } while(moveup != Multi_pxo_channels);
2663 Multi_pxo_channels = NULL;
2666 // head of the list of available channels
2667 Multi_pxo_channels = NULL;
2668 Multi_pxo_channel_count = 0;
2670 // item we're going to start displaying at
2671 Multi_pxo_channel_start = NULL;
2672 Multi_pxo_channel_start_index = -1;
2674 // items we've currently got selected
2675 Multi_pxo_channel_select = NULL;
2679 // parse the input string and make a list of new channels
2680 void multi_pxo_make_channels(char *chan_str)
2682 char *name_tok,*user_tok,*desc_tok;
2684 pxo_channel *lookup;
2687 nprintf(("Network","Making some channels!\n"));
2689 // clear the channel list
2690 // multi_pxo_clear_channels();
2692 // set the last get time
2693 Multi_pxo_channel_last_refresh = f2fl(timer_get_fixed_seconds());
2695 name_tok = strtok(chan_str," ");
2696 if(name_tok == NULL){
2701 // parse the user count token
2702 user_tok = strtok(NULL," ");
2704 // parse the channel description token
2705 desc_tok = strtok(NULL,"$");
2707 // something invalid in the data, return here.....
2708 if((name_tok == NULL) || (user_tok == NULL) || (desc_tok == NULL)){
2712 // get the # of users
2713 num_users = (ubyte)atoi(user_tok);
2715 // if the # of users is > 0, or its not an autojoin, place it on the display list
2716 if((num_users > 0) || !multi_pxo_is_autojoin(name_tok)){
2717 // see if it exists already, and if so, just update the user count
2718 lookup = multi_pxo_find_channel(name_tok,Multi_pxo_channels);
2721 lookup->num_users = (short)num_users;
2725 res = multi_pxo_add_channel(name_tok,&Multi_pxo_channels);
2727 //Multi_pxo_channel_count++;
2728 res->num_users = (short)num_users;
2729 SDL_strlcpy(res->desc, desc_tok, SDL_arraysize(res->desc));
2734 // get the next name token
2735 name_tok = strtok(NULL," ");
2736 } while(name_tok != NULL);
2738 // if we need to autojoin, do so now
2739 //if(Multi_pxo_must_autojoin){
2740 // Multi_pxo_must_autojoin = 0;
2742 // multi_pxo_autojoin();
2746 multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
2748 // if we haven't refreshed server counts yet, do it now
2749 if(Multi_pxo_channel_server_refresh < 0.0f){
2750 multi_pxo_channel_refresh_servers();
2753 // if we don't already have this guy on the list, add him
2755 pxo_channel *lookup;
2756 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2758 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2759 multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2764 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2765 pxo_channel *multi_pxo_add_channel(char *name,pxo_channel **list)
2767 pxo_channel *new_channel;
2769 // try and allocate a new pxo_channel struct
2770 new_channel = (pxo_channel *)malloc(sizeof(pxo_channel));
2771 if ( new_channel == NULL ) {
2772 nprintf(("Network", "Cannot allocate space for new pxo_channel structure\n"));
2775 memset(new_channel,0,sizeof(pxo_channel));
2776 // try and allocate a string for the channel name
2777 SDL_strlcpy(new_channel->name, name, SDL_arraysize(new_channel->name));
2779 // insert it on the list
2780 if ( *list != NULL ) {
2781 new_channel->next = (*list)->next;
2782 new_channel->next->prev = new_channel;
2783 (*list)->next = new_channel;
2784 new_channel->prev = *list;
2786 *list = new_channel;
2787 (*list)->next = (*list)->prev = *list;
2790 Multi_pxo_channel_count++;
2794 // lookup a channel with the specified name
2795 pxo_channel *multi_pxo_find_channel(char *name,pxo_channel *list)
2797 pxo_channel *moveup;
2799 // look the sucker up
2805 if(!SDL_strcasecmp(name,moveup->name)){
2809 moveup = moveup->next;
2810 } while((moveup != list) && (moveup != NULL));
2815 // process the channel list (select, etc)
2816 void multi_pxo_process_channels()
2821 // if we don't have a start item, but the list is non-null
2822 if((Multi_pxo_channel_start == NULL) && (Multi_pxo_channels != NULL)){
2823 Multi_pxo_channel_start = Multi_pxo_channels;
2824 Multi_pxo_channel_start_index = 0;
2827 // if we don't have a selected item, but the list is non-null
2828 if((Multi_pxo_channel_select == NULL) && (Multi_pxo_channels != NULL)){
2829 Multi_pxo_channel_select = Multi_pxo_channels;
2832 multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2835 // if the "switch" delay timestamp is set, see if it has expired
2836 if((Multi_pxo_switch_delay != -1) && timestamp_elapsed(Multi_pxo_switch_delay)){
2837 Multi_pxo_switch_delay = -1;
2840 // see if we have a mouse click on the channel region
2841 if(Multi_pxo_channel_button.pressed()){
2842 Multi_pxo_channel_button.get_mouse_pos(NULL,&my);
2844 // index from the top
2845 item_index = my / 10;
2847 // select the item if possible
2848 if((item_index + Multi_pxo_channel_start_index) < Multi_pxo_channel_count){
2849 Multi_pxo_channel_select = Multi_pxo_channel_start;
2850 for(idx=0;idx<item_index;idx++){
2851 Multi_pxo_channel_select = Multi_pxo_channel_select->next;
2855 multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2859 // last refresh time
2860 if((Multi_pxo_channel_last_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_last_refresh) > CHANNEL_REFRESH_TIME) ){
2862 multi_pxo_set_status_text(XSTR("Refreshing Public Channel List",952));
2864 // get a list of channels on the server (clear any old list as well)
2865 multi_pxo_get_channels();
2868 Multi_pxo_channel_last_refresh = -1.0f;
2870 nprintf(("Network","Refreshing channels\n"));
2873 // if we haven't updated our server channel counts in a while, do so again
2874 // last refresh time
2875 if((Multi_pxo_channel_server_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_server_refresh) > CHANNEL_SERVER_REFRESH_TIME) ){
2876 // refresh server counts
2877 // multi_pxo_set_status_text("Refreshing Public Channel Server Counts");
2879 // do it _NOW_ I"M RIGHT HERE KILL ME WHAT ARE YOU WAITING FOR DO IT KILL ME DO IT NOW!
2880 multi_pxo_channel_refresh_servers();
2884 // send a request to refresh our channel server counts
2885 void multi_pxo_channel_refresh_servers()
2887 pxo_channel *lookup;
2888 filter_game_list_struct filter;
2890 // traverse the list of existing channels we know about and query the game tracker about them
2891 lookup = Multi_pxo_channels;
2896 if(strlen(lookup->name)){
2898 memset(&filter,0,sizeof(filter_game_list_struct));
2899 SDL_strlcpy(filter.channel, lookup->name, SDL_arraysize(filter.channel));
2902 RequestGameCountWithFilter(&filter);
2906 lookup = lookup->next;
2907 } while((lookup != NULL) && (lookup != Multi_pxo_channels));
2910 Multi_pxo_channel_server_refresh = f2fl(timer_get_fixed_seconds());
2913 // refresh current channel server count
2914 void multi_pxo_channel_refresh_current()
2916 // send a request for a server count on this channel
2917 if(strlen(Multi_pxo_channel_current.name)){
2919 filter_game_list_struct filter;
2920 memset(&filter,0,sizeof(filter_game_list_struct));
2921 SDL_strlcpy(filter.channel, Multi_pxo_channel_current.name, SDL_arraysize(filter.channel));
2924 RequestGameCountWithFilter(&filter);
2928 // display the channel list
2929 void multi_pxo_blit_channels()
2931 pxo_channel *moveup;
2932 char chan_name[255];
2933 char chan_users[15];
2934 char chan_servers[15];
2935 int user_w,server_w;
2936 int disp_count,y_start;
2938 // blit as many channels as we can
2940 y_start = Multi_pxo_chan_coords[gr_screen.res][1];
2941 moveup = Multi_pxo_channel_start;
2946 // if this is the currently selected item, highlight it
2947 if(moveup == Multi_pxo_channel_select){
2948 gr_set_color_fast(&Color_bright);
2950 // otherwise draw it normally
2952 gr_set_color_fast(&Color_normal);
2955 // get the # of users on the channel
2956 SDL_snprintf(chan_users, SDL_arraysize(chan_users), "%d", moveup->num_users);
2958 // get the width of the user count string
2959 gr_get_string_size(&user_w, NULL, chan_users);
2961 // get the # of servers on the channel
2962 SDL_snprintf(chan_servers, SDL_arraysize(chan_servers), "%d", moveup->num_servers);
2964 // get the width of the user count string
2965 gr_get_string_size(&server_w, NULL, chan_servers);
2967 // make sure the name fits
2968 SDL_assert(moveup->name);
2969 SDL_strlcpy(chan_name, moveup->name, SDL_arraysize(chan_name));
2970 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]);
2973 gr_string(Multi_pxo_chan_coords[gr_screen.res][0], y_start, chan_name + 1);
2974 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);
2975 gr_set_color_fast(&Color_bright);
2976 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);
2978 // increment the displayed count
2983 moveup = moveup->next;
2984 } while((moveup != Multi_pxo_channels) && (disp_count < Multi_pxo_max_chan_display[gr_screen.res]));
2987 // scroll channel list up
2988 void multi_pxo_scroll_channels_up()
2990 // if we're already at the head of the list, do nothing
2991 if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start == Multi_pxo_channels)){
2992 gamesnd_play_iface(SND_GENERAL_FAIL);
2996 // otherwise move up one
2997 Multi_pxo_channel_start = Multi_pxo_channel_start->prev;
2998 Multi_pxo_channel_start_index--;
2999 gamesnd_play_iface(SND_USER_SELECT);
3002 // scroll channel list down
3003 void multi_pxo_scroll_channels_down()
3005 // if we're already at the tail of the list, do nothing
3006 if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start->next == Multi_pxo_channels)){
3007 gamesnd_play_iface(SND_GENERAL_FAIL);
3011 // if we can't scroll further without going past the end of the viewable list, don't
3012 if((Multi_pxo_channel_start_index + Multi_pxo_max_chan_display[gr_screen.res]) >= Multi_pxo_channel_count){
3013 gamesnd_play_iface(SND_GENERAL_FAIL);
3017 // otherwise move down one
3018 Multi_pxo_channel_start = Multi_pxo_channel_start->next;
3019 Multi_pxo_channel_start_index++;
3020 gamesnd_play_iface(SND_USER_SELECT);
3023 // attempt to join a channel
3024 void multi_pxo_join_channel(pxo_channel *chan)
3026 char switch_msg[256];
3028 // if we're already on this channel, do nothing
3029 if(ON_CHANNEL() && !SDL_strcasecmp(chan->name,Multi_pxo_channel_current.name)){
3033 // if we're already trying to join a channel, do nothing
3034 if(SWITCHING_CHANNELS()){
3038 // try and join the channel
3039 switch(SetNewChatChannel(chan->name)){
3045 // decrement the count of our current channel
3046 pxo_channel *lookup;
3047 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
3049 lookup->num_users--;
3052 // set our current channel as none
3053 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
3054 Multi_pxo_channel_current.num_users = -1;
3056 multi_pxo_set_status_text(XSTR("Switching channels",953));
3059 memcpy(&Multi_pxo_channel_switch,chan,sizeof(pxo_channel));
3061 // clear the player list
3062 multi_pxo_clear_players();
3064 // display a line of text indicating that we're switching channels
3065 if(strlen(Multi_pxo_channel_switch.name) > 1){
3066 SDL_snprintf(switch_msg, SDL_arraysize(switch_msg), "[Switching to channel %s]", Multi_pxo_channel_switch.name + 1);
3068 SDL_snprintf(switch_msg, SDL_arraysize(switch_msg), "[Switching to channel %s]", Multi_pxo_channel_switch.name);
3070 multi_pxo_chat_process_incoming(switch_msg, CHAT_MODE_CHANNEL_SWITCH);
3078 // handle any processing details if we're currently trying to join a channel
3079 void multi_pxo_handle_channel_change()
3081 // if we're not switching channels, do nothing
3082 if(!SWITCHING_CHANNELS()){
3086 // if we are, check the status
3087 switch(SetNewChatChannel(NULL)){
3090 // unset our switching struct
3091 memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
3092 Multi_pxo_channel_switch.num_users = -1;
3095 multi_pxo_set_status_text(XSTR("No channel (error while switching)",954));
3102 // successfully changed
3104 // copy the current channel info, and unset the switching status
3105 memcpy(&Multi_pxo_channel_current,&Multi_pxo_channel_switch,sizeof(pxo_channel));
3106 Multi_pxo_channel_switch.num_users = -1;
3108 // set our "last" channel
3109 SDL_strlcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name, SDL_arraysize(Multi_pxo_channel_last));
3112 multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
3114 // if we don't already have this guy on the list, add him
3115 pxo_channel *lookup;
3116 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
3118 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
3119 lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
3122 // set the user count to be 1 (just me)
3124 lookup->num_users = 1;
3127 // clear the chat area
3128 // multi_pxo_chat_clear();
3130 // set the "switch" delay timestamp
3131 Multi_pxo_switch_delay = timestamp(MULTI_PXO_SWITCH_DELAY_TIME);
3133 // refresh current channel server count
3134 multi_pxo_channel_refresh_current();
3140 // player related stuff -------------------------------------------
3142 // clear the old player list
3143 void multi_pxo_clear_players()
3145 player_list *moveup,*backup;
3147 // if the list is null, don't free it up
3148 if(Multi_pxo_players != NULL){
3150 moveup = Multi_pxo_players;
3155 moveup = moveup->next;
3157 // free the struct itself
3160 } while(moveup != Multi_pxo_players);
3161 Multi_pxo_players = NULL;
3165 Multi_pxo_player_start = NULL;
3166 // Multi_pxo_player_start_index = -1;
3167 Multi_pxo_player_select = NULL;
3170 // Multi_pxo_player_slider.set_numberItems(0);
3172 // add a bunch of bogus players
3175 for(int idx=0;idx<30;idx++){
3176 sprintf(str,"player%d",idx);
3177 multi_pxo_add_player(str);
3182 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
3183 player_list *multi_pxo_add_player(char *name)
3185 player_list *new_player;
3187 // try and allocate a new player_list struct
3188 new_player = (player_list *)malloc(sizeof(player_list));
3189 if ( new_player == NULL ) {
3190 nprintf(("Network", "Cannot allocate space for new player_list structure\n"));
3193 // try and allocate a string for the channel name
3194 SDL_strlcpy(new_player->name, name, SDL_arraysize(new_player->name));
3196 // insert it on the list
3197 if ( Multi_pxo_players != NULL ) {
3198 new_player->next = Multi_pxo_players->next;
3199 new_player->next->prev = new_player;
3200 Multi_pxo_players->next = new_player;
3201 new_player->prev = Multi_pxo_players;
3203 Multi_pxo_players = new_player;
3204 Multi_pxo_players->next = Multi_pxo_players->prev = Multi_pxo_players;
3207 // increment the start position
3208 if(Multi_pxo_player_start != NULL){
3209 // Multi_pxo_player_start_index++;
3213 Multi_pxo_player_count++;
3215 // update the count on the slider
3216 // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3221 // remove a player with the given name
3222 void multi_pxo_del_player(char *name)
3224 player_list *lookup;
3226 // try and find this guy
3227 lookup = Multi_pxo_players;
3232 // if we found a match, delete it
3233 if(!SDL_strcasecmp(name,lookup->name)){
3234 // if this is the only item on the list, free stuff up
3235 if(lookup->next == lookup){
3236 SDL_assert(lookup == Multi_pxo_players);
3238 Multi_pxo_players = NULL;
3239 multi_pxo_clear_players();
3241 // otherwise, just delete it
3243 lookup->next->prev = lookup->prev;
3244 lookup->prev->next = lookup->next;
3246 // if this was our selected item, unselect it
3247 if((lookup == Multi_pxo_player_select) && (Multi_pxo_player_select != NULL)){
3248 Multi_pxo_player_select = Multi_pxo_player_select->next;
3251 // if this was our point to start viewing from, select another
3252 if(lookup == Multi_pxo_player_start){
3253 // if this is the head of the list, move up one
3254 if(Multi_pxo_players == lookup){
3255 Multi_pxo_player_start = Multi_pxo_player_start->next;
3256 // Multi_pxo_player_start_index = 0;
3258 // otherwise move back
3260 Multi_pxo_player_start = Multi_pxo_player_start->prev;
3261 // Multi_pxo_player_start_index++;
3265 // if this is the head of the list, move it up
3266 if(lookup == Multi_pxo_players){
3267 Multi_pxo_players = Multi_pxo_players->next;
3271 lookup->next = NULL;
3272 lookup->prev = NULL;
3277 Multi_pxo_player_count--;
3278 SDL_assert(Multi_pxo_player_count >= 0);
3280 // update the count on the slider
3281 // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3282 // Multi_pxo_player_slider.force_currentItem(Multi_pxo_player_start_index);
3289 lookup = lookup->next;
3290 } while((lookup != NULL) && (lookup != Multi_pxo_players));
3293 // try and find a player with the given name, return a pointer to his entry (or NULL)
3294 player_list *multi_pxo_find_player(char *name)
3296 player_list *lookup;
3298 // look through all players
3299 lookup = Multi_pxo_players;
3304 if(!SDL_strcasecmp(name,lookup->name)){
3308 lookup = lookup->next;
3309 } while((lookup != NULL) && (lookup != Multi_pxo_players));
3315 // process the player list (select, etc)
3316 void multi_pxo_process_players()
3319 player_list *lookup;
3321 // if we don't have a start item, but the list is non-null
3322 if((Multi_pxo_player_start == NULL) && (Multi_pxo_players != NULL)){
3323 Multi_pxo_player_start = Multi_pxo_players;
3324 // Multi_pxo_player_start_index = 0;
3326 // update the slider
3327 // Multi_pxo_player_slider.set_currentItem(Multi_pxo_player_start_index);
3330 // if we don't have a selected item, but the list is non-null
3331 if((Multi_pxo_player_select == NULL) && (Multi_pxo_players != NULL)){
3332 Multi_pxo_player_select = Multi_pxo_players;
3335 // see if we have a mouse click on the channel region
3336 if(Multi_pxo_player_button.pressed()){
3337 Multi_pxo_player_button.get_mouse_pos(NULL,&my);
3339 // index from the top
3340 item_index = my / 10;
3342 // select the item if possible
3343 lookup = Multi_pxo_player_start;
3348 if(item_index == 0){
3349 Multi_pxo_player_select = Multi_pxo_player_start;
3353 // move to the next item
3354 lookup = lookup->next;
3357 // if this item is our guy
3358 if((item_index == 0) && (lookup != Multi_pxo_players)){
3359 Multi_pxo_player_select = lookup;
3362 } while((lookup != Multi_pxo_players) && (item_index > 0));
3366 // display the player list
3367 void multi_pxo_blit_players()
3369 player_list *moveup;
3370 char player_name[255];
3371 int disp_count,y_start;
3373 // blit as many channels as we can
3375 y_start = Multi_pxo_player_coords[gr_screen.res][1];
3376 moveup = Multi_pxo_player_start;
3381 // if this is the currently selected item, highlight it
3382 if(moveup == Multi_pxo_player_select){
3383 gr_set_color_fast(&Color_bright);
3385 // otherwise draw it normally
3387 gr_set_color_fast(&Color_normal);
3390 // make sure the string fits
3391 SDL_strlcpy(player_name, moveup->name, SDL_arraysize(player_name));
3392 gr_force_fit_string(player_name, 254, Multi_pxo_player_coords[gr_screen.res][2]);
3395 gr_string(Multi_pxo_player_coords[gr_screen.res][0], y_start, player_name);
3397 // increment the displayed count
3402 moveup = moveup->next;
3403 } while((moveup != Multi_pxo_players) && (disp_count < Multi_pxo_max_player_display[gr_screen.res]));
3406 // scroll player list up
3407 void multi_pxo_scroll_players_up()
3409 // if we're already at the head of the list, do nothing
3410 if((Multi_pxo_player_start == NULL) || (Multi_pxo_player_start == Multi_pxo_players)){
3411 gamesnd_play_iface(SND_GENERAL_FAIL);
3415 // otherwise move up one
3416 Multi_pxo_player_start = Multi_pxo_player_start->prev;
3417 // Multi_pxo_player_start_index--;
3418 // SDL_assert(Multi_pxo_player_start_index >= 0);
3420 gamesnd_play_iface(SND_USER_SELECT);
3423 // scroll player list down
3424 void multi_pxo_scroll_players_down()
3426 player_list *lookup;
3429 // see if its okay to scroll down
3430 lookup = Multi_pxo_player_start;
3431 if(lookup == NULL ){
3432 gamesnd_play_iface(SND_GENERAL_FAIL);
3436 while(lookup->next != Multi_pxo_players){
3437 lookup = lookup->next;
3441 // if we can move down
3442 if(count >= Multi_pxo_max_player_display[gr_screen.res]){
3443 Multi_pxo_player_start = Multi_pxo_player_start->next;
3445 // Multi_pxo_player_start_index++;
3447 gamesnd_play_iface(SND_USER_SELECT);
3449 gamesnd_play_iface(SND_GENERAL_FAIL);
3454 // chat text stuff -----------------------------------------
3456 // initialize and create the chat text linked list
3457 void multi_pxo_chat_init()
3460 chat_line *new_line;
3463 Multi_pxo_chat = NULL;
3464 Multi_pxo_chat_add = NULL;
3465 Multi_pxo_chat_start = NULL;
3466 Multi_pxo_chat_start_index = -1;
3468 // create the lines in a non-circular doubly linked list
3469 for(idx=0;idx<MAX_CHAT_LINES;idx++){
3470 new_line = (chat_line*)malloc(sizeof(chat_line));
3472 // clear the line out
3473 SDL_assert(new_line != NULL);
3474 if(new_line == NULL){
3477 memset(new_line,0,sizeof(chat_line));
3478 new_line->prev = NULL;
3479 new_line->next = NULL;
3481 // insert it into the (empty) list
3482 if(Multi_pxo_chat == NULL){
3483 Multi_pxo_chat = new_line;
3485 // insert it onto the (non-empty) list
3487 Multi_pxo_chat->prev = new_line;
3488 new_line->next = Multi_pxo_chat;
3489 Multi_pxo_chat = new_line;
3493 // start adding chat lines at the beginning of the list
3494 Multi_pxo_chat_add = Multi_pxo_chat;
3497 // free up all chat list stuff
3498 void multi_pxo_chat_free()
3500 chat_line *moveup, *backup;
3502 // free all items up
3503 moveup = Multi_pxo_chat;
3504 while(moveup != NULL){
3506 moveup = moveup->next;
3512 Multi_pxo_chat = NULL;
3513 Multi_pxo_chat_add = NULL;
3514 Multi_pxo_chat_start = NULL;
3515 Multi_pxo_chat_start_index = -1;
3516 Multi_pxo_chat_count = 0;
3518 Multi_pxo_chat_slider.set_numberItems(0);
3522 // clear all lines of chat text in the chat area
3523 void multi_pxo_chat_clear()
3527 // clear the text in all the lines
3528 moveup = Multi_pxo_chat;
3529 while(moveup != NULL){
3530 SDL_zero(moveup->text);
3531 moveup = moveup->next;
3534 // how many chat lines we have
3535 Multi_pxo_chat_count = 0;
3537 // start adding chat lines at the beginning of the list
3538 Multi_pxo_chat_add = Multi_pxo_chat;
3541 // add a line of text
3542 void multi_pxo_chat_add_line(char *txt, int mode)
3547 SDL_assert(Multi_pxo_chat_add != NULL);
3548 SDL_strlcpy(Multi_pxo_chat_add->text, txt, SDL_arraysize(Multi_pxo_chat_add->text));
3549 Multi_pxo_chat_add->mode = mode;
3551 // if we're at the end of the list, move the front item down
3552 if(Multi_pxo_chat_add->next == NULL) {
3553 // store the new "head" of the list
3554 temp = Multi_pxo_chat->next;
3556 // move the current head to the end of the list
3557 Multi_pxo_chat_add->next = Multi_pxo_chat;
3559 Multi_pxo_chat->prev = Multi_pxo_chat_add;
3560 Multi_pxo_chat->next = NULL;
3562 // reset the head of the list
3563 Multi_pxo_chat = temp;
3565 // set the new add line
3566 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3567 SDL_zero(Multi_pxo_chat_add->text);
3568 Multi_pxo_chat_add->mode = CHAT_MODE_NORMAL;
3570 // if we're not at the end of the list, just move up by one
3572 // set the new add line
3573 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3576 // if we've reached max chat lines, don't increment
3577 if(Multi_pxo_chat_count < MAX_CHAT_LINES) {
3578 Multi_pxo_chat_count++;
3583 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
3586 // force the position, in case we arent at the bottom of the list
3589 // move to the bottom of the chat area
3592 multi_pxo_goto_bottom();
3595 // if we have more than the # of lines displayable
3596 if(Multi_pxo_chat_count >= MULTI_PXO_MAX_CHAT_DISPLAY){
3598 multi_pxo_goto_bottom();
3601 multi_pxo_goto_bottom();
3604 // process an incoming line of text
3605 void multi_pxo_chat_process_incoming(const char *txt,int mode)
3607 char msg_total[512],line[512];
3610 char *p_str[20]; // the initial line (unindented)
3611 const char *priv_ptr = NULL;
3613 // filter out "has left" channel messages, when switching channels
3614 if((SWITCHING_CHANNELS() || ((Multi_pxo_switch_delay != -1) && !timestamp_elapsed(Multi_pxo_switch_delay))) &&
3615 multi_pxo_chat_is_left_message(txt)){
3619 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3620 priv_ptr = multi_pxo_chat_is_private(txt);
3621 if(priv_ptr != NULL){
3622 SDL_strlcpy(msg_total, priv_ptr, SDL_arraysize(msg_total));
3624 SDL_strlcpy(msg_total, txt, SDL_arraysize(msg_total));
3627 // determine what mode to display this text in
3629 // if this is private chat
3630 if(priv_ptr != NULL){
3631 mode = CHAT_MODE_PRIVATE;
3635 // if this is a server message
3636 if(multi_pxo_is_server_text(txt)){
3637 mode = CHAT_MODE_SERVER;
3639 // if this is a MOTD
3640 else if(multi_pxo_is_motd_text(txt)){
3643 SDL_strlcpy(msg_total, txt + strlen(PXO_CHAT_MOTD_PREFIX), SDL_arraysize(msg_total));
3645 mode = CHAT_MODE_MOTD;
3648 multi_pxo_motd_add_text(txt);
3652 // if this is the end of motd text
3653 else if(multi_pxo_is_end_of_motd_text(txt)){
3655 multi_pxo_set_end_of_motd();
3661 // split the text up into as many lines as necessary
3662 n_lines = split_str(msg_total, Multi_pxo_chat_coords[gr_screen.res][2] - 5, n_chars, p_str, 3);
3663 SDL_assert((n_lines != -1) && (n_lines <= 20));
3664 if((n_lines < 0) || (n_lines > 20)) {
3668 // if the string fits on one line
3670 multi_pxo_chat_add_line(msg_total,mode);
3672 // don't pad with extra spaces if its from the server
3674 if(mode != CHAT_MODE_SERVER){
3675 multi_pxo_chat_add_line("",CHAT_MODE_NORMAL);
3679 // if the string was split into multiple lines
3681 // add the first line
3682 memcpy(line,p_str[0],n_chars[0]);
3683 line[n_chars[0]] = '\0';
3684 multi_pxo_chat_add_line(line,mode);
3686 // copy the rest of the lines
3687 for(idx=1; idx<n_lines; idx++){
3688 memcpy(line,p_str[idx],n_chars[idx]);
3689 line[n_chars[idx]] = '\0';
3691 // unless the current mode is server or "switching channels", make all these CHAT_MODE_CARRY
3692 if((mode != CHAT_MODE_SERVER) && (mode != CHAT_MODE_CHANNEL_SWITCH)){
3693 mode = CHAT_MODE_CARRY;
3695 multi_pxo_chat_add_line(line, mode);
3700 // blit the chat text
3701 void multi_pxo_chat_blit()
3704 int disp_count,token_width;
3710 // blit the title line
3712 if(strlen(Multi_pxo_channel_current.name) > 1){
3713 SDL_snprintf(title, SDL_arraysize(title), XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name+1); // [[ <who> on <channel> ]]
3715 SDL_snprintf(title, SDL_arraysize(title), XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name); // [[ <who> on <channel> ]]
3718 SDL_strlcpy(title, XSTR("Parallax Online - No Channel", 956), SDL_arraysize(title));
3720 gr_force_fit_string(title, 254, Multi_pxo_chat_coords[gr_screen.res][2] - 10);
3721 gr_get_string_size(&token_width,NULL,title);
3722 gr_set_color_fast(&Color_normal);
3723 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);
3725 // blit all active lines of text
3726 moveup = Multi_pxo_chat_start;
3728 y_start = Multi_pxo_chat_coords[gr_screen.res][1];
3729 while((moveup != NULL) && (moveup != Multi_pxo_chat_add) && (disp_count < (Multi_pxo_max_chat_display[gr_screen.res]))){
3730 switch(moveup->mode){
3731 // if this is text from the server, display it all "bright"
3732 case CHAT_MODE_SERVER:
3733 gr_set_color_fast(&Color_bright);
3734 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3737 // if this is motd, display it all "bright"
3738 case CHAT_MODE_MOTD:
3739 gr_set_color_fast(&Color_bright_white);
3740 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3743 // normal mode, just highlight the server
3744 case CHAT_MODE_PRIVATE:
3745 case CHAT_MODE_NORMAL:
3746 SDL_strlcpy(piece, moveup->text, SDL_arraysize(piece));
3747 tok = strtok(piece," ");
3749 // get the width of just the first "piece"
3750 gr_get_string_size(&token_width, NULL, tok);
3753 gr_set_color_fast(&Color_bright);
3754 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, tok);
3756 // draw the rest of the string normally
3757 tok = strtok(NULL,"");
3759 gr_set_color_fast(&Color_normal);
3760 gr_string(Multi_pxo_chat_coords[gr_screen.res][0] + token_width + 6, y_start, tok);
3765 // carry mode, display with no highlight
3766 case CHAT_MODE_CARRY:
3767 gr_set_color_fast(&Color_normal);
3768 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3771 // "switching channels mode", display it bright
3772 case CHAT_MODE_CHANNEL_SWITCH:
3773 gr_set_color_fast(&Color_bright);
3774 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3779 moveup = moveup->next;
3784 if ((moveup != Multi_pxo_chat_add) && (moveup != NULL)) {
3785 Can_scroll_down = 1;
3787 Can_scroll_down = 0;
3791 // scroll to the very bottom of the chat area
3792 void multi_pxo_goto_bottom()
3797 if (Multi_pxo_chat == NULL) {
3801 // if we have less than the displayable amount of lines, do nothing
3802 if(Multi_pxo_chat_count <= Multi_pxo_max_chat_display[gr_screen.res]){
3803 Multi_pxo_chat_start = Multi_pxo_chat;
3806 // nothing to do for the slider
3807 Multi_pxo_chat_slider.set_numberItems(0);
3812 if (!Can_scroll_down)
3814 // otherwise move back the right # of items
3815 backup = Multi_pxo_chat_add;
3816 for(idx=0; idx<Multi_pxo_max_chat_display[gr_screen.res]; idx++){
3817 SDL_assert(backup->prev != NULL);
3818 backup = backup->prev;
3821 Multi_pxo_chat_start = backup;
3823 // fixup the start index
3824 multi_pxo_chat_adjust_start();
3828 // scroll the text up
3829 void multi_pxo_scroll_chat_up()
3831 // if we're already at the top of the list, don't do anything
3832 if ((Multi_pxo_chat_start == NULL) || (Multi_pxo_chat_start == Multi_pxo_chat)) {
3833 gamesnd_play_iface(SND_GENERAL_FAIL);
3837 // otherwise move up one
3838 Multi_pxo_chat_start = Multi_pxo_chat_start->prev;
3840 multi_pxo_chat_adjust_start();
3842 gamesnd_play_iface(SND_USER_SELECT);
3845 // returns 1 if we can scroll down, 0 otherwise
3846 int multi_pxo_can_scroll_down()
3851 // see if its okay to scroll down
3852 lookup = Multi_pxo_chat_start;
3853 if (lookup == NULL) {
3854 // gamesnd_play_iface(SND_GENERAL_FAIL);
3858 while (lookup != Multi_pxo_chat_add) {
3859 lookup = lookup->next;
3863 // check if we can move down, return accordingly
3864 if (count > Multi_pxo_max_chat_display[gr_screen.res]) {
3871 // scroll the text down
3872 void multi_pxo_scroll_chat_down()
3874 // if we can move down
3875 if (multi_pxo_can_scroll_down()) {
3876 Multi_pxo_chat_start = Multi_pxo_chat_start->next;
3877 multi_pxo_chat_adjust_start();
3878 gamesnd_play_iface(SND_USER_SELECT);
3880 gamesnd_play_iface(SND_GENERAL_FAIL);
3884 // process chat controls
3885 void multi_pxo_chat_process()
3887 char *remainder = NULL;
3888 const char *result = NULL;
3890 int msg_pixel_width;
3892 // if the chat line is getting too long, fire off the message, putting the last
3893 // word on the next input line.
3895 Multi_pxo_chat_input.get_text(msg);
3897 // determine if the width of the string in pixels is > than the inputbox width -- if so,
3898 // then send the message
3899 gr_get_string_size(&msg_pixel_width, NULL, msg);
3900 // if ( msg_pixel_width >= (Chatbox_inputbox_w - Player->short_callsign_width) ) {
3901 if ( msg_pixel_width >= (Multi_pxo_input_coords[gr_screen.res][2])) {
3902 remainder = strrchr(msg, ' ');
3908 // if we're connected to a channel, send the chat to the server
3910 result = SendChatString(msg,1);
3912 multi_pxo_chat_process_incoming(result);
3915 // display any remainder of text on the next line
3916 Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3918 Multi_pxo_chat_input.set_text("");
3920 } else if((Multi_pxo_chat_input.pressed() && (strlen(msg) > 0)) || (strlen(msg) >= MAX_CHAT_LINE_LEN)) {
3921 // tack on the null terminator in the boundary case
3922 int x = strlen(msg);
3923 if(x >= MAX_CHAT_LINE_LEN){
3924 msg[MAX_CHAT_LINE_LEN-1] = '\0';
3927 // ignore "/nick" commands
3928 if(multi_pxo_is_nick_command(msg)){
3929 Multi_pxo_chat_input.set_text("");
3933 // send the chat to the server
3934 // if we're connected to a channel, send the chat to the server
3936 result = SendChatString(msg,1);
3938 multi_pxo_chat_process_incoming(result);
3941 // display any remainder of text on the next line
3942 Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3944 Multi_pxo_chat_input.set_text("");
3949 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3952 // NOTE : DO NOT LOCALIZE THESE STRINGS!!!! THEY ARE CONSTANTS WHICH ARE CHECKED AGAINST
3953 // PXO CHAT SERVER DATA. THEY CANNOT CHANGE!!!
3954 #define PMSG_FROM "private message from "
3955 #define PMSG_TO "private message to "
3956 const char *multi_pxo_chat_is_private(const char *txt)
3959 if( strlen(txt) > strlen( PMSG_FROM ) ){
3960 // otherwise do a comparison
3961 if(!SDL_strncasecmp( txt, PMSG_FROM, strlen(PMSG_FROM) )){
3962 return &txt[strlen( PMSG_FROM )];
3967 if(strlen(txt) > strlen( PMSG_TO )){
3968 // otherwise do a comparison
3969 if(!SDL_strncasecmp(txt,PMSG_TO,strlen(PMSG_TO))){
3970 return &txt[strlen(PMSG_TO)];
3978 // if the text came from the server
3979 int multi_pxo_is_server_text(const char *txt)
3981 // if the message is prefaced by a ***
3982 if((strlen(txt) >= strlen(MULTI_PXO_SERVER_PREFIX)) && !strncmp(txt, MULTI_PXO_SERVER_PREFIX, strlen(MULTI_PXO_SERVER_PREFIX))){
3989 // if the text is message of the day text
3990 int multi_pxo_is_motd_text(const char *txt)
3992 // if we're not on a channel, and this is not a channel switching message assume its coming from a server
3993 if((strlen(txt) >= strlen(PXO_CHAT_MOTD_PREFIX)) && !strncmp(txt, PXO_CHAT_MOTD_PREFIX, strlen(PXO_CHAT_MOTD_PREFIX))){
4000 // if the text is the end of motd text
4001 int multi_pxo_is_end_of_motd_text(const char *txt)
4003 // if we're not on a channel, and this is not a channel switching message assume its coming from a server
4004 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))){
4011 // if the text is a "has left message" from the server
4012 int multi_pxo_chat_is_left_message(const char *txt)
4014 char last_portion[100];
4016 // if the text is not server text
4017 if(!multi_pxo_is_server_text(txt)){
4021 // check to see if the last portion is the correct wording
4022 SDL_zero(last_portion);
4023 if((strlen(txt) > strlen(MULTI_PXO_HAS_LEFT)) && !strcmp(&txt[strlen(txt) - strlen(MULTI_PXO_HAS_LEFT)], MULTI_PXO_HAS_LEFT)){
4027 // check the end of the line
4031 // recalculate the chat start index, and adjust the slider properly
4032 void multi_pxo_chat_adjust_start()
4036 // if we have no chat
4037 if (Multi_pxo_chat == NULL) {
4038 Multi_pxo_chat_start_index = -1;
4043 Multi_pxo_chat_start_index = 0;
4044 moveup = Multi_pxo_chat;
4045 while((moveup != Multi_pxo_chat_start) && (moveup != NULL)){
4046 Multi_pxo_chat_start_index++;
4047 moveup = moveup->next;
4050 // set the slider index
4051 Multi_pxo_chat_slider.force_currentItem(Multi_pxo_chat_start_index);
4055 // motd stuff ---------------------------------------------------------
4057 // initialize motd when going into this screen
4058 void multi_pxo_motd_init()
4060 // zero the motd string
4061 SDL_strlcpy(Pxo_motd, "", SDL_arraysize(Pxo_motd));
4063 // haven't gotten it yet
4066 // haven't read it yet either
4070 // set the motd text
4071 void multi_pxo_motd_add_text(const char *text)
4073 int cur_len = strlen(Pxo_motd);
4081 // make sure its motd text
4082 SDL_assert(multi_pxo_is_motd_text(text));
4083 if(!multi_pxo_is_motd_text(text)){
4087 // if its a 0 line motd
4088 if(strlen(text) <= strlen(PXO_CHAT_MOTD_PREFIX)){
4092 // add text to the motd
4093 new_len = strlen(text + strlen(PXO_CHAT_MOTD_PREFIX)) - 1;
4094 if((cur_len + new_len + 1) < MAX_PXO_MOTD_LEN){
4095 SDL_strlcat(Pxo_motd, text + strlen(PXO_CHAT_MOTD_PREFIX) + 1, SDL_arraysize(Pxo_motd));
4096 SDL_strlcat(Pxo_motd, "\n", SDL_arraysize(Pxo_motd));
4097 mprintf(("MOTD ADD : %s\n", Pxo_motd));
4102 void multi_pxo_set_end_of_motd()
4107 mprintf(("MOTD ALL : %s\n", Pxo_motd));
4111 // do we have an old MOTD file laying around? If so, read it in and see if its the same
4115 // checksum the current motd
4116 new_chksum = cf_add_chksum_long(0, Pxo_motd, strlen(Pxo_motd));
4118 // checksum the old motd if its lying around
4119 CFILE *in = cfopen("oldmotd.txt", "rb");
4121 // read the old checksum
4122 old_chksum = cfread_uint(in);
4125 // same checksum? no blink
4126 if(new_chksum == old_chksum){
4131 // write out the motd for next time
4132 if(strlen(Pxo_motd)){
4133 CFILE *out = cfopen("oldmotd.txt", "wb", CFILE_NORMAL, CF_TYPE_DATA);
4135 // write all the text
4136 cfwrite_uint(new_chksum, out);
4138 // close the outfile
4143 // set the blink stamp
4144 Pxo_motd_blink_stamp = -1;
4146 Pxo_motd_blink_on = 0;
4147 if(!Pxo_motd_blinked_already){
4148 Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4149 Pxo_motd_blink_on = 1;
4153 Pxo_motd_blinked_already = 1;
4156 // display the motd dialog
4157 void multi_pxo_motd_dialog()
4159 // mark the motd as read
4162 // simple popup, with a slider
4163 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, Pxo_motd);
4166 // call to maybe blink the motd button
4167 void multi_pxo_motd_maybe_blit()
4170 // if we got the end of the motd, and he hasn't read it yet
4171 if(Pxo_motd_end && !Pxo_motd_read && (Pxo_motd_blink_stamp != -1)){
4172 // if the timestamp elapsed, flip the blink flag
4173 if(timestamp_elapsed(Pxo_motd_blink_stamp)){
4174 Pxo_motd_blink_on = !Pxo_motd_blink_on;
4175 Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4179 if(Pxo_motd_blink_on){
4180 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_MOTD].button.draw_forced(2);
4187 // common dialog stuff ------------------------------------------------
4189 int Multi_pxo_searching = 0;
4191 // initialize the common dialog with the passed max input length
4192 void multi_pxo_com_init(int input_len)
4196 // create the interface window
4197 Multi_pxo_com_window.create(0, 0, gr_screen.max_w,gr_screen.max_h, 0);
4198 Multi_pxo_com_window.set_mask_bmap(Multi_pxo_com_mask_fname[gr_screen.res]);
4200 // create the interface buttons
4201 for(idx=0; idx<MULTI_PXO_COM_NUM_BUTTONS; idx++){
4202 // create the object
4203 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);
4205 // set the sound to play when highlighted
4206 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4208 // set the ani for the button
4209 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_com_buttons[gr_screen.res][idx].filename);
4212 Multi_pxo_com_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_com_buttons[gr_screen.res][idx].hotspot);
4217 for(idx=0; idx<MULTI_PXO_COM_NUM_TEXT; idx++){
4218 Multi_pxo_com_window.add_XSTR(&Multi_pxo_com_text[gr_screen.res][idx]);
4222 // create the input box
4223 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);
4224 Multi_pxo_com_input.set_focus();
4226 // clear all text lines
4227 SDL_zero(Multi_pxo_com_bottom_text);
4228 SDL_zero(Multi_pxo_com_middle_text);
4229 SDL_zero(Multi_pxo_com_top_text);
4232 // close down the common dialog
4233 void multi_pxo_com_close()
4235 // destroy the UI_WINDOW
4236 Multi_pxo_com_window.destroy();
4239 // blit all text lines, top, middle, bottoms
4240 void multi_pxo_com_blit_text()
4242 // blit top, middle and bottom text if possible
4243 if(strlen(Multi_pxo_com_top_text) > 0){
4244 gr_set_color_fast(&Color_bright);
4245 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);
4247 if(strlen(Multi_pxo_com_middle_text) > 0){
4248 gr_set_color_fast(&Color_bright);
4249 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);
4251 if(strlen(Multi_pxo_com_bottom_text) > 0){
4252 gr_set_color_fast(&Color_bright);
4253 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);
4257 // set the top text, shortening as necessary
4258 void multi_pxo_com_set_top_text(const char *txt)
4260 if((txt != NULL) && strlen(txt)){
4261 SDL_strlcpy(Multi_pxo_com_top_text, txt, SDL_arraysize(Multi_pxo_com_top_text));
4262 gr_force_fit_string(Multi_pxo_com_top_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4266 // set the middle text, shortening as necessary
4267 void multi_pxo_com_set_middle_text(const char *txt)
4269 if((txt != NULL) && strlen(txt)){
4270 SDL_strlcpy(Multi_pxo_com_middle_text, txt, SDL_arraysize(Multi_pxo_com_middle_text));
4271 gr_force_fit_string(Multi_pxo_com_middle_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4275 // set the bottom text, shortening as necessary
4276 void multi_pxo_com_set_bottom_text(const char *txt)
4278 if((txt != NULL) && strlen(txt)){
4279 SDL_strlcpy(Multi_pxo_com_bottom_text, txt, SDL_arraysize(Multi_pxo_com_bottom_text));
4280 gr_force_fit_string(Multi_pxo_com_bottom_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4285 // private channel join stuff -----------------------------------------
4287 // initialize the popup
4288 void multi_pxo_priv_init()
4290 SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE);
4292 // initialize the common dialog with the passed max input length
4293 multi_pxo_com_init(MULTI_PXO_PRIV_MAX_TEXT_LEN);
4295 // initialize the return code
4296 Multi_pxo_priv_return_code = -1;
4298 // mark us as running
4299 Multi_pxo_mode = MULTI_PXO_MODE_PRIVATE;
4302 multi_pxo_com_set_middle_text(XSTR("Type the name of the channel to join/create",961));
4305 // close down the popup
4306 void multi_pxo_priv_close()
4308 // close down the common dialog
4309 multi_pxo_com_close();
4311 // mark us as not running any more
4312 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4315 // run the popup, 0 if still running, -1 if cancel, 1 if ok
4316 int multi_pxo_priv_popup()
4320 // if we're not already running, initialize stuff
4321 if(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE){
4323 multi_pxo_priv_init();
4325 // return "still running"
4329 k = Multi_pxo_com_window.process();
4331 // process keypresses
4333 // like hitting the cancel button
4335 Multi_pxo_priv_return_code = 0;
4339 // process button presses
4340 multi_pxo_priv_process_buttons();
4342 // process the inputbox
4343 multi_pxo_priv_process_input();
4345 // blit the background
4346 multi_pxo_blit_all();
4350 gr_set_bitmap(Multi_pxo_com_bitmap);
4351 gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4352 Multi_pxo_com_window.draw();
4354 // blit all text lines, top, middle, bottoms
4355 multi_pxo_com_blit_text();
4359 // check the return code
4360 switch(Multi_pxo_priv_return_code){
4361 // still in progress
4367 multi_pxo_priv_close();
4372 multi_pxo_priv_close();
4379 // process button presses
4380 void multi_pxo_priv_process_buttons()
4384 // check all buttons
4385 for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4386 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4387 multi_pxo_priv_button_pressed(idx);
4393 // handle a button press
4394 void multi_pxo_priv_button_pressed(int n)
4396 char priv_chan_name[128];
4399 case MULTI_PXO_COM_CANCEL:
4400 Multi_pxo_priv_return_code = 0;
4403 case MULTI_PXO_COM_OK:
4404 Multi_pxo_com_input.get_text(priv_chan_name);
4405 multi_pxo_strip_space(priv_chan_name, priv_chan_name, SDL_arraysize(priv_chan_name));
4407 // if its a 0 length string, interpret as a cancel
4408 if(strlen(priv_chan_name) <= 0){
4409 Multi_pxo_priv_return_code = 0;
4413 Multi_pxo_priv_return_code = 1;
4418 // process the inputbox
4419 void multi_pxo_priv_process_input()
4421 char priv_chan_name[128];
4423 // see if the user has pressed enter
4424 if(Multi_pxo_com_input.pressed()){
4425 Multi_pxo_com_input.get_text(priv_chan_name);
4426 multi_pxo_strip_space(priv_chan_name, priv_chan_name, SDL_arraysize(priv_chan_name));
4428 // if its a 0 length string, interpret as a cancel
4429 if(strlen(priv_chan_name) <= 0){
4430 Multi_pxo_priv_return_code = 0;
4434 // otherwise interpret as "accept"
4435 Multi_pxo_priv_return_code = 1;
4437 // add in the "+" which indicates a private room
4438 SDL_strlcpy(Multi_pxo_priv_chan, "+", SDL_arraysize(Multi_pxo_priv_chan));
4439 SDL_strlcat(Multi_pxo_priv_chan, priv_chan_name, SDL_arraysize(Multi_pxo_priv_chan));
4443 // find player stuff -----------------------------------------
4445 char name_lookup[255];
4447 // initialize the popup
4448 void multi_pxo_find_init()
4450 SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_FIND);
4452 // initialize the common dialog with the passed max input length
4453 multi_pxo_com_init(MAX_PLAYER_NAME_LEN);
4455 // return code, set to something other than -1 if we're supposed to return
4456 Multi_pxo_find_return_code = -1;
4458 // mark us as running
4459 Multi_pxo_mode = MULTI_PXO_MODE_FIND;
4461 // not searching yet
4462 Multi_pxo_searching = 0;
4465 multi_pxo_com_set_top_text(XSTR("Enter user to be found",962));
4468 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4471 SDL_strlcpy(name_lookup, "", SDL_arraysize(name_lookup));
4474 // close down the popup
4475 void multi_pxo_find_close()
4477 // close down the common dialog
4478 multi_pxo_com_close();
4480 // mark us as not running any more
4481 Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4484 // run the popup, 0 if still running, -1 if cancel, 1 if ok
4485 int multi_pxo_find_popup()
4489 // if we're not already running, initialize stuff
4490 if(Multi_pxo_mode != MULTI_PXO_MODE_FIND){
4492 multi_pxo_find_init();
4494 // return "still running"
4498 k = Multi_pxo_com_window.process();
4500 // process keypresses
4502 // like hitting the cancel button
4504 Multi_pxo_find_return_code = 0;
4508 // process button presses
4509 multi_pxo_find_process_buttons();
4511 // process the inputbox
4512 multi_pxo_find_process_input();
4514 // process search mode if applicable
4515 multi_pxo_find_search_process();
4517 // blit the background
4518 multi_pxo_blit_all();
4522 gr_set_bitmap(Multi_pxo_com_bitmap);
4523 gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4524 Multi_pxo_com_window.draw();
4526 // blit any text lines
4527 multi_pxo_com_blit_text();
4531 // check the return code
4532 switch(Multi_pxo_find_return_code){
4533 // still in progress
4539 // close the popup down
4540 multi_pxo_find_close();
4545 // close the popup down
4546 multi_pxo_find_close();
4548 // if we have a channel, join it now if possible
4549 if(strlen(Multi_pxo_find_channel) > 0){
4550 pxo_channel *lookup;
4551 lookup = multi_pxo_find_channel(Multi_pxo_find_channel,Multi_pxo_channels);
4553 // if we couldn't find it, don't join
4555 multi_pxo_join_channel(lookup);
4564 // process button presses
4565 void multi_pxo_find_process_buttons()
4569 // check all buttons
4570 for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4571 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4572 multi_pxo_find_button_pressed(idx);
4578 // handle a button press
4579 void multi_pxo_find_button_pressed(int n)
4582 case MULTI_PXO_COM_CANCEL:
4583 Multi_pxo_find_return_code = 0;
4586 case MULTI_PXO_COM_OK:
4587 Multi_pxo_find_return_code = 1;
4592 // process the inputbox
4593 void multi_pxo_find_process_input()
4595 // see if the user has pressed enter
4596 if(Multi_pxo_com_input.pressed()){
4597 // if we're not already in search mode
4598 if(!Multi_pxo_searching){
4600 SDL_zero(Multi_pxo_com_middle_text);
4601 SDL_zero(Multi_pxo_com_bottom_text);
4603 Multi_pxo_com_input.get_text(name_lookup);
4604 multi_pxo_strip_space(name_lookup, name_lookup, SDL_arraysize(name_lookup));
4606 // never search with a zero length string
4607 if(strlen(name_lookup) > 0){
4608 char search_text[512];
4610 // put us in search mode
4611 Multi_pxo_searching = 1;
4614 GetChannelByUser(name_lookup);
4617 SDL_snprintf(search_text, SDL_arraysize(search_text), XSTR("Searching for %s", 963), name_lookup);
4618 multi_pxo_com_set_top_text(search_text);
4622 SDL_zero(Multi_pxo_com_top_text);
4628 // process search mode if applicable
4629 void multi_pxo_find_search_process()
4633 // if we're not searching for anything, return
4634 if(!Multi_pxo_searching){
4638 // otherwise check to see if we've found him
4639 channel = GetChannelByUser(NULL);
4641 // if we've got a result, let the user know
4643 // if he couldn't be found
4644 if(channel == (char *)-1){
4645 multi_pxo_com_set_middle_text(XSTR("User not found",964));
4646 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4648 if(channel[0] == '*'){
4649 multi_pxo_com_set_middle_text(XSTR("Player is logged in but is not on a channel",965));
4650 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4654 // if this guy is on a public channel, display which one
4655 if(channel[0] == '#'){
4656 SDL_snprintf(p_text, SDL_arraysize(p_text), XSTR("Found %s on :", 966), name_lookup);
4658 // display the results
4659 multi_pxo_com_set_middle_text(p_text);
4660 multi_pxo_com_set_bottom_text(channel+1);
4662 // mark down the channel name so we know where to find him
4663 SDL_strlcpy(Multi_pxo_find_channel, channel, SDL_arraysize(Multi_pxo_find_channel));
4664 // strip out trailing whitespace
4665 if(Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] == ' '){
4666 Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] = '\0';
4669 // if this is a private channel
4670 else if(channel[0] == '+'){
4671 SDL_snprintf(p_text, SDL_arraysize(p_text), XSTR("Found %s on a private channel", 967), name_lookup);
4672 multi_pxo_com_set_middle_text(p_text);
4674 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4679 // unset search mode
4680 Multi_pxo_searching = 0;
4682 // clear the inputbox
4683 Multi_pxo_com_input.set_text("");
4688 // player info stuff -----------------------------------------
4690 // popup conditional functions, returns 10 on successful get of stats
4691 int multi_pxo_pinfo_cond()
4694 char temp_string[255];
4697 // process common stuff
4698 multi_pxo_process_common();
4700 // run the networking functions for the PXO API
4701 multi_pxo_api_process();
4703 // process depending on what mode we're in
4704 switch(Multi_pxo_retrieve_mode){
4705 // getting his player tracker id
4707 // if the thing is non-null, do something
4708 ret_string = GetTrackerIdByUser(Multi_pxo_retrieve_name);
4709 if(ret_string != NULL){
4710 // user not-online/not found
4711 if(ret_string == (char *)-1){
4715 // user not a tracker pilot
4716 if(!SDL_strcasecmp(ret_string,"-1")){
4720 // otherwise parse into his id and callsign
4721 SDL_strlcpy(temp_string, ret_string, SDL_arraysize(temp_string));
4722 tok = strtok(temp_string," ");
4726 SDL_strlcpy(Multi_pxo_retrieve_id, tok, SDL_arraysize(Multi_pxo_retrieve_id));
4729 tok = strtok(NULL,"");
4731 SDL_strlcpy(Multi_pxo_retrieve_name, tok, SDL_arraysize(Multi_pxo_retrieve_name));
4738 // failure of some kind or another
4743 Multi_pxo_retrieve_mode = 1;
4748 // initial call to get his stats
4750 // change the popup text
4751 popup_change_text(XSTR("Getting player stats",968));
4754 memset(&Multi_pxo_pinfo, 0, sizeof(Multi_pxo_pinfo));
4755 SDL_strlcpy(Multi_pxo_pinfo.pilot_name, Multi_pxo_retrieve_name, SDL_arraysize(Multi_pxo_pinfo.pilot_name));
4756 SDL_strlcpy(Multi_pxo_pinfo.tracker_id, Multi_pxo_retrieve_id, SDL_arraysize(Multi_pxo_pinfo.tracker_id));
4758 // make the initial call to the API
4759 GetFSPilotData((vmt_stats_struct*)0xffffffff,NULL,NULL,0);
4760 if(GetFSPilotData(&Multi_pxo_pinfo,Multi_pxo_retrieve_name,Multi_pxo_retrieve_id,0) != 0){
4763 // if the call went through, set the mode to 2
4765 Multi_pxo_retrieve_mode = 2;
4769 // busy retrieving his stats
4771 switch(GetFSPilotData(NULL,NULL,NULL,0)){
4772 // timeout, fail, cancel
4790 // return not done yet
4794 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
4795 int multi_pxo_pinfo_get(char *name)
4798 Multi_pxo_retrieve_mode = 0;
4799 SDL_strlcpy(Multi_pxo_retrieve_name, name, SDL_arraysize(Multi_pxo_retrieve_name));
4800 switch(popup_till_condition(multi_pxo_pinfo_cond,XSTR("&Cancel", 779),XSTR("Retrieving player tracker id",969))){
4805 // failed to get his tracker id
4809 // failed to get his stats
4814 // we didn't get the stats
4818 // fire up the stats view popup
4819 void multi_pxo_pinfo_show()
4821 // initialize the popup
4822 multi_pxo_pinfo_init();
4826 game_set_frametime(GS_STATE_PXO);
4827 } while(!multi_pxo_pinfo_do());
4829 // close down the popup
4830 multi_pxo_pinfo_close();
4833 // build the stats labels values
4834 void multi_pxo_pinfo_build_vals()
4836 vmt_stats_struct *fs = &Multi_pxo_pinfo;
4838 SDL_zero(Multi_pxo_pinfo_vals);
4841 SDL_strlcpy(Multi_pxo_pinfo_vals[0], fs->pilot_name, SDL_arraysize(Multi_pxo_pinfo_vals[0]));
4842 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]));
4845 multi_sg_rank_build_name(Ranks[fs->rank].name, Multi_pxo_pinfo_vals[1], SDL_arraysize(Multi_pxo_pinfo_vals[1]));
4846 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]));
4849 SDL_snprintf(Multi_pxo_pinfo_vals[2], SDL_arraysize(Multi_pxo_pinfo_vals[2]), "%d", fs->kill_count);
4852 SDL_snprintf(Multi_pxo_pinfo_vals[3], SDL_arraysize(Multi_pxo_pinfo_vals[3]), "%d", fs->assists);
4855 SDL_snprintf(Multi_pxo_pinfo_vals[4], SDL_arraysize(Multi_pxo_pinfo_vals[4]), "%d", fs->kill_count - fs->kill_count_ok);
4858 SDL_snprintf(Multi_pxo_pinfo_vals[5], SDL_arraysize(Multi_pxo_pinfo_vals[5]), "%d", (int)fs->missions_flown);
4861 game_format_time(fl2f((float)fs->flight_time), Multi_pxo_pinfo_vals[6], SDL_arraysize(Multi_pxo_pinfo_vals[6]));
4864 if(fs->last_flown == 0){
4865 SDL_strlcpy(Multi_pxo_pinfo_vals[7], XSTR("No missions flown", 970), SDL_arraysize(Multi_pxo_pinfo_vals[7]));
4867 tm *tmr = gmtime((time_t*)&fs->last_flown);
4869 strftime(Multi_pxo_pinfo_vals[7],30,"%m/%d/%y %H:%M",tmr);
4871 SDL_strlcpy(Multi_pxo_pinfo_vals[7], "", SDL_arraysize(Multi_pxo_pinfo_vals[7]));
4875 // primary shots fired
4876 SDL_snprintf(Multi_pxo_pinfo_vals[8], SDL_arraysize(Multi_pxo_pinfo_vals[8]), "%d", (int)fs->p_shots_fired);
4878 // primary shots hit
4879 SDL_snprintf(Multi_pxo_pinfo_vals[9], SDL_arraysize(Multi_pxo_pinfo_vals[9]), "%d", (int)fs->p_shots_hit);
4882 if(fs->p_shots_fired > 0){
4883 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));
4885 SDL_strlcpy(Multi_pxo_pinfo_vals[10], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[10]));
4888 // secondary shots fired
4889 SDL_snprintf(Multi_pxo_pinfo_vals[11], SDL_arraysize(Multi_pxo_pinfo_vals[11]), "%d", (int)fs->s_shots_fired);
4891 // secondary shots hit
4892 SDL_snprintf(Multi_pxo_pinfo_vals[12], SDL_arraysize(Multi_pxo_pinfo_vals[12]), "%d", (int)fs->s_shots_hit);
4894 // secondary hit pct
4895 if(fs->s_shots_fired > 0){
4896 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));
4898 SDL_strlcpy(Multi_pxo_pinfo_vals[13], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[13]));
4901 // primary friendly hits
4902 SDL_snprintf(Multi_pxo_pinfo_vals[14], SDL_arraysize(Multi_pxo_pinfo_vals[14]), "%d", fs->p_bonehead_hits);
4904 // primary friendly hit %
4905 if(fs->p_shots_fired > 0){
4906 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)));
4908 SDL_strlcpy(Multi_pxo_pinfo_vals[15], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[15]));
4911 // secondary friendly hits
4912 SDL_snprintf(Multi_pxo_pinfo_vals[16], SDL_arraysize(Multi_pxo_pinfo_vals[16]), "%d", fs->s_bonehead_hits);
4914 // secondary friendly hit %
4915 if(fs->s_shots_fired > 0){
4916 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)));
4918 SDL_strlcpy(Multi_pxo_pinfo_vals[17], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[17]));
4922 // initialize the popup
4923 void multi_pxo_pinfo_init()
4927 // create the interface window
4928 Multi_pxo_pinfo_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4929 Multi_pxo_pinfo_window.set_mask_bmap(Multi_pxo_pinfo_mask_fname[gr_screen.res]);
4931 Multi_pxo_pinfo_bitmap = bm_load(Multi_pxo_pinfo_fname[gr_screen.res]);
4932 SDL_assert(Multi_pxo_pinfo_bitmap != -1);
4934 // create the interface buttons
4935 for(idx=0; idx<MULTI_PXO_PINFO_NUM_BUTTONS; idx++){
4936 // create the object
4937 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);
4939 // set the sound to play when highlighted
4940 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4942 // set the ani for the button
4943 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_pinfo_buttons[gr_screen.res][idx].filename);
4946 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_pinfo_buttons[gr_screen.res][idx].hotspot);
4951 for(idx=0; idx<MULTI_PXO_PINFO_NUM_TEXT; idx++){
4952 Multi_pxo_pinfo_window.add_XSTR(&Multi_pxo_pinfo_text[gr_screen.res][idx]);
4956 // set up the stats labels
4957 Multi_pxo_pinfo_stats_labels[0] = strdup(XSTR("Name", 1532));
4958 Multi_pxo_pinfo_stats_labels[1] = strdup(XSTR("Rank", 1533));
4959 Multi_pxo_pinfo_stats_labels[2] = strdup(XSTR("Kills", 1534));
4960 Multi_pxo_pinfo_stats_labels[3] = strdup(XSTR("Assists", 1535));
4961 Multi_pxo_pinfo_stats_labels[4] = strdup(XSTR("Friendly kills", 1536));
4962 Multi_pxo_pinfo_stats_labels[5] = strdup(XSTR("Missions flown", 1537));
4963 Multi_pxo_pinfo_stats_labels[6] = strdup(XSTR("Flight time", 1538));
4964 Multi_pxo_pinfo_stats_labels[7] = strdup(XSTR("Last flown", 1539));
4965 Multi_pxo_pinfo_stats_labels[8] = strdup(XSTR("Primary shots fired", 1540));
4966 Multi_pxo_pinfo_stats_labels[9] = strdup(XSTR("Primary shots hit", 1541));
4967 Multi_pxo_pinfo_stats_labels[10] = strdup(XSTR("Primary hit %", 1542));
4968 Multi_pxo_pinfo_stats_labels[11] = strdup(XSTR("Secondary shots fired", 1543));
4969 Multi_pxo_pinfo_stats_labels[12] = strdup(XSTR("Secondary shots hit", 1544));
4970 Multi_pxo_pinfo_stats_labels[13] = strdup(XSTR("Secondary hit %", 1545));
4971 Multi_pxo_pinfo_stats_labels[14] = strdup(XSTR("Primary friendly hits", 1546));
4972 Multi_pxo_pinfo_stats_labels[15] = strdup(XSTR("Primary friendly hit %", 1547));
4973 Multi_pxo_pinfo_stats_labels[16] = strdup(XSTR("Secondary friendly hits", 1548));
4974 Multi_pxo_pinfo_stats_labels[17] = strdup(XSTR("Secondary friendly hit %", 1549));
4976 // build the stats labels values
4977 multi_pxo_pinfo_build_vals();
4981 int multi_pxo_pinfo_do()
4983 int k = Multi_pxo_pinfo_window.process();
4985 // process common stuff
4986 multi_pxo_process_common();
4988 // run the networking functions for the PXO API
4989 multi_pxo_api_process();
4991 // check to see if he pressed escp
4992 if(k == SDLK_ESCAPE){
4996 // if he pressed the ok button
4997 if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_OK].button.pressed()){
5001 // if he pressed the medals buttons, run the medals screen
5002 if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_MEDALS].button.pressed()){
5004 game_feature_not_in_demo_popup();
5006 multi_pxo_run_medals();
5012 // blit everything on the "normal" screen
5013 multi_pxo_blit_all();
5015 // blit our own stuff
5017 gr_set_bitmap(Multi_pxo_pinfo_bitmap);
5023 Multi_pxo_pinfo_window.draw();
5025 // blit the stats themselves
5026 multi_pxo_pinfo_blit();
5036 void multi_pxo_pinfo_close()
5040 // destroy the UI_WINDOW
5041 Multi_pxo_pinfo_window.destroy();
5043 // unload the bitmap
5044 if(Multi_pxo_pinfo_bitmap != -1){
5045 bm_unload(Multi_pxo_pinfo_bitmap);
5048 // free the stats labels strings
5049 for (i=0; i<MULTI_PXO_PINFO_NUM_LABELS; i++) {
5050 free(Multi_pxo_pinfo_stats_labels[i]);
5054 // blit all the stats on this screen
5055 void multi_pxo_pinfo_blit()
5060 // blit all the labels
5061 y_start = Multi_pxo_pinfo_coords[gr_screen.res][1];
5062 for(idx=0; idx<MULTI_PXO_PINFO_NUM_LABELS; idx++){
5064 gr_set_color_fast(&Color_bright);
5065 gr_string(Multi_pxo_pinfo_coords[gr_screen.res][0], y_start, Multi_pxo_pinfo_stats_labels[idx]);
5067 // blit the label's value
5068 gr_set_color_fast(&Color_normal);
5069 gr_string(Multi_pxo_pinfo_val_x[gr_screen.res], y_start, Multi_pxo_pinfo_vals[idx]);
5072 y_start += Multi_pxo_pinfo_stats_spacing[idx];
5076 // run the medals screen
5077 void multi_pxo_run_medals()
5081 // process common stuff
5082 multi_pxo_process_common();
5084 // run the networking functions for the PXO API
5085 multi_pxo_api_process();
5087 // initialize the freespace data and the player struct
5088 multi_stats_tracker_to_fs(&Multi_pxo_pinfo, &Multi_pxo_pinfo_player.stats);
5089 SDL_strlcpy(Multi_pxo_pinfo_player.callsign, Multi_pxo_pinfo.pilot_name, SDL_arraysize(Multi_pxo_pinfo_player.callsign));
5091 // initialize the medals screen
5092 medal_main_init(&Multi_pxo_pinfo_player, MM_POPUP);
5094 // run the medals screen until it says that it should be closed
5096 // set frametime and run common functions
5097 game_set_frametime(-1);
5098 game_do_state_common(gameseq_get_state());
5100 // run the medals screen
5101 ret_code = medal_main_do();
5104 // close the medals screen down
5107 // reset the palette
5108 multi_pxo_load_palette();
5112 // notify stuff stuff -----------------------------------------
5114 // add a notification string
5115 void multi_pxo_notify_add(const char *txt)
5118 SDL_strlcpy(Multi_pxo_notify_text, txt, SDL_arraysize(Multi_pxo_notify_text));
5120 // set the timestamp
5121 Multi_pxo_notify_stamp = timestamp(MULTI_PXO_NOTIFY_TIME);
5124 // blit and process the notification string
5125 void multi_pxo_notify_blit()
5129 // if the timestamp is -1, do nothing
5130 if(Multi_pxo_notify_stamp == -1){
5134 // if it has expired, do nothing
5135 if(timestamp_elapsed(Multi_pxo_notify_stamp)){
5136 Multi_pxo_notify_stamp = -1;
5139 // otherwise blit the text
5140 gr_set_color_fast(&Color_bright);
5141 gr_get_string_size(&w,NULL,Multi_pxo_notify_text);
5142 gr_string((gr_screen.max_w - w)/2,MULTI_PXO_NOTIFY_Y,Multi_pxo_notify_text);
5146 // initialize the PXO help screen
5147 void multi_pxo_help_init()
5151 // load the background bitmap
5152 Multi_pxo_help_bitmap = bm_load(Multi_pxo_help_fname[gr_screen.res]);
5153 if(Multi_pxo_help_bitmap < 0){
5154 // we failed to load the bitmap - this is very bad
5157 // create the interface window
5158 Multi_pxo_help_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
5159 Multi_pxo_help_window.set_mask_bmap(Multi_pxo_help_mask_fname[gr_screen.res]);
5161 // create the interface buttons
5162 for(idx=0; idx<MULTI_PXO_HELP_NUM_BUTTONS; idx++){
5163 // create the object
5164 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);
5166 // set the sound to play when highlighted
5167 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
5169 // set the ani for the button
5170 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_help_buttons[gr_screen.res][idx].filename);
5173 Multi_pxo_help_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_help_buttons[gr_screen.res][idx].hotspot);
5178 for(idx=0; idx<MULTI_PXO_HELP_NUM_TEXT; idx++){
5179 Multi_pxo_help_window.add_XSTR(&Multi_pxo_help_text[gr_screen.res][idx]);
5183 // if we haven't already loaded in the text, do so
5184 // if(!Multi_pxo_help_loaded){
5185 multi_pxo_help_load();
5188 // set the current page to 0
5189 Multi_pxo_help_cur = 0;
5192 // do frame for PXO help
5193 void multi_pxo_help_do()
5196 if(Multi_pxo_connected){
5197 multi_pxo_api_process();
5200 // process common stuff
5201 multi_pxo_process_common();
5203 int k = Multi_pxo_help_window.process();
5205 // process any keypresses
5208 gamesnd_play_iface(SND_USER_SELECT);
5209 gameseq_post_event(GS_EVENT_PXO);
5213 // process button presses
5214 multi_pxo_help_process_buttons();
5216 // draw the background, etc
5218 GR_MAYBE_CLEAR_RES(Multi_pxo_help_bitmap);
5219 if(Multi_pxo_help_bitmap != -1){
5220 gr_set_bitmap(Multi_pxo_help_bitmap);
5223 Multi_pxo_help_window.draw();
5225 // blit the current page
5226 multi_pxo_help_blit_page();
5232 // close the pxo screen
5233 void multi_pxo_help_close()
5237 // unload any bitmaps
5238 bm_unload(Multi_pxo_help_bitmap);
5240 // destroy the UI_WINDOW
5241 Multi_pxo_help_window.destroy();
5244 for(idx=0; idx<Multi_pxo_help_num_pages; idx++){
5245 for(idx2=0; idx2<Multi_pxo_help_pages[idx].num_lines; idx2++){
5247 if(Multi_pxo_help_pages[idx].text[idx2] != NULL){
5248 free(Multi_pxo_help_pages[idx].text[idx2]);
5249 Multi_pxo_help_pages[idx].text[idx2] = NULL;
5255 // load the help file up
5256 void multi_pxo_help_load()
5261 // if its already loaded, do nothing
5262 // if(Multi_pxo_help_loaded){
5266 // read in the text file
5268 in = cfopen(MULTI_PXO_HELP_FILE,"rt",CFILE_NORMAL,CF_TYPE_DATA);
5269 SDL_assert(in != NULL);
5274 Multi_pxo_help_num_pages = 0;
5276 // blast all the help pages clear
5277 memset(Multi_pxo_help_pages, 0, sizeof(help_page) * MULTI_PXO_MAX_PAGES);
5278 Multi_pxo_help_num_pages = 0;
5279 cp = &Multi_pxo_help_pages[0];
5283 cp->text[cp->num_lines] = (char*)malloc(Multi_pxo_chars_per_line[gr_screen.res]);
5284 if(cp->text[cp->num_lines] == NULL){
5288 // read in the next line
5289 cfgets(cp->text[cp->num_lines++], Multi_pxo_chars_per_line[gr_screen.res], in);
5291 // skip to the next page if necessary
5292 if(cp->num_lines == Multi_pxo_lines_pp[gr_screen.res]){
5293 Multi_pxo_help_num_pages++;
5294 SDL_assert(Multi_pxo_help_num_pages < MULTI_PXO_MAX_PAGES);
5295 if(Multi_pxo_help_num_pages >= MULTI_PXO_MAX_PAGES){
5296 Multi_pxo_help_num_pages--;
5299 cp = &Multi_pxo_help_pages[Multi_pxo_help_num_pages];
5306 // mark the help as having been loaded
5307 // Multi_pxo_help_loaded = 1;
5310 // blit the current page
5311 void multi_pxo_help_blit_page()
5316 help_page *cp = &Multi_pxo_help_pages[Multi_pxo_help_cur];
5319 y_start = Multi_pxo_help_coords[gr_screen.res][1];
5320 for(idx=0;idx<cp->num_lines;idx++){
5321 // if the first symbol is "@", highlight the line
5322 if(cp->text[idx][0] == '@'){
5323 gr_set_color_fast(&Color_bright);
5326 gr_set_color_fast(&Color_normal);
5331 gr_string(Multi_pxo_help_coords[gr_screen.res][0], y_start, cp->text[idx] + start_pos);
5333 // increment the y location
5338 // process button presses
5339 void multi_pxo_help_process_buttons()
5343 // process all buttons
5344 for(idx=0;idx<MULTI_PXO_HELP_NUM_BUTTONS;idx++){
5345 if(Multi_pxo_help_buttons[gr_screen.res][idx].button.pressed()){
5346 multi_pxo_help_button_pressed(idx);
5353 void multi_pxo_help_button_pressed(int n)
5356 case MULTI_PXO_HELP_PREV:
5357 // if we're already at page 0, do nothing
5358 if(Multi_pxo_help_cur == 0){
5359 gamesnd_play_iface(SND_GENERAL_FAIL);
5361 Multi_pxo_help_cur--;
5362 gamesnd_play_iface(SND_USER_SELECT);
5366 case MULTI_PXO_HELP_NEXT:
5367 // if we're already at max pages, do nothing
5368 if(Multi_pxo_help_cur == Multi_pxo_help_num_pages){
5369 gamesnd_play_iface(SND_GENERAL_FAIL);
5371 Multi_pxo_help_cur++;
5372 gamesnd_play_iface(SND_USER_SELECT);
5376 case MULTI_PXO_HELP_CONTINUE:
5377 gamesnd_play_iface(SND_USER_SELECT);
5378 gameseq_post_event(GS_EVENT_PXO);
5383 // http banner stuff ---------------------------------------------
5386 void multi_pxo_ban_init()
5388 // zero the active banner bitmap
5389 Multi_pxo_banner.ban_bitmap = -1;
5391 // are we doing banners at all?
5392 if(os_config_read_uint(NULL, "PXOBanners", 1)){
5393 // if we're already in idle mode, we're done downloading for this instance of freespace. pick a random image we already have
5394 if(Multi_pxo_ban_mode == PXO_BAN_MODE_IDLE){
5395 Multi_pxo_ban_mode = PXO_BAN_MODE_CHOOSE_RANDOM;
5399 // set ourselves to startup mode
5400 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
5401 Multi_pxo_ban_get = NULL;
5403 // set ourselves to idle mode
5404 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5405 Multi_pxo_ban_get = NULL;
5408 // zero the active banner bitmap
5409 SDL_zero(Multi_pxo_banner);
5410 Multi_pxo_banner.ban_bitmap = -1;
5413 // process http download details
5414 void multi_pxo_ban_process()
5416 char url_string[512] = "";
5417 char local_file[MAX_PATH_LEN] = "";
5420 switch(Multi_pxo_ban_mode){
5421 // start downloading list
5422 case PXO_BAN_MODE_LIST_STARTUP:
5424 SDL_snprintf(url_string, SDL_arraysize(url_string), "%s/%s", Multi_options_g.pxo_banner_url, PXO_BANNERS_CONFIG_FILE);
5427 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, PXO_BANNERS_CONFIG_FILE);
5429 // try creating the file get object
5430 Multi_pxo_ban_get = new InetGetFile(url_string, local_file);
5433 if(Multi_pxo_ban_get == NULL){
5434 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5437 // go to the downloading list mode
5438 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST;
5442 case PXO_BAN_MODE_LIST:
5444 if(Multi_pxo_ban_get->IsFileError()){
5445 delete Multi_pxo_ban_get;
5446 Multi_pxo_ban_get = NULL;
5447 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5451 // connecting, receiving
5452 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5457 if(Multi_pxo_ban_get->IsFileReceived()){
5458 delete Multi_pxo_ban_get;
5459 Multi_pxo_ban_get = NULL;
5460 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_STARTUP;
5464 // start downloading files
5465 case PXO_BAN_MODE_IMAGES_STARTUP:
5466 // first thing - parse the banners file and pick a file
5467 multi_pxo_ban_parse_banner_file(0);
5469 // if we have no active file, we're done
5470 if((strlen(Multi_pxo_banner.ban_file) <= 0) || (strlen(Multi_pxo_banner.ban_file_url) <= 0)){
5471 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5475 // if the file already exists, we're done
5476 if(cf_exist(Multi_pxo_banner.ban_file, CF_TYPE_MULTI_CACHE)){
5477 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5481 // otherwise try and download it
5482 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, Multi_pxo_banner.ban_file);
5483 // try creating the file get object
5484 Multi_pxo_ban_get = new InetGetFile(Multi_pxo_banner.ban_file_url, local_file);
5487 if(Multi_pxo_ban_get == NULL){
5488 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5491 // go to the downloading images mode
5492 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES;
5495 // downloading files
5496 case PXO_BAN_MODE_IMAGES:
5498 if(Multi_pxo_ban_get->IsFileError()){
5499 delete Multi_pxo_ban_get;
5500 Multi_pxo_ban_get = NULL;
5501 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5505 // connecting, receiving
5506 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5511 if(Multi_pxo_ban_get->IsFileReceived()){
5512 delete Multi_pxo_ban_get;
5513 Multi_pxo_ban_get = NULL;
5514 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5518 // done downloading - maybe load an image
5519 case PXO_BAN_MODE_IMAGES_DONE:
5520 // make sure we have a valid filename
5521 // SDL_assert(strlen(Multi_pxo_banner.ban_file) > 0);
5522 if(strlen(Multi_pxo_banner.ban_file) > 0){
5523 Multi_pxo_banner.ban_bitmap = bm_load(Multi_pxo_banner.ban_file);
5527 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5530 // idle (done with EVERYTHING)
5531 case PXO_BAN_MODE_IDLE:
5532 // if the banner button was clicked
5533 if(Multi_pxo_ban_button.pressed()){
5534 multi_pxo_ban_clicked();
5538 case PXO_BAN_MODE_CHOOSE_RANDOM:
5539 // first thing - parse the banners file and pick a file
5540 multi_pxo_ban_parse_banner_file(1);
5542 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5548 void multi_pxo_ban_close()
5550 // if we have a currently active transfer
5551 if(Multi_pxo_ban_get != NULL){
5552 Multi_pxo_ban_get->AbortGet();
5553 delete Multi_pxo_ban_get;
5554 Multi_pxo_ban_get = NULL;
5557 // if we have a loaded bitmap, unload it
5558 if(Multi_pxo_banner.ban_bitmap != -1){
5559 bm_unload(Multi_pxo_banner.ban_bitmap);
5560 Multi_pxo_banner.ban_bitmap = -1;
5564 // parse the banners file and maybe fill in Multi_pxo_dl_file
5565 void multi_pxo_ban_parse_banner_file(int choose_existing)
5567 char file_url[MAX_PATH_LEN] = "";
5568 char banners[10][MAX_PATH_LEN];
5569 char urls[10][MAX_PATH_LEN];
5572 int num_banners, idx;
5573 CFILE *in = cfopen(PXO_BANNERS_CONFIG_FILE, "rt", CFILE_NORMAL, CF_TYPE_MULTI_CACHE);
5575 SDL_zero(Multi_pxo_banner);
5576 Multi_pxo_banner.ban_bitmap = -1;
5583 // clear all strings
5587 // get the global banner url
5588 if(cfgets(file_url, SDL_arraysize(file_url), in) == NULL){
5590 cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5593 drop_leading_white_space(file_url);
5594 drop_trailing_white_space(file_url);
5596 // otherwise read in
5598 while(num_banners < 10){
5599 // try and get the pcx
5600 if(cfgets(banners[num_banners], SDL_arraysize(banners[0]), in) == NULL){
5603 // try and get the url
5604 if(cfgets(urls[num_banners], SDL_arraysize(urls[0]), in) == NULL){
5608 // strip off trailing and leading whitespace
5609 drop_leading_white_space(banners[num_banners]);
5610 drop_trailing_white_space(banners[num_banners]);
5611 drop_leading_white_space(urls[num_banners]);
5612 drop_trailing_white_space(urls[num_banners]);
5622 if(num_banners <= 0){
5626 // if we're only selecting files which already exist (previously downloaded)
5627 if(choose_existing){
5629 for(idx=0; idx<10; idx++){
5633 // build a list of existing files
5635 for(idx=0; idx<num_banners; idx++){
5636 if(cf_exist(banners[idx], CF_TYPE_MULTI_CACHE)){
5643 if(exist_count <= 0){
5648 int select = (int)frand_range(0.0f, (float)exist_count);
5649 if(select >= exist_count){
5650 select = exist_count - 1;
5655 for(idx=0; idx<exist_count; idx++){
5665 if(idx < exist_count){
5667 SDL_strlcpy(Multi_pxo_banner.ban_file, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file));
5669 // get the full file url
5670 SDL_strlcpy(Multi_pxo_banner.ban_file_url, file_url, SDL_arraysize(Multi_pxo_banner.ban_file_url));
5671 SDL_strlcat(Multi_pxo_banner.ban_file_url, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file_url));
5673 // url of where to go to when clicked
5674 SDL_strlcpy(Multi_pxo_banner.ban_url, urls[idx], SDL_arraysize(Multi_pxo_banner.ban_url));
5677 // randomly pick a file for download
5679 idx = (int)frand_range(0.0f, (float)num_banners);
5681 if(idx >= num_banners){
5682 idx = num_banners - 1;
5689 SDL_strlcpy(Multi_pxo_banner.ban_file, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file));
5691 // get the full file url
5692 SDL_strlcpy(Multi_pxo_banner.ban_file_url, file_url, SDL_arraysize(Multi_pxo_banner.ban_file_url));
5693 SDL_strlcat(Multi_pxo_banner.ban_file_url, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file_url));
5695 // url of where to go to when clicked
5696 SDL_strlcpy(Multi_pxo_banner.ban_url, urls[idx], SDL_arraysize(Multi_pxo_banner.ban_url));
5699 // delete the banner config file
5700 // cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5703 // any bitmap or info or whatever
5704 void multi_pxo_ban_draw()
5706 // if we have a valid bitmap
5707 if(Multi_pxo_banner.ban_bitmap >= 0){
5708 // if the mouse is over the banner button, highlight with a rectangle
5709 if(Multi_pxo_ban_button.is_mouse_on()){
5710 gr_set_color_fast(&Color_bright_blue);
5711 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);
5714 // draw the bitmap itself
5715 gr_set_bitmap(Multi_pxo_banner.ban_bitmap);
5716 gr_bitmap(Pxo_ban_coords[gr_screen.res][0], Pxo_ban_coords[gr_screen.res][1]);
5720 // called when the URL button is clicked
5721 void multi_pxo_ban_clicked()
5723 // if we have a valid bitmap and URL, launch the URL
5724 if((Multi_pxo_banner.ban_bitmap >= 0) && (strlen(Multi_pxo_banner.ban_url) > 0)){
5725 multi_pxo_url(Multi_pxo_banner.ban_url);